diff --git a/.ci/metrics/metrics.py b/.ci/metrics/metrics.py index bd2b51154768d..e1712bd015151 100644 --- a/.ci/metrics/metrics.py +++ b/.ci/metrics/metrics.py @@ -1,21 +1,57 @@ -import requests -import time +import collections +import datetime +import github +import logging import os -from dataclasses import dataclass +import requests import sys -import logging +import time -import github -from github import Github +from dataclasses import dataclass from github import Auth +from github import Github GRAFANA_URL = ( "https://influx-prod-13-prod-us-east-0.grafana.net/api/v1/push/influx/write" ) -GITHUB_PROJECT = "llvm/llvm-project" -WORKFLOWS_TO_TRACK = ["LLVM Premerge Checks"] SCRAPE_INTERVAL_SECONDS = 5 * 60 +# Lists the Github workflows we want to track. Maps the Github job name to +# the metric name prefix in grafana. +# This metric name is also used as a key in the job->name map. +GITHUB_WORKFLOW_TO_TRACK = {"LLVM Premerge Checks": "github_llvm_premerge_checks"} + +# Lists the Github jobs to track for a given workflow. The key is the stable +# name (metric name) of the workflow (see GITHUB_WORKFLOW_TO_TRACK). +# Each value is a map to link the github job name to the corresponding metric +# name. +GITHUB_JOB_TO_TRACK = { + "github_llvm_premerge_checks": { + "Linux Premerge Checks (Test Only - Please Ignore Results)": "premerge_linux", + "Windows Premerge Checks (Test Only - Please Ignore Results)": "premerge_windows", + } +} + +# The number of workflows to pull when sampling Github workflows. +# - Github API filtering is broken: we cannot apply any filtering: +# - See https://github.com/orgs/community/discussions/86766 +# - A workflow can complete before another workflow, even when starting later. +# - We don't want to sample the same workflow twice. +# +# This means we essentially have a list of workflows sorted by creation date, +# and that's all we can deduce from it. So for each iteration, we'll blindly +# process the last N workflows. +GITHUB_WORKFLOWS_MAX_PROCESS_COUNT = 1000 +# Second reason for the cut: reaching a workflow older than X. +# This means we will miss long-tails (exceptional jobs running for more than +# X hours), but that's also the case with the count cutoff above. +# Only solution to avoid missing any workflow would be to process the complete +# list, which is not possible. +GITHUB_WORKFLOW_MAX_CREATED_AGE_HOURS = 8 + +# Grafana will fail to insert any metric older than ~2 hours (value determined +# by trial and error). +GRAFANA_METRIC_MAX_AGE_MN = 120 @dataclass class JobMetrics: @@ -23,11 +59,10 @@ class JobMetrics: queue_time: int run_time: int status: int - created_at_ns: int + completed_at_ns: int workflow_id: int workflow_name: str - @dataclass class GaugeMetric: name: str @@ -35,141 +70,88 @@ class GaugeMetric: time_ns: int -def get_sampled_workflow_metrics(github_repo: github.Repository): - """Gets global statistics about the Github workflow queue - - Args: - github_repo: A github repo object to use to query the relevant information. - - Returns: - Returns a list of GaugeMetric objects, containing the relevant metrics about - the workflow - """ - queued_job_counts = {} - running_job_counts = {} - - # Other states are available (pending, waiting, etc), but the meaning - # is not documented (See #70540). - # "queued" seems to be the info we want. - for queued_workflow in github_repo.get_workflow_runs(status="queued"): - if queued_workflow.name not in WORKFLOWS_TO_TRACK: - continue - for queued_workflow_job in queued_workflow.jobs(): - job_name = queued_workflow_job.name - # Workflows marked as queued can potentially only have some jobs - # queued, so make sure to also count jobs currently in progress. - if queued_workflow_job.status == "queued": - if job_name not in queued_job_counts: - queued_job_counts[job_name] = 1 - else: - queued_job_counts[job_name] += 1 - elif queued_workflow_job.status == "in_progress": - if job_name not in running_job_counts: - running_job_counts[job_name] = 1 - else: - running_job_counts[job_name] += 1 - - for running_workflow in github_repo.get_workflow_runs(status="in_progress"): - if running_workflow.name not in WORKFLOWS_TO_TRACK: - continue - for running_workflow_job in running_workflow.jobs(): - job_name = running_workflow_job.name - if running_workflow_job.status != "in_progress": - continue - - if job_name not in running_job_counts: - running_job_counts[job_name] = 1 - else: - running_job_counts[job_name] += 1 - - workflow_metrics = [] - for queued_job in queued_job_counts: - workflow_metrics.append( - GaugeMetric( - f"workflow_queue_size_{queued_job}", - queued_job_counts[queued_job], - time.time_ns(), - ) - ) - for running_job in running_job_counts: - workflow_metrics.append( - GaugeMetric( - f"running_workflow_count_{running_job}", - running_job_counts[running_job], - time.time_ns(), - ) - ) - # Always send a hearbeat metric so we can monitor is this container is still able to log to Grafana. - workflow_metrics.append( - GaugeMetric("metrics_container_heartbeat", 1, time.time_ns()) - ) - return workflow_metrics - - -def get_per_workflow_metrics( - github_repo: github.Repository, workflows_to_track: dict[str, int] -): +def github_get_metrics( + github_repo: github.Repository, last_workflows_seen_as_completed: set[int] +) -> tuple[list[JobMetrics], int]: """Gets the metrics for specified Github workflows. This function takes in a list of workflows to track, and optionally the workflow ID of the last tracked invocation. It grabs the relevant data from Github, returning it to the caller. + If the last_seen_workflow parameter is None, this returns no metrics, but + returns the id of the most recent workflow. Args: github_repo: A github repo object to use to query the relevant information. - workflows_to_track: A dictionary mapping workflow names to the last - invocation ID where metrics have been collected, or None to collect the - last five results. + last_seen_workflow: the last workflow this function processed. Returns: - Returns a list of JobMetrics objects, containing the relevant metrics about - the workflow. + Returns a tuple with 2 elements: + - a list of JobMetrics objects, one per processed job. + - the ID of the most recent processed workflow run. """ workflow_metrics = [] - - workflows_to_include = set(workflows_to_track.keys()) - - for workflow_run in iter(github_repo.get_workflow_runs()): - if len(workflows_to_include) == 0: + queued_count = collections.Counter() + running_count = collections.Counter() + + # The list of workflows this iteration will process. + # MaxSize = GITHUB_WORKFLOWS_MAX_PROCESS_COUNT + workflow_seen_as_completed = set() + + # Since we process a fixed count of workflows, we want to know when + # the depth is too small and if we miss workflows. + # E.g.: is there was more than N workflows int last 2 hours. + # To monitor this, we'll log the age of the oldest workflow processed, + # and setup alterting in Grafana to help us adjust this depth. + oldest_seen_workflow_age_mn = None + + # Do not apply any filters to this query. + # See https://github.com/orgs/community/discussions/86766 + # Applying filters like `status=completed` will break pagination, and + # return a non-sorted and incomplete list of workflows. + i = 0 + for task in iter(github_repo.get_workflow_runs()): + # Max depth reached, stopping. + if i >= GITHUB_WORKFLOWS_MAX_PROCESS_COUNT: + break + i += 1 + + workflow_age_mn = ( + datetime.datetime.now(datetime.timezone.utc) - task.created_at + ).total_seconds() / 60 + oldest_seen_workflow_age_mn = workflow_age_mn + # If we reach a workflow older than X, stop. + if workflow_age_mn > GITHUB_WORKFLOW_MAX_CREATED_AGE_HOURS * 60: break - if workflow_run.status != "completed": + # This workflow is not interesting to us. + if task.name not in GITHUB_WORKFLOW_TO_TRACK: continue - # This workflow was already sampled for this run, or is not tracked at - # all. Ignoring. - if workflow_run.name not in workflows_to_include: - continue + if task.status == "completed": + workflow_seen_as_completed.add(task.id) - # There were no new workflow invocations since the previous scrape. - # The API returns a sorted list with the most recent invocations first, - # so we can stop looking for this particular workflow. Continue to grab - # information on the other workflows of interest, if present. - if workflows_to_track[workflow_run.name] == workflow_run.id: - workflows_to_include.remove(workflow_run.name) + # This workflow has already been seen completed in the previous run. + if task.id in last_workflows_seen_as_completed: continue - workflow_jobs = workflow_run.jobs() - if workflow_jobs.totalCount == 0: - continue + name_prefix = GITHUB_WORKFLOW_TO_TRACK[task.name] + for job in task.jobs(): + # This job is not interesting to us. + if job.name not in GITHUB_JOB_TO_TRACK[name_prefix]: + continue - if ( - workflows_to_track[workflow_run.name] is None - or workflows_to_track[workflow_run.name] == workflow_run.id - ): - workflows_to_include.remove(workflow_run.name) - if ( - workflows_to_track[workflow_run.name] is not None - and len(workflows_to_include) == 0 - ): - break + name_suffix = GITHUB_JOB_TO_TRACK[name_prefix][job.name] + metric_name = name_prefix + "_" + name_suffix - for workflow_job in workflow_jobs: - created_at = workflow_job.created_at - started_at = workflow_job.started_at - completed_at = workflow_job.completed_at + if task.status != "completed": + if job.status == "queued": + queued_count[metric_name] += 1 + elif job.status == "in_progress": + running_count[metric_name] += 1 + continue - job_result = int(workflow_job.conclusion == "success") + job_result = int(job.conclusion == "success") if job_result: # We still might want to mark the job as a failure if one of the steps # failed. This is required due to use setting continue-on-error in @@ -178,38 +160,72 @@ def get_per_workflow_metrics( # TODO(boomanaiden154): Remove this once the premerge pipeline is no # longer in a testing state and we can directly assert the workflow # result. - for step in workflow_job.steps: + for step in job.steps: if step.conclusion != "success" and step.conclusion != "skipped": job_result = 0 break + created_at = job.created_at + started_at = job.started_at + completed_at = job.completed_at queue_time = started_at - created_at run_time = completed_at - started_at - if run_time.seconds == 0: continue + # Grafana will refuse to ingest metrics older than ~2 hours, so we + # should avoid sending historical data. + metric_age_mn = ( + datetime.datetime.now(datetime.timezone.utc) - completed_at + ).total_seconds() / 60 + if metric_age_mn > GRAFANA_METRIC_MAX_AGE_MN: + logging.info( + f"Job {job.id} from workflow {task.id} dropped due" + + f" to staleness: {metric_age_mn}mn old." + ) + continue + + logging.info(f"Adding a job metric for job {job.id} in workflow {task.id}") # The timestamp associated with the event is expected by Grafana to be # in nanoseconds. - created_at_ns = int(created_at.timestamp()) * 10**9 - - logging.info( - f"Adding a job metric for job {workflow_job.id} in workflow {workflow_run.id}" - ) - + completed_at_ns = int(completed_at.timestamp()) * 10**9 workflow_metrics.append( JobMetrics( - workflow_run.name + "-" + workflow_job.name, + metric_name, queue_time.seconds, run_time.seconds, job_result, - created_at_ns, - workflow_run.id, - workflow_run.name, + completed_at_ns, + task.id, + task.name, ) ) - return workflow_metrics + for name, value in queued_count.items(): + workflow_metrics.append( + GaugeMetric(f"workflow_queue_size_{name}", value, time.time_ns()) + ) + for name, value in running_count.items(): + workflow_metrics.append( + GaugeMetric(f"running_workflow_count_{name}", value, time.time_ns()) + ) + + # Always send a hearbeat metric so we can monitor is this container is still able to log to Grafana. + workflow_metrics.append( + GaugeMetric("metrics_container_heartbeat", 1, time.time_ns()) + ) + + # Log the oldest workflow we saw, allowing us to monitor if the processing + # depth is correctly set-up. + if oldest_seen_workflow_age_mn is not None: + workflow_metrics.append( + GaugeMetric( + "github_oldest_processed_workflow_mn", + oldest_seen_workflow_age_mn, + time.time_ns(), + ) + ) + return workflow_metrics, workflow_seen_as_completed def upload_metrics(workflow_metrics, metrics_userid, api_key): @@ -238,7 +254,7 @@ def upload_metrics(workflow_metrics, metrics_userid, api_key): elif isinstance(workflow_metric, JobMetrics): name = workflow_metric.job_name.lower().replace(" ", "_") metrics_batch.append( - f"{name} queue_time={workflow_metric.queue_time},run_time={workflow_metric.run_time},status={workflow_metric.status} {workflow_metric.created_at_ns}" + f"{name} queue_time={workflow_metric.queue_time},run_time={workflow_metric.run_time},status={workflow_metric.status} {workflow_metric.completed_at_ns}" ) else: raise ValueError( @@ -259,32 +275,26 @@ def upload_metrics(workflow_metrics, metrics_userid, api_key): def main(): # Authenticate with Github - auth = Auth.Token(os.environ["GITHUB_TOKEN"]) - + github_auth = Auth.Token(os.environ["GITHUB_TOKEN"]) grafana_api_key = os.environ["GRAFANA_API_KEY"] grafana_metrics_userid = os.environ["GRAFANA_METRICS_USERID"] - workflows_to_track = {} - for workflow_to_track in WORKFLOWS_TO_TRACK: - workflows_to_track[workflow_to_track] = None + # The last workflow this script processed. + # Because the Github queries are broken, we'll simply log a 'processed' + # bit for the last COUNT_TO_PROCESS workflows. + gh_last_workflows_seen_as_completed = set() # 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_object = Github(auth=github_auth) github_repo = github_object.get_repo("llvm/llvm-project") - current_metrics = get_per_workflow_metrics(github_repo, workflows_to_track) - current_metrics += get_sampled_workflow_metrics(github_repo) - - upload_metrics(current_metrics, grafana_metrics_userid, grafana_api_key) - logging.info(f"Uploaded {len(current_metrics)} metrics") - - for workflow_metric in reversed(current_metrics): - if isinstance(workflow_metric, JobMetrics): - workflows_to_track[ - workflow_metric.workflow_name - ] = workflow_metric.workflow_id + metrics, gh_last_workflows_seen_as_completed = github_get_metrics( + github_repo, gh_last_workflows_seen_as_completed + ) + upload_metrics(metrics, grafana_metrics_userid, grafana_api_key) + logging.info(f"Uploaded {len(metrics)} metrics") time.sleep(SCRAPE_INTERVAL_SECONDS) diff --git a/.github/workflows/issue-write.yml b/.github/workflows/issue-write.yml index f5c60f51ca213..ad17d9a33ddb7 100644 --- a/.github/workflows/issue-write.yml +++ b/.github/workflows/issue-write.yml @@ -39,7 +39,7 @@ jobs: - name: 'Comment on PR' if: steps.download-artifact.outputs.artifact-id != '' - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + uses: actions/github-script@ffc2c79a5b2490bd33e0a41c1de74b877714d736 # v3.2.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/libclang-python-tests.yml b/.github/workflows/libclang-python-tests.yml index d8f58c5b8d1ce..cbfb3e5cbabfe 100644 --- a/.github/workflows/libclang-python-tests.yml +++ b/.github/workflows/libclang-python-tests.yml @@ -30,7 +30,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.8", "3.11"] + python-version: ["3.8", "3.13"] uses: ./.github/workflows/llvm-project-tests.yml with: build_target: check-clang-python diff --git a/.github/workflows/libcxx-build-and-test.yaml b/.github/workflows/libcxx-build-and-test.yaml index 5d4394435890a..8c011425abfcf 100644 --- a/.github/workflows/libcxx-build-and-test.yaml +++ b/.github/workflows/libcxx-build-and-test.yaml @@ -55,7 +55,7 @@ jobs: cc: 'gcc-14' cxx: 'g++-14' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: ${{ matrix.config }}.${{ matrix.cxx }} run: libcxx/utils/ci/run-buildbot ${{ matrix.config }} env: @@ -105,7 +105,7 @@ jobs: cc: 'clang-18' cxx: 'clang++-18' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: ${{ matrix.config }} run: libcxx/utils/ci/run-buildbot ${{ matrix.config }} env: @@ -169,7 +169,7 @@ jobs: runs-on: ${{ matrix.machine }} container: ghcr.io/llvm/libcxx-linux-builder:b319dfef21f6c7b0bc6a356d6b9f41a3b3b98ae9 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: ${{ matrix.config }} run: libcxx/utils/ci/run-buildbot ${{ matrix.config }} env: @@ -207,11 +207,11 @@ jobs: os: macos-13 runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 - - uses: maxim-lobanov/setup-xcode@v1 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0 with: xcode-version: 'latest' - - uses: seanmiddleditch/gha-setup-ninja@master + - uses: seanmiddleditch/gha-setup-ninja@3b1f8f94a2f8254bd26914c4ab9474d4f0015f67 # v6 - name: Build and test run: | python3 -m venv .venv @@ -247,7 +247,7 @@ jobs: - { config: mingw-dll-i686, mingw: true } - { config: mingw-incomplete-sysroot, mingw: true } steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Install dependencies run: | choco install -y ninja @@ -275,7 +275,7 @@ jobs: echo "c:\Program Files\Git\usr\bin" | Out-File -FilePath $Env:GITHUB_PATH -Encoding utf8 -Append - name: Set up the MSVC dev environment if: ${{ matrix.mingw != true }} - uses: ilammy/msvc-dev-cmd@v1 + uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0 - name: Build and test run: | bash libcxx/utils/ci/run-buildbot ${{ matrix.config }} diff --git a/.github/workflows/libcxx-build-containers.yml b/.github/workflows/libcxx-build-containers.yml index b499af943c8e7..564a79341edb1 100644 --- a/.github/workflows/libcxx-build-containers.yml +++ b/.github/workflows/libcxx-build-containers.yml @@ -32,7 +32,7 @@ jobs: packages: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Build the Linux builder image working-directory: libcxx/utils/ci @@ -47,7 +47,7 @@ jobs: # TAG: ${{ github.sha }} - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 with: registry: ghcr.io username: ${{ github.actor }} diff --git a/.github/workflows/libcxx-check-generated-files.yml b/.github/workflows/libcxx-check-generated-files.yml index 1f8103a114b9f..0226edd7aa17a 100644 --- a/.github/workflows/libcxx-check-generated-files.yml +++ b/.github/workflows/libcxx-check-generated-files.yml @@ -12,10 +12,10 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Fetch LLVM sources - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Install dependencies - uses: aminya/setup-cpp@v1 + uses: aminya/setup-cpp@17c11551771948abc5752bbf3183482567c7caf0 # v1.1.1 with: clangformat: 17.0.1 ninja: true diff --git a/.github/workflows/release-asset-audit.py b/.github/workflows/release-asset-audit.py index 0ff1ce81b28dd..23b901a476dc0 100644 --- a/.github/workflows/release-asset-audit.py +++ b/.github/workflows/release-asset-audit.py @@ -24,7 +24,7 @@ def _get_uploaders(release_version): return set( [ "DimitryAndric", - "stefanp-ibm", + "stefanp-synopsys", "lei137", "omjavaid", "nicolerabjohn", diff --git a/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.cpp index 55ca4809f058a..cda9288c0531a 100644 --- a/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.cpp @@ -12,6 +12,7 @@ #include "../utils/OptionsUtils.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" #include using namespace clang::ast_matchers; @@ -27,27 +28,11 @@ AST_MATCHER_P(QualType, hasCleanType, Matcher, InnerMatcher) { Finder, Builder); } -constexpr std::array NameList{ +constexpr std::array MakeSmartPtrList{ "::std::make_unique", "::std::make_shared", }; - -Matcher constructFrom(Matcher TypeMatcher, - Matcher ArgumentMatcher) { - return expr( - anyOf( - // construct optional - cxxConstructExpr(argumentCountIs(1U), hasType(TypeMatcher), - hasArgument(0U, ArgumentMatcher)), - // known template methods in std - callExpr(argumentCountIs(1), - callee(functionDecl( - matchers::matchesAnyListedName(NameList), - hasTemplateArgument(0, refersToType(TypeMatcher)))), - hasArgument(0, ArgumentMatcher))), - unless(anyOf(hasAncestor(typeLoc()), - hasAncestor(expr(matchers::hasUnevaluatedContext()))))); -} +constexpr StringRef MakeOptional = "::std::make_optional"; } // namespace @@ -74,7 +59,7 @@ void OptionalValueConversionCheck::registerMatchers(MatchFinder *Finder) { auto EqualsBoundOptionalType = qualType(hasCleanType(equalsBoundNode("optional-type"))); - auto OptionalDereferenceMatcher = callExpr( + auto OptionalDerefMatcherImpl = callExpr( anyOf( cxxOperatorCallExpr(hasOverloadedOperatorName("*"), hasUnaryOperand(hasType(EqualsBoundOptionalType))) @@ -88,11 +73,41 @@ void OptionalValueConversionCheck::registerMatchers(MatchFinder *Finder) { auto StdMoveCallMatcher = callExpr(argumentCountIs(1), callee(functionDecl(hasName("::std::move"))), - hasArgument(0, ignoringImpCasts(OptionalDereferenceMatcher))); + hasArgument(0, ignoringImpCasts(OptionalDerefMatcherImpl))); + auto OptionalDerefMatcher = + ignoringImpCasts(anyOf(OptionalDerefMatcherImpl, StdMoveCallMatcher)); + Finder->addMatcher( - expr(constructFrom(BindOptionalType, - ignoringImpCasts(anyOf(OptionalDereferenceMatcher, - StdMoveCallMatcher)))) + expr(anyOf( + // construct optional + cxxConstructExpr(argumentCountIs(1), hasType(BindOptionalType), + hasArgument(0, OptionalDerefMatcher)), + // known template methods in std + callExpr( + argumentCountIs(1), + anyOf( + // match std::make_unique std::make_shared + callee(functionDecl( + matchers::matchesAnyListedName(MakeSmartPtrList), + hasTemplateArgument( + 0, refersToType(BindOptionalType)))), + // match first std::make_optional by limit argument count + // (1) and template count (1). + // 1. template< class T > constexpr + // std::optional> make_optional(T&& value); + // 2. template< class T, class... Args > constexpr + // std::optional make_optional(Args&&... args); + callee(functionDecl(templateArgumentCountIs(1), + hasName(MakeOptional), + returns(BindOptionalType)))), + hasArgument(0, OptionalDerefMatcher)), + callExpr( + + argumentCountIs(1), + + hasArgument(0, OptionalDerefMatcher))), + unless(anyOf(hasAncestor(typeLoc()), + hasAncestor(expr(matchers::hasUnevaluatedContext()))))) .bind("expr"), this); } diff --git a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp index 6e412e576e5f9..dbe59233df699 100644 --- a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp @@ -136,16 +136,14 @@ void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) { return; VariableCategory VC = VariableCategory::Value; - if (Variable->getType()->isReferenceType()) + const QualType VT = Variable->getType(); + if (VT->isReferenceType()) VC = VariableCategory::Reference; - if (Variable->getType()->isPointerType()) + else if (VT->isPointerType()) VC = VariableCategory::Pointer; - if (Variable->getType()->isArrayType()) { - if (const auto *ArrayT = dyn_cast(Variable->getType())) { - if (ArrayT->getElementType()->isPointerType()) - VC = VariableCategory::Pointer; - } - } + else if (const auto *ArrayT = dyn_cast(VT)) + if (ArrayT->getElementType()->isPointerType()) + VC = VariableCategory::Pointer; // Each variable can only be in one category: Value, Pointer, Reference. // Analysis can be controlled for every category. diff --git a/clang-tools-extra/clang-tidy/misc/UseInternalLinkageCheck.cpp b/clang-tools-extra/clang-tidy/misc/UseInternalLinkageCheck.cpp index 4778182944abd..a1a20c0782230 100644 --- a/clang-tools-extra/clang-tidy/misc/UseInternalLinkageCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/UseInternalLinkageCheck.cpp @@ -52,7 +52,7 @@ AST_MATCHER(FunctionDecl, hasBody) { return Node.hasBody(); } static bool isInMainFile(SourceLocation L, SourceManager &SM, const FileExtensionsSet &HeaderFileExtensions) { for (;;) { - if (utils::isSpellingLocInHeaderFile(L, SM, HeaderFileExtensions)) + if (utils::isExpansionLocInHeaderFile(L, SM, HeaderFileExtensions)) return false; if (SM.isInMainFile(L)) return true; diff --git a/clang-tools-extra/clang-tidy/modernize/UseDefaultMemberInitCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseDefaultMemberInitCheck.cpp index 6c06b0af342f6..5b0b9b59d4e3b 100644 --- a/clang-tools-extra/clang-tidy/modernize/UseDefaultMemberInitCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseDefaultMemberInitCheck.cpp @@ -194,6 +194,8 @@ void UseDefaultMemberInitCheck::storeOptions( } void UseDefaultMemberInitCheck::registerMatchers(MatchFinder *Finder) { + auto ConstExpRef = varDecl(anyOf(isConstexpr(), isStaticStorageClass())); + auto InitBase = anyOf(stringLiteral(), characterLiteral(), integerLiteral(), unaryOperator(hasAnyOperatorName("+", "-"), @@ -202,7 +204,7 @@ void UseDefaultMemberInitCheck::registerMatchers(MatchFinder *Finder) { unaryOperator(hasAnyOperatorName("+", "-"), hasUnaryOperand(floatLiteral())), cxxBoolLiteral(), cxxNullPtrLiteralExpr(), implicitValueInitExpr(), - declRefExpr(to(enumConstantDecl()))); + declRefExpr(to(anyOf(enumConstantDecl(), ConstExpRef)))); auto Init = anyOf(initListExpr(anyOf(allOf(initCountIs(1), hasInit(0, InitBase)), diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.cpp index 1548fc454cfb3..38ef7712aa4ef 100644 --- a/clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.cpp @@ -91,8 +91,11 @@ struct MatchBuilder { auto matchMathCall(const StringRef FunctionName, const Matcher ArgumentMatcher) const { + auto HasAnyPrecisionName = hasAnyName( + FunctionName, (FunctionName + "l").str(), + (FunctionName + "f").str()); // Support long double(l) and float(f). return expr(ignoreParenAndFloatingCasting( - callExpr(callee(functionDecl(hasName(FunctionName), + callExpr(callee(functionDecl(HasAnyPrecisionName, hasParameter(0, hasType(isArithmetic())))), hasArgument(0, ArgumentMatcher)))); } diff --git a/clang-tools-extra/clang-tidy/readability/AmbiguousSmartptrResetCallCheck.cpp b/clang-tools-extra/clang-tidy/readability/AmbiguousSmartptrResetCallCheck.cpp new file mode 100644 index 0000000000000..5f36c3976fc69 --- /dev/null +++ b/clang-tools-extra/clang-tidy/readability/AmbiguousSmartptrResetCallCheck.cpp @@ -0,0 +1,126 @@ +//===--- AmbiguousSmartptrResetCallCheck.cpp - clang-tidy -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "AmbiguousSmartptrResetCallCheck.h" +#include "../utils/OptionsUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Lex/Lexer.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::readability { + +namespace { + +AST_MATCHER(CXXMethodDecl, hasOnlyDefaultParameters) { + for (const auto *Param : Node.parameters()) { + if (!Param->hasDefaultArg()) + return false; + } + + return true; +} + +const auto DefaultSmartPointers = "::std::shared_ptr;::std::unique_ptr;" + "::boost::shared_ptr"; +} // namespace + +AmbiguousSmartptrResetCallCheck::AmbiguousSmartptrResetCallCheck( + StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + SmartPointers(utils::options::parseStringList( + Options.get("SmartPointers", DefaultSmartPointers))) {} + +void AmbiguousSmartptrResetCallCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "SmartPointers", + utils::options::serializeStringList(SmartPointers)); +} + +void AmbiguousSmartptrResetCallCheck::registerMatchers(MatchFinder *Finder) { + const auto IsSmartptr = hasAnyName(SmartPointers); + + const auto ResetMethod = + cxxMethodDecl(hasName("reset"), hasOnlyDefaultParameters()); + + const auto TypeWithReset = + anyOf(cxxRecordDecl( + anyOf(hasMethod(ResetMethod), + isDerivedFrom(cxxRecordDecl(hasMethod(ResetMethod))))), + classTemplateSpecializationDecl( + hasSpecializedTemplate(classTemplateDecl(has(ResetMethod))))); + + const auto SmartptrWithReset = expr(hasType(hasUnqualifiedDesugaredType( + recordType(hasDeclaration(classTemplateSpecializationDecl( + IsSmartptr, + hasTemplateArgument( + 0, templateArgument(refersToType(hasUnqualifiedDesugaredType( + recordType(hasDeclaration(TypeWithReset)))))))))))); + + Finder->addMatcher( + cxxMemberCallExpr( + callee(ResetMethod), + unless(hasAnyArgument(expr(unless(cxxDefaultArgExpr())))), + anyOf(on(cxxOperatorCallExpr(hasOverloadedOperatorName("->"), + hasArgument(0, SmartptrWithReset)) + .bind("ArrowOp")), + on(SmartptrWithReset))) + .bind("MemberCall"), + this); +} + +void AmbiguousSmartptrResetCallCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *MemberCall = + Result.Nodes.getNodeAs("MemberCall"); + assert(MemberCall); + + if (const auto *Arrow = + Result.Nodes.getNodeAs("ArrowOp")) { + const CharSourceRange SmartptrSourceRange = + Lexer::getAsCharRange(Arrow->getArg(0)->getSourceRange(), + *Result.SourceManager, getLangOpts()); + + diag(MemberCall->getBeginLoc(), + "ambiguous call to 'reset()' on a pointee of a smart pointer, prefer " + "more explicit approach"); + + diag(MemberCall->getBeginLoc(), + "consider dereferencing smart pointer to call 'reset' method " + "of the pointee here", + DiagnosticIDs::Note) + << FixItHint::CreateInsertion(SmartptrSourceRange.getBegin(), "(*") + << FixItHint::CreateInsertion(SmartptrSourceRange.getEnd(), ")") + << FixItHint::CreateReplacement( + CharSourceRange::getCharRange( + Arrow->getOperatorLoc(), + Arrow->getOperatorLoc().getLocWithOffset(2)), + "."); + } else { + const auto *Member = cast(MemberCall->getCallee()); + assert(Member); + + diag(MemberCall->getBeginLoc(), + "ambiguous call to 'reset()' on a smart pointer with pointee that " + "also has a 'reset()' method, prefer more explicit approach"); + + diag(MemberCall->getBeginLoc(), + "consider assigning the pointer to 'nullptr' here", + DiagnosticIDs::Note) + << FixItHint::CreateReplacement( + SourceRange(Member->getOperatorLoc(), Member->getOperatorLoc()), + " =") + << FixItHint::CreateReplacement( + SourceRange(Member->getMemberLoc(), MemberCall->getEndLoc()), + " nullptr"); + } +} + +} // namespace clang::tidy::readability diff --git a/clang-tools-extra/clang-tidy/readability/AmbiguousSmartptrResetCallCheck.h b/clang-tools-extra/clang-tidy/readability/AmbiguousSmartptrResetCallCheck.h new file mode 100644 index 0000000000000..05932e59e7928 --- /dev/null +++ b/clang-tools-extra/clang-tidy/readability/AmbiguousSmartptrResetCallCheck.h @@ -0,0 +1,37 @@ +//===--- AmbiguousSmartptrResetCallCheck.h - clang-tidy ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_AMBIGUOUSSMARTPTRRESETCALLCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_AMBIGUOUSSMARTPTRRESETCALLCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::readability { + +/// Finds potentially erroneous calls to 'reset' method on smart pointers when +/// the pointee type also has a 'reset' method +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/readability/ambiguous-smartptr-reset-call.html +class AmbiguousSmartptrResetCallCheck : public ClangTidyCheck { +public: + AmbiguousSmartptrResetCallCheck(StringRef Name, ClangTidyContext *Context); + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } + +private: + const std::vector SmartPointers; +}; + +} // namespace clang::tidy::readability + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_AMBIGUOUSSMARTPTRRESETCALLCHECK_H diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt index 8f303c51e1b0d..4be1a8f831339 100644 --- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt @@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS ) add_clang_library(clangTidyReadabilityModule STATIC + AmbiguousSmartptrResetCallCheck.cpp AvoidConstParamsInDecls.cpp AvoidNestedConditionalOperatorCheck.cpp AvoidReturnWithVoidValueCheck.cpp diff --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp index d61c0ba39658e..4c0812f0e6793 100644 --- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp @@ -9,6 +9,7 @@ #include "../ClangTidy.h" #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" +#include "AmbiguousSmartptrResetCallCheck.h" #include "AvoidConstParamsInDecls.h" #include "AvoidNestedConditionalOperatorCheck.h" #include "AvoidReturnWithVoidValueCheck.h" @@ -68,6 +69,8 @@ namespace readability { class ReadabilityModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + CheckFactories.registerCheck( + "readability-ambiguous-smartptr-reset-call"); CheckFactories.registerCheck( "readability-avoid-const-params-in-decls"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/clang-tidy/utils/UseRangesCheck.cpp b/clang-tools-extra/clang-tidy/utils/UseRangesCheck.cpp index aba4d17ccd035..f7a19cd72d500 100644 --- a/clang-tools-extra/clang-tidy/utils/UseRangesCheck.cpp +++ b/clang-tools-extra/clang-tidy/utils/UseRangesCheck.cpp @@ -215,6 +215,19 @@ void UseRangesCheck::check(const MatchFinder::MatchResult &Result) { const auto *Call = Result.Nodes.getNodeAs(Buffer); if (!Call) continue; + + // FIXME: This check specifically handles `CXXNullPtrLiteralExpr`, but + // a more general solution might be needed. + if (Function->getName() == "find") { + const unsigned ValueArgIndex = 2; + if (Call->getNumArgs() <= ValueArgIndex) + continue; + const Expr *ValueExpr = + Call->getArg(ValueArgIndex)->IgnoreParenImpCasts(); + if (isa(ValueExpr)) + return; + } + auto Diag = createDiag(*Call); if (auto ReplaceName = Replacer->getReplaceName(*Function)) Diag << FixItHint::CreateReplacement(Call->getCallee()->getSourceRange(), diff --git a/clang-tools-extra/clangd/Config.h b/clang-tools-extra/clangd/Config.h index 586d031d58481..3f8a3c9b060f6 100644 --- a/clang-tools-extra/clangd/Config.h +++ b/clang-tools-extra/clangd/Config.h @@ -59,6 +59,7 @@ struct Config { std::optional FixedCDBPath; }; + enum class BuiltinHeaderPolicy { Clangd, QueryDriver }; /// Controls how the compile command for the current file is determined. struct { /// Edits to apply to the compile command, in sequence. @@ -66,6 +67,10 @@ struct Config { Edits; /// Where to search for compilation databases for this file's flags. CDBSearchSpec CDBSearch = {CDBSearchSpec::Ancestors, std::nullopt}; + + /// Whether to use clangd's own builtin headers, or ones from the system + /// include extractor, if available. + BuiltinHeaderPolicy BuiltinHeaders = BuiltinHeaderPolicy::Clangd; } CompileFlags; enum class BackgroundPolicy { Build, Skip }; diff --git a/clang-tools-extra/clangd/ConfigCompile.cpp b/clang-tools-extra/clangd/ConfigCompile.cpp index aa2561e081047..31530c206acd7 100644 --- a/clang-tools-extra/clangd/ConfigCompile.cpp +++ b/clang-tools-extra/clangd/ConfigCompile.cpp @@ -290,6 +290,18 @@ struct FragmentCompiler { }); } + if (F.BuiltinHeaders) { + if (auto Val = + compileEnum("BuiltinHeaders", + *F.BuiltinHeaders) + .map("Clangd", Config::BuiltinHeaderPolicy::Clangd) + .map("QueryDriver", Config::BuiltinHeaderPolicy::QueryDriver) + .value()) + Out.Apply.push_back([Val](const Params &, Config &C) { + C.CompileFlags.BuiltinHeaders = *Val; + }); + } + if (F.CompilationDatabase) { std::optional Spec; if (**F.CompilationDatabase == "Ancestors") { diff --git a/clang-tools-extra/clangd/ConfigFragment.h b/clang-tools-extra/clangd/ConfigFragment.h index 9535b20253b13..6f95474b9c008 100644 --- a/clang-tools-extra/clangd/ConfigFragment.h +++ b/clang-tools-extra/clangd/ConfigFragment.h @@ -170,6 +170,14 @@ struct Fragment { /// - Ancestors: search all parent directories (the default) /// - std::nullopt: do not use a compilation database, just default flags. std::optional> CompilationDatabase; + + /// Controls whether Clangd should use its own built-in system headers (like + /// stddef.h), or use the system headers from the query driver. Use the + /// option value 'Clangd' (default) to indicate Clangd's headers, and use + /// 'QueryDriver' to indicate QueryDriver's headers. `Clangd` is the + /// fallback if no query driver is supplied or if the query driver regex + /// string fails to match the compiler used in the CDB. + std::optional> BuiltinHeaders; }; CompileFlagsBlock CompileFlags; diff --git a/clang-tools-extra/clangd/ConfigYAML.cpp b/clang-tools-extra/clangd/ConfigYAML.cpp index 95cc5c1f9f1cf..a46d45df0d319 100644 --- a/clang-tools-extra/clangd/ConfigYAML.cpp +++ b/clang-tools-extra/clangd/ConfigYAML.cpp @@ -104,6 +104,10 @@ class Parser { if (auto Values = scalarValues(N)) F.Remove = std::move(*Values); }); + Dict.handle("BuiltinHeaders", [&](Node &N) { + if (auto BuiltinHeaders = scalarValue(N, "BuiltinHeaders")) + F.BuiltinHeaders = *BuiltinHeaders; + }); Dict.handle("CompilationDatabase", [&](Node &N) { F.CompilationDatabase = scalarValue(N, "CompilationDatabase"); }); diff --git a/clang-tools-extra/clangd/IncludeFixer.cpp b/clang-tools-extra/clangd/IncludeFixer.cpp index fadd1105691fc..8b74c761d23ca 100644 --- a/clang-tools-extra/clangd/IncludeFixer.cpp +++ b/clang-tools-extra/clangd/IncludeFixer.cpp @@ -84,7 +84,6 @@ std::vector IncludeFixer::fix(DiagnosticsEngine::Level DiagLevel, case diag::err_array_incomplete_or_sizeless_type: case diag::err_array_size_incomplete_type: case diag::err_asm_incomplete_type: - case diag::err_assoc_type_incomplete: case diag::err_bad_cast_incomplete: case diag::err_call_function_incomplete_return: case diag::err_call_incomplete_argument: diff --git a/clang-tools-extra/clangd/Preamble.cpp b/clang-tools-extra/clangd/Preamble.cpp index b247e608eece3..b634981bb46bd 100644 --- a/clang-tools-extra/clangd/Preamble.cpp +++ b/clang-tools-extra/clangd/Preamble.cpp @@ -673,7 +673,6 @@ buildPreamble(PathRef FileName, CompilerInvocation CI, // Reset references to ref-counted-ptrs before executing the callbacks, to // prevent resetting them concurrently. PreambleDiagsEngine.reset(); - CI.DiagnosticOpts.reset(); // When building the AST for the main file, we do want the function // bodies. diff --git a/clang-tools-extra/clangd/SystemIncludeExtractor.cpp b/clang-tools-extra/clangd/SystemIncludeExtractor.cpp index c1c2e9fab9664..9399b910025b6 100644 --- a/clang-tools-extra/clangd/SystemIncludeExtractor.cpp +++ b/clang-tools-extra/clangd/SystemIncludeExtractor.cpp @@ -30,6 +30,7 @@ // in the paths that are explicitly included by the user. #include "CompileCommands.h" +#include "Config.h" #include "GlobalCompilationDatabase.h" #include "support/Logger.h" #include "support/Threading.h" @@ -401,22 +402,30 @@ extractSystemIncludesAndTarget(const DriverArgs &InputArgs, if (!Info) return std::nullopt; - // The built-in headers are tightly coupled to parser builtins. - // (These are clang's "resource dir", GCC's GCC_INCLUDE_DIR.) - // We should keep using clangd's versions, so exclude the queried builtins. - // They're not specially marked in the -v output, but we can get the path - // with `$DRIVER -print-file-name=include`. - if (auto BuiltinHeaders = - run({Driver, "-print-file-name=include"}, /*OutputIsStderr=*/false)) { - auto Path = llvm::StringRef(*BuiltinHeaders).trim(); - if (!Path.empty() && llvm::sys::path::is_absolute(Path)) { - auto Size = Info->SystemIncludes.size(); - llvm::erase(Info->SystemIncludes, Path); - vlog("System includes extractor: builtin headers {0} {1}", Path, - (Info->SystemIncludes.size() != Size) - ? "excluded" - : "not found in driver's response"); + switch (Config::current().CompileFlags.BuiltinHeaders) { + case Config::BuiltinHeaderPolicy::Clangd: { + // The built-in headers are tightly coupled to parser builtins. + // (These are clang's "resource dir", GCC's GCC_INCLUDE_DIR.) + // We should keep using clangd's versions, so exclude the queried + // builtins. They're not specially marked in the -v output, but we can + // get the path with `$DRIVER -print-file-name=include`. + if (auto BuiltinHeaders = run({Driver, "-print-file-name=include"}, + /*OutputIsStderr=*/false)) { + auto Path = llvm::StringRef(*BuiltinHeaders).trim(); + if (!Path.empty() && llvm::sys::path::is_absolute(Path)) { + auto Size = Info->SystemIncludes.size(); + llvm::erase(Info->SystemIncludes, Path); + vlog("System includes extractor: builtin headers {0} {1}", Path, + (Info->SystemIncludes.size() != Size) + ? "excluded" + : "not found in driver's response"); + } } + break; + } + case Config::BuiltinHeaderPolicy::QueryDriver: + vlog("System includes extractor: Using builtin headers from query driver."); + break; } log("System includes extractor: successfully executed {0}\n\tgot includes: " diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index ce1418a2a7d58..110f949741c3f 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -58,6 +58,9 @@ Semantic Highlighting Compile flags ^^^^^^^^^^^^^ +- Added `BuiltinHeaders` config key which controls whether clangd's built-in + headers are used or ones extracted from the driver. + Hover ^^^^^ @@ -97,12 +100,22 @@ New checks Finds unintended character output from ``unsigned char`` and ``signed char`` to an ``ostream``. +- New :doc:`readability-ambiguous-smartptr-reset-call + ` check. + + Finds potentially erroneous calls to ``reset`` method on smart pointers when + the pointee type also has a ``reset`` method. + New check aliases ^^^^^^^^^^^^^^^^^ Changes in existing checks ^^^^^^^^^^^^^^^^^^^^^^^^^^ +- Improved :doc:`bugprone-optional-value-conversion + ` check to detect + conversion in argument of ``std::make_optional``. + - Improved :doc:`bugprone-string-constructor ` check to find suspicious calls of ``std::string`` constructor with char pointer, start position and @@ -112,7 +125,7 @@ Changes in existing checks ` fixing false positives from smart pointer accessors repeated in checking ``has_value`` and accessing ``value``. The option `IgnoreSmartPointerDereference` should - no longer be needed and will be removed. Also fixing false positive from + no longer be needed and will be removed. Also fixing false positive from const reference accessors to objects containing optional member. - Improved :doc:`bugprone-unsafe-functions @@ -128,10 +141,26 @@ Changes in existing checks - Improved :doc:`misc-redundant-expression ` check by providing additional examples and fixing some macro related false positives. + +- Improved :doc:`modernize-use-ranges + ` check by updating suppress + warnings logic for ``nullptr`` in ``std::find``. + +- Improved :doc:`modernize-use-std-numbers + ` check to support math + functions of different precisions. + +- Improved :doc:`misc-use-internal-linkage + ` check by fix false positives + for function or variable in header file which contains macro expansion. + +- Improved :doc:`modernize-use-default-member-init + ` check by matching + ``constexpr`` and ``static`` values on member initialization. - Improved :doc:`performance/unnecessary-value-param ` check performance by - tolerating fix-it breaking compilation when functions is used as pointers + tolerating fix-it breaking compilation when functions is used as pointers to avoid matching usage of functions within the current compilation unit. - Improved :doc:`performance-move-const-arg diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 5f03ef72cc603..c73bc8bff3539 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -353,6 +353,7 @@ Clang-Tidy Checks :doc:`portability-simd-intrinsics `, :doc:`portability-std-allocator-const `, :doc:`portability-template-virtual-member-function `, + :doc:`readability-ambiguous-smartptr-reset-call `, "Yes" :doc:`readability-avoid-const-params-in-decls `, "Yes" :doc:`readability-avoid-nested-conditional-operator `, :doc:`readability-avoid-return-with-void-value `, "Yes" diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/ambiguous-smartptr-reset-call.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/ambiguous-smartptr-reset-call.rst new file mode 100644 index 0000000000000..cf73839a46cfb --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/readability/ambiguous-smartptr-reset-call.rst @@ -0,0 +1,62 @@ +.. title:: clang-tidy - readability-ambiguous-smartptr-reset-call + +readability-ambiguous-smartptr-reset-call +========================================= + +Finds potentially erroneous calls to ``reset`` method on smart pointers when +the pointee type also has a ``reset`` method. Having a ``reset`` method in +both classes makes it easy to accidentally make the pointer null when +intending to reset the underlying object. + +.. code-block:: c++ + + struct Resettable { + void reset() { /* Own reset logic */ } + }; + + auto ptr = std::make_unique(); + + ptr->reset(); // Calls underlying reset method + ptr.reset(); // Makes the pointer null + +Both calls are valid C++ code, but the second one might not be what the +developer intended, as it destroys the pointed-to object rather than resetting +its state. It's easy to make such a typo because the difference between +``.`` and ``->`` is really small. + +The recommended approach is to make the intent explicit by using either member +access or direct assignment: + +.. code-block:: c++ + + std::unique_ptr ptr = std::make_unique(); + + (*ptr).reset(); // Clearly calls underlying reset method + ptr = nullptr; // Clearly makes the pointer null + +The default smart pointers and classes that are considered are +``std::unique_ptr``, ``std::shared_ptr``, ``boost::shared_ptr``. To specify +other smart pointers or other classes use the :option:`SmartPointers` option. + + +.. note:: + + The check may emit invalid fix-its and misleading warning messages when + specifying custom smart pointers or other classes in the + :option:`SmartPointers` option. For example, ``boost::scoped_ptr`` does not + have an ``operator=`` which makes fix-its invalid. + +.. note:: + + Automatic fix-its are enabled only if :program:`clang-tidy` is invoked with + the `--fix-notes` option. + + +Options +------- + +.. option:: SmartPointers + + Semicolon-separated list of fully qualified class names of custom smart + pointers. Default value is `::std::unique_ptr;::std::shared_ptr; + ::boost::shared_ptr`. diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/optional-value-conversion-construct-from-std.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/optional-value-conversion-construct-from-std.cpp index 768ab1ce014ce..305fd6890710d 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/optional-value-conversion-construct-from-std.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/optional-value-conversion-construct-from-std.cpp @@ -27,9 +27,19 @@ class unique_ptr {}; template class shared_ptr {}; +template +class initializer_list {}; + template unique_ptr make_unique(Args &&...args); template shared_ptr make_shared(Args &&...args); +template +constexpr std::optional<__decay(T)> make_optional(T &&value); +template +constexpr std::optional make_optional(Args &&...args); +template +constexpr std::optional make_optional(std::initializer_list il, Args &&...args); + } // namespace std struct A { @@ -45,9 +55,12 @@ void invalid() { // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: conversion from 'std::optional' into 'int' and back into 'std::optional', remove potentially error-prone optional dereference [bugprone-optional-value-conversion] std::make_shared>(opt.value()); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: conversion from 'std::optional' into 'int' and back into 'std::optional', remove potentially error-prone optional dereference [bugprone-optional-value-conversion] + std::make_optional(opt.value()); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: conversion from 'std::optional' into 'int' and back into 'std::optional', remove potentially error-prone optional dereference [bugprone-optional-value-conversion] } void valid() { std::make_unique(opt.value()); std::make_shared(opt.value()); + std::make_optional(opt.value()); } diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/use-internal-linkage-macro.hpp b/clang-tools-extra/test/clang-tidy/checkers/misc/use-internal-linkage-macro.hpp new file mode 100644 index 0000000000000..209bd56028c3d --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/use-internal-linkage-macro.hpp @@ -0,0 +1,5 @@ +// RUN: %check_clang_tidy %s misc-use-internal-linkage %t -- -- -I%S/Inputs/use-internal-linkage + +#define B A##C + +inline void B() {} diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-default-member-init.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-default-member-init.cpp index 81c980e0217e6..8b9bfaf0df34f 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-default-member-init.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-default-member-init.cpp @@ -518,3 +518,22 @@ class ArrayBraceInitMultipleValues { }; } // namespace PR63285 + +namespace PR122480 { + + static int STATIC_VAL = 23; + constexpr const char* CONSTEXPR_REF = "Const"; + + class StaticConstExprInit { + + StaticConstExprInit() : a{CONSTEXPR_REF}, b{STATIC_VAL}{} + // CHECK-FIXES: StaticConstExprInit() {} + const char* a; + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use default member initializer for 'a' [modernize-use-default-member-init] + // CHECK-FIXES: const char* a{CONSTEXPR_REF}; + int b; + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use default member initializer for 'b' [modernize-use-default-member-init] + // CHECK-FIXES: int b{STATIC_VAL}; + }; + +} //namespace PR122480 diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-ranges.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-ranges.cpp index b022efebfdf4d..5aa026038b1cd 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-ranges.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-ranges.cpp @@ -1,14 +1,25 @@ -// RUN: %check_clang_tidy -std=c++20 %s modernize-use-ranges %t -- -- -I %S/Inputs/use-ranges/ -// RUN: %check_clang_tidy -std=c++23 %s modernize-use-ranges %t -check-suffixes=,CPP23 -- -I %S/Inputs/use-ranges/ +// RUN: %check_clang_tidy -std=c++20 %s modernize-use-ranges %t -- -- -I %S/Inputs/ +// RUN: %check_clang_tidy -std=c++23 %s modernize-use-ranges %t -check-suffixes=,CPP23 -- -I %S/Inputs/ +// Example: ./check_clang_tidy.py -std=c++20 checkers/modernize/use-ranges.cpp modernize-use-ranges temp.txt -- -- -I ~/llvm-project/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/ // CHECK-FIXES: #include // CHECK-FIXES-CPP23: #include // CHECK-FIXES: #include -#include "fake_std.h" +#include "use-ranges/fake_std.h" +#include "smart-ptr/unique_ptr.h" void Positives() { std::vector I, J; + std::vector> K; + + // Expect to have no check messages + std::find(K.begin(), K.end(), nullptr); + + std::find(K.begin(), K.end(), std::unique_ptr()); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm + // CHECK-FIXES: std::ranges::find(K, std::unique_ptr()); + std::find(I.begin(), I.end(), 0); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm // CHECK-FIXES: std::ranges::find(I, 0); @@ -76,6 +87,15 @@ void Positives() { void Reverse(){ std::vector I, J; + std::vector> K; + + // Expect to have no check messages + std::find(K.rbegin(), K.rend(), nullptr); + + std::find(K.rbegin(), K.rend(), std::unique_ptr()); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm + // CHECK-FIXES: std::ranges::find(std::ranges::reverse_view(K), std::unique_ptr()); + std::find(I.rbegin(), I.rend(), 0); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm // CHECK-FIXES: std::ranges::find(std::ranges::reverse_view(I), 0); diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-numbers.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-numbers.cpp index 6c5762da5e2e8..11121ae6d8e93 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-numbers.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-numbers.cpp @@ -9,12 +9,17 @@ namespace bar { template auto sqrt(T val) { return sqrt(static_cast(val)); } + float sqrtf(float Arg); + long double sqrtl(long double Arg); + static constexpr double e = 2.718281828459045235360287471352662497757247093; // CHECK-MESSAGES-ALL: :[[@LINE-1]]:33: warning: prefer 'std::numbers::e' to this literal, differs by '0.00e+00' [modernize-use-std-numbers] // CHECK-FIXES-ALL: static constexpr double e = std::numbers::e; } +float expf(float Arg); double exp(double Arg); +long double expl(long double Arg); double log(double Arg); double log2(double Arg); @@ -110,6 +115,14 @@ void foo(){ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt2_v' to this formula [modernize-use-std-numbers] // CHECK-FIXES-ALL: std::numbers::sqrt2_v; + bar::sqrtf(2.0F); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt2_v' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt2_v; + + bar::sqrtl(2.0); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt2_v' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt2_v; + sink(MY_PI); // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: prefer 'std::numbers::pi' to this macro, differs by '5.36e-08' [modernize-use-std-numbers] // CHECK-FIXES-ALL: sink(std::numbers::pi); @@ -155,6 +168,14 @@ void foo(){ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::e' to this formula [modernize-use-std-numbers] // CHECK-FIXES-ALL: std::numbers::e; + expf(1); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::e_v' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::e_v; + + expl(1); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::e_v' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::e_v; + log2(exp(1)); // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::log2e' to this formula [modernize-use-std-numbers] // CHECK-MESSAGES-ALL: :[[@LINE-2]]:10: warning: prefer 'std::numbers::e' to this formula [modernize-use-std-numbers] diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/ambiguous-smartptr-reset-call-custom-pointers.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/ambiguous-smartptr-reset-call-custom-pointers.cpp new file mode 100644 index 0000000000000..df3f16a9cf9ec --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/ambiguous-smartptr-reset-call-custom-pointers.cpp @@ -0,0 +1,52 @@ +// RUN: %check_clang_tidy %s readability-ambiguous-smartptr-reset-call %t -- \ +// RUN: -config='{CheckOptions: \ +// RUN: {readability-ambiguous-smartptr-reset-call.SmartPointers: "::std::unique_ptr;::other_ptr"}}' \ +// RUN: --fix-notes -- -I %S/../modernize/Inputs/smart-ptr + +#include "unique_ptr.h" +#include "shared_ptr.h" + +template +struct other_ptr { + T& operator*() const; + T* operator->() const; + void reset(); +}; + +struct Resettable { + void reset(); + void doSomething(); +}; + +void Positive() { + std::unique_ptr u; + u.reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: u = nullptr; + u->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*u).reset(); + + other_ptr s; + s.reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: s = nullptr; + s->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*s).reset(); +} + +void Negative() { + std::shared_ptr s_ptr; + s_ptr.reset(); + s_ptr->reset(); + s_ptr->doSomething(); + + std::unique_ptr u_ptr; + u_ptr.reset(nullptr); + u_ptr->doSomething(); +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/ambiguous-smartptr-reset-call.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/ambiguous-smartptr-reset-call.cpp new file mode 100644 index 0000000000000..e6e7eb9231ec2 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/ambiguous-smartptr-reset-call.cpp @@ -0,0 +1,397 @@ +// RUN: %check_clang_tidy %s readability-ambiguous-smartptr-reset-call %t --fix-notes -- -I %S/../modernize/Inputs/smart-ptr + +#include "unique_ptr.h" +#include "shared_ptr.h" + +template +struct non_default_reset_ptr { + T& operator*() const; + T* operator->() const; + void reset(T* p); +}; + +struct Resettable { + void reset(); + void doSomething(); +}; + +struct ResettableWithParam { + void reset(int a); + void doSomething(); +}; + +struct ResettableWithDefaultParams { + void reset(int a = 0, double b = 0.0); + void doSomething(); +}; + +struct NonResettable { + void doSomething(); +}; + +void Positive() { + std::unique_ptr u; + u.reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: u = nullptr; + u->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*u).reset(); + + std::shared_ptr s; + s.reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: s = nullptr; + s->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*s).reset(); + + std::unique_ptr> uu_ptr; + uu_ptr.reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: uu_ptr = nullptr; + uu_ptr->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*uu_ptr).reset(); + + std::unique_ptr> su_ptr; + su_ptr.reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: su_ptr = nullptr; + su_ptr->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*su_ptr).reset(); + + std::unique_ptr rd; + rd.reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: rd = nullptr; + rd->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*rd).reset(); + + std::unique_ptr>> nested_ptr; + nested_ptr.reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: nested_ptr = nullptr; + nested_ptr->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*nested_ptr).reset(); + (*nested_ptr).reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: (*nested_ptr) = nullptr; + (*nested_ptr)->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*(*nested_ptr)).reset(); + (**nested_ptr).reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: (**nested_ptr) = nullptr; + (**nested_ptr)->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*(**nested_ptr)).reset(); +} + +void Negative() { + std::unique_ptr u_ptr; + u_ptr.reset(nullptr); + u_ptr->doSomething(); + + std::shared_ptr s_ptr; + s_ptr.reset(nullptr); + s_ptr->doSomething(); + + Resettable* raw_ptr; + raw_ptr->reset(); + raw_ptr->doSomething(); + + Resettable resettable; + resettable.reset(); + resettable.doSomething(); + + std::unique_ptr u_ptr_param; + u_ptr_param.reset(); + u_ptr_param.reset(nullptr); + u_ptr_param->reset(0); + + std::unique_ptr u_ptr_no_reset; + u_ptr_no_reset.reset(); + + std::shared_ptr s_ptr_param; + s_ptr_param.reset(); + s_ptr_param->reset(0); + s_ptr_param->doSomething(); + + std::shared_ptr s_ptr_no_reset; + s_ptr_no_reset.reset(); + + std::unique_ptr u_ptr_default_params; + u_ptr_default_params.reset(nullptr); + u_ptr_default_params->reset(1); + u_ptr_default_params->reset(1, 2.0); + + non_default_reset_ptr non_default_reset_ptr; + non_default_reset_ptr.reset(new Resettable); + non_default_reset_ptr->reset(); +} + +template +void TemplatePositiveTest() { + std::unique_ptr u_ptr; + + u_ptr.reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: u_ptr = nullptr; + u_ptr->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*u_ptr).reset(); + + std::shared_ptr s_ptr; + s_ptr.reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: s_ptr = nullptr; + s_ptr->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*s_ptr).reset(); +} + +template +void TemplatNegativeTestTypeWithReset() { + std::unique_ptr u_ptr; + u_ptr.reset(); + u_ptr->reset(0); + + std::shared_ptr s_ptr; + s_ptr.reset(); + s_ptr->reset(0); +} + +template +void TemplatNegativeTestTypeWithoutReset() { + std::unique_ptr u_ptr; + u_ptr.reset(); + + std::unique_ptr s_ptr; + s_ptr.reset(); +} + +void instantiate() { + TemplatePositiveTest(); + TemplatePositiveTest>(); + TemplatePositiveTest>(); + TemplatNegativeTestTypeWithReset(); + TemplatNegativeTestTypeWithoutReset(); +} + +struct S { + void foo() { + u_ptr.reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:5: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: u_ptr = nullptr; + u_ptr->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:5: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*u_ptr).reset(); + u_ptr.reset(nullptr); + u_ptr->doSomething(); + + s_ptr.reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:5: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: s_ptr = nullptr; + s_ptr->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:5: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*s_ptr).reset(); + s_ptr.reset(nullptr); + + ptr.reset(); + } + + std::unique_ptr u_ptr; + std::unique_ptr> s_ptr; + std::unique_ptr ptr; +}; + + +typedef std::unique_ptr TypedefResettableUniquePtr; +typedef std::shared_ptr TypedefResettableSharedPtr; + +void TypedefPositive() { + TypedefResettableUniquePtr u_ptr; + u_ptr.reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: u_ptr = nullptr; + u_ptr->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*u_ptr).reset(); + + TypedefResettableSharedPtr s_ptr; + s_ptr.reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: s_ptr = nullptr; + + s_ptr->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*s_ptr).reset(); +} + +using UsingResettableUniquePtr = std::unique_ptr; +using UsingResettableSharedPtr = std::shared_ptr; + +void UsingPositive() { + UsingResettableUniquePtr u_ptr; + u_ptr.reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: u_ptr = nullptr; + u_ptr->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*u_ptr).reset(); + + UsingResettableSharedPtr s_ptr; + s_ptr.reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: s_ptr = nullptr; + + s_ptr->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*s_ptr).reset(); +} + +template +using UsingUniquePtr = std::unique_ptr; +template +using UsingSharedPtr = std::shared_ptr; + +void UsingTemplatePositive() { + UsingUniquePtr u_ptr; + u_ptr.reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: u_ptr = nullptr; + u_ptr->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*u_ptr).reset(); + + UsingSharedPtr s_ptr; + s_ptr.reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: s_ptr = nullptr; + + s_ptr->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*s_ptr).reset(); +} + +template +void UsingByTemplatePositive() { + UsingUniquePtr u_ptr; + u_ptr.reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: u_ptr = nullptr; + u_ptr->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*u_ptr).reset(); + + UsingSharedPtr s_ptr; + s_ptr.reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: s_ptr = nullptr; + + s_ptr->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*s_ptr).reset(); +} + +void instantiate2() { + UsingByTemplatePositive(); +} + +void NestedUsingPositive() { + UsingUniquePtr> nested_ptr; + nested_ptr.reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: nested_ptr = nullptr; + nested_ptr->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*nested_ptr).reset(); + (*nested_ptr).reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: (*nested_ptr) = nullptr; + (*nested_ptr)->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*(*nested_ptr)).reset(); + (**nested_ptr).reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: (**nested_ptr) = nullptr; + (**nested_ptr)->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*(**nested_ptr)).reset(); +} + +// Check other default pointers and classes. +namespace boost { + +template +struct shared_ptr { + T& operator*() const; + T* operator->() const; + void reset(); + void reset(T*); +}; + +} // namespace boost + +void PositiveOtherClasses() { + boost::shared_ptr sh; + sh.reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a smart pointer with pointee that also has a 'reset()' method, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider assigning the pointer to 'nullptr' here + // CHECK-FIXES: sh = nullptr; + sh->reset(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: consider dereferencing smart pointer to call 'reset' method of the pointee here + // CHECK-FIXES: (*sh).reset(); +} + diff --git a/clang/AreaTeamMembers.txt b/clang/AreaTeamMembers.txt new file mode 100644 index 0000000000000..964d11e79f694 --- /dev/null +++ b/clang/AreaTeamMembers.txt @@ -0,0 +1,17 @@ +This is a list of the current Clang Area Team members. + +Chair +----- +Aaron Ballman +aaron@aaronballman.com (email), AaronBallman (Discourse), AaronBallman (GitHub), AaronBallman (Discord) + +Secretary +--------- +Reid Kleckner +rnk@google.com (email), rnk (Discourse), rnk (GitHub), rnk (Discord) + +Other Members +------------- +Eli Friedman +efriedma@quicinc.com> (email), efriedma-quic (Discourse), efriedma-quic (GitHub) + diff --git a/clang/Maintainers.rst b/clang/Maintainers.rst index 9eaa1ec19f0d3..ef41628c0e75d 100644 --- a/clang/Maintainers.rst +++ b/clang/Maintainers.rst @@ -4,7 +4,8 @@ Clang Maintainers This file is a list of the `maintainers `_ for -Clang. +Clang. The list of current Clang Area Team members can be found +`here `_. .. contents:: :depth: 2 diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 578ee02f09b9b..387251804fa76 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -786,6 +786,7 @@ of different sizes and signs is forbidden in binary and ternary builtins. T __builtin_elementwise_bitreverse(T x) return the integer represented after reversing the bits of x integer types T __builtin_elementwise_exp(T x) returns the base-e exponential, e^x, of the specified value floating point types T __builtin_elementwise_exp2(T x) returns the base-2 exponential, 2^x, of the specified value floating point types + T __builtin_elementwise_exp10(T x) returns the base-10 exponential, 10^x, of the specified value floating point types T __builtin_elementwise_sqrt(T x) return the square root of a floating-point number floating point types T __builtin_elementwise_roundeven(T x) round x to the nearest integer value in floating point format, floating point types @@ -1604,53 +1605,54 @@ More information could be found `here Language Extensions Back-ported to Previous Standards ===================================================== -============================================ ================================ ============= ============= -Feature Feature Test Macro Introduced In Backported To -============================================ ================================ ============= ============= -variadic templates __cpp_variadic_templates C++11 C++03 -Alias templates __cpp_alias_templates C++11 C++03 -Non-static data member initializers __cpp_nsdmi C++11 C++03 -Range-based ``for`` loop __cpp_range_based_for C++11 C++03 -RValue references __cpp_rvalue_references C++11 C++03 -Attributes __cpp_attributes C++11 C++03 -Lambdas __cpp_lambdas C++11 C++03 -Generalized lambda captures __cpp_init_captures C++14 C++03 -Generic lambda expressions __cpp_generic_lambdas C++14 C++03 -variable templates __cpp_variable_templates C++14 C++03 -Binary literals __cpp_binary_literals C++14 C++03 -Relaxed constexpr __cpp_constexpr C++14 C++11 -Static assert with no message __cpp_static_assert >= 201411L C++17 C++11 -Pack expansion in generalized lambda-capture __cpp_init_captures C++17 C++03 -``if constexpr`` __cpp_if_constexpr C++17 C++11 -fold expressions __cpp_fold_expressions C++17 C++03 -Lambda capture of \*this by value __cpp_capture_star_this C++17 C++03 -Attributes on enums __cpp_enumerator_attributes C++17 C++03 -Guaranteed copy elision __cpp_guaranteed_copy_elision C++17 C++03 -Hexadecimal floating literals __cpp_hex_float C++17 C++03 -``inline`` variables __cpp_inline_variables C++17 C++03 -Attributes on namespaces __cpp_namespace_attributes C++17 C++11 -Structured bindings __cpp_structured_bindings C++17 C++03 -template template arguments __cpp_template_template_args C++17 C++03 -Familiar template syntax for generic lambdas __cpp_generic_lambdas C++20 C++03 -``static operator[]`` __cpp_multidimensional_subscript C++20 C++03 -Designated initializers __cpp_designated_initializers C++20 C++03 -Conditional ``explicit`` __cpp_conditional_explicit C++20 C++03 -``using enum`` __cpp_using_enum C++20 C++03 -``if consteval`` __cpp_if_consteval C++23 C++20 -``static operator()`` __cpp_static_call_operator C++23 C++03 -Attributes on Lambda-Expressions C++23 C++11 -Attributes on Structured Bindings __cpp_structured_bindings C++26 C++03 -Packs in Structured Bindings __cpp_structured_bindings C++26 C++03 -Static assert with user-generated message __cpp_static_assert >= 202306L C++26 C++11 -Pack Indexing __cpp_pack_indexing C++26 C++03 -``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03 -Variadic Friends __cpp_variadic_friend C++26 C++03 --------------------------------------------- -------------------------------- ------------- ------------- -Designated initializers (N494) C99 C89 -Array & element qualification (N2607) C23 C89 -Attributes (N2335) C23 C89 -``#embed`` (N3017) C23 C89, C++ -============================================ ================================ ============= ============= +============================================= ================================ ============= ============= +Feature Feature Test Macro Introduced In Backported To +============================================= ================================ ============= ============= +variadic templates __cpp_variadic_templates C++11 C++03 +Alias templates __cpp_alias_templates C++11 C++03 +Non-static data member initializers __cpp_nsdmi C++11 C++03 +Range-based ``for`` loop __cpp_range_based_for C++11 C++03 +RValue references __cpp_rvalue_references C++11 C++03 +Attributes __cpp_attributes C++11 C++03 +Lambdas __cpp_lambdas C++11 C++03 +Generalized lambda captures __cpp_init_captures C++14 C++03 +Generic lambda expressions __cpp_generic_lambdas C++14 C++03 +variable templates __cpp_variable_templates C++14 C++03 +Binary literals __cpp_binary_literals C++14 C++03 +Relaxed constexpr __cpp_constexpr C++14 C++11 +Static assert with no message __cpp_static_assert >= 201411L C++17 C++11 +Pack expansion in generalized lambda-capture __cpp_init_captures C++17 C++03 +``if constexpr`` __cpp_if_constexpr C++17 C++11 +fold expressions __cpp_fold_expressions C++17 C++03 +Lambda capture of \*this by value __cpp_capture_star_this C++17 C++03 +Attributes on enums __cpp_enumerator_attributes C++17 C++03 +Guaranteed copy elision __cpp_guaranteed_copy_elision C++17 C++03 +Hexadecimal floating literals __cpp_hex_float C++17 C++03 +``inline`` variables __cpp_inline_variables C++17 C++03 +Attributes on namespaces __cpp_namespace_attributes C++17 C++11 +Structured bindings __cpp_structured_bindings C++17 C++03 +template template arguments __cpp_template_template_args C++17 C++03 +Familiar template syntax for generic lambdas __cpp_generic_lambdas C++20 C++03 +``static operator[]`` __cpp_multidimensional_subscript C++20 C++03 +Designated initializers __cpp_designated_initializers C++20 C++03 +Conditional ``explicit`` __cpp_conditional_explicit C++20 C++03 +``using enum`` __cpp_using_enum C++20 C++03 +``if consteval`` __cpp_if_consteval C++23 C++20 +``static operator()`` __cpp_static_call_operator C++23 C++03 +Attributes on Lambda-Expressions C++23 C++11 +Attributes on Structured Bindings __cpp_structured_bindings C++26 C++03 +Packs in Structured Bindings __cpp_structured_bindings C++26 C++03 +Structured binding declaration as a condition __cpp_structured_bindings C++26 C++98 +Static assert with user-generated message __cpp_static_assert >= 202306L C++26 C++11 +Pack Indexing __cpp_pack_indexing C++26 C++03 +``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03 +Variadic Friends __cpp_variadic_friend C++26 C++03 +--------------------------------------------- -------------------------------- ------------- ------------- +Designated initializers (N494) C99 C89 +Array & element qualification (N2607) C23 C89 +Attributes (N2335) C23 C89 +``#embed`` (N3017) C23 C89, C++ +============================================= ================================ ============= ============= Builtin type aliases ==================== diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html index 48dfd9cac0033..9b30057b5257f 100644 --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -1842,12 +1842,6 @@

Node Matchers

if (x) {} -Matcher<
Stmt>dependentScopeDeclRefExprMatcher<DependentScopeDeclRefExpr>... -
Matches expressions that refer to dependent scope declarations.
-
-Example matches T::v
-   template  class X : T { void f() { T::v; } };
-
Matcher<Stmt>declStmtMatcher<DeclStmt>...
Matches declaration statements.
@@ -1874,6 +1868,14 @@ 

Node Matchers

+Matcher<Stmt>dependentScopeDeclRefExprMatcher<DependentScopeDeclRefExpr>... +
Matches expressions that refer to dependent scope declarations.
+
+example matches T::v;
+ template <class T> class X : T { void f() { T::v; } };
+
+ + Matcher<Stmt>designatedInitExprMatcher<DesignatedInitExpr>...
Matches C99 designated initializer expressions [C99 6.7.8].
 
@@ -1931,7 +1933,24 @@ 

Node Matchers

Matcher<Stmt>fixedPointLiteralMatcher<FixedPointLiteral>... -
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:
+   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;
+
+The matcher matches
+but does not
+match and from the code block.
 
@@ -2536,26 +2555,6 @@

Node Matchers

matches "decltype(i + j)"
-Matcher<Type>dependentNameTypeMatcher<DependentNameType>... -
Matches a dependent name type.
-
-Example matches T::type
-
-  template  struct declToImport {
-    typedef typename T::type dependent_name;
-  };
-
- -Matcher<Type>dependentTemplateSpecializationTypeMatcher<DependentTemplateSpecializationType>... -
Matches a dependent template specialization type.
-
-Example matches A::template B
-
-  template struct A;
-  template struct declToImport {
-    typename A::template B a;
-  };
-
Matcher<Type>deducedTemplateSpecializationTypeMatcher<DeducedTemplateSpecializationType>...
Matches C++17 deduced template specialization types, e.g. deduced class
@@ -2571,6 +2570,16 @@ 

Node Matchers

+Matcher<Type>dependentNameTypeMatcher<DependentNameType>... +
Matches a dependent name type
+
+Example matches T::type
+ template <typename T> struct declToImport {
+   typedef typename T::type dependent_name;
+ };
+
+ + Matcher<Type>dependentSizedArrayTypeMatcher<DependentSizedArrayType>...
Matches C++ arrays whose size is a value-dependent expression.
 
@@ -2598,6 +2607,17 @@ 

Node Matchers

+Matcher<Type>dependentTemplateSpecializationTypeMatcher<DependentTemplateSpecializationType>... +
Matches a dependent template specialization type
+
+Example matches A<T>::template B<T>
+  template<typename T> struct A;
+  template<typename T> struct declToImport {
+    typename A<T>::template B<T> a;
+  };
+
+ + Matcher<Type>elaboratedTypeMatcher<ElaboratedType>...
Matches types specified with an elaborated type keyword or with a
 qualified name.
@@ -3449,34 +3469,6 @@ 

Narrowing Matchers

-Matcher<DependentScopeDeclRefExpr>hasDependentNamestd::string N -
Matches the dependent name of a DependentScopeDeclRefExpr.
-
-Matches the dependent name of a DependentScopeDeclRefExpr
-
-Given:
-
-  template <class T< class X : T { void f() { T::v; } };
-
-dependentScopeDeclRefExpr(hasDependentName("v")) matches `T::v`
-
- - -Matcher<DependentNameType>hasDependentNamestd::string N -
Matches the dependent name of a DependentNameType.
-
-Matches the dependent name of a DependentNameType
-
-Given:
-
-  template <typename T< struct declToImport {
-    typedef typename T::type dependent_name;
-  };
-
-dependentNameType(hasDependentName("type")) matches `T::type`
-
- - Matcher<CXXDependentScopeMemberExpr>memberHasSameNameAsBoundNodestd::string BindingID
Matches template-dependent, but known, member names against an already-bound
 node
@@ -4116,8 +4108,14 @@ 

Narrowing Matchers

Given template<typename T> struct C {}; C<int> c; + template<typename T> void f() {} + void func() { f<int>(); }; + classTemplateSpecializationDecl(templateArgumentCountIs(1)) matches C<int>. + +functionDecl(templateArgumentCountIs(1)) + matches f<int>();
@@ -4373,6 +4371,38 @@

Narrowing Matchers

+Matcher<DependentNameType>hasDependentNamestd::string N +
Matches the dependent name of a DependentScopeDeclRefExpr or
+DependentNameType
+
+Given:
+ template <class T> class X : T { void f() { T::v; } };
+dependentScopeDeclRefExpr(hasDependentName("v")) matches `T::v`
+
+Given:
+ template <typename T> struct declToImport {
+   typedef typename T::type dependent_name;
+ };
+dependentNameType(hasDependentName("type")) matches `T::type`
+
+ + +Matcher<DependentScopeDeclRefExpr>hasDependentNamestd::string N +
Matches the dependent name of a DependentScopeDeclRefExpr or
+DependentNameType
+
+Given:
+ template <class T> class X : T { void f() { T::v; } };
+dependentScopeDeclRefExpr(hasDependentName("v")) matches `T::v`
+
+Given:
+ template <typename T> struct declToImport {
+   typedef typename T::type dependent_name;
+ };
+dependentNameType(hasDependentName("type")) matches `T::type`
+
+ + Matcher<DesignatedInitExpr>designatorCountIsunsigned N
Matches designated initializer expressions that contain
 a specific number of designators.
@@ -4809,6 +4839,23 @@ 

Narrowing Matchers

+Matcher<FunctionDecl>templateArgumentCountIsunsigned N +
Matches if the number of template arguments equals N.
+
+Given
+  template<typename T> struct C {};
+  C<int> c;
+  template<typename T> void f() {}
+  void func() { f<int>(); };
+
+classTemplateSpecializationDecl(templateArgumentCountIs(1))
+  matches C<int>.
+
+functionDecl(templateArgumentCountIs(1))
+  matches f<int>();
+
+ + Matcher<FunctionProtoType>hasDynamicExceptionSpec
Matches functions that have a dynamic exception specification.
 
@@ -5759,14 +5806,20 @@ 

Narrowing Matchers

-Matcher<TemplateSpecializationType>templateArgumentCountIsunsigned N -
Matches if the number of template arguments equals N.
+Matcher<TemplateSpecializationType>templateArgumentCountIsunsigned N
+
Matches if the number of template arguments equals N.
 
 Given
   template<typename T> struct C {};
   C<int> c;
+  template<typename T> void f() {}
+  void func() { f<int>(); };
+
 classTemplateSpecializationDecl(templateArgumentCountIs(1))
   matches C<int>.
+
+functionDecl(templateArgumentCountIs(1))
+  matches f<int>();
 
@@ -6195,6 +6248,23 @@

Narrowing Matchers

Usable as: Matcher<FunctionDecl>, Matcher<VarDecl>, Matcher<CXXRecordDecl>
+ +Matcher<VarTemplateSpecializationDecl>templateArgumentCountIsunsigned N +
Matches if the number of template arguments equals N.
+
+Given
+  template<typename T> struct C {};
+  C<int> c;
+  template<typename T> void f() {}
+  void func() { f<int>(); };
+
+classTemplateSpecializationDecl(templateArgumentCountIs(1))
+  matches C<int>.
+
+functionDecl(templateArgumentCountIs(1))
+  matches f<int>();
+
+ @@ -6799,7 +6869,8 @@

AST Traversal Matchers

matches "int const *b" Usable as: Matcher<BlockPointerType>, Matcher<MemberPointerType>, - Matcher<PointerType>, Matcher<ReferenceType> + Matcher<PointerType>, Matcher<ReferenceType>, + Matcher<ObjCObjectPointerType> @@ -9300,7 +9371,8 @@

AST Traversal Matchers

matches "int const *b" Usable as: Matcher<BlockPointerType>, Matcher<MemberPointerType>, - Matcher<PointerType>, Matcher<ReferenceType> + Matcher<PointerType>, Matcher<ReferenceType>, + Matcher<ObjCObjectPointerType> @@ -9414,6 +9486,36 @@

AST Traversal Matchers

+Matcher<ObjCInterfaceDecl>hasTypeMatcher<Decl> InnerMatcher +
Overloaded to match the declaration of the expression's or value
+declaration's type.
+
+In case of a value declaration (for example a variable declaration),
+this resolves one layer of indirection. For example, in the value
+declaration "X x;", cxxRecordDecl(hasName("X")) matches the declaration of
+X, while varDecl(hasType(cxxRecordDecl(hasName("X")))) matches the
+declaration of x.
+
+Example matches x (matcher = expr(hasType(cxxRecordDecl(hasName("X")))))
+            and z (matcher = varDecl(hasType(cxxRecordDecl(hasName("X")))))
+            and friend class X (matcher = friendDecl(hasType("X"))
+            and public virtual X (matcher = cxxBaseSpecifier(hasType(
+                                              cxxRecordDecl(hasName("X"))))
+ class X {};
+ void y(X &x) { x; X z; }
+ class Y { friend class X; };
+ class Z : public virtual X {};
+
+Example matches class Derived
+(matcher = cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl(hasName("Base"))))))
+class Base {};
+class Derived : Base {};
+
+Usable as: Matcher<Expr>, Matcher<FriendDecl>, Matcher<ValueDecl>,
+Matcher<CXXBaseSpecifier>
+
+ + Matcher<ObjCInterfaceDecl>isDerivedFromMatcher<NamedDecl> Base
Matches C++ classes that are directly or indirectly derived from a class
 matching Base, or Objective-C classes that directly or indirectly
@@ -9592,6 +9694,23 @@ 

AST Traversal Matchers

+Matcher<ObjCObjectPointerType>pointeeMatcher<Type> +
Narrows PointerType (and similar) matchers to those where the
+pointee matches a given matcher.
+
+Given
+  int *a;
+  int const *b;
+  float const *f;
+pointerType(pointee(isConstQualified(), isInteger()))
+  matches "int const *b"
+
+Usable as: Matcher<BlockPointerType>, Matcher<MemberPointerType>,
+  Matcher<PointerType>, Matcher<ReferenceType>,
+  Matcher<ObjCObjectPointerType>
+
+ + Matcher<ObjCPropertyDecl>hasTypeLocMatcher<TypeLoc> Inner
Matches if the type location of a node matches the inner matcher.
 
@@ -9689,7 +9808,8 @@ 

AST Traversal Matchers

matches "int const *b" Usable as: Matcher<BlockPointerType>, Matcher<MemberPointerType>, - Matcher<PointerType>, Matcher<ReferenceType> + Matcher<PointerType>, Matcher<ReferenceType>, + Matcher<ObjCObjectPointerType>
@@ -9858,7 +9978,8 @@

AST Traversal Matchers

matches "int const *b" Usable as: Matcher<BlockPointerType>, Matcher<MemberPointerType>, - Matcher<PointerType>, Matcher<ReferenceType> + Matcher<PointerType>, Matcher<ReferenceType>, + Matcher<ObjCObjectPointerType> diff --git a/clang/docs/OpenMPSupport.rst b/clang/docs/OpenMPSupport.rst index 88af120d06edb..dcee1db2a18e0 100644 --- a/clang/docs/OpenMPSupport.rst +++ b/clang/docs/OpenMPSupport.rst @@ -408,7 +408,7 @@ implementation. +-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+ | Private reductions | :none:`unclaimed` | :none:`unclaimed` | | +-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+ -| Self maps | :none:`unclaimed` | :none:`unclaimed` | | +| Self maps | :part:`partial` | :none:`unclaimed` | parsing/sema done: https://github.com/llvm/llvm-project/pull/129888 | +-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+ | Release map type for declare mapper | :none:`unclaimed` | :none:`unclaimed` | | +-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index e1a8dad87ad29..f6e4d6a34e5dc 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -62,6 +62,9 @@ AST Dumping Potentially Breaking Changes Clang Frontend Potentially Breaking Changes ------------------------------------------- +- The ``-Wglobal-constructors`` flag now applies to ``[[gnu::constructor]]`` and + ``[[gnu::destructor]]`` attributes. + Clang Python Bindings Potentially Breaking Changes -------------------------------------------------- @@ -76,6 +79,8 @@ C++2c Feature Support - Implemented `P1061R10 Structured Bindings can introduce a Pack `_. +- Implemented `P0963R3 Structured binding declaration as a condition `_. + C++23 Feature Support ^^^^^^^^^^^^^^^^^^^^^ @@ -108,6 +113,13 @@ C Language Changes C2y Feature Support ^^^^^^^^^^^^^^^^^^^ +- Implement `WG14 N3409 `_ + which removes UB around use of ``void`` expressions. In practice, this means + that ``_Generic`` selection associations may now have ``void`` type, but it + also removes UB with code like ``(void)(void)1;``. +- Implemented `WG14 N3411 `_ + which allows a source file to not end with a newline character. This is still + reported as a conforming extension in earlier language modes. C23 Feature Support ^^^^^^^^^^^^^^^^^^^ @@ -116,6 +128,7 @@ Non-comprehensive list of changes in this release ------------------------------------------------- - Support parsing the `cc` operand modifier and alias it to the `c` modifier (#GH127719). +- Added `__builtin_elementwise_exp10`. New Compiler Flags ------------------ @@ -145,13 +158,14 @@ Adding [[clang::unsafe_buffer_usage]] attribute to a method definition now turns related warnings within the method body. - The ``no_sanitize`` attribute now accepts both ``gnu`` and ``clang`` names. +- The ``ext_vector_type(n)`` attribute can now be used as a generic type attribute. - Clang now diagnoses use of declaration attributes on void parameters. (#GH108819) - Clang now allows ``__attribute__((model("small")))`` and ``__attribute__((model("large")))`` on non-TLS globals in x86-64 compilations. This forces the global to be considered small or large in regards to the x86-64 code model, regardless of the code model specified for the compilation. -- Clang now emits a warning ``-Wreserved-init-priority`` instead of a hard error - when ``__attribute__((init_priority(n)))`` is used with values of n in the +- Clang now emits a warning ``-Wreserved-init-priority`` instead of a hard error + when ``__attribute__((init_priority(n)))`` is used with values of n in the reserved range [0, 100]. The warning will be treated as an error by default. - There is a new ``format_matches`` attribute to complement the existing @@ -223,12 +237,16 @@ Improvements to Clang's diagnostics - Diagnostics on chained comparisons (``a < b < c``) are now an error by default. This can be disabled with ``-Wno-error=parentheses``. - The ``-Wshift-bool`` warning has been added to warn about shifting a boolean. (#GH28334) - +- Fixed diagnostics adding a trailing ``::`` when printing some source code + constructs, like base classes. - The :doc:`ThreadSafetyAnalysis` now supports ``-Wthread-safety-pointer``, which enables warning on passing or returning pointers to guarded variables as function arguments or return value respectively. Note that :doc:`ThreadSafetyAnalysis` still does not perform alias analysis. The feature will be default-enabled with ``-Wthread-safety`` in a future release. +- The ``-Wsign-compare`` warning now treats expressions with bitwise not(~) and minus(-) as signed integers + except for the case where the operand is an unsigned integer + and throws warning if they are compared with unsigned integers (##18878). - Improve the diagnostics for chained comparisons to report actual expressions and operators (#GH129069). @@ -245,6 +263,7 @@ Bug Fixes in This Version - Clang now outputs correct values when #embed data contains bytes with negative signed char values (#GH102798). +- Fixed a crash when merging named enumerations in modules (#GH114240). - Fixed rejects-valid problem when #embed appears in std::initializer_list or when it can affect template argument deduction (#GH122306). - Fix crash on code completion of function calls involving partial order of function templates @@ -282,20 +301,19 @@ Bug Fixes to C++ Support direct-list-initialized from an array is corrected to direct-initialization. - Clang no longer crashes when a coroutine is declared ``[[noreturn]]``. (#GH127327) - Clang now uses the parameter location for abbreviated function templates in ``extern "C"``. (#GH46386) - -Improvements to C++ diagnostics -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -- Clang now more consistently adds a note pointing to the relevant template - parameter. Some diagnostics are reworded to better take advantage of this. -- Template Template Parameter diagnostics now stop referring to template - parameters as template arguments, in some circumstances, better hiding - from the users template template parameter partial ordering arcana. - +- Clang will emit an error instead of crash when use co_await or co_yield in + C++26 braced-init-list template parameter initialization. (#GH78426) +- Fixes matching of nested template template parameters. (#GH130362) +- Correctly diagnoses template template paramters which have a pack parameter + not in the last position. +- Clang now correctly parses ``if constexpr`` expressions in immediate function context. (#GH123524) +- Fixed an assertion failure affecting code that uses C++23 "deducing this". (#GH130272) Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed type checking when a statement expression ends in an l-value of atomic type. (#GH106576) +- Fixed uninitialized use check in a lambda within CXXOperatorCallExpr. (#GH129198) +- Fixed a malformed printout of ``CXXParenListInitExpr`` in certain contexts. Miscellaneous Bug Fixes ^^^^^^^^^^^^^^^^^^^^^^^ @@ -380,6 +398,8 @@ AST Matchers ------------ - Ensure ``isDerivedFrom`` matches the correct base in case more than one alias exists. +- Extend ``templateArgumentCountIs`` to support function and variable template + specialization. clang-format ------------ @@ -443,6 +463,7 @@ Python Binding Changes OpenMP Support -------------- - Added support 'no_openmp_constructs' assumption clause. +- Added support for 'self_maps' in map and requirement clause. - Added support for 'omp stripe' directive. Improvements diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index 8213334b61c22..b4a994444d5e8 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -4681,25 +4681,7 @@ Clang supports generation of SPIR-V conformant to `the OpenCL Environment Specification `_. -To generate SPIR-V binaries, Clang uses the external ``llvm-spirv`` tool from the -`SPIRV-LLVM-Translator repo -`_. - -Prior to the generation of SPIR-V binary with Clang, ``llvm-spirv`` -should be built or installed. Please refer to `the following instructions -`_ -for more details. Clang will look for ``llvm-spirv-`` and -``llvm-spirv`` executables, in this order, in the ``PATH`` environment variable. -Clang uses ``llvm-spirv`` with `the widely adopted assembly syntax package -`_. - -`The versioning -`_ of -``llvm-spirv`` is aligned with Clang major releases. The same applies to the -main development branch. It is therefore important to ensure the ``llvm-spirv`` -version is in alignment with the Clang version. For troubleshooting purposes -``llvm-spirv`` can be `tested in isolation -`_. +To generate SPIR-V binaries, Clang uses the in-tree LLVM SPIR-V backend. Example usage for OpenCL kernel compilation: @@ -4717,18 +4699,6 @@ Converting to SPIR-V produced with the optimization levels other than `-O0` is currently available as an experimental feature and it is not guaranteed to work in all cases. -Clang also supports integrated generation of SPIR-V without use of ``llvm-spirv`` -tool as an experimental feature when ``-fintegrated-objemitter`` flag is passed in -the command line. - - .. code-block:: console - - $ clang --target=spirv32 -fintegrated-objemitter -c test.cl - -Note that only very basic functionality is supported at this point and therefore -it is not suitable for arbitrary use cases. This feature is only enabled when clang -build is configured with ``-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=SPIRV`` option. - Linking is done using ``spirv-link`` from `the SPIRV-Tools project `_. Similar to other external linkers, Clang will expect ``spirv-link`` to be installed separately and to be diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst index b817a99a1c56f..c947f26204159 100644 --- a/clang/docs/analyzer/checkers.rst +++ b/clang/docs/analyzer/checkers.rst @@ -3522,6 +3522,31 @@ Raw pointers and references to an object which supports CheckedPtr or CheckedRef See `WebKit Guidelines for Safer C++ Programming `_ for details. +alpha.webkit.NoUnretainedMemberChecker +"""""""""""""""""""""""""""""""""""""""" +Raw pointers and references to a NS or CF object can't be used as class members or ivars. Only RetainPtr is allowed for CF types regardless of whether ARC is enabled or disabled. Only RetainPtr is allowed for NS types when ARC is disabled. + +.. code-block:: cpp + + struct Foo { + NSObject *ptr; // warn + // ... + }; + +See `WebKit Guidelines for Safer C++ Programming `_ for details. + +alpha.webkit.UnretainedLambdaCapturesChecker +"""""""""""""""""""""""""""""""""""""""""""" +Raw pointers and references to NS or CF types can't be captured in lambdas. Only RetainPtr is allowed for CF types regardless of whether ARC is enabled or disabled, and only RetainPtr is allowed for NS types when ARC is disabled. + +.. code-block:: cpp + + void foo(NSObject *a, NSObject *b) { + [&, a](){ // warn about 'a' + do_something(b); // warn about 'b' + }; + }; + .. _alpha-webkit-UncountedCallArgsChecker: alpha.webkit.UncountedCallArgsChecker diff --git a/clang/docs/tools/dump_ast_matchers.py b/clang/docs/tools/dump_ast_matchers.py index b6f00657ec914..46b7bb718ba08 100755 --- a/clang/docs/tools/dump_ast_matchers.py +++ b/clang/docs/tools/dump_ast_matchers.py @@ -106,7 +106,7 @@ def extract_result_types(comment): def strip_doxygen(comment): - """Returns the given comment without \-escaped words.""" + """Returns the given comment without -escaped words.""" # If there is only a doxygen keyword in the line, delete the whole line. comment = re.sub(r"^\\[^\s]+\n", r"", comment, flags=re.M) @@ -241,7 +241,7 @@ def act_on_decl(declaration, comment, allowed_types): # Parse the various matcher definition macros. m = re.match( - """.*AST_TYPE(LOC)?_TRAVERSE_MATCHER(?:_DECL)?\( + r""".*AST_TYPE(LOC)?_TRAVERSE_MATCHER(?:_DECL)?\( \s*([^\s,]+\s*), \s*(?:[^\s,]+\s*), \s*AST_POLYMORPHIC_SUPPORTED_TYPES\(([^)]*)\) @@ -615,5 +615,5 @@ def sort_table(matcher_type, matcher_map): flags=re.S, ) -with open("../LibASTMatchersReference.html", "w", newline="\n") as output: +with open(HTML_FILE, "w", newline="\n") as output: output.write(reference) diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index cfe49acf20b77..b81f3a403baf6 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -3005,18 +3005,6 @@ class CallExpr : public Expr { FPOptionsOverride FPFeatures, unsigned MinNumArgs = 0, ADLCallKind UsesADL = NotADL); - /// Create a temporary call expression with no arguments in the memory - /// pointed to by Mem. Mem must points to at least sizeof(CallExpr) - /// + sizeof(Stmt *) bytes of storage, aligned to alignof(CallExpr): - /// - /// \code{.cpp} - /// alignas(CallExpr) char Buffer[sizeof(CallExpr) + sizeof(Stmt *)]; - /// CallExpr *TheCall = CallExpr::CreateTemporary(Buffer, etc); - /// \endcode - static CallExpr *CreateTemporary(void *Mem, Expr *Fn, QualType Ty, - ExprValueKind VK, SourceLocation RParenLoc, - ADLCallKind UsesADL = NotADL); - /// Create an empty call expression, for deserialization. static CallExpr *CreateEmpty(const ASTContext &Ctx, unsigned NumArgs, bool HasFPFeatures, EmptyShell Empty); diff --git a/clang/include/clang/AST/Mangle.h b/clang/include/clang/AST/Mangle.h index 6134a70c04bd3..9ed8895cbfff1 100644 --- a/clang/include/clang/AST/Mangle.h +++ b/clang/include/clang/AST/Mangle.h @@ -140,7 +140,7 @@ class MangleContext { virtual void mangleCXXRTTIName(QualType T, raw_ostream &, bool NormalizeIntegers = false) = 0; virtual void mangleStringLiteral(const StringLiteral *SL, raw_ostream &) = 0; - virtual void mangleMSGuidDecl(const MSGuidDecl *GD, raw_ostream &); + virtual void mangleMSGuidDecl(const MSGuidDecl *GD, raw_ostream &) const; void mangleGlobalBlock(const BlockDecl *BD, const NamedDecl *ID, raw_ostream &Out); @@ -153,9 +153,9 @@ class MangleContext { void mangleObjCMethodName(const ObjCMethodDecl *MD, raw_ostream &OS, bool includePrefixByte = true, - bool includeCategoryNamespace = true); + bool includeCategoryNamespace = true) const; void mangleObjCMethodNameAsSourceName(const ObjCMethodDecl *MD, - raw_ostream &); + raw_ostream &) const; virtual void mangleStaticGuardVariable(const VarDecl *D, raw_ostream &) = 0; diff --git a/clang/include/clang/AST/NestedNameSpecifier.h b/clang/include/clang/AST/NestedNameSpecifier.h index a1d9e30e660d1..051d632f1cdf9 100644 --- a/clang/include/clang/AST/NestedNameSpecifier.h +++ b/clang/include/clang/AST/NestedNameSpecifier.h @@ -223,7 +223,8 @@ class NestedNameSpecifier : public llvm::FoldingSetNode { /// `ns::SomeTemplate` instead of /// `ns::SomeTemplate`. void print(raw_ostream &OS, const PrintingPolicy &Policy, - bool ResolveTemplateArguments = false) const; + bool ResolveTemplateArguments = false, + bool PrintFinalScopeResOp = true) const; void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddPointer(Prefix.getOpaqueValue()); diff --git a/clang/include/clang/AST/OpenACCClause.h b/clang/include/clang/AST/OpenACCClause.h index b2cf621bc0a78..049389229b8d5 100644 --- a/clang/include/clang/AST/OpenACCClause.h +++ b/clang/include/clang/AST/OpenACCClause.h @@ -18,6 +18,7 @@ #include "clang/Basic/OpenACCKinds.h" #include +#include namespace clang { /// This is the base type for all OpenACC Clauses. @@ -206,6 +207,50 @@ class OpenACCClauseWithParams : public OpenACCClause { } }; +class OpenACCBindClause final : public OpenACCClauseWithParams { + std::variant Argument; + + OpenACCBindClause(SourceLocation BeginLoc, SourceLocation LParenLoc, + const clang::StringLiteral *SL, SourceLocation EndLoc) + : OpenACCClauseWithParams(OpenACCClauseKind::Bind, BeginLoc, LParenLoc, + EndLoc), + Argument(SL) {} + OpenACCBindClause(SourceLocation BeginLoc, SourceLocation LParenLoc, + const IdentifierInfo *ID, SourceLocation EndLoc) + : OpenACCClauseWithParams(OpenACCClauseKind::Bind, BeginLoc, LParenLoc, + EndLoc), + Argument(ID) {} + +public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::Bind; + } + static OpenACCBindClause *Create(const ASTContext &C, SourceLocation BeginLoc, + SourceLocation LParenLoc, + const IdentifierInfo *ID, + SourceLocation EndLoc); + static OpenACCBindClause *Create(const ASTContext &C, SourceLocation BeginLoc, + SourceLocation LParenLoc, + const StringLiteral *SL, + SourceLocation EndLoc); + + bool isStringArgument() const { + return std::holds_alternative(Argument); + } + + const StringLiteral *getStringArgument() const { + return std::get(Argument); + } + + bool isIdentifierArgument() const { + return std::holds_alternative(Argument); + } + + const IdentifierInfo *getIdentifierArgument() const { + return std::get(Argument); + } +}; + using DeviceTypeArgument = std::pair; /// A 'device_type' or 'dtype' clause, takes a list of either an 'asterisk' or /// an identifier. The 'asterisk' means 'the rest'. diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h index 154ecfbaa4418..46f34a7302847 100644 --- a/clang/include/clang/AST/OpenMPClause.h +++ b/clang/include/clang/AST/OpenMPClause.h @@ -1656,6 +1656,49 @@ class OMPAtomicDefaultMemOrderClause final : public OMPClause { } }; +/// This represents 'self_maps' clause in the '#pragma omp requires' +/// directive. +/// +/// \code +/// #pragma omp requires self_maps +/// \endcode +/// In this example directive '#pragma omp requires' has 'self_maps' +/// clause. +class OMPSelfMapsClause final : public OMPClause { +public: + friend class OMPClauseReader; + /// Build 'self_maps' clause. + /// + /// \param StartLoc Starting location of the clause. + /// \param EndLoc Ending location of the clause. + OMPSelfMapsClause(SourceLocation StartLoc, SourceLocation EndLoc) + : OMPClause(llvm::omp::OMPC_self_maps, StartLoc, EndLoc) {} + + /// Build an empty clause. + OMPSelfMapsClause() + : OMPClause(llvm::omp::OMPC_self_maps, SourceLocation(), + SourceLocation()) {} + + child_range children() { + return child_range(child_iterator(), child_iterator()); + } + + const_child_range children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } + + child_range used_children() { + return child_range(child_iterator(), child_iterator()); + } + const_child_range used_children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } + + static bool classof(const OMPClause *T) { + return T->getClauseKind() == llvm::omp::OMPC_self_maps; + } +}; + /// This represents 'at' clause in the '#pragma omp error' directive /// /// \code @@ -6349,7 +6392,8 @@ class OMPMapClause final : public OMPMappableExprListClause, OpenMPMapModifierKind MapTypeModifiers[NumberOfOMPMapClauseModifiers] = { OMPC_MAP_MODIFIER_unknown, OMPC_MAP_MODIFIER_unknown, OMPC_MAP_MODIFIER_unknown, OMPC_MAP_MODIFIER_unknown, - OMPC_MAP_MODIFIER_unknown, OMPC_MAP_MODIFIER_unknown}; + OMPC_MAP_MODIFIER_unknown, OMPC_MAP_MODIFIER_unknown, + OMPC_MAP_MODIFIER_unknown}; /// Location of map-type-modifiers for the 'map' clause. SourceLocation MapTypeModifiersLoc[NumberOfOMPMapClauseModifiers]; diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 5ca3e435f033b..fac4c10987157 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -3443,6 +3443,11 @@ bool RecursiveASTVisitor::VisitOMPAtomicDefaultMemOrderClause( return true; } +template +bool RecursiveASTVisitor::VisitOMPSelfMapsClause(OMPSelfMapsClause *) { + return true; +} + template bool RecursiveASTVisitor::VisitOMPAtClause(OMPAtClause *) { return true; diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index ef59bd1621fb8..3c942f2ed7486 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -2568,6 +2568,9 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { bool isVectorType() const; // GCC vector type. bool isExtVectorType() const; // Extended vector type. bool isExtVectorBoolType() const; // Extended vector type with bool element. + // Extended vector type with bool element that is packed. HLSL doesn't pack + // its bool vectors. + bool isPackedVectorBoolType(const ASTContext &ctx) const; bool isSubscriptableVectorType() const; bool isMatrixType() const; // Matrix type. bool isConstantMatrixType() const; // Constant matrix type. diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h index 0f7e3a8a01762..738617759eb29 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -1084,12 +1084,19 @@ AST_POLYMORPHIC_MATCHER_P2( /// \code /// template struct C {}; /// C c; +/// template void f() {} +/// void func() { f(); }; /// \endcode +/// /// classTemplateSpecializationDecl(templateArgumentCountIs(1)) /// matches C. +/// +/// functionDecl(templateArgumentCountIs(1)) +/// matches f(); AST_POLYMORPHIC_MATCHER_P( templateArgumentCountIs, AST_POLYMORPHIC_SUPPORTED_TYPES(ClassTemplateSpecializationDecl, + VarTemplateSpecializationDecl, FunctionDecl, TemplateSpecializationType), unsigned, N) { return internal::getTemplateSpecializationArgs(Node).size() == N; @@ -7494,7 +7501,8 @@ extern const AstTypeMatcher rValueReferenceType; /// matches "int const *b" /// /// Usable as: Matcher, Matcher, -/// Matcher, Matcher +/// Matcher, Matcher, +/// Matcher AST_TYPELOC_TRAVERSE_MATCHER_DECL( pointee, getPointee, AST_POLYMORPHIC_SUPPORTED_TYPES(BlockPointerType, MemberPointerType, diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index ccfe69d32e0a6..4d34346460561 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1721,17 +1721,10 @@ def EnableIf : InheritableAttr { let Documentation = [EnableIfDocs]; } -def ExtVectorType : Attr { - // This is an OpenCL-related attribute and does not receive a [[]] spelling. - let Spellings = [GNU<"ext_vector_type">]; - // FIXME: This subject list is wrong; this is a type attribute. - let Subjects = SubjectList<[TypedefName], ErrorDiag>; +def ExtVectorType : TypeAttr { + let Spellings = [Clang<"ext_vector_type">]; let Args = [ExprArgument<"NumElements">]; - let ASTNode = 0; - let Documentation = [Undocumented]; - // This is a type attribute with an incorrect subject list, so should not be - // permitted by #pragma clang attribute. - let PragmaAttributeSupport = 0; + let Documentation = [ExtVectorTypeDocs]; } def FallThrough : StmtAttr { @@ -4724,21 +4717,21 @@ def HLSLNumThreads: InheritableAttr { } def HLSLSV_GroupThreadID: HLSLAnnotationAttr { - let Spellings = [HLSLAnnotation<"SV_GroupThreadID">]; + let Spellings = [HLSLAnnotation<"sv_groupthreadid">]; let Subjects = SubjectList<[ParmVar, Field]>; let LangOpts = [HLSL]; let Documentation = [HLSLSV_GroupThreadIDDocs]; } def HLSLSV_GroupID: HLSLAnnotationAttr { - let Spellings = [HLSLAnnotation<"SV_GroupID">]; + let Spellings = [HLSLAnnotation<"sv_groupid">]; let Subjects = SubjectList<[ParmVar, Field]>; let LangOpts = [HLSL]; let Documentation = [HLSLSV_GroupIDDocs]; } def HLSLSV_GroupIndex: HLSLAnnotationAttr { - let Spellings = [HLSLAnnotation<"SV_GroupIndex">]; + let Spellings = [HLSLAnnotation<"sv_groupindex">]; let Subjects = SubjectList<[ParmVar, GlobalVar]>; let LangOpts = [HLSL]; let Documentation = [HLSLSV_GroupIndexDocs]; @@ -4790,7 +4783,7 @@ def HLSLPackOffset: HLSLAnnotationAttr { } def HLSLSV_DispatchThreadID: HLSLAnnotationAttr { - let Spellings = [HLSLAnnotation<"SV_DispatchThreadID">]; + let Spellings = [HLSLAnnotation<"sv_dispatchthreadid">]; let Subjects = SubjectList<[ParmVar, Field]>; let LangOpts = [HLSL]; let Documentation = [HLSLSV_DispatchThreadIDDocs]; @@ -5033,4 +5026,7 @@ def OpenACCRoutineAnnot : InheritableAttr { let Spellings = []; let Subjects = SubjectList<[Function]>; let Documentation = [InternalOnly]; + let AdditionalMembers = [{ + SourceLocation BindClause; + }]; } diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index fdc58d1c92c0d..34e7ff9612859 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -1113,6 +1113,41 @@ template instantiation, so the value for ``T::number`` is known. }]; } +def ExtVectorTypeDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``ext_vector_type(N)`` attribute specifies that a type is a vector with N +elements, directly mapping to an LLVM vector type. Originally from OpenCL, it +allows element access the array subscript operator ``[]``, ``sN`` where N is +a hexadecimal value, or ``x, y, z, w`` for graphics-style indexing. +This attribute enables efficient SIMD operations and is usable in +general-purpose code. + +.. code-block:: c++ + + template + constexpr T simd_reduce(T [[clang::ext_vector_type(N)]] v) { + static_assert((N & (N - 1)) == 0, "N must be a power of two"); + if constexpr (N == 1) + return v[0]; + else + return simd_reduce(v.hi + v.lo); + } + +The vector type also supports swizzling up to sixteen elements. This can be done +using the object accessors. The OpenCL documentation lists all of the accepted +values. + +.. code-block:: c++ + + using f16_x16 = _Float16 __attribute__((ext_vector_type(16))); + + f16_x16 reverse(f16_x16 v) { return v.sfedcba9876543210; } + +See the OpenCL documentation for some more complete examples. + }]; +} + def DiagnoseIfDocs : Documentation { let Category = DocCatFunction; let Content = [{ @@ -2848,7 +2883,7 @@ https://gcc.gnu.org/onlinedocs/gcc/RISC-V-Function-Attributes.html https://riscv.org/specifications/privileged-isa/ The RISC-V Instruction Set Manual Volume II: Privileged Architecture Version 1.10. -https://github.com/quic/riscv-unified-db/releases/tag/Xqci-0.6 +https://github.com/quic/riscv-unified-db/releases/tag/Xqci-0.7 }]; } diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 2268df70927a7..2fbdfaea57ccd 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -1340,6 +1340,12 @@ def ElementwiseExp2 : Builtin { let Prototype = "void(...)"; } +def ElementwiseExp10 : Builtin { + let Spellings = ["__builtin_elementwise_exp10"]; + let Attributes = [NoThrow, Const, CustomTypeChecking]; + let Prototype = "void(...)"; +} + def ElementwiseFloor : Builtin { let Spellings = ["__builtin_elementwise_floor"]; let Attributes = [NoThrow, Const, CustomTypeChecking]; diff --git a/clang/include/clang/Basic/DiagnosticOptions.h b/clang/include/clang/Basic/DiagnosticOptions.h index e2faf3d0df95c..29146532f9524 100644 --- a/clang/include/clang/Basic/DiagnosticOptions.h +++ b/clang/include/clang/Basic/DiagnosticOptions.h @@ -67,7 +67,8 @@ inline DiagnosticLevelMask operator&(DiagnosticLevelMask LHS, raw_ostream& operator<<(raw_ostream& Out, DiagnosticLevelMask M); /// Options for controlling the compiler diagnostics engine. -class DiagnosticOptions : public RefCountedBase{ +class DiagnosticOptions + : public llvm::ThreadSafeRefCountedBase { friend bool ParseDiagnosticArgs(DiagnosticOptions &, llvm::opt::ArgList &, clang::DiagnosticsEngine *, bool); diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index c513dab810d1f..092a55f9e9e0c 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1495,7 +1495,7 @@ def note_previous_map_type_specified_here : Note<"map type '%0' is previous specified here">; def err_omp_unknown_map_type_modifier : Error< "incorrect map type modifier, expected one of: 'always', 'close', 'mapper'" - "%select{|, 'present'|, 'present', 'iterator'}0%select{|, 'ompx_hold'}1">; + "%select{|, 'present'|, 'present', 'iterator'}0%select{|, 'ompx_hold'}1%select{|, 'self'}2">; def err_omp_map_type_missing : Error< "missing map type">; def err_omp_map_type_modifier_missing : Error< @@ -1817,5 +1817,9 @@ def ext_hlsl_access_specifiers : ExtWarn< InGroup; def err_hlsl_unsupported_component : Error<"invalid component '%0' used; expected 'x', 'y', 'z', or 'w'">; def err_hlsl_packoffset_invalid_reg : Error<"invalid resource class specifier '%0' for packoffset, expected 'c'">; +def err_hlsl_virtual_function + : Error<"virtual functions are unsupported in HLSL">; +def err_hlsl_virtual_inheritance + : Error<"virtual inheritance is unsupported in HLSL">; } // end of Parser diagnostics diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index cf0e9846d4259..8e6e6e892cdd7 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -529,8 +529,12 @@ def warn_cxx14_compat_decomp_decl : Warning< def ext_decomp_decl : ExtWarn< "decomposition declarations are a C++17 extension">, InGroup; def ext_decomp_decl_cond : ExtWarn< - "ISO C++17 does not permit structured binding declaration in a condition">, - InGroup>; + "structured binding declaration in a condition is a C++2c extenstion">, + InGroup; +def warn_cxx26_decomp_decl_cond : Warning< + "structured binding declaration in a condition is incompatible with " + "C++ standards before C++2c">, + InGroup, DefaultIgnore; def err_decomp_decl_spec : Error< "decomposition declaration cannot be declared " "%plural{1:'%1'|:with '%1' specifiers}0">; @@ -551,7 +555,7 @@ def err_decomp_decl_constraint : Error< def err_decomp_decl_parens : Error< "decomposition declaration cannot be declared with parentheses">; def err_decomp_decl_template : Error< - "decomposition declaration template not supported">; + "decomposition declaration cannot be a template">; def err_decomp_decl_not_alone : Error< "decomposition declaration must be the only declaration in its group">; def err_decomp_decl_requires_init : Error< @@ -599,16 +603,17 @@ def err_using_typename_non_type : Error< "'typename' keyword used on a non-type">; def err_using_dependent_value_is_type : Error< "dependent using declaration resolved to type without 'typename'">; -def err_using_decl_nested_name_specifier_is_not_class : Error< - "using declaration in class refers into '%0', which is not a class">; +def err_using_decl_nested_name_specifier_is_not_class + : Error<"using declaration in class refers into %0, which is not a class">; def warn_cxx17_compat_using_decl_non_member_enumerator : Warning< "member using declaration naming non-class '%0' enumerator is " "incompatible with C++ standards before C++20">, InGroup, DefaultIgnore; def err_using_decl_nested_name_specifier_is_current_class : Error< "using declaration refers to its own class">; -def err_using_decl_nested_name_specifier_is_not_base_class : Error< - "using declaration refers into '%0', which is not a base class of %1">; +def err_using_decl_nested_name_specifier_is_not_base_class + : Error< + "using declaration refers into %0, which is not a base class of %1">; def err_using_decl_constructor_not_in_direct_base : Error< "%0 is not a direct base of %1, cannot inherit constructors">; def err_using_decl_can_not_refer_to_class_member : Error< @@ -1733,8 +1738,8 @@ def err_no_matching_local_friend_suggest : Error< "cannot define friend function %0 in a local class definition; did you mean %3?">; def err_partial_specialization_friend : Error< "partial specialization cannot be declared as a friend">; -def err_qualified_friend_def : Error< - "friend function definition cannot be qualified with '%0'">; +def err_qualified_friend_def + : Error<"friend function definition cannot be qualified with %0">; def err_friend_def_in_local_class : Error< "friend function cannot be defined in a local class">; def err_friend_specialization_def : Error< @@ -1743,14 +1748,16 @@ def err_friend_not_first_in_declaration : Error< "'friend' must appear first in a non-function declaration">; def err_using_decl_friend : Error< "cannot befriend target of using declaration">; -def warn_template_qualified_friend_unsupported : Warning< - "dependent nested name specifier '%0' for friend class declaration is " - "not supported; turning off access control for %1">, - InGroup; -def warn_template_qualified_friend_ignored : Warning< - "dependent nested name specifier '%0' for friend template declaration is " - "not supported; ignoring this friend declaration">, - InGroup; +def warn_template_qualified_friend_unsupported + : Warning< + "dependent nested name specifier %0 for friend class declaration is " + "not supported; turning off access control for %1">, + InGroup; +def warn_template_qualified_friend_ignored + : Warning<"dependent nested name specifier %0 for friend template " + "declaration is " + "not supported; ignoring this friend declaration">, + InGroup; def ext_friend_tag_redecl_outside_namespace : ExtWarn< "unqualified friend declaration referring to type outside of the nearest " "enclosing namespace is a Microsoft extension; add a nested name specifier">, @@ -5210,11 +5217,16 @@ def err_template_unnamed_class : Error< def err_template_param_list_different_arity : Error< "%select{too few|too many}0 template parameters in template " "%select{|template parameter }1redeclaration">; +def note_template_param_list_different_arity : Note< + "%select{too few|too many}0 template parameters in template template " + "argument">; def note_template_prev_declaration : Note< "previous template %select{declaration|template parameter}0 is here">; def err_template_param_different_kind : Error< "template parameter has a different kind in template " "%select{|template parameter }0redeclaration">; +def note_template_param_different_kind : Note< + "template parameter has a different kind in template argument">; def err_invalid_decl_specifier_in_nontype_parm : Error< "invalid declaration specifier in template non-type parameter">; @@ -5223,6 +5235,8 @@ def err_template_nontype_parm_different_type : Error< "template non-type parameter has a different type %0 in template " "%select{|template parameter }1redeclaration">; +def note_template_nontype_parm_different_type : Note< + "template non-type parameter has a different type %0 in template argument">; def note_template_nontype_parm_prev_declaration : Note< "previous non-type template parameter with type %0 is here">; def err_template_nontype_parm_bad_type : Error< @@ -5313,15 +5327,10 @@ def err_template_missing_args : Error< "%select{class template|function template|variable template|alias template|" "template template parameter|concept|template}0 %1 requires template " "arguments">; -def err_template_param_missing_arg : Error< - "missing template argument for template parameter">; -def err_template_template_param_missing_param : Error< - "no template parameter in this template template parameter " - "corresponds to non-defaulted template parameter of argument template">; -def err_template_too_many_args : Error< - "too many template arguments for " +def err_template_arg_list_different_arity : Error< + "%select{too few|too many}0 template arguments for " "%select{class template|function template|variable template|alias template|" - "template template parameter|concept|template}0 %1">; + "template template parameter|concept|template}1 %2">; def note_template_decl_here : Note<"template is declared here">; def note_template_decl_external : Note< "template declaration from hidden source: %0">; @@ -5359,8 +5368,11 @@ def err_template_arg_not_valid_template : Error< "template parameter">; def note_template_arg_refers_here_func : Note< "template argument refers to function template %0, here">; +def err_template_arg_template_params_mismatch : Error< + "template template argument has different template parameters than its " + "corresponding template template parameter">; def note_template_arg_template_params_mismatch : Note< - "template template argument is incompatible with its " + "template template argument has different template parameters than its " "corresponding template template parameter">; def err_non_deduced_mismatch : Error< "could not match %diff{$ against $|types}0,1">; @@ -5551,9 +5563,10 @@ def ext_template_spec_extra_headers : ExtWarn< def note_explicit_template_spec_does_not_need_header : Note< "'template<>' header not required for explicitly-specialized class %0 " "declared here">; -def err_template_qualified_declarator_no_match : Error< - "nested name specifier '%0' for declaration does not refer into a class, " - "class template or class template partial specialization">; +def err_template_qualified_declarator_no_match + : Error<"nested name specifier %0 for declaration does not refer into a " + "class, " + "class template or class template partial specialization">; def err_specialize_member_of_template : Error< "cannot specialize %select{|(with 'template<>') }0a member of an " "unspecialized template">; @@ -5853,13 +5866,13 @@ def note_typename_member_refers_here : Note< "referenced member %0 is declared here">; def note_typename_refers_here : Note< "referenced %0 is declared here">; -def err_typename_missing : Error< - "missing 'typename' prior to dependent type name '%0%1'">; -def err_typename_missing_template : Error< - "missing 'typename' prior to dependent type template name '%0%1'">; -def ext_typename_missing : ExtWarn< - "missing 'typename' prior to dependent type name '%0%1'">, - InGroup>; +def err_typename_missing + : Error<"missing 'typename' prior to dependent type name %0">; +def err_typename_missing_template + : Error<"missing 'typename' prior to dependent type template name %0">; +def ext_typename_missing + : ExtWarn<"missing 'typename' prior to dependent type name %0">, + InGroup>; def ext_typename_outside_of_template : ExtWarn< "'typename' occurs outside of a template">, InGroup; def warn_cxx98_compat_typename_outside_of_template : Warning< @@ -5873,9 +5886,10 @@ def note_using_value_decl_missing_typename : Note< def warn_cxx17_compat_implicit_typename : Warning<"use of implicit 'typename' is " "incompatible with C++ standards before C++20">, InGroup, DefaultIgnore; -def ext_implicit_typename : ExtWarn<"missing 'typename' prior to dependent " - "type name %0%1; implicit 'typename' is a C++20 extension">, - InGroup; +def ext_implicit_typename + : ExtWarn<"missing 'typename' prior to dependent " + "type name %0; implicit 'typename' is a C++20 extension">, + InGroup; def err_template_kw_refers_to_non_template : Error< "%0%select{| following the 'template' keyword}1 " @@ -5885,12 +5899,13 @@ def note_template_kw_refers_to_non_template : Note< def err_template_kw_refers_to_dependent_non_template : Error< "%0%select{| following the 'template' keyword}1 " "cannot refer to a dependent template">; -def err_template_kw_refers_to_type_template : Error< - "'%0%1' is expected to be a non-type template, but instantiated to a %select{class|type alias}2 template">; +def err_template_kw_refers_to_type_template + : Error<"%0 is expected to be a non-type template, but instantiated to a " + "%select{class|type alias}1 template">; def note_referenced_type_template : Note< "%select{class|type alias}0 template declared here">; -def err_template_kw_missing : Error< - "missing 'template' keyword prior to dependent template name '%0%1'">; +def err_template_kw_missing + : Error<"missing 'template' keyword prior to dependent template name %0">; def ext_template_outside_of_template : ExtWarn< "'template' keyword outside of a template">, InGroup; def warn_cxx98_compat_template_outside_of_template : Warning< @@ -5922,6 +5937,10 @@ def err_template_parameter_pack_non_pack : Error< "%select{template type|non-type template|template template}0 parameter" "%select{| pack}1 conflicts with previous %select{template type|" "non-type template|template template}0 parameter%select{ pack|}1">; +def note_template_parameter_pack_non_pack : Note< + "%select{template type|non-type template|template template}0 parameter" + "%select{| pack}1 does not match %select{template type|non-type template" + "|template template}0 parameter%select{ pack|}1 in template argument">; def note_template_parameter_pack_here : Note< "previous %select{template type|non-type template|template template}0 " "parameter%select{| pack}1 declared here">; @@ -7879,8 +7898,8 @@ def err_nogetter_property_incdec : Error< "no getter method %1 for %select{increment|decrement}0 of property">; def err_no_subobject_property_setting : Error< "expression is not assignable">; -def err_qualified_objc_access : Error< - "%select{property|instance variable}0 access cannot be qualified with '%1'">; +def err_qualified_objc_access : Error<"%select{property|instance variable}0 " + "access cannot be qualified with %1">; def ext_freestanding_complex : Extension< "complex numbers are an extension in a freestanding C99 implementation">; @@ -9830,8 +9849,8 @@ def note_non_usual_function_declared_here : Note< // C++ literal operators def err_literal_operator_outside_namespace : Error< "literal operator %0 must be in a namespace or global scope">; -def err_literal_operator_id_outside_namespace : Error< - "non-namespace scope '%0' cannot have a literal operator member">; +def err_literal_operator_id_outside_namespace + : Error<"non-namespace scope %0 cannot have a literal operator member">; def err_literal_operator_default_argument : Error< "literal operator cannot have a default argument">; def err_literal_operator_bad_param_count : Error< @@ -10425,8 +10444,13 @@ def warn_type_safety_null_pointer_required : Warning< "specified %0 type tag requires a null pointer">, InGroup; // Generic selections. -def err_assoc_type_incomplete : Error< - "type %0 in generic association incomplete">; +def ext_assoc_type_incomplete : Extension< + "incomplete type %0 in a '_Generic' association is a C2y extension">, + InGroup; +def warn_c2y_compat_assoc_type_incomplete : Warning< + "use of incomplete type %0 in a '_Generic' association is incompatible with " + "C standards before C2y">, + InGroup, DefaultIgnore; def err_assoc_type_nonobject : Error< "type %0 in generic association not an object type">; def err_assoc_type_variably_modified : Error< @@ -12702,6 +12726,10 @@ def err_hlsl_param_qualifier_mismatch : def err_hlsl_vector_compound_assignment_truncation : Error< "left hand operand of type %0 to compound assignment cannot be truncated " "when used with right hand operand of type %1">; +def err_hlsl_builtin_scalar_vector_mismatch + : Error< + "%select{all|second and third}0 arguments to %1 must be of scalar or " + "vector type with matching scalar element type%diff{: $ vs $|}2,3">; def warn_hlsl_impcast_vector_truncation : Warning< "implicit conversion truncates vector: %0 to %1">, InGroup; @@ -12820,9 +12848,6 @@ def warn_acc_routine_unimplemented : Warning<"OpenACC construct 'routine' with implicit function not yet " "implemented, pragma ignored">, InGroup; -def warn_acc_clause_unimplemented - : Warning<"OpenACC clause '%0' not yet implemented, clause ignored">, - InGroup; def err_acc_construct_appertainment : Error<"OpenACC construct '%0' cannot be used here; it can only " "be used in a statement context">; @@ -13063,6 +13088,9 @@ def err_acc_routine_overload_set def err_acc_magic_static_in_routine : Error<"function static variables are not permitted in functions to which " "an OpenACC 'routine' directive applies">; +def err_acc_duplicate_bind + : Error<"multiple 'routine' directives with 'bind' clauses are not " + "permitted to refer to the same function">; // AMDGCN builtins diagnostics def err_amdgcn_global_load_lds_size_invalid_value : Error<"invalid size value">; diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 383440ddbc0ea..3879cc7942877 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -515,7 +515,7 @@ VALUE_LANGOPT(FuchsiaAPILevel, 32, 0, "Fuchsia API level") // on large _BitInts. BENIGN_VALUE_LANGOPT(MaxBitIntWidth, 32, 128, "Maximum width of a _BitInt") -COMPATIBLE_LANGOPT(IncrementalExtensions, 1, 0, " True if we want to process statements" +COMPATIBLE_LANGOPT(IncrementalExtensions, 1, 0, "True if we want to process statements " "on the global scope, ignore EOF token and continue later on (thus " "avoid tearing the Lexer and etc. down). Controlled by " "-fincremental-extensions.") diff --git a/clang/include/clang/Basic/OpenACCClauses.def b/clang/include/clang/Basic/OpenACCClauses.def index f04965363f25e..a19ee69d3d190 100644 --- a/clang/include/clang/Basic/OpenACCClauses.def +++ b/clang/include/clang/Basic/OpenACCClauses.def @@ -24,6 +24,7 @@ VISIT_CLAUSE(Auto) VISIT_CLAUSE(Async) VISIT_CLAUSE(Attach) +VISIT_CLAUSE(Bind) VISIT_CLAUSE(Collapse) VISIT_CLAUSE(Copy) CLAUSE_ALIAS(PCopy, Copy, true) diff --git a/clang/include/clang/Basic/OpenMPKinds.def b/clang/include/clang/Basic/OpenMPKinds.def index 76a861f416fd5..c4a2cca731607 100644 --- a/clang/include/clang/Basic/OpenMPKinds.def +++ b/clang/include/clang/Basic/OpenMPKinds.def @@ -172,6 +172,7 @@ OPENMP_MAP_MODIFIER_KIND(close) OPENMP_MAP_MODIFIER_KIND(mapper) OPENMP_MAP_MODIFIER_KIND(iterator) OPENMP_MAP_MODIFIER_KIND(present) +OPENMP_MAP_MODIFIER_KIND(self) // This is an OpenMP extension for the sake of OpenACC support. OPENMP_MAP_MODIFIER_KIND(ompx_hold) diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index b65797e40d5f9..017ae0c53a984 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -26,6 +26,10 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { CIRBaseBuilderTy(mlir::MLIRContext &mlirContext) : mlir::OpBuilder(&mlirContext) {} + cir::ConstantOp getConstant(mlir::Location loc, mlir::TypedAttr attr) { + return create(loc, attr.getType(), attr); + } + cir::ConstantOp getBool(bool state, mlir::Location loc) { return create(loc, getBoolTy(), getCIRBoolAttr(state)); } diff --git a/clang/include/clang/CIR/Dialect/CMakeLists.txt b/clang/include/clang/CIR/Dialect/CMakeLists.txt index f33061b2d87cf..3d4e6586e1c62 100644 --- a/clang/include/clang/CIR/Dialect/CMakeLists.txt +++ b/clang/include/clang/CIR/Dialect/CMakeLists.txt @@ -1 +1,7 @@ add_subdirectory(IR) + +set(LLVM_TARGET_DEFINITIONS Passes.td) +mlir_tablegen(Passes.h.inc -gen-pass-decls -name CIR) +mlir_tablegen(Passes.capi.h.inc -gen-pass-capi-header --prefix CIR) +mlir_tablegen(Passes.capi.cpp.inc -gen-pass-capi-impl --prefix CIR) +add_public_tablegen_target(MLIRCIRPassIncGen) diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index ece04c225e322..7b3741de29075 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -54,6 +54,20 @@ def CIR_BoolAttr : CIR_Attr<"Bool", "bool", [TypedAttrInterface]> { }]; } +//===----------------------------------------------------------------------===// +// ZeroAttr +//===----------------------------------------------------------------------===// + +def ZeroAttr : CIR_Attr<"Zero", "zero", [TypedAttrInterface]> { + let summary = "Attribute to represent zero initialization"; + let description = [{ + The ZeroAttr is used to indicate zero initialization on structs. + }]; + + let parameters = (ins AttributeSelfTypeParameter<"">:$type); + let assemblyFormat = [{}]; +} + //===----------------------------------------------------------------------===// // UndefAttr //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index e2ab50c78ec2d..77c43e5ace64a 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -428,6 +428,46 @@ def ScopeOp : CIR_Op<"scope", [ ]; } +//===----------------------------------------------------------------------===// +// BrOp +//===----------------------------------------------------------------------===// + +def BrOp : CIR_Op<"br", + [DeclareOpInterfaceMethods, + Pure, Terminator]> { + let summary = "Unconditional branch"; + let description = [{ + The `cir.br` branches unconditionally to a block. Used to represent C/C++ + goto's and general block branching. + + Note that for source level `goto`'s crossing scope boundaries, those are + usually represented with the "symbolic" `cir.goto` operation. + + Example: + + ```mlir + ... + cir.br ^bb3 + ^bb3: + cir.return + ``` + }]; + + let builders = [ + OpBuilder<(ins "mlir::Block *":$dest, + CArg<"mlir::ValueRange", "{}">:$destOperands), [{ + $_state.addSuccessors(dest); + $_state.addOperands(destOperands); + }]> + ]; + + let arguments = (ins Variadic:$destOperands); + let successors = (successor AnySuccessor:$dest); + let assemblyFormat = [{ + $dest (`(` $destOperands^ `:` type($destOperands) `)`)? attr-dict + }]; +} + //===----------------------------------------------------------------------===// // GlobalOp //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/Passes.h b/clang/include/clang/CIR/Dialect/Passes.h new file mode 100644 index 0000000000000..b691849dfc563 --- /dev/null +++ b/clang/include/clang/CIR/Dialect/Passes.h @@ -0,0 +1,39 @@ +//===- Passes.h - CIR pass entry points -------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This header file defines prototypes that expose pass constructors. +// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_CIR_DIALECT_PASSES_H +#define CLANG_CIR_DIALECT_PASSES_H + +#include "mlir/Pass/Pass.h" + +namespace clang { +class ASTContext; +} +namespace mlir { + +std::unique_ptr createCIRFlattenCFGPass(); + +void populateCIRPreLoweringPasses(mlir::OpPassManager &pm); + +//===----------------------------------------------------------------------===// +// Registration +//===----------------------------------------------------------------------===// + +void registerCIRDialectTranslation(mlir::MLIRContext &context); + +/// Generate the code for registering passes. +#define GEN_PASS_REGISTRATION +#include "clang/CIR/Dialect/Passes.h.inc" + +} // namespace mlir + +#endif // CLANG_CIR_DIALECT_PASSES_H diff --git a/clang/include/clang/CIR/Dialect/Passes.td b/clang/include/clang/CIR/Dialect/Passes.td new file mode 100644 index 0000000000000..84b7ecba2630a --- /dev/null +++ b/clang/include/clang/CIR/Dialect/Passes.td @@ -0,0 +1,28 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_CIR_DIALECT_PASSES_TD +#define CLANG_CIR_DIALECT_PASSES_TD + +include "mlir/Pass/PassBase.td" + +def CIRFlattenCFG : Pass<"cir-flatten-cfg"> { + let summary = "Produces flatten CFG"; + let description = [{ + This pass transforms CIR by inlining all the nested regions. Thus, + the following conditions are true after the pass applied: + - there are no nested regions in any function body + - all the blocks in a function belong to the parent region + In other words, this pass removes such CIR operations like IfOp, LoopOp, + ScopeOp and etc. and produces a flat CIR. + }]; + let constructor = "mlir::createCIRFlattenCFGPass()"; + let dependentDialects = ["cir::CIRDialect"]; +} + +#endif // CLANG_CIR_DIALECT_PASSES_TD diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 6f845b7689e51..ddfe654009644 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -41,16 +41,17 @@ struct MissingFeatures { static bool supportComdat() { return false; } // Load/store attributes - static bool opLoadThreadLocal() { return false; } + static bool opLoadStoreThreadLocal() { return false; } static bool opLoadEmitScalarRangeCheck() { return false; } static bool opLoadBooleanRepresentation() { return false; } static bool opLoadStoreTbaa() { return false; } static bool opLoadStoreMemOrder() { return false; } static bool opLoadStoreVolatile() { return false; } static bool opLoadStoreAlignment() { return false; } + static bool opLoadStoreAtomic() { return false; } + static bool opLoadStoreObjC() { return false; } // AllocaOp handling - static bool opAllocaVarDeclContext() { return false; } static bool opAllocaStaticLocal() { return false; } static bool opAllocaNonGC() { return false; } static bool opAllocaImpreciseLifetime() { return false; } @@ -61,6 +62,7 @@ struct MissingFeatures { static bool opAllocaReference() { return false; } static bool opAllocaAnnotations() { return false; } static bool opAllocaDynAllocSize() { return false; } + static bool opAllocaCaptureByInit() { return false; } // FuncOp handling static bool opFuncOpenCLKernelMetadata() { return false; } @@ -76,6 +78,11 @@ struct MissingFeatures { static bool constructABIArgDirectExtend() { return false; } static bool opGlobalViewAttr() { return false; } static bool lowerModeOptLevel() { return false; } + static bool opTBAA() { return false; } + static bool objCLifetime() { return false; } + static bool emitNullabilityCheck() { return false; } + static bool astVarDeclInterface() { return false; } + static bool stackSaveOp() { return false; } }; } // namespace cir diff --git a/clang/include/clang/Driver/Action.h b/clang/include/clang/Driver/Action.h index e5307b0fcedd5..92bb19314e3d6 100644 --- a/clang/include/clang/Driver/Action.h +++ b/clang/include/clang/Driver/Action.h @@ -75,9 +75,10 @@ class Action { LinkerWrapperJobClass, StaticLibJobClass, BinaryAnalyzeJobClass, + BinaryTranslatorJobClass, JobClassFirst = PreprocessJobClass, - JobClassLast = BinaryAnalyzeJobClass + JobClassLast = BinaryTranslatorJobClass }; // The offloading kind determines if this action is binded to a particular @@ -675,6 +676,17 @@ class BinaryAnalyzeJobAction : public JobAction { } }; +class BinaryTranslatorJobAction : public JobAction { + void anchor() override; + +public: + BinaryTranslatorJobAction(Action *Input, types::ID Type); + + static bool classof(const Action *A) { + return A->getKind() == BinaryTranslatorJobClass; + } +}; + } // namespace driver } // namespace clang diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index d0414aba35209..e69cd6b833c3a 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -876,7 +876,7 @@ def Wa_COMMA : CommaJoined<["-"], "Wa,">, MetaVarName<"">; def warning_suppression_mappings_EQ : Joined<["--"], "warning-suppression-mappings=">, Group, - HelpText<"File containing diagnostic suppresion mappings. See user manual " + HelpText<"File containing diagnostic suppression mappings. See user manual " "for file format.">, Visibility<[ClangOption, CC1Option]>; def Wall : Flag<["-"], "Wall">, Group, Flags<[HelpHidden]>, Visibility<[ClangOption, CC1Option, FlangOption]>; @@ -941,7 +941,7 @@ def Xarch__ target matches the specified architecture. This can be used with the target CPU, triple architecture, or offloading host and device. It is most useful for separating behavior undesirable on one of the targets when combining many - compilation jobs, as is commong with offloading. For example, -Xarch_x86_64, + compilation jobs, as is common with offloading. For example, -Xarch_x86_64, -Xarch_gfx90a, and -Xarch_device are all valid selectors. -Xarch_device will forward the argument to the offloading device while -Xarch_host will target the host system, which can be used to suppress incompatible GPU arguments.}]>, @@ -1678,7 +1678,7 @@ def fsample_profile_use_profi : Flag<["-"], "fsample-profile-use-profi">, HelpText<"Use profi to infer block and edge counts">, DocBrief<[{Infer block and edge counts. If the profiles have errors or missing blocks caused by sampling, profile inference (profi) can convert - basic block counts to branch probabilites to fix them by extended + basic block counts to branch probabilities to fix them by extended and re-engineered classic MCMF (min-cost max-flow) approach.}]>; def fno_profile_sample_accurate : Flag<["-"], "fno-profile-sample-accurate">, Group; def fno_auto_profile : Flag<["-"], "fno-auto-profile">, Group, @@ -4725,9 +4725,9 @@ def mno_long_calls : Flag<["-"], "mno-long-calls">, Group, HelpText<"Restore the default behaviour of not generating long calls">; } // let Flags = [TargetSpecific] def mexecute_only : Flag<["-"], "mexecute-only">, Group, - HelpText<"Disallow generation of data access to code sections (ARM only)">; + HelpText<"Disallow generation of data access to code sections (AArch64/ARM only)">; def mno_execute_only : Flag<["-"], "mno-execute-only">, Group, - HelpText<"Allow generation of data access to code sections (ARM only)">; + HelpText<"Allow generation of data access to code sections (AArch64/ARM only)">; let Flags = [TargetSpecific] in { def mtp_mode_EQ : Joined<["-"], "mtp=">, Group, Values<"soft,cp15,tpidrurw,tpidruro,tpidrprw,el0,el1,el2,el3,tpidr_el0,tpidr_el1,tpidr_el2,tpidr_el3,tpidrro_el0,auto">, HelpText<"Thread pointer access method. " @@ -9085,6 +9085,7 @@ def : Option<["/", "-"], "Qembed_debug", KIND_FLAG>, Group, HelpText<"Embed PDB in shader container (ignored)">; def spirv : DXCFlag<"spirv">, HelpText<"Generate SPIR-V code">; +def metal : DXCFlag<"metal">, HelpText<"Generate Metal library">; def fspv_target_env_EQ : Joined<["-"], "fspv-target-env=">, Group, HelpText<"Specify the target environment">, Values<"vulkan1.2, vulkan1.3">; diff --git a/clang/include/clang/Frontend/CompilerInvocation.h b/clang/include/clang/Frontend/CompilerInvocation.h index 9daa0a1ecf948..1e4d2da86c2be 100644 --- a/clang/include/clang/Frontend/CompilerInvocation.h +++ b/clang/include/clang/Frontend/CompilerInvocation.h @@ -269,7 +269,6 @@ class CompilerInvocation : public CompilerInvocationBase { /// @{ using CompilerInvocationBase::LangOpts; using CompilerInvocationBase::TargetOpts; - using CompilerInvocationBase::DiagnosticOpts; std::shared_ptr getHeaderSearchOptsPtr() { return HSOpts; } diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 049156e266c70..1c8caf55a546d 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -3783,8 +3783,9 @@ class Parser : public CodeCompletionHandler { OpenACCWaitParseInfo ParseOpenACCWaitArgument(SourceLocation Loc, bool IsDirective); /// Parses the clause of the 'bind' argument, which can be a string literal or - /// an ID expression. - ExprResult ParseOpenACCBindClauseArgument(); + /// an identifier. + std::variant + ParseOpenACCBindClauseArgument(); /// A type to represent the state of parsing after an attempt to parse an /// OpenACC int-expr. This is useful to determine whether an int-expr list can diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 08621e633b55c..9ac26d8728446 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4008,7 +4008,7 @@ class Sema final : public SemaBase { /// Perform ODR-like check for C/ObjC when merging tag types from modules. /// Differently from C++, actually parse the body and reject / error out /// in case of a structural mismatch. - bool ActOnDuplicateDefinition(Decl *Prev, SkipBodyInfo &SkipBody); + bool ActOnDuplicateDefinition(Scope *S, Decl *Prev, SkipBodyInfo &SkipBody); typedef void *SkippedDefinitionContext; @@ -4132,6 +4132,12 @@ class Sema final : public SemaBase { void MergeTypedefNameDecl(Scope *S, TypedefNameDecl *New, LookupResult &OldDecls); + /// CleanupMergedEnum - We have just merged the decl 'New' by making another + /// definition visible. + /// This method performs any necessary cleanup on the parser state to discard + /// child nodes from newly parsed decl we are retiring. + void CleanupMergedEnum(Scope *S, Decl *New); + /// MergeFunctionDecl - We just parsed a function 'New' from /// declarator D which has the same name and scope as a previous /// declaration 'Old'. Figure out how to resolve this situation, @@ -11357,14 +11363,16 @@ class Sema final : public SemaBase { /// The context in which we are checking a template parameter list. enum TemplateParamListContext { - TPC_ClassTemplate, - TPC_VarTemplate, + // For this context, Class, Variable, TypeAlias, and non-pack Template + // Template Parameters are treated uniformly. + TPC_Other, + TPC_FunctionTemplate, TPC_ClassTemplateMember, TPC_FriendClassTemplate, TPC_FriendFunctionTemplate, TPC_FriendFunctionTemplateDefinition, - TPC_TypeAliasTemplate + TPC_TemplateTemplateParameterPack, }; /// Checks the validity of a template parameter list, possibly @@ -11827,7 +11835,7 @@ class Sema final : public SemaBase { bool *ConstraintsNotSatisfied = nullptr); bool CheckTemplateTypeArgument( - TemplateArgumentLoc &Arg, + TemplateTypeParmDecl *Param, TemplateArgumentLoc &Arg, SmallVectorImpl &SugaredConverted, SmallVectorImpl &CanonicalConverted); @@ -11863,13 +11871,9 @@ class Sema final : public SemaBase { bool PartialOrdering, bool *StrictPackMatch); - /// Print the given named declaration to a string, - /// using the current PrintingPolicy, except that - /// TerseOutput will always be set. - SmallString<128> toTerseString(const NamedDecl &D) const; - void NoteTemplateLocation(const NamedDecl &Decl, std::optional ParamRange = {}); + void NoteTemplateParameterLocation(const NamedDecl &Decl); /// Given a non-type template argument that refers to a /// declaration and the type of its corresponding non-type template @@ -11984,13 +11988,15 @@ class Sema final : public SemaBase { bool TemplateParameterListsAreEqual( const TemplateCompareNewDeclInfo &NewInstFrom, TemplateParameterList *New, const NamedDecl *OldInstFrom, TemplateParameterList *Old, bool Complain, - TemplateParameterListEqualKind Kind); + TemplateParameterListEqualKind Kind, + SourceLocation TemplateArgLoc = SourceLocation()); - bool TemplateParameterListsAreEqual(TemplateParameterList *New, - TemplateParameterList *Old, bool Complain, - TemplateParameterListEqualKind Kind) { + bool TemplateParameterListsAreEqual( + TemplateParameterList *New, TemplateParameterList *Old, bool Complain, + TemplateParameterListEqualKind Kind, + SourceLocation TemplateArgLoc = SourceLocation()) { return TemplateParameterListsAreEqual(nullptr, New, nullptr, Old, Complain, - Kind); + Kind, TemplateArgLoc); } /// Check whether a template can be declared within this scope. @@ -12870,11 +12876,6 @@ class Sema final : public SemaBase { /// We are performing partial ordering for template template parameters. PartialOrderingTTP, - - /// We are Checking a Template Parameter, so for any diagnostics which - /// occur in this scope, we will add a context note which points to this - /// template parameter. - CheckTemplateParameter, } Kind; /// Was the enclosing context a non-instantiation SFINAE context? @@ -13102,11 +13103,6 @@ class Sema final : public SemaBase { PartialOrderingTTP, TemplateDecl *PArg, SourceRange InstantiationRange = SourceRange()); - struct CheckTemplateParameter {}; - /// \brief Note that we are checking a template parameter. - InstantiatingTemplate(Sema &SemaRef, CheckTemplateParameter, - NamedDecl *Param); - /// Note that we have finished instantiating this template. void Clear(); @@ -13140,13 +13136,6 @@ class Sema final : public SemaBase { InstantiatingTemplate &operator=(const InstantiatingTemplate &) = delete; }; - /// For any diagnostics which occur within its scope, adds a context note - /// pointing to the declaration of the template parameter. - struct CheckTemplateParameterRAII : InstantiatingTemplate { - CheckTemplateParameterRAII(Sema &S, NamedDecl *Param) - : InstantiatingTemplate(S, CheckTemplateParameter(), Param) {} - }; - bool SubstTemplateArgument(const TemplateArgumentLoc &Input, const MultiLevelTemplateArgumentList &TemplateArgs, TemplateArgumentLoc &Output, diff --git a/clang/include/clang/Sema/SemaOpenACC.h b/clang/include/clang/Sema/SemaOpenACC.h index 748dcdd251a92..358dec4cadb72 100644 --- a/clang/include/clang/Sema/SemaOpenACC.h +++ b/clang/include/clang/Sema/SemaOpenACC.h @@ -261,10 +261,14 @@ class SemaOpenACC : public SemaBase { SmallVector GangKinds; SmallVector IntExprs; }; + struct BindDetails { + std::variant + Argument; + }; std::variant + ReductionDetails, CollapseDetails, GangDetails, BindDetails> Details = std::monostate{}; public: @@ -468,6 +472,13 @@ class SemaOpenACC : public SemaBase { return std::get(Details).Archs; } + std::variant + getBindDetails() const { + assert(ClauseKind == OpenACCClauseKind::Bind && + "Only 'bind' has bind details"); + return std::get(Details).Argument; + } + void setLParenLoc(SourceLocation EndLoc) { LParenLoc = EndLoc; } void setEndLoc(SourceLocation EndLoc) { ClauseRange.setEnd(EndLoc); } @@ -652,6 +663,14 @@ class SemaOpenACC : public SemaBase { "Only 'collapse' has collapse details"); Details = CollapseDetails{IsForce, LoopCount}; } + + void setBindDetails( + std::variant + Arg) { + assert(ClauseKind == OpenACCClauseKind::Bind && + "Only 'bind' has bind details"); + Details = BindDetails{Arg}; + } }; SemaOpenACC(Sema &S); diff --git a/clang/include/clang/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h index 64f0cfa0676af..7fc9b64f0ae65 100644 --- a/clang/include/clang/Sema/SemaOpenMP.h +++ b/clang/include/clang/Sema/SemaOpenMP.h @@ -1112,6 +1112,10 @@ class SemaOpenMP : public SemaBase { OpenMPAtomicDefaultMemOrderClauseKind Kind, SourceLocation KindLoc, SourceLocation StartLoc, SourceLocation LParenLoc, SourceLocation EndLoc); + /// Called on well-formed 'self_maps' clause. + OMPClause *ActOnOpenMPSelfMapsClause(SourceLocation StartLoc, + SourceLocation EndLoc); + /// Called on well-formed 'at' clause. OMPClause *ActOnOpenMPAtClause(OpenMPAtClauseKind Kind, SourceLocation KindLoc, diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h index ad291d0948b57..7371a679988e9 100644 --- a/clang/include/clang/Serialization/ASTWriter.h +++ b/clang/include/clang/Serialization/ASTWriter.h @@ -571,7 +571,7 @@ class ASTWriter : public ASTDeserializationListener, std::pair createSignature() const; ASTFileSignature createSignatureForNamedModule() const; - void WriteInputFiles(SourceManager &SourceMgr, HeaderSearchOptions &HSOpts); + void WriteInputFiles(SourceManager &SourceMgr); void WriteSourceManagerBlock(SourceManager &SourceMgr); void WritePreprocessor(const Preprocessor &PP, bool IsModule); void WriteHeaderSearch(const HeaderSearch &HS); diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index c8895db914d13..7ee2ec1aaf09f 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -385,7 +385,7 @@ def TrustReturnsNonnullChecker : Checker<"TrustReturnsNonnull">, } // end "apiModeling" //===----------------------------------------------------------------------===// -// Evaluate "builtin" functions. +// Evaluate "builtin" functions and assumptions. //===----------------------------------------------------------------------===// let ParentPackage = CoreBuiltin in { @@ -399,6 +399,10 @@ def BuiltinFunctionChecker : Checker<"BuiltinFunctions">, HelpText<"Evaluate compiler builtin functions (e.g., alloca())">, Documentation; +def AssumeModeling : Checker<"AssumeModeling">, + HelpText<"Model compiler builtin assume functions and the assume attribute">, + Documentation; + } // end "core.builtin" //===----------------------------------------------------------------------===// @@ -1765,6 +1769,14 @@ def NoUncheckedPtrMemberChecker : Checker<"NoUncheckedPtrMemberChecker">, HelpText<"Check for no unchecked member variables.">, Documentation; +def NoUnretainedMemberChecker : Checker<"NoUnretainedMemberChecker">, + HelpText<"Check for no unretained member variables.">, + Documentation; + +def UnretainedLambdaCapturesChecker : Checker<"UnretainedLambdaCapturesChecker">, + HelpText<"Check unretained lambda captures.">, + Documentation; + def UncountedCallArgsChecker : Checker<"UncountedCallArgsChecker">, HelpText<"Check uncounted call arguments.">, Documentation; diff --git a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h index e50afd6d0da7e..97cf7eda0ae2f 100644 --- a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h +++ b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h @@ -41,19 +41,19 @@ class BugType { Checker(nullptr), SuppressOnSink(SuppressOnSink) {} BugType(const CheckerBase *Checker, StringRef Desc, StringRef Cat = categories::LogicError, bool SuppressOnSink = false) - : CheckerName(Checker->getCheckerName()), Description(Desc), - Category(Cat), Checker(Checker), SuppressOnSink(SuppressOnSink) {} + : CheckerName(), Description(Desc), Category(Cat), Checker(Checker), + SuppressOnSink(SuppressOnSink) {} virtual ~BugType() = default; StringRef getDescription() const { return Description; } StringRef getCategory() const { return Category; } StringRef getCheckerName() const { - // FIXME: This is a workaround to ensure that the correct checerk name is + // FIXME: This is a workaround to ensure that the correct checker name is // used. The checker names are set after the constructors are run. // In case the BugType object is initialized in the checker's ctor // the CheckerName field will be empty. To circumvent this problem we use // CheckerBase whenever it is possible. - StringRef Ret = Checker ? Checker->getCheckerName() : CheckerName; + StringRef Ret = Checker ? Checker->getName() : CheckerName; assert(!Ret.empty() && "Checker name is not set properly."); return Ret; } diff --git a/clang/include/clang/StaticAnalyzer/Core/Checker.h b/clang/include/clang/StaticAnalyzer/Core/Checker.h index 2ec54a837c42c..4d9b33cc559b8 100644 --- a/clang/include/clang/StaticAnalyzer/Core/Checker.h +++ b/clang/include/clang/StaticAnalyzer/Core/Checker.h @@ -490,7 +490,7 @@ class CheckerBase : public ProgramPointTag { public: StringRef getTagDescription() const override; - CheckerNameRef getCheckerName() const; + CheckerNameRef getName() const; /// See CheckerManager::runCheckersForPrintState. virtual void printState(raw_ostream &Out, ProgramStateRef State, diff --git a/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h b/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h index 7d3120b5395c4..929ce96824e95 100644 --- a/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h +++ b/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -113,7 +113,6 @@ class CheckerNameRef { public: CheckerNameRef() = default; - StringRef getName() const { return Name; } operator StringRef() const { return Name; } }; diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h index 168983fd5cb68..bb33a6912bec7 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h @@ -7,7 +7,7 @@ //===----------------------------------------------------------------------===// // // This file defines CheckerContext that provides contextual info for -// path-sensitive checkers. +// path-sensitive checkers. // //===----------------------------------------------------------------------===// @@ -152,7 +152,7 @@ class CheckerContext { } /// Returns true if the value of \p E is greater than or equal to \p - /// Val under unsigned comparison + /// Val under unsigned comparison. bool isGreaterOrEqual(const Expr *E, unsigned long long Val); /// Returns true if the value of \p E is negative. @@ -392,7 +392,7 @@ class CheckerContext { /// hardened variant that's not yet covered by it. static bool isHardenedVariantOf(const FunctionDecl *FD, StringRef Name); - /// Depending on wither the location corresponds to a macro, return + /// Depending on whether the location corresponds to a macro, return /// either the macro name or the token spelling. /// /// This could be useful when checkers' logic depends on whether a function diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h index 80b79fd4e928f..b2b4e8729af25 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h @@ -156,7 +156,7 @@ class CoreEngine { void dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, const WorkListUnit& WU); - // Functions for external checking of whether we have unfinished work + // Functions for external checking of whether we have unfinished work. bool wasBlockAborted() const { return !blocksAborted.empty(); } bool wasBlocksExhausted() const { return !blocksExhausted.empty(); } bool hasWorkRemaining() const { return wasBlocksExhausted() || diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h index 2fb05ac46e8fa..967b25e5696f6 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h @@ -7,7 +7,7 @@ //===----------------------------------------------------------------------===// // // This file defines the template classes ExplodedNode and ExplodedGraph, -// which represent a path-sensitive, intra-procedural "exploded graph." +// which represent a path-sensitive, intra-procedural "exploded graph". // See "Precise interprocedural dataflow analysis via graph reachability" // by Reps, Horwitz, and Sagiv // (http://portal.acm.org/citation.cfm?id=199462) for the definition of an @@ -426,8 +426,8 @@ class ExplodedGraph { /// /// \param Nodes The nodes which must appear in the final graph. Presumably /// these are end-of-path nodes (i.e. they have no successors). - /// \param[out] ForwardMap A optional map from nodes in this graph to nodes in - /// the returned graph. + /// \param[out] ForwardMap An optional map from nodes in this graph to nodes + /// in the returned graph. /// \param[out] InverseMap An optional map from nodes in the returned graph to /// nodes in this graph. /// \returns The trimmed graph diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 804fc74b009df..5f855251b3cde 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -429,7 +429,7 @@ class ExprEngine { DataTag::Factory &getDataTags() { return Engine.getDataTags(); } - // Functions for external checking of whether we have unfinished work + // Functions for external checking of whether we have unfinished work. bool wasBlocksExhausted() const { return Engine.wasBlocksExhausted(); } bool hasEmptyWorkList() const { return !Engine.getWorkList()->hasWork(); } bool hasWorkRemaining() const { return Engine.hasWorkRemaining(); } @@ -498,11 +498,11 @@ class ExprEngine { void VisitInitListExpr(const InitListExpr *E, ExplodedNode *Pred, ExplodedNodeSet &Dst); - /// VisitAttributedStmt - Transfer function logic for AttributedStmt + /// VisitAttributedStmt - Transfer function logic for AttributedStmt. void VisitAttributedStmt(const AttributedStmt *A, ExplodedNode *Pred, ExplodedNodeSet &Dst); - /// VisitLogicalExpr - Transfer function logic for '&&', '||' + /// VisitLogicalExpr - Transfer function logic for '&&', '||'. void VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, ExplodedNodeSet &Dst); @@ -510,7 +510,7 @@ class ExprEngine { void VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, ExplodedNodeSet &Dst); - /// VisitAtomicExpr - Transfer function for builtin atomic expressions + /// VisitAtomicExpr - Transfer function for builtin atomic expressions. void VisitAtomicExpr(const AtomicExpr *E, ExplodedNode *Pred, ExplodedNodeSet &Dst); @@ -725,7 +725,7 @@ class ExprEngine { /// For `int arr[4]` this index can be 0,1,2,3. /// For `int arr2[3][3]` this index can be 0,1,...,7,8. /// A multi-dimensional array is also a continuous memory location in a - /// row major order, so for arr[0][0] Idx is 0 and for arr[2][2] Idx is 8. + /// row major order, so for arr[0][0] Idx is 0 and for arr[3][3] Idx is 8. SVal computeObjectUnderConstruction(const Expr *E, ProgramStateRef State, const NodeBuilderContext *BldrCtx, const LocationContext *LCtx, @@ -806,7 +806,7 @@ class ExprEngine { /// should inline, just by looking at the declaration of the function. bool mayInlineDecl(AnalysisDeclContext *ADC) const; - /// Checks our policies and decides weither the given call should be inlined. + /// Checks our policies and decides whether the given call should be inlined. bool shouldInlineCall(const CallEvent &Call, const Decl *D, const ExplodedNode *Pred, const EvalCallOptions &CallOpts = {}); @@ -902,8 +902,8 @@ class ExprEngine { ExplodedNodeSet &Dst); public: - /// Note whether this loop has any more iteratios to model. These methods are - /// essentially an interface for a GDM trait. Further reading in + /// Note whether this loop has any more iterations to model. These methods + // are essentially an interface for a GDM trait. Further reading in /// ExprEngine::VisitObjCForCollectionStmt(). [[nodiscard]] static ProgramStateRef setWhetherHasMoreIteration(ProgramStateRef State, @@ -967,7 +967,7 @@ class ExprEngine { const ConstructionContextItem &Item, const LocationContext *LC, SVal V); - /// Mark the object sa fully constructed, cleaning up the state trait + /// Mark the object as fully constructed, cleaning up the state trait /// that tracks objects under construction. static ProgramStateRef finishObjectConstruction(ProgramStateRef State, diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h index 208b32e376883..4d66e086a2c2c 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// // -// This file defines the state of the program along the analysisa path. +// This file defines the state of the program along the analysis path. // //===----------------------------------------------------------------------===// @@ -161,7 +161,6 @@ class ProgramState : public llvm::FoldingSetNode { /// is a mapping from locations to values. Store getStore() const { return store; } - /// getGDM - Return the generic data map associated with this state. GenericDataMap getGDM() const { return GDM; } @@ -382,7 +381,7 @@ class ProgramState : public llvm::FoldingSetNode { /// Returns UnknownVal() if none found. SVal getSVal(Loc LV, QualType T = QualType()) const; - /// Returns the "raw" SVal bound to LV before any value simplfication. + /// Returns the "raw" SVal bound to LV before any value simplification. SVal getRawSVal(Loc LV, QualType T= QualType()) const; /// Return the value bound to the specified location. diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h index fcc9c02999b3b..580b49a38dc72 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// // -// This file defines a set of functions to create SMT expressions +// This file defines a set of functions to create SMT expressions. // //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index fce1c34897da7..55022a4bc000a 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -2005,8 +2005,9 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { case Type::Vector: { const auto *VT = cast(T); TypeInfo EltInfo = getTypeInfo(VT->getElementType()); - Width = VT->isExtVectorBoolType() ? VT->getNumElements() - : EltInfo.Width * VT->getNumElements(); + Width = VT->isPackedVectorBoolType(*this) + ? VT->getNumElements() + : EltInfo.Width * VT->getNumElements(); // Enforce at least byte size and alignment. Width = std::max(8, Width); Align = std::max(8, Width); diff --git a/clang/lib/AST/ASTDiagnostic.cpp b/clang/lib/AST/ASTDiagnostic.cpp index 7b873ee9833b3..b4e7360e126fb 100644 --- a/clang/lib/AST/ASTDiagnostic.cpp +++ b/clang/lib/AST/ASTDiagnostic.cpp @@ -461,8 +461,9 @@ void clang::FormatASTNodeDiagnosticArgument( } case DiagnosticsEngine::ak_nestednamespec: { NestedNameSpecifier *NNS = reinterpret_cast(Val); - NNS->print(OS, Context.getPrintingPolicy()); - NeedQuotes = false; + NNS->print(OS, Context.getPrintingPolicy(), + /*ResolveTemplateArguments=*/false, + /*PrintFinalScopeResOp=*/false); break; } case DiagnosticsEngine::ak_declcontext: { diff --git a/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp b/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp index 5bd1b73133d65..4162b55070da9 100644 --- a/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp +++ b/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp @@ -367,6 +367,16 @@ bool ByteCodeEmitter::fallthrough(const LabelTy &Label) { return true; } +bool ByteCodeEmitter::speculate(const CallExpr *E, const LabelTy &EndLabel) { + const Expr *Arg = E->getArg(0); + PrimType T = Ctx.classify(Arg->getType()).value_or(PT_Ptr); + if (!this->emitBCP(getOffset(EndLabel), T, E)) + return false; + if (!this->visit(Arg)) + return false; + return true; +} + //===----------------------------------------------------------------------===// // Opcode emitters //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/ByteCode/ByteCodeEmitter.h b/clang/lib/AST/ByteCode/ByteCodeEmitter.h index ac728830527a2..64670c32cbcf6 100644 --- a/clang/lib/AST/ByteCode/ByteCodeEmitter.h +++ b/clang/lib/AST/ByteCode/ByteCodeEmitter.h @@ -48,12 +48,16 @@ class ByteCodeEmitter { virtual bool visitFunc(const FunctionDecl *E) = 0; virtual bool visitExpr(const Expr *E, bool DestroyToplevelScope) = 0; virtual bool visitDeclAndReturn(const VarDecl *E, bool ConstantContext) = 0; + virtual bool visit(const Expr *E) = 0; + virtual bool emitBool(bool V, const Expr *E) = 0; /// Emits jumps. bool jumpTrue(const LabelTy &Label); bool jumpFalse(const LabelTy &Label); bool jump(const LabelTy &Label); bool fallthrough(const LabelTy &Label); + /// Speculative execution. + bool speculate(const CallExpr *E, const LabelTy &EndLabel); /// We're always emitting bytecode. bool isActive() const { return true; } diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 281fb7e14a57d..b9f88230007b5 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -25,6 +25,16 @@ using APSInt = llvm::APSInt; namespace clang { namespace interp { +static std::optional getBoolValue(const Expr *E) { + if (const auto *CE = dyn_cast_if_present(E); + CE && CE->hasAPValueResult() && + CE->getResultAPValueKind() == APValue::ValueKind::Int) { + return CE->getResultAsAPSInt().getBoolValue(); + } + + return std::nullopt; +} + /// Scope used to handle temporaries in toplevel variable declarations. template class DeclScope final : public LocalScope { public: @@ -2286,39 +2296,49 @@ bool Compiler::VisitAbstractConditionalOperator( const Expr *TrueExpr = E->getTrueExpr(); const Expr *FalseExpr = E->getFalseExpr(); + auto visitChildExpr = [&](const Expr *E) -> bool { + LocalScope S(this); + if (!this->delegate(E)) + return false; + return S.destroyLocals(); + }; + + if (std::optional BoolValue = getBoolValue(Condition)) { + if (BoolValue) + return visitChildExpr(TrueExpr); + return visitChildExpr(FalseExpr); + } + + bool IsBcpCall = false; + if (const auto *CE = dyn_cast(Condition->IgnoreParenCasts()); + CE && CE->getBuiltinCallee() == Builtin::BI__builtin_constant_p) { + IsBcpCall = true; + } + LabelTy LabelEnd = this->getLabel(); // Label after the operator. LabelTy LabelFalse = this->getLabel(); // Label for the false expr. + if (IsBcpCall) { + if (!this->emitStartSpeculation(E)) + return false; + } + if (!this->visitBool(Condition)) return false; - if (!this->jumpFalse(LabelFalse)) return false; - - { - LocalScope S(this); - if (!this->delegate(TrueExpr)) - return false; - if (!S.destroyLocals()) - return false; - } - + if (!visitChildExpr(TrueExpr)) + return false; if (!this->jump(LabelEnd)) return false; - this->emitLabel(LabelFalse); - - { - LocalScope S(this); - if (!this->delegate(FalseExpr)) - return false; - if (!S.destroyLocals()) - return false; - } - + if (!visitChildExpr(FalseExpr)) + return false; this->fallthrough(LabelEnd); this->emitLabel(LabelEnd); + if (IsBcpCall) + return this->emitEndSpeculation(E); return true; } @@ -4681,6 +4701,28 @@ bool Compiler::visitAPValueInitializer(const APValue &Val, template bool Compiler::VisitBuiltinCallExpr(const CallExpr *E, unsigned BuiltinID) { + + if (BuiltinID == Builtin::BI__builtin_constant_p) { + // Void argument is always invalid and harder to handle later. + if (E->getArg(0)->getType()->isVoidType()) { + if (DiscardResult) + return true; + return this->emitConst(0, E); + } + + if (!this->emitStartSpeculation(E)) + return false; + LabelTy EndLabel = this->getLabel(); + if (!this->speculate(E, EndLabel)) + return false; + this->fallthrough(EndLabel); + if (!this->emitEndSpeculation(E)) + return false; + if (DiscardResult) + return this->emitPop(classifyPrim(E), E); + return true; + } + const Function *Func = getFunction(E->getDirectCallee()); if (!Func) return false; @@ -5051,7 +5093,7 @@ template bool Compiler::visitStmt(const Stmt *S) { case Stmt::CompoundStmtClass: return visitCompoundStmt(cast(S)); case Stmt::DeclStmtClass: - return visitDeclStmt(cast(S)); + return visitDeclStmt(cast(S), /*EvaluateConditionDecl=*/true); case Stmt::ReturnStmtClass: return visitReturnStmt(cast(S)); case Stmt::IfStmtClass: @@ -5105,7 +5147,18 @@ bool Compiler::visitCompoundStmt(const CompoundStmt *S) { } template -bool Compiler::visitDeclStmt(const DeclStmt *DS) { +bool Compiler::maybeEmitDeferredVarInit(const VarDecl *VD) { + if (auto *DD = dyn_cast_if_present(VD)) { + for (auto *BD : DD->bindings()) + if (auto *KD = BD->getHoldingVar(); KD && !this->visitVarDecl(KD)) + return false; + } + return true; +} + +template +bool Compiler::visitDeclStmt(const DeclStmt *DS, + bool EvaluateConditionDecl) { for (const auto *D : DS->decls()) { if (isa(D)) @@ -5118,13 +5171,8 @@ bool Compiler::visitDeclStmt(const DeclStmt *DS) { return false; // Register decomposition decl holding vars. - if (const auto *DD = dyn_cast(VD)) { - for (auto *BD : DD->bindings()) - if (auto *KD = BD->getHoldingVar()) { - if (!this->visitVarDecl(KD)) - return false; - } - } + if (EvaluateConditionDecl && !this->maybeEmitDeferredVarInit(VD)) + return false; } return true; @@ -5167,6 +5215,12 @@ bool Compiler::visitReturnStmt(const ReturnStmt *RS) { } template bool Compiler::visitIfStmt(const IfStmt *IS) { + auto visitChildStmt = [&](const Stmt *S) -> bool { + LocalScope SScope(this); + if (!visitStmt(S)) + return false; + return SScope.destroyLocals(); + }; if (auto *CondInit = IS->getInit()) if (!visitStmt(CondInit)) return false; @@ -5175,7 +5229,19 @@ template bool Compiler::visitIfStmt(const IfStmt *IS) { if (!visitDeclStmt(CondDecl)) return false; - // Compile condition. + // Save ourselves compiling some code and the jumps, etc. if the condition is + // stataically known to be either true or false. We could look at more cases + // here, but I think all the ones that actually happen are using a + // ConstantExpr. + if (std::optional BoolValue = getBoolValue(IS->getCond())) { + if (*BoolValue) + return visitChildStmt(IS->getThen()); + else if (const Stmt *Else = IS->getElse()) + return visitChildStmt(Else); + return true; + } + + // Otherwise, compile the condition. if (IS->isNonNegatedConsteval()) { if (!this->emitIsConstantContext(IS)) return false; @@ -5189,40 +5255,28 @@ template bool Compiler::visitIfStmt(const IfStmt *IS) { return false; } + if (!this->maybeEmitDeferredVarInit(IS->getConditionVariable())) + return false; + if (const Stmt *Else = IS->getElse()) { LabelTy LabelElse = this->getLabel(); LabelTy LabelEnd = this->getLabel(); if (!this->jumpFalse(LabelElse)) return false; - { - LocalScope ThenScope(this); - if (!visitStmt(IS->getThen())) - return false; - if (!ThenScope.destroyLocals()) - return false; - } + if (!visitChildStmt(IS->getThen())) + return false; if (!this->jump(LabelEnd)) return false; this->emitLabel(LabelElse); - { - LocalScope ElseScope(this); - if (!visitStmt(Else)) - return false; - if (!ElseScope.destroyLocals()) - return false; - } + if (!visitChildStmt(Else)) + return false; this->emitLabel(LabelEnd); } else { LabelTy LabelEnd = this->getLabel(); if (!this->jumpFalse(LabelEnd)) return false; - { - LocalScope ThenScope(this); - if (!visitStmt(IS->getThen())) - return false; - if (!ThenScope.destroyLocals()) - return false; - } + if (!visitChildStmt(IS->getThen())) + return false; this->emitLabel(LabelEnd); } @@ -5249,6 +5303,10 @@ bool Compiler::visitWhileStmt(const WhileStmt *S) { if (!this->visitBool(Cond)) return false; + + if (!this->maybeEmitDeferredVarInit(S->getConditionVariable())) + return false; + if (!this->jumpFalse(EndLabel)) return false; @@ -5330,6 +5388,9 @@ bool Compiler::visitForStmt(const ForStmt *S) { return false; } + if (!this->maybeEmitDeferredVarInit(S->getConditionVariable())) + return false; + if (Body && !this->visitStmt(Body)) return false; @@ -5452,6 +5513,9 @@ bool Compiler::visitSwitchStmt(const SwitchStmt *S) { if (!this->emitSetLocal(CondT, CondVar, S)) return false; + if (!this->maybeEmitDeferredVarInit(S->getConditionVariable())) + return false; + CaseMap CaseLabels; // Create labels and comparison ops for all case statements. for (const SwitchCase *SC = S->getSwitchCaseList(); SC; diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h index 77fcc3d1b41ce..902da8ffdd330 100644 --- a/clang/lib/AST/ByteCode/Compiler.h +++ b/clang/lib/AST/ByteCode/Compiler.h @@ -213,7 +213,7 @@ class Compiler : public ConstStmtVisitor, bool>, // Statements. bool visitCompoundStmt(const CompoundStmt *S); - bool visitDeclStmt(const DeclStmt *DS); + bool visitDeclStmt(const DeclStmt *DS, bool EvaluateConditionDecl = false); bool visitReturnStmt(const ReturnStmt *RS); bool visitIfStmt(const IfStmt *IS); bool visitWhileStmt(const WhileStmt *S); @@ -274,7 +274,7 @@ class Compiler : public ConstStmtVisitor, bool>, /// Evaluates an expression and places the result on the stack. If the /// expression is of composite type, a local variable will be created /// and a pointer to said variable will be placed on the stack. - bool visit(const Expr *E); + bool visit(const Expr *E) override; /// Compiles an initializer. This is like visit() but it will never /// create a variable and instead rely on a variable already having /// been created. visitInitializer() then relies on a pointer to this @@ -342,6 +342,9 @@ class Compiler : public ConstStmtVisitor, bool>, /// Emits an integer constant. template bool emitConst(T Value, PrimType Ty, const Expr *E); template bool emitConst(T Value, const Expr *E); + bool emitBool(bool V, const Expr *E) override { + return this->emitConst(V, E); + } llvm::RoundingMode getRoundingMode(const Expr *E) const { FPOptions FPO = E->getFPFeaturesInEffect(Ctx.getLangOpts()); @@ -386,6 +389,7 @@ class Compiler : public ConstStmtVisitor, bool>, bool compileUnionAssignmentOperator(const CXXMethodDecl *MD); bool checkLiteralType(const Expr *E); + bool maybeEmitDeferredVarInit(const VarDecl *VD); protected: /// Variable to storage mapping. diff --git a/clang/lib/AST/ByteCode/EvalEmitter.cpp b/clang/lib/AST/ByteCode/EvalEmitter.cpp index 95149efbea992..5326862c506d1 100644 --- a/clang/lib/AST/ByteCode/EvalEmitter.cpp +++ b/clang/lib/AST/ByteCode/EvalEmitter.cpp @@ -127,6 +127,33 @@ bool EvalEmitter::fallthrough(const LabelTy &Label) { return true; } +bool EvalEmitter::speculate(const CallExpr *E, const LabelTy &EndLabel) { + size_t StackSizeBefore = S.Stk.size(); + const Expr *Arg = E->getArg(0); + if (!this->visit(Arg)) { + S.Stk.clearTo(StackSizeBefore); + + if (S.inConstantContext() || Arg->HasSideEffects(S.getASTContext())) + return this->emitBool(false, E); + return Invalid(S, OpPC); + } + + PrimType T = Ctx.classify(Arg->getType()).value_or(PT_Ptr); + if (T == PT_Ptr) { + const auto &Ptr = S.Stk.pop(); + return this->emitBool(CheckBCPResult(S, Ptr), E); + } else if (T == PT_FnPtr) { + S.Stk.discard(); + // Never accepted + return this->emitBool(false, E); + } + + // Otherwise, this is fine! + if (!this->emitPop(T, E)) + return false; + return this->emitBool(true, E); +} + template bool EvalEmitter::emitRet(const SourceInfo &Info) { if (!isActive()) return true; diff --git a/clang/lib/AST/ByteCode/EvalEmitter.h b/clang/lib/AST/ByteCode/EvalEmitter.h index 2cac2ba2ef221..f53f86c31ec1e 100644 --- a/clang/lib/AST/ByteCode/EvalEmitter.h +++ b/clang/lib/AST/ByteCode/EvalEmitter.h @@ -55,12 +55,16 @@ class EvalEmitter : public SourceMapper { virtual bool visitExpr(const Expr *E, bool DestroyToplevelScope) = 0; virtual bool visitDeclAndReturn(const VarDecl *VD, bool ConstantContext) = 0; virtual bool visitFunc(const FunctionDecl *F) = 0; + virtual bool visit(const Expr *E) = 0; + virtual bool emitBool(bool V, const Expr *E) = 0; /// Emits jumps. bool jumpTrue(const LabelTy &Label); bool jumpFalse(const LabelTy &Label); bool jump(const LabelTy &Label); bool fallthrough(const LabelTy &Label); + /// Speculative execution. + bool speculate(const CallExpr *E, const LabelTy &EndLabel); /// Since expressions can only jump forward, predicated execution is /// used to deal with if-else statements. diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index 1107c0c32792f..201fec3e864d5 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -54,6 +54,79 @@ static bool Jf(InterpState &S, CodePtr &PC, int32_t Offset) { return true; } +// https://github.com/llvm/llvm-project/issues/102513 +#if defined(_MSC_VER) && !defined(__clang__) && !defined(NDEBUG) +#pragma optimize("", off) +#endif +// FIXME: We have the large switch over all opcodes here again, and in +// Interpret(). +static bool BCP(InterpState &S, CodePtr &RealPC, int32_t Offset, PrimType PT) { + [[maybe_unused]] CodePtr PCBefore = RealPC; + size_t StackSizeBefore = S.Stk.size(); + + auto SpeculativeInterp = [&S, RealPC]() -> bool { + const InterpFrame *StartFrame = S.Current; + CodePtr PC = RealPC; + + for (;;) { + auto Op = PC.read(); + if (Op == OP_EndSpeculation) + return true; + CodePtr OpPC = PC; + + switch (Op) { +#define GET_INTERP +#include "Opcodes.inc" +#undef GET_INTERP + } + } + llvm_unreachable("We didn't see an EndSpeculation op?"); + }; + + if (SpeculativeInterp()) { + if (PT == PT_Ptr) { + const auto &Ptr = S.Stk.pop(); + assert(S.Stk.size() == StackSizeBefore); + S.Stk.push>( + Integral<32, true>::from(CheckBCPResult(S, Ptr))); + } else if (PT == PT_FnPtr) { + S.Stk.discard(); + S.Stk.push>(Integral<32, true>::from(0)); + } else { + // Pop the result from the stack and return success. + TYPE_SWITCH(PT, S.Stk.pop();); + assert(S.Stk.size() == StackSizeBefore); + S.Stk.push>(Integral<32, true>::from(1)); + } + } else { + if (!S.inConstantContext()) + return Invalid(S, RealPC); + + S.Stk.clearTo(StackSizeBefore); + S.Stk.push>(Integral<32, true>::from(0)); + } + + // RealPC should not have been modified. + assert(*RealPC == *PCBefore); + + // Jump to end label. This is a little tricker than just RealPC += Offset + // because our usual jump instructions don't have any arguments, to the offset + // we get is a little too much and we need to subtract the size of the + // bool and PrimType arguments again. + int32_t ParamSize = align(sizeof(PrimType)); + assert(Offset >= ParamSize); + RealPC += Offset - ParamSize; + + [[maybe_unused]] CodePtr PCCopy = RealPC; + assert(PCCopy.read() == OP_EndSpeculation); + + return true; +} +// https://github.com/llvm/llvm-project/issues/102513 +#if defined(_MSC_VER) && !defined(__clang__) && !defined(NDEBUG) +#pragma optimize("", on) +#endif + static void diagnoseMissingInitializer(InterpState &S, CodePtr OpPC, const ValueDecl *VD) { const SourceInfo &E = S.Current->getSource(OpPC); @@ -290,6 +363,22 @@ void cleanupAfterFunctionCall(InterpState &S, CodePtr OpPC, TYPE_SWITCH(Ty, S.Stk.discard()); } +bool CheckBCPResult(InterpState &S, const Pointer &Ptr) { + if (Ptr.isDummy()) + return false; + if (Ptr.isZero()) + return true; + if (Ptr.isIntegralPointer()) + return true; + if (Ptr.isTypeidPointer()) + return true; + + if (const Expr *Base = Ptr.getDeclDesc()->asExpr()) + return isa(Base); + + return false; +} + bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { if (!Ptr.isExtern()) return true; diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index d8f90e45b0ced..f2ddeac99cd7e 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -159,6 +159,7 @@ bool CheckLiteralType(InterpState &S, CodePtr OpPC, const Type *T); bool InvalidShuffleVectorIndex(InterpState &S, CodePtr OpPC, uint32_t Index); bool CheckBitCast(InterpState &S, CodePtr OpPC, bool HasIndeterminateBits, bool TargetIsUCharOrByte); +bool CheckBCPResult(InterpState &S, const Pointer &Ptr); template static bool handleOverflow(InterpState &S, CodePtr OpPC, const T &SrcValue) { @@ -2431,9 +2432,12 @@ inline bool This(InterpState &S, CodePtr OpPC) { // Ensure the This pointer has been cast to the correct base. if (!This.isDummy()) { assert(isa(S.Current->getFunction()->getDecl())); - assert(This.getRecord()); + [[maybe_unused]] const Record *R = This.getRecord(); + if (!R) + R = This.narrow().getRecord(); + assert(R); assert( - This.getRecord()->getDecl() == + R->getDecl() == cast(S.Current->getFunction()->getDecl())->getParent()); } @@ -2776,8 +2780,29 @@ inline bool Unsupported(InterpState &S, CodePtr OpPC) { return false; } +inline bool StartSpeculation(InterpState &S, CodePtr OpPC) { + ++S.SpeculationDepth; + if (S.SpeculationDepth != 1) + return true; + + assert(S.PrevDiags == nullptr); + S.PrevDiags = S.getEvalStatus().Diag; + S.getEvalStatus().Diag = nullptr; + return true; +} +inline bool EndSpeculation(InterpState &S, CodePtr OpPC) { + assert(S.SpeculationDepth != 0); + --S.SpeculationDepth; + if (S.SpeculationDepth == 0) { + S.getEvalStatus().Diag = S.PrevDiags; + S.PrevDiags = nullptr; + } + return true; +} + /// Do nothing and just abort execution. inline bool Error(InterpState &S, CodePtr OpPC) { return false; } + inline bool SideEffect(InterpState &S, CodePtr OpPC) { return S.noteSideEffect(); } diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index df9c2bc24b15f..4660c80fc90db 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -1483,80 +1483,6 @@ static bool interp__builtin_ptrauth_string_discriminator( return true; } -// FIXME: This implementation is not complete. -// The Compiler instance we create cannot access the current stack frame, local -// variables, function parameters, etc. We also need protection from -// side-effects, fatal errors, etc. -static bool interp__builtin_constant_p(InterpState &S, CodePtr OpPC, - const InterpFrame *Frame, - const Function *Func, - const CallExpr *Call) { - const Expr *Arg = Call->getArg(0); - QualType ArgType = Arg->getType(); - - auto returnInt = [&S, Call](bool Value) -> bool { - pushInteger(S, Value, Call->getType()); - return true; - }; - - // __builtin_constant_p always has one operand. The rules which gcc follows - // are not precisely documented, but are as follows: - // - // - If the operand is of integral, floating, complex or enumeration type, - // and can be folded to a known value of that type, it returns 1. - // - If the operand can be folded to a pointer to the first character - // of a string literal (or such a pointer cast to an integral type) - // or to a null pointer or an integer cast to a pointer, it returns 1. - // - // Otherwise, it returns 0. - // - // FIXME: GCC also intends to return 1 for literals of aggregate types, but - // its support for this did not work prior to GCC 9 and is not yet well - // understood. - if (ArgType->isIntegralOrEnumerationType() || ArgType->isFloatingType() || - ArgType->isAnyComplexType() || ArgType->isPointerType() || - ArgType->isNullPtrType()) { - auto PrevDiags = S.getEvalStatus().Diag; - S.getEvalStatus().Diag = nullptr; - InterpStack Stk; - Compiler C(S.Ctx, S.P, S, Stk); - auto Res = C.interpretExpr(Arg, /*ConvertResultToRValue=*/Arg->isGLValue()); - S.getEvalStatus().Diag = PrevDiags; - if (Res.isInvalid()) { - C.cleanup(); - Stk.clear(); - return returnInt(false); - } - - if (!Res.empty()) { - const APValue &LV = Res.toAPValue(); - if (LV.isLValue()) { - APValue::LValueBase Base = LV.getLValueBase(); - if (Base.isNull()) { - // A null base is acceptable. - return returnInt(true); - } else if (const auto *E = Base.dyn_cast()) { - if (!isa(E)) - return returnInt(false); - return returnInt(LV.getLValueOffset().isZero()); - } else if (Base.is()) { - // Surprisingly, GCC considers __builtin_constant_p(&typeid(int)) to - // evaluate to true. - return returnInt(true); - } else { - // Any other base is not constant enough for GCC. - return returnInt(false); - } - } - } - - // Otherwise, any constant value is good enough. - return returnInt(true); - } - - return returnInt(false); -} - static bool interp__builtin_operator_new(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *Func, @@ -1955,11 +1881,23 @@ static bool interp__builtin_memcmp(InterpState &S, CodePtr OpPC, bool IsWide = (ID == Builtin::BIwmemcmp || ID == Builtin::BI__builtin_wmemcmp); + auto getElemType = [](const Pointer &P) -> QualType { + const Descriptor *Desc = P.getFieldDesc(); + QualType T = Desc->getType(); + if (T->isPointerType()) + return T->getAs()->getPointeeType(); + if (Desc->isArray()) + return Desc->getElemQualType(); + return T; + }; + const ASTContext &ASTCtx = S.getASTContext(); + QualType ElemTypeA = getElemType(PtrA); + QualType ElemTypeB = getElemType(PtrB); // FIXME: This is an arbitrary limitation the current constant interpreter // had. We could remove this. - if (!IsWide && (!isOneByteCharacterType(PtrA.getType()) || - !isOneByteCharacterType(PtrB.getType()))) { + if (!IsWide && (!isOneByteCharacterType(ElemTypeA) || + !isOneByteCharacterType(ElemTypeB))) { S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_memcmp_unsupported) << ASTCtx.BuiltinInfo.getQuotedName(ID) << PtrA.getType() @@ -1972,7 +1910,7 @@ static bool interp__builtin_memcmp(InterpState &S, CodePtr OpPC, // Now, read both pointers to a buffer and compare those. BitcastBuffer BufferA( - Bits(ASTCtx.getTypeSize(PtrA.getFieldDesc()->getType()))); + Bits(ASTCtx.getTypeSize(ElemTypeA) * PtrA.getNumElems())); readPointerToBuffer(S.getContext(), PtrA, BufferA, false); // FIXME: The swapping here is UNDOING something we do when reading the // data into the buffer. @@ -1980,7 +1918,7 @@ static bool interp__builtin_memcmp(InterpState &S, CodePtr OpPC, swapBytes(BufferA.Data.get(), BufferA.byteSize().getQuantity()); BitcastBuffer BufferB( - Bits(ASTCtx.getTypeSize(PtrB.getFieldDesc()->getType()))); + Bits(ASTCtx.getTypeSize(ElemTypeB) * PtrB.getNumElems())); readPointerToBuffer(S.getContext(), PtrB, BufferB, false); // FIXME: The swapping here is UNDOING something we do when reading the // data into the buffer. @@ -2034,15 +1972,108 @@ static bool interp__builtin_memcmp(InterpState &S, CodePtr OpPC, // However, if we read all the available bytes but were instructed to read // even more, diagnose this as a "read of dereferenced one-past-the-end - // pointer". This is what would happen if we called CheckRead() on every array + // pointer". This is what would happen if we called CheckLoad() on every array // element. S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_access_past_end) << AK_Read << S.Current->getRange(OpPC); return false; } +static bool interp__builtin_memchr(InterpState &S, CodePtr OpPC, + const InterpFrame *Frame, + const Function *Func, const CallExpr *Call) { + unsigned ID = Func->getBuiltinID(); + if (ID == Builtin::BImemchr || ID == Builtin::BIwcschr || + ID == Builtin::BIstrchr || ID == Builtin::BIwmemchr) + diagnoseNonConstexprBuiltin(S, OpPC, ID); + + const Pointer &Ptr = getParam(Frame, 0); + APSInt Desired; + std::optional MaxLength; + if (Call->getNumArgs() == 3) { + MaxLength = + peekToAPSInt(S.Stk, *S.getContext().classify(Call->getArg(2)), 0); + Desired = peekToAPSInt( + S.Stk, *S.getContext().classify(Call->getArg(1)), + align(primSize(*S.getContext().classify(Call->getArg(2)))) + + align(primSize(*S.getContext().classify(Call->getArg(1))))); + } else { + Desired = peekToAPSInt(S.Stk, *S.getContext().classify(Call->getArg(1))); + } + + if (MaxLength && MaxLength->isZero()) { + S.Stk.push(); + return true; + } + + if (Ptr.isDummy()) + return false; + + // Null is only okay if the given size is 0. + if (Ptr.isZero()) { + S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_access_null) + << AK_Read; + return false; + } + + QualType ElemTy = Ptr.getFieldDesc()->isArray() + ? Ptr.getFieldDesc()->getElemQualType() + : Ptr.getFieldDesc()->getType(); + bool IsRawByte = ID == Builtin::BImemchr || ID == Builtin::BI__builtin_memchr; + + // Give up on byte-oriented matching against multibyte elements. + if (IsRawByte && !isOneByteCharacterType(ElemTy)) { + S.FFDiag(S.Current->getSource(OpPC), + diag::note_constexpr_memchr_unsupported) + << S.getASTContext().BuiltinInfo.getQuotedName(ID) << ElemTy; + return false; + } + + if (ID == Builtin::BIstrchr || ID == Builtin::BI__builtin_strchr) { + // strchr compares directly to the passed integer, and therefore + // always fails if given an int that is not a char. + if (Desired != + Desired.trunc(S.getASTContext().getCharWidth()).getSExtValue()) { + S.Stk.push(); + return true; + } + } + + uint64_t DesiredVal = + Desired.trunc(S.getASTContext().getCharWidth()).getZExtValue(); + bool StopAtZero = + (ID == Builtin::BIstrchr || ID == Builtin::BI__builtin_strchr); + + size_t Index = Ptr.getIndex(); + for (;;) { + const Pointer &ElemPtr = Index > 0 ? Ptr.atIndex(Index) : Ptr; + + if (!CheckLoad(S, OpPC, ElemPtr)) + return false; + + unsigned char V = static_cast(ElemPtr.deref()); + if (V == DesiredVal) { + S.Stk.push(ElemPtr); + return true; + } + + if (StopAtZero && V == 0) + break; + + ++Index; + if (MaxLength && Index == MaxLength->getZExtValue()) + break; + } + + S.Stk.push(); + return true; +} + bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F, const CallExpr *Call, uint32_t BuiltinID) { + if (!S.getASTContext().BuiltinInfo.isConstantEvaluated(BuiltinID)) + return false; + const InterpFrame *Frame = S.Current; std::optional ReturnT = S.getContext().classify(Call); @@ -2468,11 +2499,6 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F, return false; break; - case Builtin::BI__builtin_constant_p: - if (!interp__builtin_constant_p(S, OpPC, Frame, F, Call)) - return false; - break; - case Builtin::BI__noop: pushInteger(S, 0, Call->getType()); break; @@ -2524,6 +2550,21 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F, return false; break; + case Builtin::BImemchr: + case Builtin::BI__builtin_memchr: + case Builtin::BIstrchr: + case Builtin::BI__builtin_strchr: +#if 0 + case Builtin::BIwcschr: + case Builtin::BI__builtin_wcschr: + case Builtin::BImemchr: + case Builtin::BI__builtin_wmemchr: +#endif + case Builtin::BI__builtin_char_memchr: + if (!interp__builtin_memchr(S, OpPC, Frame, F, Call)) + return false; + break; + default: S.FFDiag(S.Current->getLocation(OpPC), diag::note_invalid_subexpr_in_const_expr) diff --git a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp index f4c54551a9a60..6b8860c09167c 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp @@ -94,7 +94,8 @@ static bool enumerateData(const Pointer &P, const Context &Ctx, Bits Offset, Bits ElemSize = Bits(Ctx.getASTContext().getTypeSize(ElemType)); PrimType ElemT = *Ctx.classify(ElemType); // Special case, since the bools here are packed. - bool PackedBools = FieldDesc->getType()->isExtVectorBoolType(); + bool PackedBools = + FieldDesc->getType()->isPackedVectorBoolType(Ctx.getASTContext()); unsigned NumElems = FieldDesc->getNumElems(); bool Ok = true; for (unsigned I = P.getIndex(); I != NumElems; ++I) { @@ -227,7 +228,7 @@ static bool CheckBitcastType(InterpState &S, CodePtr OpPC, QualType T, QualType EltTy = VT->getElementType(); unsigned NElts = VT->getNumElements(); unsigned EltSize = - VT->isExtVectorBoolType() ? 1 : ASTCtx.getTypeSize(EltTy); + VT->isPackedVectorBoolType(ASTCtx) ? 1 : ASTCtx.getTypeSize(EltTy); if ((NElts * EltSize) % ASTCtx.getCharWidth() != 0) { // The vector's size in bits is not a multiple of the target's byte size, diff --git a/clang/lib/AST/ByteCode/InterpState.h b/clang/lib/AST/ByteCode/InterpState.h index d6adfff1a713a..74001b80d9c00 100644 --- a/clang/lib/AST/ByteCode/InterpState.h +++ b/clang/lib/AST/ByteCode/InterpState.h @@ -144,6 +144,9 @@ class InterpState final : public State, public SourceMapper { SourceLocation EvalLocation; /// Declaration we're initializing/evaluting, if any. const VarDecl *EvaluatingDecl = nullptr; + /// Things needed to do speculative execution. + SmallVectorImpl *PrevDiags = nullptr; + unsigned SpeculationDepth = 0; llvm::SmallVector< std::pair> diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td index 41e4bae65c195..98f9818cb5ffb 100644 --- a/clang/lib/AST/ByteCode/Opcodes.td +++ b/clang/lib/AST/ByteCode/Opcodes.td @@ -58,7 +58,7 @@ def ArgRecordField : ArgType { let Name = "const Record::Field *"; } def ArgFltSemantics : ArgType { let Name = "const llvm::fltSemantics *"; } def ArgRoundingMode : ArgType { let Name = "llvm::RoundingMode"; } def ArgLETD: ArgType { let Name = "const LifetimeExtendedTemporaryDecl *"; } -def ArgCastKind : ArgType { let Name = "CastKind"; } +def ArgCastKind : ArgType { let Name = "interp::CastKind"; } def ArgCallExpr : ArgType { let Name = "const CallExpr *"; } def ArgExpr : ArgType { let Name = "const Expr *"; } def ArgOffsetOfExpr : ArgType { let Name = "const OffsetOfExpr *"; } @@ -172,6 +172,14 @@ def Jt : JumpOpcode; // [Bool] -> [], jumps if false. def Jf : JumpOpcode; +def StartSpeculation : Opcode; +def EndSpeculation : Opcode; +def BCP : Opcode { + let ChangesPC = 1; + let HasCustomEval = 1; + let Args = [ArgSint32, ArgPrimType]; +} + //===----------------------------------------------------------------------===// // Returns //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index ccfec7fda0cbc..901ebf9592680 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -1509,16 +1509,6 @@ CallExpr *CallExpr::Create(const ASTContext &Ctx, Expr *Fn, RParenLoc, FPFeatures, MinNumArgs, UsesADL); } -CallExpr *CallExpr::CreateTemporary(void *Mem, Expr *Fn, QualType Ty, - ExprValueKind VK, SourceLocation RParenLoc, - ADLCallKind UsesADL) { - assert(!(reinterpret_cast(Mem) % alignof(CallExpr)) && - "Misaligned memory in CallExpr::CreateTemporary!"); - return new (Mem) CallExpr(CallExprClass, Fn, /*PreArgs=*/{}, /*Args=*/{}, Ty, - VK, RParenLoc, FPOptionsOverride(), - /*MinNumArgs=*/0, UsesADL); -} - CallExpr *CallExpr::CreateEmpty(const ASTContext &Ctx, unsigned NumArgs, bool HasFPFeatures, EmptyShell Empty) { unsigned SizeOfTrailingObjects = @@ -1655,12 +1645,8 @@ SourceLocation CallExpr::getBeginLoc() const { if (!isTypeDependent()) { if (const auto *Method = dyn_cast_if_present(getCalleeDecl()); - Method && Method->isExplicitObjectMemberFunction()) { - bool HasFirstArg = getNumArgs() > 0 && getArg(0); - assert(HasFirstArg); - if (HasFirstArg) - return getArg(0)->getBeginLoc(); - } + Method && Method->isExplicitObjectMemberFunction()) + return getArg(0)->getBeginLoc(); } SourceLocation begin = getCallee()->getBeginLoc(); diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index d9a1e5bb42343..f8e8aaddbfdbd 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -2419,6 +2419,16 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc, LVal.getLValueCallIndex() == 0) && "have call index for global lvalue"); + if (LVal.allowConstexprUnknown()) { + if (BaseVD) { + Info.FFDiag(Loc, diag::note_constexpr_var_init_non_constant, 1) << BaseVD; + NoteLValueLocation(Info, Base); + } else { + Info.FFDiag(Loc); + } + return false; + } + if (Base.is()) { Info.FFDiag(Loc, diag::note_constexpr_dynamic_alloc) << IsReferenceType << !Designator.Entries.empty(); @@ -3597,7 +3607,8 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E, // expressions here; doing so would regress diagnostics for things like // reading from a volatile constexpr variable. if ((Info.getLangOpts().CPlusPlus && !VD->hasConstantInitialization() && - VD->mightBeUsableInConstantExpressions(Info.Ctx)) || + VD->mightBeUsableInConstantExpressions(Info.Ctx) && + !AllowConstexprUnknown) || ((Info.getLangOpts().CPlusPlus || Info.getLangOpts().OpenCL) && !Info.getLangOpts().CPlusPlus11 && !VD->hasICEInitializer(Info.Ctx))) { if (Init) { @@ -5216,20 +5227,41 @@ static bool EvaluateVarDecl(EvalInfo &Info, const VarDecl *VD) { return true; } -static bool EvaluateDecl(EvalInfo &Info, const Decl *D) { - bool OK = true; +static bool EvaluateDecompositionDeclInit(EvalInfo &Info, + const DecompositionDecl *DD); +static bool EvaluateDecl(EvalInfo &Info, const Decl *D, + bool EvaluateConditionDecl = false) { + bool OK = true; if (const VarDecl *VD = dyn_cast(D)) OK &= EvaluateVarDecl(Info, VD); - if (const DecompositionDecl *DD = dyn_cast(D)) - for (auto *BD : DD->flat_bindings()) - if (auto *VD = BD->getHoldingVar()) - OK &= EvaluateDecl(Info, VD); + if (const DecompositionDecl *DD = dyn_cast(D); + EvaluateConditionDecl && DD) + OK &= EvaluateDecompositionDeclInit(Info, DD); return OK; } +static bool EvaluateDecompositionDeclInit(EvalInfo &Info, + const DecompositionDecl *DD) { + bool OK = true; + for (auto *BD : DD->flat_bindings()) + if (auto *VD = BD->getHoldingVar()) + OK &= EvaluateDecl(Info, VD, /*EvaluateConditionDecl=*/true); + + return OK; +} + +static bool MaybeEvaluateDeferredVarDeclInit(EvalInfo &Info, + const VarDecl *VD) { + if (auto *DD = dyn_cast_if_present(VD)) { + if (!EvaluateDecompositionDeclInit(Info, DD)) + return false; + } + return true; +} + static bool EvaluateDependentExpr(const Expr *E, EvalInfo &Info) { assert(E->isValueDependent()); if (Info.noteSideEffect()) @@ -5249,6 +5281,8 @@ static bool EvaluateCond(EvalInfo &Info, const VarDecl *CondDecl, return false; if (!EvaluateAsBooleanCondition(Cond, Result, Info)) return false; + if (!MaybeEvaluateDeferredVarDeclInit(Info, CondDecl)) + return false; return Scope.destroy(); } @@ -5333,6 +5367,9 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info, if (!EvaluateInteger(SS->getCond(), Value, Info)) return ESR_Failed; + if (!MaybeEvaluateDeferredVarDeclInit(Info, SS->getConditionVariable())) + return ESR_Failed; + if (!CondScope.destroy()) return ESR_Failed; } @@ -5557,7 +5594,8 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, return ESR_Failed; // Each declaration initialization is its own full-expression. FullExpressionRAII Scope(Info); - if (!EvaluateDecl(Info, D) && !Info.noteFailure()) + if (!EvaluateDecl(Info, D, /*EvaluateConditionDecl=*/true) && + !Info.noteFailure()) return ESR_Failed; if (!Scope.destroy()) return ESR_Failed; @@ -7436,7 +7474,7 @@ class APValueToBufferConverter { QualType EltTy = VTy->getElementType(); unsigned NElts = VTy->getNumElements(); - if (VTy->isExtVectorBoolType()) { + if (VTy->isPackedVectorBoolType(Info.Ctx)) { // Special handling for OpenCL bool vectors: // Since these vectors are stored as packed bits, but we can't write // individual bits to the BitCastBuffer, we'll buffer all of the elements @@ -7699,11 +7737,11 @@ class BufferToAPValueConverter { QualType EltTy = VTy->getElementType(); unsigned NElts = VTy->getNumElements(); unsigned EltSize = - VTy->isExtVectorBoolType() ? 1 : Info.Ctx.getTypeSize(EltTy); + VTy->isPackedVectorBoolType(Info.Ctx) ? 1 : Info.Ctx.getTypeSize(EltTy); SmallVector Elts; Elts.reserve(NElts); - if (VTy->isExtVectorBoolType()) { + if (VTy->isPackedVectorBoolType(Info.Ctx)) { // Special handling for OpenCL bool vectors: // Since these vectors are stored as packed bits, but we can't read // individual bits from the BitCastBuffer, we'll buffer all of the @@ -7832,7 +7870,8 @@ static bool checkBitCastConstexprEligibilityType(SourceLocation Loc, if (const auto *VTy = Ty->getAs()) { QualType EltTy = VTy->getElementType(); unsigned NElts = VTy->getNumElements(); - unsigned EltSize = VTy->isExtVectorBoolType() ? 1 : Ctx.getTypeSize(EltTy); + unsigned EltSize = + VTy->isPackedVectorBoolType(Ctx) ? 1 : Ctx.getTypeSize(EltTy); if ((NElts * EltSize) % Ctx.getCharWidth() != 0) { // The vector's size in bits is not a multiple of the target's byte size, @@ -16892,24 +16931,19 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx, APValue::LValueBase Base(&BaseMTE); Info.setEvaluatingDecl(Base, Result.Val); - if (Info.EnableNewConstInterp) { - if (!Info.Ctx.getInterpContext().evaluateAsRValue(Info, this, Result.Val)) - return false; - } else { - LValue LVal; - LVal.set(Base); - // C++23 [intro.execution]/p5 - // A full-expression is [...] a constant-expression - // So we need to make sure temporary objects are destroyed after having - // evaluating the expression (per C++23 [class.temporary]/p4). - FullExpressionRAII Scope(Info); - if (!::EvaluateInPlace(Result.Val, Info, LVal, this) || - Result.HasSideEffects || !Scope.destroy()) - return false; + LValue LVal; + LVal.set(Base); + // C++23 [intro.execution]/p5 + // A full-expression is [...] a constant-expression + // So we need to make sure temporary objects are destroyed after having + // evaluating the expression (per C++23 [class.temporary]/p4). + FullExpressionRAII Scope(Info); + if (!::EvaluateInPlace(Result.Val, Info, LVal, this) || + Result.HasSideEffects || !Scope.destroy()) + return false; - if (!Info.discardCleanups()) - llvm_unreachable("Unhandled cleanup; missing full expression marker?"); - } + if (!Info.discardCleanups()) + llvm_unreachable("Unhandled cleanup; missing full expression marker?"); if (!CheckConstantExpression(Info, getExprLoc(), getStorageType(Ctx, this), Result.Val, Kind)) @@ -16993,18 +17027,6 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, if (!Info.discardCleanups()) llvm_unreachable("Unhandled cleanup; missing full expression marker?"); - - if (Value.allowConstexprUnknown()) { - assert(Value.isLValue() && "Expected an lvalue"); - auto Base = Value.getLValueBase(); - const auto *NewVD = Base.dyn_cast(); - if (!NewVD) - NewVD = VD; - Info.FFDiag(getExprLoc(), diag::note_constexpr_var_init_non_constant, 1) - << NewVD; - NoteLValueLocation(Info, Base); - return false; - } } return CheckConstantExpression(Info, DeclLoc, DeclTy, Value, diff --git a/clang/lib/AST/Mangle.cpp b/clang/lib/AST/Mangle.cpp index 4c4f2038c51e6..b44ab23f1d0e1 100644 --- a/clang/lib/AST/Mangle.cpp +++ b/clang/lib/AST/Mangle.cpp @@ -240,7 +240,8 @@ void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) { Out << ((DefaultPtrWidth / 8) * ArgWords); } -void MangleContext::mangleMSGuidDecl(const MSGuidDecl *GD, raw_ostream &Out) { +void MangleContext::mangleMSGuidDecl(const MSGuidDecl *GD, + raw_ostream &Out) const { // For now, follow the MSVC naming convention for GUID objects on all // targets. MSGuidDecl::Parts P = GD->getParts(); @@ -327,7 +328,7 @@ void MangleContext::mangleBlock(const DeclContext *DC, const BlockDecl *BD, void MangleContext::mangleObjCMethodName(const ObjCMethodDecl *MD, raw_ostream &OS, bool includePrefixByte, - bool includeCategoryNamespace) { + bool includeCategoryNamespace) const { if (getASTContext().getLangOpts().ObjCRuntime.isGNUFamily()) { // This is the mangling we've always used on the GNU runtimes, but it // has obvious collisions in the face of underscores within class @@ -382,7 +383,7 @@ void MangleContext::mangleObjCMethodName(const ObjCMethodDecl *MD, } void MangleContext::mangleObjCMethodNameAsSourceName(const ObjCMethodDecl *MD, - raw_ostream &Out) { + raw_ostream &Out) const { SmallString<64> Name; llvm::raw_svector_ostream OS(Name); diff --git a/clang/lib/AST/NestedNameSpecifier.cpp b/clang/lib/AST/NestedNameSpecifier.cpp index 76c77569da9fd..593f2fcc0159c 100644 --- a/clang/lib/AST/NestedNameSpecifier.cpp +++ b/clang/lib/AST/NestedNameSpecifier.cpp @@ -248,7 +248,8 @@ bool NestedNameSpecifier::containsErrors() const { /// Print this nested name specifier to the given output /// stream. void NestedNameSpecifier::print(raw_ostream &OS, const PrintingPolicy &Policy, - bool ResolveTemplateArguments) const { + bool ResolveTemplateArguments, + bool PrintFinalScopeResOp) const { if (getPrefix()) getPrefix()->print(OS, Policy); @@ -269,7 +270,8 @@ void NestedNameSpecifier::print(raw_ostream &OS, const PrintingPolicy &Policy, break; case Global: - break; + OS << "::"; + return; case Super: OS << "__super"; @@ -331,7 +333,8 @@ void NestedNameSpecifier::print(raw_ostream &OS, const PrintingPolicy &Policy, } } - OS << "::"; + if (PrintFinalScopeResOp) + OS << "::"; } LLVM_DUMP_METHOD void NestedNameSpecifier::dump(const LangOptions &LO) const { diff --git a/clang/lib/AST/OpenACCClause.cpp b/clang/lib/AST/OpenACCClause.cpp index fd2c38a0e64e7..579ee91b7ec99 100644 --- a/clang/lib/AST/OpenACCClause.cpp +++ b/clang/lib/AST/OpenACCClause.cpp @@ -20,7 +20,8 @@ using namespace clang; bool OpenACCClauseWithParams::classof(const OpenACCClause *C) { return OpenACCDeviceTypeClause::classof(C) || OpenACCClauseWithCondition::classof(C) || - OpenACCClauseWithExprs::classof(C) || OpenACCSelfClause::classof(C); + OpenACCBindClause::classof(C) || OpenACCClauseWithExprs::classof(C) || + OpenACCSelfClause::classof(C); } bool OpenACCClauseWithExprs::classof(const OpenACCClause *C) { return OpenACCWaitClause::classof(C) || OpenACCNumGangsClause::classof(C) || @@ -609,6 +610,24 @@ OpenACCIfPresentClause *OpenACCIfPresentClause::Create(const ASTContext &C, return new (Mem) OpenACCIfPresentClause(BeginLoc, EndLoc); } +OpenACCBindClause *OpenACCBindClause::Create(const ASTContext &C, + SourceLocation BeginLoc, + SourceLocation LParenLoc, + const StringLiteral *SL, + SourceLocation EndLoc) { + void *Mem = C.Allocate(sizeof(OpenACCBindClause), alignof(OpenACCBindClause)); + return new (Mem) OpenACCBindClause(BeginLoc, LParenLoc, SL, EndLoc); +} + +OpenACCBindClause *OpenACCBindClause::Create(const ASTContext &C, + SourceLocation BeginLoc, + SourceLocation LParenLoc, + const IdentifierInfo *ID, + SourceLocation EndLoc) { + void *Mem = C.Allocate(sizeof(OpenACCBindClause), alignof(OpenACCBindClause)); + return new (Mem) OpenACCBindClause(BeginLoc, LParenLoc, ID, EndLoc); +} + //===----------------------------------------------------------------------===// // OpenACC clauses printing methods //===----------------------------------------------------------------------===// @@ -936,3 +955,12 @@ void OpenACCClausePrinter::VisitIfPresentClause( const OpenACCIfPresentClause &C) { OS << "if_present"; } + +void OpenACCClausePrinter::VisitBindClause(const OpenACCBindClause &C) { + OS << "bind("; + if (C.isStringArgument()) + OS << '"' << C.getStringArgument()->getString() << '"'; + else + OS << C.getIdentifierArgument()->getName(); + OS << ")"; +} diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp index 424cab3a7de35..648a273c4a2c6 100644 --- a/clang/lib/AST/OpenMPClause.cpp +++ b/clang/lib/AST/OpenMPClause.cpp @@ -155,6 +155,7 @@ const OMPClauseWithPreInit *OMPClauseWithPreInit::get(const OMPClause *C) { case OMPC_reverse_offload: case OMPC_dynamic_allocators: case OMPC_atomic_default_mem_order: + case OMPC_self_maps: case OMPC_at: case OMPC_severity: case OMPC_message: @@ -259,6 +260,7 @@ const OMPClauseWithPostUpdate *OMPClauseWithPostUpdate::get(const OMPClause *C) case OMPC_reverse_offload: case OMPC_dynamic_allocators: case OMPC_atomic_default_mem_order: + case OMPC_self_maps: case OMPC_at: case OMPC_severity: case OMPC_message: @@ -1941,6 +1943,10 @@ void OMPClausePrinter::VisitOMPAtomicDefaultMemOrderClause( << ")"; } +void OMPClausePrinter::VisitOMPSelfMapsClause(OMPSelfMapsClause *) { + OS << "self_maps"; +} + void OMPClausePrinter::VisitOMPAtClause(OMPAtClause *Node) { OS << "at(" << getOpenMPSimpleClauseTypeName(OMPC_at, Node->getAtKind()) << ")"; diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index facdc4104c374..e0063ec5f25eb 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -2659,10 +2659,8 @@ void StmtPrinter::VisitCXXFoldExpr(CXXFoldExpr *E) { } void StmtPrinter::VisitCXXParenListInitExpr(CXXParenListInitExpr *Node) { - OS << "("; - llvm::interleaveComma(Node->getInitExprs(), OS, + llvm::interleaveComma(Node->getUserSpecifiedInitExprs(), OS, [&](Expr *E) { PrintExpr(E); }); - OS << ")"; } void StmtPrinter::VisitConceptSpecializationExpr(ConceptSpecializationExpr *E) { diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 574f67f4274e7..bf72de3854f4b 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -557,6 +557,8 @@ void OMPClauseProfiler::VisitOMPDynamicAllocatorsClause( void OMPClauseProfiler::VisitOMPAtomicDefaultMemOrderClause( const OMPAtomicDefaultMemOrderClause *C) {} +void OMPClauseProfiler::VisitOMPSelfMapsClause(const OMPSelfMapsClause *C) {} + void OMPClauseProfiler::VisitOMPAtClause(const OMPAtClause *C) {} void OMPClauseProfiler::VisitOMPSeverityClause(const OMPSeverityClause *C) {} @@ -2732,6 +2734,10 @@ void OpenACCClauseProfiler::VisitReductionClause( const OpenACCReductionClause &Clause) { VisitClauseWithVarList(Clause); } + +void OpenACCClauseProfiler::VisitBindClause(const OpenACCBindClause &Clause) { + assert(false && "not implemented... what can we do about our expr?"); +} } // namespace void StmtProfiler::VisitOpenACCComputeConstruct( diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 91f3f14c6b454..7600b40a7482e 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -435,6 +435,7 @@ void TextNodeDumper::Visit(const OpenACCClause *C) { case OpenACCClauseKind::UseDevice: case OpenACCClauseKind::Vector: case OpenACCClauseKind::VectorLength: + case OpenACCClauseKind::Invalid: // The condition expression will be printed as a part of the 'children', // but print 'clause' here so it is clear what is happening from the dump. OS << " clause"; @@ -501,9 +502,15 @@ void TextNodeDumper::Visit(const OpenACCClause *C) { OS << " clause Operator: " << cast(C)->getReductionOp(); break; - default: - // Nothing to do here. - break; + case OpenACCClauseKind::Bind: + OS << " clause"; + if (cast(C)->isIdentifierArgument()) + OS << " identifier '" + << cast(C)->getIdentifierArgument()->getName() + << "'"; + else + AddChild( + [=] { Visit(cast(C)->getStringArgument()); }); } } dumpPointer(C); diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 2fd7f5800594a..72161c06a88d4 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -410,6 +410,12 @@ VectorType::VectorType(TypeClass tc, QualType vecType, unsigned nElements, VectorTypeBits.NumElements = nElements; } +bool Type::isPackedVectorBoolType(const ASTContext &ctx) const { + if (ctx.getLangOpts().HLSL) + return false; + return isExtVectorBoolType(); +} + BitIntType::BitIntType(bool IsUnsigned, unsigned NumBits) : Type(BitInt, QualType{}, TypeDependence::None), IsUnsigned(IsUnsigned), NumBits(NumBits) {} diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 8762cc7b1e4e1..dfd1959bbdcb0 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -2093,6 +2093,9 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, case attr::ArmMveStrictPolymorphism: OS << "__clang_arm_mve_strict_polymorphism"; break; + case attr::ExtVectorType: + OS << "ext_vector_type"; + break; } OS << "))"; } diff --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp index 9381c5c42e566..c28424fac8fef 100644 --- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp +++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp @@ -551,83 +551,92 @@ void transferCallReturningOptional(const CallExpr *E, setHasValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env); } -void handleConstMemberCall(const CallExpr *CE, +// Returns true if the const accessor is handled by caching. +// Returns false if we could not cache. We should perform default handling +// in that case. +bool handleConstMemberCall(const CallExpr *CE, dataflow::RecordStorageLocation *RecordLoc, const MatchFinder::MatchResult &Result, LatticeTransferState &State) { - // If the const method returns an optional or reference to an optional. - if (RecordLoc != nullptr && isSupportedOptionalType(CE->getType())) { - const FunctionDecl *DirectCallee = CE->getDirectCallee(); - if (DirectCallee == nullptr) - return; - StorageLocation &Loc = - State.Lattice.getOrCreateConstMethodReturnStorageLocation( - *RecordLoc, DirectCallee, State.Env, [&](StorageLocation &Loc) { - setHasValue(cast(Loc), - State.Env.makeAtomicBoolValue(), State.Env); - }); - if (CE->isGLValue()) { - // If the call to the const method returns a reference to an optional, - // link the call expression to the cached StorageLocation. - State.Env.setStorageLocation(*CE, Loc); - } else { - // If the call to the const method returns an optional by value, we - // need to use CopyRecord to link the optional to the result object - // of the call expression. - auto &ResultLoc = State.Env.getResultObjectLocation(*CE); - copyRecord(cast(Loc), ResultLoc, State.Env); - } - return; - } + if (RecordLoc == nullptr) + return false; - // Cache if the const method returns a reference - if (RecordLoc != nullptr && CE->isGLValue()) { + // Cache if the const method returns a reference. + if (CE->isGLValue()) { const FunctionDecl *DirectCallee = CE->getDirectCallee(); if (DirectCallee == nullptr) - return; + return false; + // Initialize the optional's "has_value" property to true if the type is + // optional, otherwise no-op. If we want to support const ref to pointers or + // bools we should initialize their values here too. + auto Init = [&](StorageLocation &Loc) { + if (isSupportedOptionalType(CE->getType())) + setHasValue(cast(Loc), + State.Env.makeAtomicBoolValue(), State.Env); + }; StorageLocation &Loc = State.Lattice.getOrCreateConstMethodReturnStorageLocation( - *RecordLoc, DirectCallee, State.Env, [&](StorageLocation &Loc) { - // no-op - }); + *RecordLoc, DirectCallee, State.Env, Init); State.Env.setStorageLocation(*CE, Loc); - return; + return true; } - - // Cache if the const method returns a boolean or pointer type. - // We may decide to cache other return types in the future. - if (RecordLoc != nullptr && - (CE->getType()->isBooleanType() || CE->getType()->isPointerType())) { + // PRValue cases: + if (CE->getType()->isBooleanType() || CE->getType()->isPointerType()) { + // If the const method returns a boolean or pointer type. Value *Val = State.Lattice.getOrCreateConstMethodReturnValue(*RecordLoc, CE, State.Env); if (Val == nullptr) - return; + return false; State.Env.setValue(*CE, *Val); - return; + return true; } - - // Perform default handling if the call returns an optional - // but wasn't handled above (if RecordLoc is nullptr). if (isSupportedOptionalType(CE->getType())) { - transferCallReturningOptional(CE, Result, State); + // If the const method returns an optional by value. + const FunctionDecl *DirectCallee = CE->getDirectCallee(); + if (DirectCallee == nullptr) + return false; + StorageLocation &Loc = + State.Lattice.getOrCreateConstMethodReturnStorageLocation( + *RecordLoc, DirectCallee, State.Env, [&](StorageLocation &Loc) { + setHasValue(cast(Loc), + State.Env.makeAtomicBoolValue(), State.Env); + }); + // Use copyRecord to link the optional to the result object of the call + // expression. + auto &ResultLoc = State.Env.getResultObjectLocation(*CE); + copyRecord(cast(Loc), ResultLoc, State.Env); + return true; } + + return false; } -void transferValue_ConstMemberCall(const CXXMemberCallExpr *MCE, - const MatchFinder::MatchResult &Result, - LatticeTransferState &State) { - handleConstMemberCall( +void handleConstMemberCallWithFallbacks( + const CallExpr *CE, dataflow::RecordStorageLocation *RecordLoc, + const MatchFinder::MatchResult &Result, LatticeTransferState &State) { + if (handleConstMemberCall(CE, RecordLoc, Result, State)) + return; + // Perform default handling if the call returns an optional, but wasn't + // handled by caching. + if (isSupportedOptionalType(CE->getType())) + transferCallReturningOptional(CE, Result, State); +} + +void transferConstMemberCall(const CXXMemberCallExpr *MCE, + const MatchFinder::MatchResult &Result, + LatticeTransferState &State) { + handleConstMemberCallWithFallbacks( MCE, dataflow::getImplicitObjectLocation(*MCE, State.Env), Result, State); } -void transferValue_ConstMemberOperatorCall( - const CXXOperatorCallExpr *OCE, const MatchFinder::MatchResult &Result, - LatticeTransferState &State) { +void transferConstMemberOperatorCall(const CXXOperatorCallExpr *OCE, + const MatchFinder::MatchResult &Result, + LatticeTransferState &State) { auto *RecordLoc = cast_or_null( State.Env.getStorageLocation(*OCE->getArg(0))); - handleConstMemberCall(OCE, RecordLoc, Result, State); + handleConstMemberCallWithFallbacks(OCE, RecordLoc, Result, State); } void handleNonConstMemberCall(const CallExpr *CE, @@ -1094,9 +1103,9 @@ auto buildTransferMatchSwitch() { // const accessor calls .CaseOfCFGStmt(isZeroParamConstMemberCall(), - transferValue_ConstMemberCall) + transferConstMemberCall) .CaseOfCFGStmt(isZeroParamConstMemberOperatorCall(), - transferValue_ConstMemberOperatorCall) + transferConstMemberOperatorCall) // non-const member calls that may modify the state of an object. .CaseOfCFGStmt(isNonConstMemberCall(), transferValue_NonConstMemberCall) diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp index 12e99143cb148..89b16c9c3179a 100644 --- a/clang/lib/Analysis/UnsafeBufferUsage.cpp +++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp @@ -7,7 +7,9 @@ //===----------------------------------------------------------------------===// #include "clang/Analysis/Analyses/UnsafeBufferUsage.h" +#include "clang/AST/APValue.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Attr.h" #include "clang/AST/Decl.h" #include "clang/AST/DynamicRecursiveASTVisitor.h" #include "clang/AST/Expr.h" @@ -353,23 +355,90 @@ isInUnspecifiedUntypedContext(internal::Matcher InnerMatcher) { return stmt(anyOf(CompStmt, IfStmtThen, IfStmtElse)); } +// Returns true iff integer E1 is equivalent to integer E2. +// +// For now we only support such expressions: +// expr := DRE | const-value | expr BO expr +// BO := '*' | '+' +// +// FIXME: We can reuse the expression comparator of the interop analysis after +// it has been upstreamed. +static bool areEqualIntegers(const Expr *E1, const Expr *E2, ASTContext &Ctx); +static bool areEqualIntegralBinaryOperators(const BinaryOperator *E1, + const Expr *E2_LHS, + BinaryOperatorKind BOP, + const Expr *E2_RHS, + ASTContext &Ctx) { + if (E1->getOpcode() == BOP) { + switch (BOP) { + // Commutative operators: + case BO_Mul: + case BO_Add: + return (areEqualIntegers(E1->getLHS(), E2_LHS, Ctx) && + areEqualIntegers(E1->getRHS(), E2_RHS, Ctx)) || + (areEqualIntegers(E1->getLHS(), E2_RHS, Ctx) && + areEqualIntegers(E1->getRHS(), E2_LHS, Ctx)); + default: + return false; + } + } + return false; +} + +static bool areEqualIntegers(const Expr *E1, const Expr *E2, ASTContext &Ctx) { + E1 = E1->IgnoreParenImpCasts(); + E2 = E2->IgnoreParenImpCasts(); + if (!E1->getType()->isIntegerType() || E1->getType() != E2->getType()) + return false; + + Expr::EvalResult ER1, ER2; + + // If both are constants: + if (E1->EvaluateAsInt(ER1, Ctx) && + E2->EvaluateAsInt(ER2, Ctx)) + return ER1.Val.getInt() == ER2.Val.getInt(); + + // Otherwise, they should have identical stmt kind: + if (E1->getStmtClass() != E2->getStmtClass()) + return false; + switch (E1->getStmtClass()) { + case Stmt::DeclRefExprClass: + return cast(E1)->getDecl() == cast(E2)->getDecl(); + case Stmt::BinaryOperatorClass: { + auto BO2 = cast(E2); + return areEqualIntegralBinaryOperators(cast(E1), + BO2->getLHS(), BO2->getOpcode(), + BO2->getRHS(), Ctx); + } + default: + return false; + } +} + // Given a two-param std::span construct call, matches iff the call has the // following forms: // 1. `std::span{new T[n], n}`, where `n` is a literal or a DRE // 2. `std::span{new T, 1}` -// 3. `std::span{&var, 1}` +// 3. `std::span{&var, 1}` or `std::span{std::addressof(...), 1}` // 4. `std::span{a, n}`, where `a` is of an array-of-T with constant size // `n` // 5. `std::span{any, 0}` -// 6. `std::span{std::addressof(...), 1}` +// 6. `std::span{ (char *)f(args), args[N] * arg*[M]}`, where +// `f` is a function with attribute `alloc_size(N, M)`; +// `args` represents the list of arguments; +// `N, M` are parameter indexes to the allocating element number and size. +// Sometimes, there is only one parameter index representing the total +// size. AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) { assert(Node.getNumArgs() == 2 && "expecting a two-parameter std::span constructor"); - const Expr *Arg0 = Node.getArg(0)->IgnoreImplicit(); - const Expr *Arg1 = Node.getArg(1)->IgnoreImplicit(); - auto HaveEqualConstantValues = [&Finder](const Expr *E0, const Expr *E1) { - if (auto E0CV = E0->getIntegerConstantExpr(Finder->getASTContext())) - if (auto E1CV = E1->getIntegerConstantExpr(Finder->getASTContext())) { + const Expr *Arg0 = Node.getArg(0)->IgnoreParenImpCasts(); + const Expr *Arg1 = Node.getArg(1)->IgnoreParenImpCasts(); + ASTContext &Ctx = Finder->getASTContext(); + + auto HaveEqualConstantValues = [&Ctx](const Expr *E0, const Expr *E1) { + if (auto E0CV = E0->getIntegerConstantExpr(Ctx)) + if (auto E1CV = E1->getIntegerConstantExpr(Ctx)) { return APSInt::compareValues(*E0CV, *E1CV) == 0; } return false; @@ -381,13 +450,14 @@ AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) { } return false; }; - std::optional Arg1CV = - Arg1->getIntegerConstantExpr(Finder->getASTContext()); + std::optional Arg1CV = Arg1->getIntegerConstantExpr(Ctx); if (Arg1CV && Arg1CV->isZero()) // Check form 5: return true; - switch (Arg0->IgnoreImplicit()->getStmtClass()) { + + // Check forms 1-3: + switch (Arg0->getStmtClass()) { case Stmt::CXXNewExprClass: if (auto Size = cast(Arg0)->getArraySize()) { // Check form 1: @@ -407,6 +477,7 @@ AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) { return Arg1CV && Arg1CV->isOne(); break; case Stmt::CallExprClass: + // Check form 3: if (const auto *CE = dyn_cast(Arg0)) { const auto FnDecl = CE->getDirectCallee(); if (FnDecl && FnDecl->getNameAsString() == "addressof" && @@ -421,13 +492,41 @@ AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) { QualType Arg0Ty = Arg0->IgnoreImplicit()->getType(); - if (auto *ConstArrTy = - Finder->getASTContext().getAsConstantArrayType(Arg0Ty)) { + if (auto *ConstArrTy = Ctx.getAsConstantArrayType(Arg0Ty)) { const APSInt ConstArrSize = APSInt(ConstArrTy->getSize()); // Check form 4: return Arg1CV && APSInt::compareValues(ConstArrSize, *Arg1CV) == 0; } + // Check form 6: + if (auto CCast = dyn_cast(Arg0)) { + if (!CCast->getType()->isPointerType()) + return false; + + QualType PteTy = CCast->getType()->getPointeeType(); + + if (!(PteTy->isConstantSizeType() && Ctx.getTypeSizeInChars(PteTy).isOne())) + return false; + + if (const auto *Call = dyn_cast(CCast->getSubExpr())) { + if (const FunctionDecl *FD = Call->getDirectCallee()) + if (auto *AllocAttr = FD->getAttr()) { + const Expr *EleSizeExpr = + Call->getArg(AllocAttr->getElemSizeParam().getASTIndex()); + // NumElemIdx is invalid if AllocSizeAttr has 1 argument: + ParamIdx NumElemIdx = AllocAttr->getNumElemsParam(); + + if (!NumElemIdx.isValid()) + return areEqualIntegers(Arg1, EleSizeExpr, Ctx); + + const Expr *NumElesExpr = Call->getArg(NumElemIdx.getASTIndex()); + + if (auto BO = dyn_cast(Arg1)) + return areEqualIntegralBinaryOperators(BO, NumElesExpr, BO_Mul, + EleSizeExpr, Ctx); + } + } + } return false; } diff --git a/clang/lib/Basic/Attributes.cpp b/clang/lib/Basic/Attributes.cpp index 2035d4c0a5768..6a070a99c8d96 100644 --- a/clang/lib/Basic/Attributes.cpp +++ b/clang/lib/Basic/Attributes.cpp @@ -143,13 +143,17 @@ static SmallString<64> normalizeName(const IdentifierInfo *Name, StringRef ScopeName = normalizeAttrScopeName(Scope, SyntaxUsed); StringRef AttrName = normalizeAttrName(Name, ScopeName, SyntaxUsed); + std::string StrAttrName = AttrName.str(); + if (SyntaxUsed == AttributeCommonInfo::AS_HLSLAnnotation) + StrAttrName = AttrName.lower(); + SmallString<64> FullName = ScopeName; if (!ScopeName.empty()) { assert(SyntaxUsed == AttributeCommonInfo::AS_CXX11 || SyntaxUsed == AttributeCommonInfo::AS_C23); FullName += "::"; } - FullName += AttrName; + FullName += StrAttrName; return FullName; } diff --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp index 1ff342cb22a03..09921e3b1edfc 100644 --- a/clang/lib/Basic/OpenMPKinds.cpp +++ b/clang/lib/Basic/OpenMPKinds.cpp @@ -235,6 +235,7 @@ unsigned clang::getOpenMPSimpleClauseType(OpenMPClauseKind Kind, StringRef Str, case OMPC_unified_shared_memory: case OMPC_reverse_offload: case OMPC_dynamic_allocators: + case OMPC_self_maps: case OMPC_match: case OMPC_nontemporal: case OMPC_destroy: @@ -569,6 +570,7 @@ const char *clang::getOpenMPSimpleClauseTypeName(OpenMPClauseKind Kind, case OMPC_unified_shared_memory: case OMPC_reverse_offload: case OMPC_dynamic_allocators: + case OMPC_self_maps: case OMPC_match: case OMPC_nontemporal: case OMPC_destroy: diff --git a/clang/lib/CIR/CMakeLists.txt b/clang/lib/CIR/CMakeLists.txt index 4a99ecb33dfb2..7bdf3fcc59035 100644 --- a/clang/lib/CIR/CMakeLists.txt +++ b/clang/lib/CIR/CMakeLists.txt @@ -1,6 +1,17 @@ include_directories(${LLVM_MAIN_SRC_DIR}/../mlir/include) include_directories(${CMAKE_BINARY_DIR}/tools/mlir/include) +# GCC, unlike clang, issues a warning when one virtual function is overridden +# in a derived class but one or more other virtual functions with the same +# name and different signature from a base class are not overridden. This +# leads to many warnings in the MLIR and ClangIR code when using the +# OpenConversionPattern<>::matchAndRewrite() function in the ordinary way. +# The "hiding" behavior is what we want, so we're just disabling the warning +# here. +if (LLVM_COMPILER_IS_GCC_COMPATIBLE AND (NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-overloaded-virtual") +endif() + add_subdirectory(Dialect) add_subdirectory(CodeGen) add_subdirectory(FrontendAction) diff --git a/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h b/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h new file mode 100644 index 0000000000000..5b22a8e59908d --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h @@ -0,0 +1,100 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// A helper class for emitting expressions and values as cir::ConstantOp +// and as initializers for global variables. +// +// Note: this is based on clang's LLVM IR codegen in ConstantEmitter.h, reusing +// this class interface makes it easier move forward with bringing CIR codegen +// to completion. +// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_LIB_CIR_CODEGEN_CIRGENCONSTANTEMITTER_H +#define CLANG_LIB_CIR_CODEGEN_CIRGENCONSTANTEMITTER_H + +#include "CIRGenFunction.h" +#include "CIRGenModule.h" +#include "llvm/ADT/SmallVector.h" + +namespace clang::CIRGen { + +class ConstantEmitter { +public: + CIRGenModule &cgm; + const CIRGenFunction *cgf; + +private: + bool abstract = false; + + /// Whether we're in a constant context. + bool inConstantContext = false; + +public: + /// Initialize this emission in the context of the given function. + /// Use this if the expression might contain contextual references like + /// block addresses or PredefinedExprs. + ConstantEmitter(CIRGenFunction &cgf) : cgm(cgf.cgm), cgf(&cgf) {} + + ConstantEmitter(const ConstantEmitter &other) = delete; + ConstantEmitter &operator=(const ConstantEmitter &other) = delete; + + // All of the "abstract" emission methods below permit the emission to + // be immediately discarded without finalizing anything. Therefore, they + // must also promise not to do anything that will, in the future, require + // finalization: + // + // - using the CGF (if present) for anything other than establishing + // semantic context; for example, an expression with ignored + // side-effects must not be emitted as an abstract expression + // + // - doing anything that would not be safe to duplicate within an + // initializer or to propagate to another context; for example, + // side effects, or emitting an initialization that requires a + // reference to its current location. + mlir::Attribute emitForMemory(mlir::Attribute c, QualType t); + + /// Emit the result of the given expression as an abstract constant, + /// asserting that it succeeded. This is only safe to do when the + /// expression is known to be a constant expression with either a fairly + /// simple type or a known simple form. + mlir::Attribute emitAbstract(SourceLocation loc, const APValue &value, + QualType t); + + mlir::Attribute tryEmitConstantExpr(const ConstantExpr *CE); + + // These are private helper routines of the constant emitter that + // can't actually be private because things are split out into helper + // functions and classes. + + mlir::Attribute tryEmitPrivateForVarInit(const VarDecl &d); + + mlir::Attribute tryEmitPrivate(const APValue &value, QualType destType); + mlir::Attribute tryEmitPrivateForMemory(const APValue &value, QualType t); + + /// Try to emit the initializer of the given declaration as an abstract + /// constant. + mlir::Attribute tryEmitAbstractForInitializer(const VarDecl &d); + +private: + class AbstractStateRAII { + ConstantEmitter &emitter; + bool oldValue; + + public: + AbstractStateRAII(ConstantEmitter &emitter, bool value) + : emitter(emitter), oldValue(emitter.abstract) { + emitter.abstract = value; + } + ~AbstractStateRAII() { emitter.abstract = oldValue; } + }; +}; + +} // namespace clang::CIRGen + +#endif // CLANG_LIB_CIR_CODEGEN_CIRGENCONSTANTEMITTER_H diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp index 406026b0b9f27..27ed0113a4f55 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp @@ -10,23 +10,29 @@ // //===----------------------------------------------------------------------===// +#include "CIRGenConstantEmitter.h" #include "CIRGenFunction.h" +#include "mlir/IR/Location.h" #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" #include "clang/CIR/MissingFeatures.h" using namespace clang; using namespace clang::CIRGen; -void CIRGenFunction::emitAutoVarAlloca(const VarDecl &d) { +CIRGenFunction::AutoVarEmission +CIRGenFunction::emitAutoVarAlloca(const VarDecl &d) { QualType ty = d.getType(); if (ty.getAddressSpace() != LangAS::Default) cgm.errorNYI(d.getSourceRange(), "emitAutoVarAlloca: address space"); - auto loc = getLoc(d.getSourceRange()); + mlir::Location loc = getLoc(d.getSourceRange()); - if (d.isEscapingByref()) + CIRGenFunction::AutoVarEmission emission(d); + emission.IsEscapingByRef = d.isEscapingByref(); + if (emission.IsEscapingByRef) cgm.errorNYI(d.getSourceRange(), "emitAutoVarDecl: decl escaping by reference"); @@ -46,21 +52,130 @@ void CIRGenFunction::emitAutoVarAlloca(const VarDecl &d) { address = createTempAlloca(allocaTy, alignment, loc, d.getName()); declare(address.getPointer(), &d, ty, getLoc(d.getSourceRange()), alignment); + emission.Addr = address; setAddrOfLocalVar(&d, address); + + return emission; +} + +/// Determine whether the given initializer is trivial in the sense +/// that it requires no code to be generated. +bool CIRGenFunction::isTrivialInitializer(const Expr *init) { + if (!init) + return true; + + if (const CXXConstructExpr *construct = dyn_cast(init)) + if (CXXConstructorDecl *constructor = construct->getConstructor()) + if (constructor->isTrivial() && constructor->isDefaultConstructor() && + !construct->requiresZeroInitialization()) + return true; + + return false; } -void CIRGenFunction::emitAutoVarInit(const clang::VarDecl &d) { +void CIRGenFunction::emitAutoVarInit( + const CIRGenFunction::AutoVarEmission &emission) { + assert(emission.Variable && "emission was not valid!"); + + // If this was emitted as a global constant, we're done. + if (emission.wasEmittedAsGlobal()) + return; + + const VarDecl &d = *emission.Variable; + QualType type = d.getType(); // If this local has an initializer, emit it now. const Expr *init = d.getInit(); - if (init || !type.isPODType(getContext())) { - cgm.errorNYI(d.getSourceRange(), "emitAutoVarInit"); + if (!type.isPODType(getContext())) { + cgm.errorNYI(d.getSourceRange(), "emitAutoVarInit: non-POD type"); + return; + } + + const Address addr = emission.Addr; + + // Check whether this is a byref variable that's potentially + // captured and moved by its own initializer. If so, we'll need to + // emit the initializer first, then copy into the variable. + assert(!cir::MissingFeatures::opAllocaCaptureByInit()); + + // Note: constexpr already initializes everything correctly. + LangOptions::TrivialAutoVarInitKind trivialAutoVarInit = + (d.isConstexpr() + ? LangOptions::TrivialAutoVarInitKind::Uninitialized + : (d.getAttr() + ? LangOptions::TrivialAutoVarInitKind::Uninitialized + : getContext().getLangOpts().getTrivialAutoVarInit())); + + auto initializeWhatIsTechnicallyUninitialized = [&](Address addr) { + if (trivialAutoVarInit == + LangOptions::TrivialAutoVarInitKind::Uninitialized) + return; + + cgm.errorNYI(d.getSourceRange(), "emitAutoVarInit: trivial initialization"); + }; + + if (isTrivialInitializer(init)) { + initializeWhatIsTechnicallyUninitialized(addr); + return; + } + + mlir::Attribute constant; + if (emission.IsConstantAggregate || + d.mightBeUsableInConstantExpressions(getContext())) { + // FIXME: Differently from LLVM we try not to emit / lower too much + // here for CIR since we are interested in seeing the ctor in some + // analysis later on. So CIR's implementation of ConstantEmitter will + // frequently return an empty Attribute, to signal we want to codegen + // some trivial ctor calls and whatnots. + constant = ConstantEmitter(*this).tryEmitAbstractForInitializer(d); + if (constant && !mlir::isa(constant) && + (trivialAutoVarInit != + LangOptions::TrivialAutoVarInitKind::Uninitialized)) { + cgm.errorNYI(d.getSourceRange(), "emitAutoVarInit: constant aggregate"); + return; + } + } + + // NOTE(cir): In case we have a constant initializer, we can just emit a + // store. But, in CIR, we wish to retain any ctor calls, so if it is a + // CXX temporary object creation, we ensure the ctor call is used deferring + // its removal/optimization to the CIR lowering. + if (!constant || isa(init)) { + initializeWhatIsTechnicallyUninitialized(addr); + LValue lv = LValue::makeAddr(addr, type); + emitExprAsInit(init, &d, lv); + // In case lv has uses it means we indeed initialized something + // out of it while trying to build the expression, mark it as such. + mlir::Value val = lv.getAddress().getPointer(); + assert(val && "Should have an address"); + auto allocaOp = dyn_cast_or_null(val.getDefiningOp()); + assert(allocaOp && "Address should come straight out of the alloca"); + + if (!allocaOp.use_empty()) + allocaOp.setInitAttr(mlir::UnitAttr::get(&getMLIRContext())); + return; + } + + // FIXME(cir): migrate most of this file to use mlir::TypedAttr directly. + auto typedConstant = mlir::dyn_cast(constant); + assert(typedConstant && "expected typed attribute"); + if (!emission.IsConstantAggregate) { + // For simple scalar/complex initialization, store the value directly. + LValue lv = LValue::makeAddr(addr, type); + assert(init && "expected initializer"); + mlir::Location initLoc = getLoc(init->getSourceRange()); + // lv.setNonGC(true); + return emitStoreThroughLValue( + RValue::get(builder.getConstant(initLoc, typedConstant)), lv); } } -void CIRGenFunction::emitAutoVarCleanups(const clang::VarDecl &d) { +void CIRGenFunction::emitAutoVarCleanups( + const CIRGenFunction::AutoVarEmission &emission) { + const VarDecl &d = *emission.Variable; + // Check the type for a cleanup. if (d.needsDestruction(getContext())) cgm.errorNYI(d.getSourceRange(), "emitAutoVarCleanups: type cleanup"); @@ -76,9 +191,9 @@ void CIRGenFunction::emitAutoVarCleanups(const clang::VarDecl &d) { /// register, or no storage class specifier. These turn into simple stack /// objects, globals depending on target. void CIRGenFunction::emitAutoVarDecl(const VarDecl &d) { - emitAutoVarAlloca(d); - emitAutoVarInit(d); - emitAutoVarCleanups(d); + CIRGenFunction::AutoVarEmission emission = emitAutoVarAlloca(d); + emitAutoVarInit(emission); + emitAutoVarCleanups(emission); } void CIRGenFunction::emitVarDecl(const VarDecl &d) { @@ -94,10 +209,54 @@ void CIRGenFunction::emitVarDecl(const VarDecl &d) { assert(d.hasLocalStorage()); - assert(!cir::MissingFeatures::opAllocaVarDeclContext()); + CIRGenFunction::VarDeclContext varDeclCtx{*this, &d}; return emitAutoVarDecl(d); } +void CIRGenFunction::emitScalarInit(const Expr *init, mlir::Location loc, + LValue lvalue, bool capturedByInit) { + assert(!cir::MissingFeatures::objCLifetime()); + + SourceLocRAIIObject locRAII{*this, loc}; + mlir::Value value = emitScalarExpr(init); + if (capturedByInit) { + cgm.errorNYI(init->getSourceRange(), "emitScalarInit: captured by init"); + return; + } + assert(!cir::MissingFeatures::emitNullabilityCheck()); + emitStoreThroughLValue(RValue::get(value), lvalue, true); + return; +} + +void CIRGenFunction::emitExprAsInit(const Expr *init, const ValueDecl *d, + LValue lvalue, bool capturedByInit) { + SourceLocRAIIObject loc{*this, getLoc(init->getSourceRange())}; + if (capturedByInit) { + cgm.errorNYI(init->getSourceRange(), "emitExprAsInit: captured by init"); + return; + } + + QualType type = d->getType(); + + if (type->isReferenceType()) { + cgm.errorNYI(init->getSourceRange(), "emitExprAsInit: reference type"); + return; + } + switch (CIRGenFunction::getEvaluationKind(type)) { + case cir::TEK_Scalar: + emitScalarInit(init, getLoc(d->getSourceRange()), lvalue); + return; + case cir::TEK_Complex: { + cgm.errorNYI(init->getSourceRange(), "emitExprAsInit: complex type"); + return; + } + case cir::TEK_Aggregate: + cgm.errorNYI(init->getSourceRange(), "emitExprAsInit: aggregate type"); + return; + } + llvm_unreachable("bad evaluation kind"); +} + void CIRGenFunction::emitDecl(const Decl &d) { switch (d.getKind()) { case Decl::Var: { diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index ccc3e20875263..07fb4cf8f1513 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -25,9 +25,77 @@ using namespace clang; using namespace clang::CIRGen; using namespace cir; +void CIRGenFunction::emitStoreThroughLValue(RValue src, LValue dst, + bool isInit) { + if (!dst.isSimple()) { + cgm.errorNYI(dst.getPointer().getLoc(), + "emitStoreThroughLValue: non-simple lvalue"); + return; + } + + assert(!cir::MissingFeatures::opLoadStoreObjC()); + + assert(src.isScalar() && "Can't emit an aggregate store with this method"); + emitStoreOfScalar(src.getScalarVal(), dst, isInit); +} + +void CIRGenFunction::emitStoreOfScalar(mlir::Value value, Address addr, + bool isVolatile, QualType ty, + bool isInit, bool isNontemporal) { + assert(!cir::MissingFeatures::opLoadStoreThreadLocal()); + + if (ty->getAs()) { + cgm.errorNYI(addr.getPointer().getLoc(), "emitStoreOfScalar vector type"); + return; + } + + value = emitToMemory(value, ty); + + assert(!cir::MissingFeatures::opLoadStoreAtomic()); + + // Update the alloca with more info on initialization. + assert(addr.getPointer() && "expected pointer to exist"); + auto srcAlloca = + dyn_cast_or_null(addr.getPointer().getDefiningOp()); + if (currVarDecl && srcAlloca) { + const VarDecl *vd = currVarDecl; + assert(vd && "VarDecl expected"); + if (vd->hasInit()) + srcAlloca.setInitAttr(mlir::UnitAttr::get(&getMLIRContext())); + } + + assert(currSrcLoc && "must pass in source location"); + builder.createStore(*currSrcLoc, value, addr.getPointer() /*, isVolatile*/); + + if (isNontemporal) { + cgm.errorNYI(addr.getPointer().getLoc(), "emitStoreOfScalar nontemporal"); + return; + } + + assert(!cir::MissingFeatures::opTBAA()); +} + +mlir::Value CIRGenFunction::emitToMemory(mlir::Value value, QualType ty) { + // Bool has a different representation in memory than in registers, + // but in ClangIR, it is simply represented as a cir.bool value. + // This function is here as a placeholder for possible future changes. + return value; +} + +void CIRGenFunction::emitStoreOfScalar(mlir::Value value, LValue lvalue, + bool isInit) { + if (lvalue.getType()->isConstantMatrixType()) { + assert(0 && "NYI: emitStoreOfScalar constant matrix type"); + return; + } + + emitStoreOfScalar(value, lvalue.getAddress(), lvalue.isVolatile(), + lvalue.getType(), isInit, /*isNontemporal=*/false); +} + mlir::Value CIRGenFunction::emitLoadOfScalar(LValue lvalue, SourceLocation loc) { - assert(!cir::MissingFeatures::opLoadThreadLocal()); + assert(!cir::MissingFeatures::opLoadStoreThreadLocal()); assert(!cir::MissingFeatures::opLoadEmitScalarRangeCheck()); assert(!cir::MissingFeatures::opLoadBooleanRepresentation()); @@ -115,7 +183,7 @@ mlir::Value CIRGenFunction::emitAlloca(StringRef name, mlir::Type ty, builder.restoreInsertionPoint(builder.getBestAllocaInsertPoint(entryBlock)); addr = builder.createAlloca(loc, /*addr type*/ localVarPtrTy, /*var type*/ ty, name, alignIntAttr); - assert(!cir::MissingFeatures::opAllocaVarDeclContext()); + assert(!cir::MissingFeatures::astVarDeclInterface()); } return addr; } diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp new file mode 100644 index 0000000000000..1ea7f6212766c --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp @@ -0,0 +1,309 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This contains code to emit Constant Expr nodes as LLVM code. +// +//===----------------------------------------------------------------------===// + +#include "Address.h" +#include "CIRGenConstantEmitter.h" +#include "CIRGenFunction.h" +#include "CIRGenModule.h" +#include "mlir/IR/Attributes.h" +#include "mlir/IR/BuiltinAttributeInterfaces.h" +#include "mlir/IR/BuiltinAttributes.h" +#include "clang/AST/APValue.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Attr.h" +#include "clang/AST/OperationKinds.h" +#include "clang/AST/RecordLayout.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/Specifiers.h" +#include "clang/CIR/Dialect/IR/CIRAttrs.h" +#include "clang/CIR/Dialect/IR/CIRTypes.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Sequence.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace clang; +using namespace clang::CIRGen; + +//===----------------------------------------------------------------------===// +// ConstExprEmitter +//===----------------------------------------------------------------------===// + +// This class only needs to handle arrays, structs and unions. +// +// In LLVM codegen, when outside C++11 mode, those types are not constant +// folded, while all other types are handled by constant folding. +// +// In CIR codegen, instead of folding things here, we should defer that work +// to MLIR: do not attempt to do much here. +class ConstExprEmitter + : public StmtVisitor { + CIRGenModule &cgm; + LLVM_ATTRIBUTE_UNUSED ConstantEmitter &emitter; + +public: + ConstExprEmitter(ConstantEmitter &emitter) + : cgm(emitter.cgm), emitter(emitter) {} + + //===--------------------------------------------------------------------===// + // Visitor Methods + //===--------------------------------------------------------------------===// + + mlir::Attribute VisitStmt(Stmt *S, QualType T) { return {}; } + + mlir::Attribute VisitConstantExpr(ConstantExpr *ce, QualType t) { + if (mlir::Attribute result = emitter.tryEmitConstantExpr(ce)) + return result; + return Visit(ce->getSubExpr(), t); + } + + mlir::Attribute VisitParenExpr(ParenExpr *pe, QualType t) { + return Visit(pe->getSubExpr(), t); + } + + mlir::Attribute + VisitSubstNonTypeTemplateParmExpr(SubstNonTypeTemplateParmExpr *pe, + QualType t) { + return Visit(pe->getReplacement(), t); + } + + mlir::Attribute VisitGenericSelectionExpr(GenericSelectionExpr *ge, + QualType t) { + return Visit(ge->getResultExpr(), t); + } + + mlir::Attribute VisitChooseExpr(ChooseExpr *ce, QualType t) { + return Visit(ce->getChosenSubExpr(), t); + } + + mlir::Attribute VisitCompoundLiteralExpr(CompoundLiteralExpr *e, QualType t) { + return Visit(e->getInitializer(), t); + } + + mlir::Attribute VisitCastExpr(CastExpr *e, QualType destType) { + cgm.errorNYI(e->getBeginLoc(), "ConstExprEmitter::VisitCastExpr"); + return {}; + } + + mlir::Attribute VisitCXXDefaultInitExpr(CXXDefaultInitExpr *die, QualType t) { + cgm.errorNYI(die->getBeginLoc(), + "ConstExprEmitter::VisitCXXDefaultInitExpr"); + return {}; + } + + mlir::Attribute VisitExprWithCleanups(ExprWithCleanups *e, QualType t) { + // Since this about constant emission no need to wrap this under a scope. + return Visit(e->getSubExpr(), t); + } + + mlir::Attribute VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *e, + QualType t) { + return Visit(e->getSubExpr(), t); + } + + mlir::Attribute VisitImplicitValueInitExpr(ImplicitValueInitExpr *E, + QualType T) { + cgm.errorNYI(E->getBeginLoc(), + "ConstExprEmitter::VisitImplicitValueInitExpr"); + return {}; + } + + mlir::Attribute VisitInitListExpr(InitListExpr *ile, QualType t) { + cgm.errorNYI(ile->getBeginLoc(), "ConstExprEmitter::VisitInitListExpr"); + return {}; + } + + mlir::Attribute VisitDesignatedInitUpdateExpr(DesignatedInitUpdateExpr *e, + QualType destType) { + mlir::Attribute c = Visit(e->getBase(), destType); + if (!c) + return {}; + + cgm.errorNYI(e->getBeginLoc(), + "ConstExprEmitter::VisitDesignatedInitUpdateExpr"); + return {}; + } + + mlir::Attribute VisitCXXConstructExpr(CXXConstructExpr *e, QualType ty) { + cgm.errorNYI(e->getBeginLoc(), "ConstExprEmitter::VisitCXXConstructExpr"); + return {}; + } + + mlir::Attribute VisitStringLiteral(StringLiteral *e, QualType t) { + cgm.errorNYI(e->getBeginLoc(), "ConstExprEmitter::VisitStringLiteral"); + return {}; + } + + mlir::Attribute VisitObjCEncodeExpr(ObjCEncodeExpr *e, QualType t) { + cgm.errorNYI(e->getBeginLoc(), "ConstExprEmitter::VisitObjCEncodeExpr"); + return {}; + } + + mlir::Attribute VisitUnaryExtension(const UnaryOperator *e, QualType t) { + return Visit(e->getSubExpr(), t); + } + + // Utility methods + mlir::Type convertType(QualType t) { return cgm.convertType(t); } +}; + +// TODO(cir): this can be shared with LLVM's codegen +static QualType getNonMemoryType(CIRGenModule &cgm, QualType type) { + if (auto at = type->getAs()) { + return cgm.getASTContext().getQualifiedType(at->getValueType(), + type.getQualifiers()); + } + return type; +} + +//===----------------------------------------------------------------------===// +// ConstantEmitter +//===----------------------------------------------------------------------===// + +mlir::Attribute +ConstantEmitter::tryEmitAbstractForInitializer(const VarDecl &d) { + AbstractStateRAII state(*this, true); + return tryEmitPrivateForVarInit(d); +} + +mlir::Attribute ConstantEmitter::tryEmitPrivateForVarInit(const VarDecl &d) { + // Make a quick check if variable can be default NULL initialized + // and avoid going through rest of code which may do, for c++11, + // initialization of memory to all NULLs. + if (!d.hasLocalStorage()) { + QualType ty = cgm.getASTContext().getBaseElementType(d.getType()); + if (ty->isRecordType()) + if (d.getInit() && isa(d.getInit())) { + cgm.errorNYI(d.getInit()->getBeginLoc(), + "tryEmitPrivateForVarInit CXXConstructExpr"); + return {}; + } + } + inConstantContext = d.hasConstantInitialization(); + + const Expr *e = d.getInit(); + assert(e && "No initializer to emit"); + + QualType destType = d.getType(); + + if (!destType->isReferenceType()) { + QualType nonMemoryDestType = getNonMemoryType(cgm, destType); + if (mlir::Attribute c = ConstExprEmitter(*this).Visit(const_cast(e), + nonMemoryDestType)) + return emitForMemory(c, destType); + } + + // Try to emit the initializer. Note that this can allow some things that + // are not allowed by tryEmitPrivateForMemory alone. + if (APValue *value = d.evaluateValue()) + return tryEmitPrivateForMemory(*value, destType); + + return {}; +} + +mlir::Attribute ConstantEmitter::tryEmitConstantExpr(const ConstantExpr *ce) { + if (!ce->hasAPValueResult()) + return {}; + + QualType retType = ce->getType(); + if (ce->isGLValue()) + retType = cgm.getASTContext().getLValueReferenceType(retType); + + return emitAbstract(ce->getBeginLoc(), ce->getAPValueResult(), retType); +} + +mlir::Attribute ConstantEmitter::tryEmitPrivateForMemory(const APValue &value, + QualType destType) { + QualType nonMemoryDestType = getNonMemoryType(cgm, destType); + mlir::Attribute c = tryEmitPrivate(value, nonMemoryDestType); + return (c ? emitForMemory(c, destType) : nullptr); +} + +mlir::Attribute ConstantEmitter::emitAbstract(SourceLocation loc, + const APValue &value, + QualType destType) { + AbstractStateRAII state(*this, true); + mlir::Attribute c = tryEmitPrivate(value, destType); + if (!c) + cgm.errorNYI(loc, "emitAbstract failed, emit null constaant"); + return c; +} + +mlir::Attribute ConstantEmitter::emitForMemory(mlir::Attribute c, + QualType destType) { + // For an _Atomic-qualified constant, we may need to add tail padding. + if (destType->getAs()) { + cgm.errorNYI("emitForMemory: atomic type"); + return {}; + } + + return c; +} + +mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &value, + QualType destType) { + auto &builder = cgm.getBuilder(); + switch (value.getKind()) { + case APValue::None: + case APValue::Indeterminate: + cgm.errorNYI("ConstExprEmitter::tryEmitPrivate none or indeterminate"); + return {}; + case APValue::Int: { + mlir::Type ty = cgm.convertType(destType); + if (mlir::isa(ty)) + return builder.getCIRBoolAttr(value.getInt().getZExtValue()); + assert(mlir::isa(ty) && "expected integral type"); + return cgm.getBuilder().getAttr(ty, value.getInt()); + } + case APValue::Float: { + const llvm::APFloat &init = value.getFloat(); + if (&init.getSemantics() == &llvm::APFloat::IEEEhalf() && + !cgm.getASTContext().getLangOpts().NativeHalfType && + cgm.getASTContext().getTargetInfo().useFP16ConversionIntrinsics()) { + cgm.errorNYI("ConstExprEmitter::tryEmitPrivate half"); + return {}; + } else { + mlir::Type ty = cgm.convertType(destType); + assert(mlir::isa(ty) && + "expected floating-point type"); + return cgm.getBuilder().getAttr(ty, init); + } + } + case APValue::Array: { + cgm.errorNYI("ConstExprEmitter::tryEmitPrivate array"); + return {}; + } + case APValue::Vector: { + cgm.errorNYI("ConstExprEmitter::tryEmitPrivate vector"); + return {}; + } + case APValue::MemberPointer: { + cgm.errorNYI("ConstExprEmitter::tryEmitPrivate member pointer"); + return {}; + } + case APValue::LValue: + cgm.errorNYI("ConstExprEmitter::tryEmitPrivate lvalue"); + return {}; + case APValue::Struct: + case APValue::Union: + cgm.errorNYI("ConstExprEmitter::tryEmitPrivate struct or union"); + return {}; + case APValue::FixedPoint: + case APValue::ComplexInt: + case APValue::ComplexFloat: + case APValue::AddrLabelDiff: + cgm.errorNYI("ConstExprEmitter::tryEmitPrivate fixed point, complex int, " + "complex float, addr label diff"); + return {}; + } + llvm_unreachable("Unknown APValue kind"); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index cf896d3c0a946..72445f62232a4 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -26,6 +26,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/Type.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" +#include "clang/CIR/MissingFeatures.h" #include "clang/CIR/TypeEvaluationKind.h" #include "llvm/ADT/ScopedHashTable.h" @@ -105,6 +106,27 @@ class CIRGenFunction : public CIRGenTypeCache { mlir::Value emitAlloca(llvm::StringRef name, mlir::Type ty, mlir::Location loc, clang::CharUnits alignment); +private: + // Track current variable initialization (if there's one) + const clang::VarDecl *currVarDecl = nullptr; + class VarDeclContext { + CIRGenFunction &p; + const clang::VarDecl *oldVal = nullptr; + + public: + VarDeclContext(CIRGenFunction &p, const VarDecl *value) : p(p) { + if (p.currVarDecl) + oldVal = p.currVarDecl; + p.currVarDecl = value; + } + + /// Can be used to restore the state early, before the dtor + /// is run. + void restore() { p.currVarDecl = oldVal; } + ~VarDeclContext() { restore(); } + }; + +public: /// Use to track source locations across nested visitor traversals. /// Always use a `SourceLocRAIIObject` to change currSrcLoc. std::optional currSrcLoc; @@ -170,16 +192,97 @@ class CIRGenFunction : public CIRGenTypeCache { void emitDecl(const clang::Decl &d); + void emitScalarInit(const clang::Expr *init, mlir::Location loc, + LValue lvalue, bool capturedByInit = false); + LValue emitDeclRefLValue(const clang::DeclRefExpr *e); + /// Determine whether the given initializer is trivial in the sense + /// that it requires no code to be generated. + bool isTrivialInitializer(const Expr *init); + + /// Emit an expression as an initializer for an object (variable, field, etc.) + /// at the given location. The expression is not necessarily the normal + /// initializer for the object, and the address is not necessarily + /// its normal location. + /// + /// \param init the initializing expression + /// \param d the object to act as if we're initializing + /// \param lvalue the lvalue to initialize + /// \param capturedByInit true if \p d is a __block variable whose address is + /// potentially changed by the initializer + void emitExprAsInit(const clang::Expr *init, const clang::ValueDecl *d, + LValue lvalue, bool capturedByInit = false); + /// Emit code and set up symbol table for a variable declaration with auto, /// register, or no storage class specifier. These turn into simple stack /// objects, globals depending on target. void emitAutoVarDecl(const clang::VarDecl &d); - void emitAutoVarAlloca(const clang::VarDecl &d); - void emitAutoVarInit(const clang::VarDecl &d); - void emitAutoVarCleanups(const clang::VarDecl &d); + struct AutoVarEmission { + const clang::VarDecl *Variable; + /// The address of the alloca for languages with explicit address space + /// (e.g. OpenCL) or alloca casted to generic pointer for address space + /// agnostic languages (e.g. C++). Invalid if the variable was emitted + /// as a global constant. + Address Addr; + + /// True if the variable is of aggregate type and has a constant + /// initializer. + bool IsConstantAggregate = false; + + /// True if the variable is a __block variable that is captured by an + /// escaping block. + bool IsEscapingByRef = false; + + mlir::Value NRVOFlag{}; + + struct Invalid {}; + AutoVarEmission(Invalid) : Variable(nullptr), Addr(Address::invalid()) {} + + AutoVarEmission(const clang::VarDecl &variable) + : Variable(&variable), Addr(Address::invalid()) {} + + static AutoVarEmission invalid() { return AutoVarEmission(Invalid()); } + + bool wasEmittedAsGlobal() const { return !Addr.isValid(); } + + /// Returns the raw, allocated address, which is not necessarily + /// the address of the object itself. It is casted to default + /// address space for address space agnostic languages. + Address getAllocatedAddress() const { return Addr; } + + /// Returns the address of the object within this declaration. + /// Note that this does not chase the forwarding pointer for + /// __block decls. + Address getObjectAddress(CIRGenFunction &CGF) const { + if (!IsEscapingByRef) + return Addr; + + assert(!cir::MissingFeatures::opAllocaEscapeByReference()); + return Address::invalid(); + } + }; + + AutoVarEmission emitAutoVarAlloca(const clang::VarDecl &d); + void emitAutoVarInit(const AutoVarEmission &emission); + void emitAutoVarCleanups(const AutoVarEmission &emission); + + void emitStoreOfScalar(mlir::Value value, Address addr, bool isVolatile, + clang::QualType ty, bool isInit = false, + bool isNontemporal = false); + void emitStoreOfScalar(mlir::Value value, LValue lvalue, bool isInit); + + /// Given a value and its clang type, returns the value casted to its memory + /// representation. + /// Note: CIR defers most of the special casting to the final lowering passes + /// to conserve the high level information. + mlir::Value emitToMemory(mlir::Value Value, clang::QualType Ty); + + /// Store the specified rvalue into the specified + /// lvalue, where both are guaranteed to the have the same type, and that type + /// is 'Ty'. + void emitStoreThroughLValue(RValue Src, LValue Dst, bool isInit = false); /// This method handles emission of any variable declaration /// inside a function, including static vars etc. diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 5fb5ef505a8c1..2a798f4cd56a9 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -147,6 +147,20 @@ class CIRGenModule : public CIRGenTypeCache { return diags.Report(loc, diagID) << feature << name; } + DiagnosticBuilder errorNYI(mlir::Location loc, llvm::StringRef feature) { + // TODO: Convert the location to a SourceLocation + unsigned diagID = diags.getCustomDiagID( + DiagnosticsEngine::Error, "ClangIR code gen Not Yet Implemented: %0"); + return diags.Report(diagID) << feature; + } + + DiagnosticBuilder errorNYI(llvm::StringRef feature) { + // TODO: Make a default location? currSrcLoc? + unsigned diagID = diags.getCustomDiagID( + DiagnosticsEngine::Error, "ClangIR code gen Not Yet Implemented: %0"); + return diags.Report(diagID) << feature; + } + DiagnosticBuilder errorNYI(SourceRange, llvm::StringRef); template diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt index e6d3cbabd853b..8ee65c2763e70 100644 --- a/clang/lib/CIR/CodeGen/CMakeLists.txt +++ b/clang/lib/CIR/CodeGen/CMakeLists.txt @@ -10,6 +10,7 @@ add_clang_library(clangCIR CIRGenerator.cpp CIRGenDecl.cpp CIRGenExpr.cpp + CIRGenExprConstant.cpp CIRGenExprScalar.cpp CIRGenFunction.cpp CIRGenModule.cpp diff --git a/clang/lib/CIR/Dialect/CMakeLists.txt b/clang/lib/CIR/Dialect/CMakeLists.txt index f33061b2d87cf..9f57627c321fb 100644 --- a/clang/lib/CIR/Dialect/CMakeLists.txt +++ b/clang/lib/CIR/Dialect/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(IR) +add_subdirectory(Transforms) diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 5ad369b40cda1..d041791770d82 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -268,6 +268,19 @@ LogicalResult cir::ScopeOp::verify() { return success(); } +//===----------------------------------------------------------------------===// +// BrOp +//===----------------------------------------------------------------------===// + +mlir::SuccessorOperands cir::BrOp::getSuccessorOperands(unsigned index) { + assert(index == 0 && "invalid successor index"); + return mlir::SuccessorOperands(getDestOperandsMutable()); +} + +Block *cir::BrOp::getSuccessorForOperands(ArrayRef) { + return getDest(); +} + //===----------------------------------------------------------------------===// // GlobalOp //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/IR/CMakeLists.txt b/clang/lib/CIR/Dialect/IR/CMakeLists.txt index e3a6fc6e80ecc..aa5ea52a5e93f 100644 --- a/clang/lib/CIR/Dialect/IR/CMakeLists.txt +++ b/clang/lib/CIR/Dialect/IR/CMakeLists.txt @@ -11,6 +11,7 @@ add_clang_library(MLIRCIR LINK_LIBS PUBLIC MLIRIR + MLIRCIRInterfaces MLIRDLTIDialect MLIRDataLayoutInterfaces MLIRFuncDialect diff --git a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt new file mode 100644 index 0000000000000..aa27074cc6131 --- /dev/null +++ b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt @@ -0,0 +1,18 @@ +add_clang_library(MLIRCIRTransforms + FlattenCFG.cpp + + DEPENDS + MLIRCIRPassIncGen + + LINK_LIBS PUBLIC + clangAST + clangBasic + + MLIRAnalysis + MLIRIR + MLIRPass + MLIRTransformUtils + + MLIRCIR + MLIRCIRInterfaces +) diff --git a/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp b/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp new file mode 100644 index 0000000000000..b01d3d4d43e65 --- /dev/null +++ b/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp @@ -0,0 +1,117 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements pass that inlines CIR operations regions into the parent +// function region. +// +//===----------------------------------------------------------------------===// + +#include "PassDetail.h" +#include "mlir/Dialect/Func/IR/FuncOps.h" +#include "mlir/IR/PatternMatch.h" +#include "mlir/Support/LogicalResult.h" +#include "mlir/Transforms/DialectConversion.h" +#include "mlir/Transforms/GreedyPatternRewriteDriver.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" +#include "clang/CIR/Dialect/Passes.h" +#include "clang/CIR/MissingFeatures.h" + +using namespace mlir; +using namespace cir; + +namespace { + +struct CIRFlattenCFGPass : public CIRFlattenCFGBase { + + CIRFlattenCFGPass() = default; + void runOnOperation() override; +}; + +class CIRScopeOpFlattening : public mlir::OpRewritePattern { +public: + using OpRewritePattern::OpRewritePattern; + + mlir::LogicalResult + matchAndRewrite(cir::ScopeOp scopeOp, + mlir::PatternRewriter &rewriter) const override { + mlir::OpBuilder::InsertionGuard guard(rewriter); + mlir::Location loc = scopeOp.getLoc(); + + // Empty scope: just remove it. + // TODO: Remove this logic once CIR uses MLIR infrastructure to remove + // trivially dead operations. MLIR canonicalizer is too aggressive and we + // need to either (a) make sure all our ops model all side-effects and/or + // (b) have more options in the canonicalizer in MLIR to temper + // aggressiveness level. + if (scopeOp.isEmpty()) { + rewriter.eraseOp(scopeOp); + return mlir::success(); + } + + // Split the current block before the ScopeOp to create the inlining + // point. + mlir::Block *currentBlock = rewriter.getInsertionBlock(); + mlir::Block *continueBlock = + rewriter.splitBlock(currentBlock, rewriter.getInsertionPoint()); + if (scopeOp.getNumResults() > 0) + continueBlock->addArguments(scopeOp.getResultTypes(), loc); + + // Inline body region. + mlir::Block *beforeBody = &scopeOp.getScopeRegion().front(); + mlir::Block *afterBody = &scopeOp.getScopeRegion().back(); + rewriter.inlineRegionBefore(scopeOp.getScopeRegion(), continueBlock); + + // Save stack and then branch into the body of the region. + rewriter.setInsertionPointToEnd(currentBlock); + assert(!cir::MissingFeatures::stackSaveOp()); + rewriter.create(loc, mlir::ValueRange(), beforeBody); + + // Replace the scopeop return with a branch that jumps out of the body. + // Stack restore before leaving the body region. + rewriter.setInsertionPointToEnd(afterBody); + if (auto yieldOp = dyn_cast(afterBody->getTerminator())) { + rewriter.replaceOpWithNewOp(yieldOp, yieldOp.getArgs(), + continueBlock); + } + + // Replace the op with values return from the body region. + rewriter.replaceOp(scopeOp, continueBlock->getArguments()); + + return mlir::success(); + } +}; + +void populateFlattenCFGPatterns(RewritePatternSet &patterns) { + patterns.add(patterns.getContext()); +} + +void CIRFlattenCFGPass::runOnOperation() { + RewritePatternSet patterns(&getContext()); + populateFlattenCFGPatterns(patterns); + + // Collect operations to apply patterns. + llvm::SmallVector ops; + getOperation()->walk([&](Operation *op) { + if (isa(op)) + ops.push_back(op); + }); + + // Apply patterns. + if (applyOpPatternsGreedily(ops, std::move(patterns)).failed()) + signalPassFailure(); +} + +} // namespace + +namespace mlir { + +std::unique_ptr createCIRFlattenCFGPass() { + return std::make_unique(); +} + +} // namespace mlir diff --git a/clang/lib/CIR/Dialect/Transforms/PassDetail.h b/clang/lib/CIR/Dialect/Transforms/PassDetail.h new file mode 100644 index 0000000000000..600dde56d679f --- /dev/null +++ b/clang/lib/CIR/Dialect/Transforms/PassDetail.h @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef CIR_DIALECT_TRANSFORMS_PASSDETAIL_H +#define CIR_DIALECT_TRANSFORMS_PASSDETAIL_H + +#include "mlir/IR/Dialect.h" +#include "mlir/Pass/Pass.h" + +namespace cir { +class CIRDialect; +} // namespace cir + +namespace mlir { +// Forward declaration from Dialect.h +template +void registerDialect(DialectRegistry ®istry); + +#define GEN_PASS_CLASSES +#include "clang/CIR/Dialect/Passes.h.inc" + +} // namespace mlir + +#endif // CIR_DIALECT_TRANSFORMS_PASSDETAIL_H diff --git a/clang/lib/CIR/Lowering/CIRPasses.cpp b/clang/lib/CIR/Lowering/CIRPasses.cpp new file mode 100644 index 0000000000000..235acbfee8967 --- /dev/null +++ b/clang/lib/CIR/Lowering/CIRPasses.cpp @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements machinery for any CIR <-> CIR passes used by clang. +// +//===----------------------------------------------------------------------===// + +// #include "clang/AST/ASTContext.h" +#include "clang/CIR/Dialect/Passes.h" + +#include "mlir/Pass/PassManager.h" + +namespace mlir { + +void populateCIRPreLoweringPasses(OpPassManager &pm) { + pm.addPass(createCIRFlattenCFGPass()); +} + +} // namespace mlir diff --git a/clang/lib/CIR/Lowering/CMakeLists.txt b/clang/lib/CIR/Lowering/CMakeLists.txt index 95c304ded9183..09e48862df63c 100644 --- a/clang/lib/CIR/Lowering/CMakeLists.txt +++ b/clang/lib/CIR/Lowering/CMakeLists.txt @@ -1 +1,20 @@ +set(LLVM_LINK_COMPONENTS + Core + Support + ) + +get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) + +add_clang_library(clangCIRLoweringCommon + CIRPasses.cpp + + LINK_LIBS + clangCIR + ${dialect_libs} + MLIRCIR + MLIRCIRTransforms + MLIRTransforms + MLIRSupport + ) + add_subdirectory(DirectToLLVM) diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt b/clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt index c11ecb82183d0..7baff3412a84e 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt +++ b/clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt @@ -7,6 +7,7 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) add_clang_library(clangCIRLoweringDirectToLLVM LowerToLLVM.cpp + LowerToLLVMIR.cpp DEPENDS MLIRCIREnumsGen @@ -14,9 +15,10 @@ add_clang_library(clangCIRLoweringDirectToLLVM MLIRCIROpInterfacesIncGen LINK_LIBS - MLIRIR + clangCIRLoweringCommon ${dialect_libs} MLIRCIR MLIRBuiltinToLLVMIRTranslation MLIRLLVMToLLVMIRTranslation + MLIRIR ) diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 3200527bd03af..79ec0696eb180 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -12,6 +12,8 @@ #include "LowerToLLVM.h" +#include + #include "mlir/Conversion/LLVMCommon/TypeConverter.h" #include "mlir/Dialect/DLTI/DLTI.h" #include "mlir/Dialect/Func/IR/FuncOps.h" @@ -25,11 +27,13 @@ #include "mlir/Target/LLVMIR/Export.h" #include "mlir/Transforms/DialectConversion.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" +#include "clang/CIR/Dialect/Passes.h" #include "clang/CIR/MissingFeatures.h" #include "clang/CIR/Passes.h" #include "llvm/ADT/TypeSwitch.h" #include "llvm/IR/Module.h" #include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/TimeProfiler.h" using namespace cir; @@ -135,6 +139,7 @@ mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage) { case CIR::WeakODRLinkage: return LLVM::WeakODR; }; + llvm_unreachable("Unknown CIR linkage type"); } class CIRAttrToValue { @@ -603,6 +608,69 @@ static void prepareTypeConverter(mlir::LLVMTypeConverter &converter, }); } +// The applyPartialConversion function traverses blocks in the dominance order, +// so it does not lower and operations that are not reachachable from the +// operations passed in as arguments. Since we do need to lower such code in +// order to avoid verification errors occur, we cannot just pass the module op +// to applyPartialConversion. We must build a set of unreachable ops and +// explicitly add them, along with the module, to the vector we pass to +// applyPartialConversion. +// +// For instance, this CIR code: +// +// cir.func @foo(%arg0: !s32i) -> !s32i { +// %4 = cir.cast(int_to_bool, %arg0 : !s32i), !cir.bool +// cir.if %4 { +// %5 = cir.const #cir.int<1> : !s32i +// cir.return %5 : !s32i +// } else { +// %5 = cir.const #cir.int<0> : !s32i +// cir.return %5 : !s32i +// } +// cir.return %arg0 : !s32i +// } +// +// contains an unreachable return operation (the last one). After the flattening +// pass it will be placed into the unreachable block. The possible error +// after the lowering pass is: error: 'cir.return' op expects parent op to be +// one of 'cir.func, cir.scope, cir.if ... The reason that this operation was +// not lowered and the new parent is llvm.func. +// +// In the future we may want to get rid of this function and use a DCE pass or +// something similar. But for now we need to guarantee the absence of the +// dialect verification errors. +static void collectUnreachable(mlir::Operation *parent, + llvm::SmallVector &ops) { + + llvm::SmallVector unreachableBlocks; + parent->walk([&](mlir::Block *blk) { // check + if (blk->hasNoPredecessors() && !blk->isEntryBlock()) + unreachableBlocks.push_back(blk); + }); + + std::set visited; + for (mlir::Block *root : unreachableBlocks) { + // We create a work list for each unreachable block. + // Thus we traverse operations in some order. + std::deque workList; + workList.push_back(root); + + while (!workList.empty()) { + mlir::Block *blk = workList.back(); + workList.pop_back(); + if (visited.count(blk)) + continue; + visited.emplace(blk); + + for (mlir::Operation &op : *blk) + ops.push_back(&op); + + for (mlir::Block *succ : blk->getSuccessors()) + workList.push_back(succ); + } + } +} + void ConvertCIRToLLVMPass::processCIRAttrs(mlir::ModuleOp module) { // Lower the module attributes to LLVM equivalents. if (mlir::Attribute tripleAttr = @@ -630,7 +698,13 @@ void ConvertCIRToLLVMPass::runOnOperation() { patterns.add(converter, patterns.getContext(), dl); patterns.add(converter, patterns.getContext(), dl); - patterns.add(converter, patterns.getContext()); + patterns.add< + // clang-format off + CIRToLLVMBrOpLowering, + CIRToLLVMFuncOpLowering, + CIRToLLVMTrapOpLowering + // clang-format on + >(converter, patterns.getContext()); processCIRAttrs(module); @@ -640,9 +714,36 @@ void ConvertCIRToLLVMPass::runOnOperation() { target.addIllegalDialect(); - if (failed(applyPartialConversion(module, target, std::move(patterns)))) { + llvm::SmallVector ops; + ops.push_back(module); + collectUnreachable(module, ops); + + if (failed(applyPartialConversion(ops, target, std::move(patterns)))) signalPassFailure(); - } +} + +mlir::LogicalResult CIRToLLVMBrOpLowering::matchAndRewrite( + cir::BrOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + rewriter.replaceOpWithNewOp(op, adaptor.getOperands(), + op.getDest()); + return mlir::LogicalResult::success(); +} + +mlir::LogicalResult CIRToLLVMTrapOpLowering::matchAndRewrite( + cir::TrapOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + mlir::Location loc = op->getLoc(); + rewriter.eraseOp(op); + + rewriter.create(loc); + + // Note that the call to llvm.trap is not a terminator in LLVM dialect. + // So we must emit an additional llvm.unreachable to terminate the current + // block. + rewriter.create(loc); + + return mlir::success(); } std::unique_ptr createConvertCIRToLLVMPass() { @@ -650,6 +751,7 @@ std::unique_ptr createConvertCIRToLLVMPass() { } void populateCIRToLLVMPasses(mlir::OpPassManager &pm) { + mlir::populateCIRPreLoweringPasses(pm); pm.addPass(createConvertCIRToLLVMPass()); } @@ -670,6 +772,7 @@ lowerDirectlyFromCIRToLLVMIR(mlir::ModuleOp mlirModule, LLVMContext &llvmCtx) { mlir::registerBuiltinDialectTranslation(*mlirCtx); mlir::registerLLVMDialectTranslation(*mlirCtx); + mlir::registerCIRDialectTranslation(*mlirCtx); llvm::TimeTraceScope translateScope("translateModuleToLLVMIR"); diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h index a694047e3616b..d090bbe4f2e10 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h @@ -136,6 +136,24 @@ class CIRToLLVMGlobalOpLowering cir::GlobalOp op, mlir::ConversionPatternRewriter &rewriter) const; }; +class CIRToLLVMBrOpLowering : public mlir::OpConversionPattern { +public: + using mlir::OpConversionPattern::OpConversionPattern; + + mlir::LogicalResult + matchAndRewrite(cir::BrOp op, OpAdaptor, + mlir::ConversionPatternRewriter &) const override; +}; + +class CIRToLLVMTrapOpLowering : public mlir::OpConversionPattern { +public: + using mlir::OpConversionPattern::OpConversionPattern; + + mlir::LogicalResult + matchAndRewrite(cir::TrapOp op, OpAdaptor, + mlir::ConversionPatternRewriter &) const override; +}; + } // namespace direct } // namespace cir diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp new file mode 100644 index 0000000000000..30b9eaaca2d37 --- /dev/null +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp @@ -0,0 +1,68 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements lowering of CIR attributes and operations directly to +// LLVMIR. +// +//===----------------------------------------------------------------------===// + +#include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "mlir/IR/DialectRegistry.h" +#include "mlir/Target/LLVMIR/LLVMTranslationInterface.h" +#include "mlir/Target/LLVMIR/ModuleTranslation.h" +#include "clang/CIR/Dialect/IR/CIRAttrs.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" +#include "clang/CIR/MissingFeatures.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/IR/Constant.h" +#include "llvm/IR/GlobalVariable.h" + +using namespace llvm; + +namespace cir { +namespace direct { + +/// Implementation of the dialect interface that converts CIR attributes to LLVM +/// IR metadata. +class CIRDialectLLVMIRTranslationInterface + : public mlir::LLVMTranslationDialectInterface { +public: + using LLVMTranslationDialectInterface::LLVMTranslationDialectInterface; + + /// Translates the given operation to LLVM IR using the provided IR builder + /// and saving the state in `moduleTranslation`. + mlir::LogicalResult convertOperation( + mlir::Operation *op, llvm::IRBuilderBase &builder, + mlir::LLVM::ModuleTranslation &moduleTranslation) const final { + + if (auto cirOp = llvm::dyn_cast(op)) + moduleTranslation.mapValue(cirOp.getResult()) = + llvm::Constant::getNullValue( + moduleTranslation.convertType(cirOp.getType())); + + return mlir::success(); + } +}; + +void registerCIRDialectTranslation(mlir::DialectRegistry ®istry) { + registry.insert(); + registry.addExtension(+[](mlir::MLIRContext *ctx, cir::CIRDialect *dialect) { + dialect->addInterfaces(); + }); +} + +} // namespace direct +} // namespace cir + +namespace mlir { +void registerCIRDialectTranslation(mlir::MLIRContext &context) { + mlir::DialectRegistry registry; + cir::direct::registerCIRDialectTranslation(registry); + context.appendDialectRegistry(registry); +} +} // namespace mlir diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index b86bb242755be..a5ed2595bad4d 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -4309,6 +4309,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, case Builtin::BI__builtin_elementwise_exp2: return RValue::get(emitBuiltinWithOneOverloadedType<1>( *this, E, llvm::Intrinsic::exp2, "elt.exp2")); + case Builtin::BI__builtin_elementwise_exp10: + return RValue::get(emitBuiltinWithOneOverloadedType<1>( + *this, E, llvm::Intrinsic::exp10, "elt.exp10")); case Builtin::BI__builtin_elementwise_log: return RValue::get(emitBuiltinWithOneOverloadedType<1>( *this, E, llvm::Intrinsic::log, "elt.log")); @@ -19836,6 +19839,14 @@ case Builtin::BI__builtin_hlsl_elementwise_isinf: { RValFalse.isScalar() ? RValFalse.getScalarVal() : RValFalse.getAggregatePointer(E->getArg(2)->getType(), *this); + if (auto *VTy = E->getType()->getAs()) { + if (!OpTrue->getType()->isVectorTy()) + OpTrue = + Builder.CreateVectorSplat(VTy->getNumElements(), OpTrue, "splat"); + if (!OpFalse->getType()->isVectorTy()) + OpFalse = + Builder.CreateVectorSplat(VTy->getNumElements(), OpFalse, "splat"); + } Value *SelectVal = Builder.CreateSelect(OpCond, OpTrue, OpFalse, "hlsl.select"); diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index bfcbc273dbda7..7aa77e55dbfcc 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -2705,7 +2705,7 @@ void CodeGenModule::ConstructAttributeList(StringRef Name, llvm::AttributeSet::get(getLLVMContext(), Attrs); } - // Apply `nonnull`, `dereferencable(N)` and `align N` to the `this` argument, + // Apply `nonnull`, `dereferenceable(N)` and `align N` to the `this` argument, // unless this is a thunk function. // FIXME: fix this properly, https://reviews.llvm.org/D100388 if (FI.isInstanceMethod() && !IRFunctionArgs.hasInallocaArg() && diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 0e6daa42ee7bf..7020cef875170 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -3308,7 +3308,7 @@ llvm::DIType *CGDebugInfo::CreateTypeDefinition(const ObjCInterfaceType *Ty, llvm::DIType *CGDebugInfo::CreateType(const VectorType *Ty, llvm::DIFile *Unit) { - if (Ty->isExtVectorBoolType()) { + if (Ty->isPackedVectorBoolType(CGM.getContext())) { // Boolean ext_vector_type(N) are special because their real element type // (bits of bit size) is not their Clang element type (_Bool of size byte). // For now, we pretend the boolean vector were actually a vector of bytes diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 3ad9ebf624143..eab1ebfb2369b 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -48,7 +48,7 @@ using namespace CodeGen; static_assert(clang::Sema::MaximumAlignment <= llvm::Value::MaximumAlignment, "Clang max alignment greater than what LLVM supports?"); -void CodeGenFunction::EmitDecl(const Decl &D) { +void CodeGenFunction::EmitDecl(const Decl &D, bool EvaluateConditionDecl) { switch (D.getKind()) { case Decl::BuiltinTemplate: case Decl::TranslationUnit: @@ -152,7 +152,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) { return; case Decl::UsingPack: for (auto *Using : cast(D).expansions()) - EmitDecl(*Using); + EmitDecl(*Using, /*EvaluateConditionDecl=*/EvaluateConditionDecl); return; case Decl::UsingDirective: // using namespace X; [C++] if (CGDebugInfo *DI = getDebugInfo()) @@ -164,10 +164,8 @@ void CodeGenFunction::EmitDecl(const Decl &D) { assert(VD.isLocalVarDecl() && "Should not see file-scope variables inside a function!"); EmitVarDecl(VD); - if (auto *DD = dyn_cast(&VD)) - for (auto *B : DD->flat_bindings()) - if (auto *HD = B->getHoldingVar()) - EmitVarDecl(*HD); + if (EvaluateConditionDecl) + MaybeEmitDeferredVarDeclInit(&VD); return; } @@ -2059,6 +2057,14 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) { /*IsAutoInit=*/false); } +void CodeGenFunction::MaybeEmitDeferredVarDeclInit(const VarDecl *VD) { + if (auto *DD = dyn_cast_if_present(VD)) { + for (auto *B : DD->flat_bindings()) + if (auto *HD = B->getHoldingVar()) + EmitVarDecl(*HD); + } +} + /// Emit an expression as an initializer for an object (variable, field, etc.) /// at the given location. The expression is not necessarily the normal /// initializer for the object, and the address is not necessarily diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 191912ca7d800..5943ff9294e1a 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1985,7 +1985,7 @@ llvm::Value *CodeGenFunction::EmitLoadOfScalar(Address Addr, bool Volatile, if (const auto *ClangVecTy = Ty->getAs()) { // Boolean vectors use `iN` as storage type. - if (ClangVecTy->isExtVectorBoolType()) { + if (ClangVecTy->isPackedVectorBoolType(getContext())) { llvm::Type *ValTy = ConvertType(Ty); unsigned ValNumElems = cast(ValTy)->getNumElements(); @@ -2064,6 +2064,10 @@ llvm::Value *CodeGenFunction::EmitToMemory(llvm::Value *Value, QualType Ty) { if (Ty->isExtVectorBoolType()) { llvm::Type *StoreTy = convertTypeForLoadStore(Ty, Value->getType()); + if (StoreTy->isVectorTy() && StoreTy->getScalarSizeInBits() > + Value->getType()->getScalarSizeInBits()) + return Builder.CreateZExt(Value, StoreTy); + // Expand to the memory bit width. unsigned MemNumElems = StoreTy->getPrimitiveSizeInBits(); // -->

. @@ -2079,8 +2083,9 @@ llvm::Value *CodeGenFunction::EmitToMemory(llvm::Value *Value, QualType Ty) { /// by convertTypeForLoadStore) to its primary IR type (as returned /// by ConvertType). llvm::Value *CodeGenFunction::EmitFromMemory(llvm::Value *Value, QualType Ty) { - if (Ty->isExtVectorBoolType()) { + if (Ty->isPackedVectorBoolType(getContext())) { const auto *RawIntTy = Value->getType(); + // Bitcast iP -->

. auto *PaddedVecTy = llvm::FixedVectorType::get( Builder.getInt1Ty(), RawIntTy->getPrimitiveSizeInBits()); @@ -2091,10 +2096,10 @@ llvm::Value *CodeGenFunction::EmitFromMemory(llvm::Value *Value, QualType Ty) { return emitBoolVecConversion(V, ValNumElems, "extractvec"); } - if (hasBooleanRepresentation(Ty) || Ty->isBitIntType()) { - llvm::Type *ResTy = ConvertType(Ty); + llvm::Type *ResTy = ConvertType(Ty); + if (hasBooleanRepresentation(Ty) || Ty->isBitIntType() || + Ty->isExtVectorBoolType()) return Builder.CreateTrunc(Value, ResTy, "loadedv"); - } return Value; } @@ -2152,7 +2157,8 @@ void CodeGenFunction::EmitStoreOfScalar(llvm::Value *Value, Address Addr, if (auto *VecTy = dyn_cast(SrcTy)) { auto *NewVecTy = CGM.getABIInfo().getOptimalVectorMemoryType(VecTy, getLangOpts()); - if (!ClangVecTy->isExtVectorBoolType() && VecTy != NewVecTy) { + if (!ClangVecTy->isPackedVectorBoolType(getContext()) && + VecTy != NewVecTy) { SmallVector Mask(NewVecTy->getNumElements(), -1); std::iota(Mask.begin(), Mask.begin() + VecTy->getNumElements(), 0); Value = Builder.CreateShuffleVector(Value, Mask, "extractVec"); @@ -2343,7 +2349,15 @@ RValue CodeGenFunction::EmitLoadOfExtVectorElementLValue(LValue LV) { if (!ExprVT) { unsigned InIdx = getAccessedFieldNo(0, Elts); llvm::Value *Elt = llvm::ConstantInt::get(SizeTy, InIdx); - return RValue::get(Builder.CreateExtractElement(Vec, Elt)); + + llvm::Value *Element = Builder.CreateExtractElement(Vec, Elt); + + llvm::Type *LVTy = ConvertType(LV.getType()); + if (Element->getType()->getPrimitiveSizeInBits() > + LVTy->getPrimitiveSizeInBits()) + Element = Builder.CreateTrunc(Element, LVTy); + + return RValue::get(Element); } // Always use shuffle vector to try to retain the original program structure @@ -2354,6 +2368,10 @@ RValue CodeGenFunction::EmitLoadOfExtVectorElementLValue(LValue LV) { Mask.push_back(getAccessedFieldNo(i, Elts)); Vec = Builder.CreateShuffleVector(Vec, Mask); + + if (LV.getType()->isExtVectorBoolType()) + Vec = Builder.CreateTrunc(Vec, ConvertType(LV.getType()), "truncv"); + return RValue::get(Vec); } @@ -2407,6 +2425,13 @@ void CodeGenFunction::EmitStoreThroughLValue(RValue Src, LValue Dst, // Read/modify/write the vector, inserting the new element. llvm::Value *Vec = Builder.CreateLoad(Dst.getVectorAddress(), Dst.isVolatileQualified()); + llvm::Type *VecTy = Vec->getType(); + llvm::Value *SrcVal = Src.getScalarVal(); + + if (SrcVal->getType()->getPrimitiveSizeInBits() < + VecTy->getScalarSizeInBits()) + SrcVal = Builder.CreateZExt(SrcVal, VecTy->getScalarType()); + auto *IRStoreTy = dyn_cast(Vec->getType()); if (IRStoreTy) { auto *IRVecTy = llvm::FixedVectorType::get( @@ -2414,19 +2439,21 @@ void CodeGenFunction::EmitStoreThroughLValue(RValue Src, LValue Dst, Vec = Builder.CreateBitCast(Vec, IRVecTy); // iN --> . } - llvm::Value *SrcVal = Src.getScalarVal(); + // Allow inserting `<1 x T>` into an ``. It can happen with scalar // types which are mapped to vector LLVM IR types (e.g. for implementing // an ABI). if (auto *EltTy = dyn_cast(SrcVal->getType()); EltTy && EltTy->getNumElements() == 1) SrcVal = Builder.CreateBitCast(SrcVal, EltTy->getElementType()); + Vec = Builder.CreateInsertElement(Vec, SrcVal, Dst.getVectorIdx(), "vecins"); if (IRStoreTy) { // --> . Vec = Builder.CreateBitCast(Vec, IRStoreTy); } + Builder.CreateStore(Vec, Dst.getVectorAddress(), Dst.isVolatileQualified()); return; @@ -2623,14 +2650,12 @@ void CodeGenFunction::EmitStoreThroughExtVectorComponentLValue(RValue Src, // This access turns into a read/modify/write of the vector. Load the input // value now. llvm::Value *Vec = Builder.CreateLoad(DstAddr, Dst.isVolatileQualified()); + llvm::Type *VecTy = Vec->getType(); const llvm::Constant *Elts = Dst.getExtVectorElts(); - llvm::Value *SrcVal = Src.getScalarVal(); - if (const VectorType *VTy = Dst.getType()->getAs()) { unsigned NumSrcElts = VTy->getNumElements(); - unsigned NumDstElts = - cast(Vec->getType())->getNumElements(); + unsigned NumDstElts = cast(VecTy)->getNumElements(); if (NumDstElts == NumSrcElts) { // Use shuffle vector is the src and destination are the same number of // elements and restore the vector mask since it is on the side it will be @@ -2639,6 +2664,11 @@ void CodeGenFunction::EmitStoreThroughExtVectorComponentLValue(RValue Src, for (unsigned i = 0; i != NumSrcElts; ++i) Mask[getAccessedFieldNo(i, Elts)] = i; + llvm::Value *SrcVal = Src.getScalarVal(); + if (VecTy->getScalarSizeInBits() > + SrcVal->getType()->getScalarSizeInBits()) + SrcVal = Builder.CreateZExt(SrcVal, VecTy); + Vec = Builder.CreateShuffleVector(SrcVal, Mask); } else if (NumDstElts > NumSrcElts) { // Extended the source vector to the same length and then shuffle it @@ -2649,7 +2679,8 @@ void CodeGenFunction::EmitStoreThroughExtVectorComponentLValue(RValue Src, for (unsigned i = 0; i != NumSrcElts; ++i) ExtMask.push_back(i); ExtMask.resize(NumDstElts, -1); - llvm::Value *ExtSrcVal = Builder.CreateShuffleVector(SrcVal, ExtMask); + llvm::Value *ExtSrcVal = + Builder.CreateShuffleVector(Src.getScalarVal(), ExtMask); // build identity SmallVector Mask; for (unsigned i = 0; i != NumDstElts; ++i) @@ -2674,6 +2705,11 @@ void CodeGenFunction::EmitStoreThroughExtVectorComponentLValue(RValue Src, // be updating one element. unsigned InIdx = getAccessedFieldNo(0, Elts); llvm::Value *Elt = llvm::ConstantInt::get(SizeTy, InIdx); + + llvm::Value *SrcVal = Src.getScalarVal(); + if (VecTy->getScalarSizeInBits() > SrcVal->getType()->getScalarSizeInBits()) + SrcVal = Builder.CreateZExt(SrcVal, VecTy->getScalarType()); + Vec = Builder.CreateInsertElement(Vec, SrcVal, Elt); } @@ -4701,9 +4737,13 @@ EmitExtVectorElementExpr(const ExtVectorElementExpr *E) { // Store the vector to memory (because LValue wants an address). Address VecMem = CreateMemTemp(E->getBase()->getType()); + // need to zero extend an hlsl boolean vector to store it back to memory + QualType Ty = E->getBase()->getType(); + llvm::Type *LTy = convertTypeForLoadStore(Ty, Vec->getType()); + if (LTy->getScalarSizeInBits() > Vec->getType()->getScalarSizeInBits()) + Vec = Builder.CreateZExt(Vec, LTy); Builder.CreateStore(Vec, VecMem); - Base = MakeAddrLValue(VecMem, E->getBase()->getType(), - AlignmentSource::Decl); + Base = MakeAddrLValue(VecMem, Ty, AlignmentSource::Decl); } QualType type = diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index 08e42a9e1dcf3..e90881a9743bf 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -1981,7 +1981,10 @@ llvm::Constant *ConstantEmitter::emitForMemory(CodeGenModule &CGM, } // Zero-extend bool. - if (C->getType()->isIntegerTy(1) && !destType->isBitIntType()) { + // In HLSL bool vectors are stored in memory as a vector of i32 + if ((C->getType()->isIntegerTy(1) && !destType->isBitIntType()) || + (destType->isExtVectorBoolType() && + !destType->isPackedVectorBoolType(CGM.getContext()))) { llvm::Type *boolTy = CGM.getTypes().ConvertTypeForMem(destType); llvm::Constant *Res = llvm::ConstantFoldCastOperand( llvm::Instruction::ZExt, C, boolTy, CGM.getDataLayout()); diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index abe799af32c6e..99b6f563d7c82 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -913,6 +913,7 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) { if (CondConstant) incrementProfileCounter(&S); if (Executed) { + MaybeEmitDeferredVarDeclInit(S.getConditionVariable()); RunCleanupsScope ExecutedScope(*this); EmitStmt(Executed); } @@ -952,10 +953,13 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) { // there is a 'return' within the body, but this is particularly beneficial // when one if-stmt is nested within another if-stmt so that all of the MC/DC // updates are kept linear and consistent. - if (!CGM.getCodeGenOpts().MCDCCoverage) - EmitBranchOnBoolExpr(S.getCond(), ThenBlock, ElseBlock, ThenCount, LH); - else { + if (!CGM.getCodeGenOpts().MCDCCoverage) { + EmitBranchOnBoolExpr(S.getCond(), ThenBlock, ElseBlock, ThenCount, LH, + /*ConditionalOp=*/nullptr, + /*ConditionalDecl=*/S.getConditionVariable()); + } else { llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond()); + MaybeEmitDeferredVarDeclInit(S.getConditionVariable()); Builder.CreateCondBr(BoolCondVal, ThenBlock, ElseBlock); } @@ -1099,6 +1103,8 @@ void CodeGenFunction::EmitWhileStmt(const WhileStmt &S, // execution of the loop body. llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond()); + MaybeEmitDeferredVarDeclInit(S.getConditionVariable()); + // while(1) is common, avoid extra exit blocks. Be sure // to correctly handle break/continue though. llvm::ConstantInt *C = dyn_cast(BoolCondVal); @@ -1332,6 +1338,9 @@ void CodeGenFunction::EmitForStmt(const ForStmt &S, // C99 6.8.5p2/p4: The first substatement is executed if the expression // compares unequal to 0. The condition must be a scalar type. llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond()); + + MaybeEmitDeferredVarDeclInit(S.getConditionVariable()); + llvm::MDNode *Weights = createProfileWeightsForLoop(S.getCond(), getProfileCount(S.getBody())); if (!Weights && CGM.getCodeGenOpts().OptimizationLevel) @@ -1662,7 +1671,7 @@ void CodeGenFunction::EmitDeclStmt(const DeclStmt &S) { EmitStopPoint(&S); for (const auto *I : S.decls()) - EmitDecl(*I); + EmitDecl(*I, /*EvaluateConditionDecl=*/true); } void CodeGenFunction::EmitBreakStmt(const BreakStmt &S) { @@ -2227,7 +2236,7 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) { // Emit the condition variable if needed inside the entire cleanup scope // used by this special case for constant folded switches. if (S.getConditionVariable()) - EmitDecl(*S.getConditionVariable()); + EmitDecl(*S.getConditionVariable(), /*EvaluateConditionDecl=*/true); // At this point, we are no longer "within" a switch instance, so // we can temporarily enforce this to ensure that any embedded case @@ -2259,6 +2268,7 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) { if (S.getConditionVariable()) EmitDecl(*S.getConditionVariable()); llvm::Value *CondV = EmitScalarExpr(S.getCond()); + MaybeEmitDeferredVarDeclInit(S.getConditionVariable()); // Create basic block to hold stuff that comes after switch // statement. We also need to create a default block now so that diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 08165e0b28406..447192bc7f60c 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -1534,7 +1534,7 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn, // Initialize helper which will detect jumps which can cause invalid // lifetime markers. if (ShouldEmitLifetimeMarkers) - Bypasses.Init(Body); + Bypasses.Init(CGM, Body); } // Emit the standard function prologue. @@ -1846,7 +1846,8 @@ void CodeGenFunction::EmitBranchToCounterBlock( /// LHS and RHS nodes. void CodeGenFunction::EmitBranchOnBoolExpr( const Expr *Cond, llvm::BasicBlock *TrueBlock, llvm::BasicBlock *FalseBlock, - uint64_t TrueCount, Stmt::Likelihood LH, const Expr *ConditionalOp) { + uint64_t TrueCount, Stmt::Likelihood LH, const Expr *ConditionalOp, + const VarDecl *ConditionalDecl) { Cond = Cond->IgnoreParens(); if (const BinaryOperator *CondBOp = dyn_cast(Cond)) { @@ -2047,6 +2048,8 @@ void CodeGenFunction::EmitBranchOnBoolExpr( CondV = EvaluateExprAsBool(Cond); } + MaybeEmitDeferredVarDeclInit(ConditionalDecl); + // If not at the top of the logical operator nest, update MCDC temp with the // boolean result of the evaluated condition. if (!MCDCLogOpStack.empty()) { diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 018fc66b72a1e..ca00a0e8c6cf4 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3377,7 +3377,7 @@ class CodeGenFunction : public CodeGenTypeCache { /// EmitDecl - Emit a declaration. /// /// This function can be called with a null (unreachable) insert point. - void EmitDecl(const Decl &D); + void EmitDecl(const Decl &D, bool EvaluateConditionDecl = false); /// EmitVarDecl - Emit a local variable declaration. /// @@ -3474,6 +3474,8 @@ class CodeGenFunction : public CodeGenTypeCache { void emitAutoVarTypeCleanup(const AutoVarEmission &emission, QualType::DestructionKind dtorKind); + void MaybeEmitDeferredVarDeclInit(const VarDecl *var); + /// Emits the alloca and debug information for the size expressions for each /// dimension of an array. It registers the association of its (1-dimensional) /// QualTypes and size expression's debug node, so that CGDebugInfo can @@ -5180,7 +5182,8 @@ class CodeGenFunction : public CodeGenTypeCache { void EmitBranchOnBoolExpr(const Expr *Cond, llvm::BasicBlock *TrueBlock, llvm::BasicBlock *FalseBlock, uint64_t TrueCount, Stmt::Likelihood LH = Stmt::LH_None, - const Expr *ConditionalOp = nullptr); + const Expr *ConditionalOp = nullptr, + const VarDecl *ConditionalDecl = nullptr); /// Given an assignment `*LHS = RHS`, emit a test that checks if \p RHS is /// nonnull, if \p LHS is marked _Nonnull. diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index bca0a932b3495..1f6321a5908e6 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -2068,9 +2068,10 @@ StringRef CodeGenModule::getMangledName(GlobalDecl GD) { // Prior work: // https://discourse.llvm.org/t/rfc-clang-diagnostic-for-demangling-failures/82835/8 // https://github.com/llvm/llvm-project/issues/111345 - // assert((MangledName.startswith("_Z") || MangledName.startswith("?")) && - // !GD->hasAttr() && - // llvm::demangle(MangledName) != MangledName && + // assert(!((StringRef(MangledName).starts_with("_Z") || + // StringRef(MangledName).starts_with("?")) && + // !GD.getDecl()->hasAttr() && + // llvm::demangle(MangledName) == MangledName) && // "LLVM demangler must demangle clang-generated names"); auto Result = Manglings.insert(std::make_pair(MangledName, GD)); @@ -7300,6 +7301,13 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { getHLSLRuntime().addBuffer(cast(D)); break; + case Decl::OpenACCDeclare: + EmitOpenACCDeclare(cast(D)); + break; + case Decl::OpenACCRoutine: + EmitOpenACCRoutine(cast(D)); + break; + default: // Make sure we handled everything we should, every other kind is a // non-top-level decl. FIXME: Would be nice to have an isTopLevelDeclKind diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 83bb5bc54d077..e7c923834238f 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1574,9 +1574,11 @@ class CodeGenModule : public CodeGenTypeCache { CodeGenFunction *CGF = nullptr); // Emit code for the OpenACC Declare declaration. - void EmitOpenACCDeclare(const OpenACCDeclareDecl *D, CodeGenFunction *CGF); + void EmitOpenACCDeclare(const OpenACCDeclareDecl *D, + CodeGenFunction *CGF = nullptr); // Emit code for the OpenACC Routine declaration. - void EmitOpenACCRoutine(const OpenACCRoutineDecl *D, CodeGenFunction *CGF); + void EmitOpenACCRoutine(const OpenACCRoutineDecl *D, + CodeGenFunction *CGF = nullptr); /// Emit a code for requires directive. /// \param D Requires declaration diff --git a/clang/lib/CodeGen/CodeGenTypes.cpp b/clang/lib/CodeGen/CodeGenTypes.cpp index bd625052cb5ed..dfbd444a850a5 100644 --- a/clang/lib/CodeGen/CodeGenTypes.cpp +++ b/clang/lib/CodeGen/CodeGenTypes.cpp @@ -115,6 +115,12 @@ llvm::Type *CodeGenTypes::ConvertTypeForMem(QualType T) { // Check for the boolean vector case. if (T->isExtVectorBoolType()) { auto *FixedVT = cast(R); + + if (Context.getLangOpts().HLSL) { + llvm::Type *IRElemTy = ConvertTypeForMem(Context.BoolTy); + return llvm::FixedVectorType::get(IRElemTy, FixedVT->getNumElements()); + } + // Pad to at least one byte. uint64_t BytePadded = std::max(FixedVT->getNumElements(), 8); return llvm::IntegerType::get(FixedVT->getContext(), BytePadded); @@ -657,7 +663,7 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) { case Type::Vector: { const auto *VT = cast(Ty); // An ext_vector_type of Bool is really a vector of bits. - llvm::Type *IRElemTy = VT->isExtVectorBoolType() + llvm::Type *IRElemTy = VT->isPackedVectorBoolType(Context) ? llvm::Type::getInt1Ty(getLLVMContext()) : VT->getElementType()->isMFloat8Type() ? llvm::Type::getInt8Ty(getLLVMContext()) diff --git a/clang/lib/CodeGen/Targets/AMDGPU.cpp b/clang/lib/CodeGen/Targets/AMDGPU.cpp index 9d29f31c77881..7d84310ba0bf5 100644 --- a/clang/lib/CodeGen/Targets/AMDGPU.cpp +++ b/clang/lib/CodeGen/Targets/AMDGPU.cpp @@ -614,6 +614,20 @@ void AMDGPUTargetCodeGenInfo::setCUDAKernelCallingConvention( FT, FT->getExtInfo().withCallingConv(CC_OpenCLKernel)); } +/// Return IR struct type for rtinfo struct in rocm-device-libs used for device +/// enqueue. +/// +/// ptr addrspace(1) kernel_object, i32 private_segment_size, +/// i32 group_segment_size + +static llvm::StructType * +getAMDGPURuntimeHandleType(llvm::LLVMContext &C, + llvm::Type *KernelDescriptorPtrTy) { + llvm::Type *Int32 = llvm::Type::getInt32Ty(C); + return llvm::StructType::create(C, {KernelDescriptorPtrTy, Int32, Int32}, + "block.runtime.handle.t"); +} + /// Create an OpenCL kernel for an enqueued block. /// /// The type of the first argument (the block literal) is the struct type @@ -653,23 +667,29 @@ llvm::Value *AMDGPUTargetCodeGenInfo::createEnqueuedBlockKernel( ArgNames.push_back( llvm::MDString::get(C, (Twine("local_arg") + Twine(I)).str())); } - std::string Name = Invoke->getName().str() + "_kernel"; + + llvm::Module &Mod = CGF.CGM.getModule(); + const llvm::DataLayout &DL = Mod.getDataLayout(); + + llvm::Twine Name = Invoke->getName() + "_kernel"; auto *FT = llvm::FunctionType::get(llvm::Type::getVoidTy(C), ArgTys, false); + + // The kernel itself can be internal, the runtime does not directly access the + // kernel address (only the kernel descriptor). auto *F = llvm::Function::Create(FT, llvm::GlobalValue::InternalLinkage, Name, - &CGF.CGM.getModule()); + &Mod); F->setCallingConv(llvm::CallingConv::AMDGPU_KERNEL); llvm::AttrBuilder KernelAttrs(C); // FIXME: The invoke isn't applying the right attributes either // FIXME: This is missing setTargetAttributes CGF.CGM.addDefaultFunctionDefinitionAttributes(KernelAttrs); - KernelAttrs.addAttribute("enqueued-block"); F->addFnAttrs(KernelAttrs); auto IP = CGF.Builder.saveIP(); auto *BB = llvm::BasicBlock::Create(C, "entry", F); Builder.SetInsertPoint(BB); - const auto BlockAlign = CGF.CGM.getDataLayout().getPrefTypeAlign(BlockTy); + const auto BlockAlign = DL.getPrefTypeAlign(BlockTy); auto *BlockPtr = Builder.CreateAlloca(BlockTy, nullptr); BlockPtr->setAlignment(BlockAlign); Builder.CreateAlignedStore(F->arg_begin(), BlockPtr, BlockAlign); @@ -692,7 +712,39 @@ llvm::Value *AMDGPUTargetCodeGenInfo::createEnqueuedBlockKernel( if (CGF.CGM.getCodeGenOpts().EmitOpenCLArgMetadata) F->setMetadata("kernel_arg_name", llvm::MDNode::get(C, ArgNames)); - return F; + llvm::StructType *HandleTy = getAMDGPURuntimeHandleType( + C, llvm::PointerType::get(C, DL.getDefaultGlobalsAddressSpace())); + llvm::Constant *RuntimeHandleInitializer = + llvm::ConstantAggregateZero::get(HandleTy); + + llvm::Twine RuntimeHandleName = F->getName() + ".runtime.handle"; + + // The runtime needs access to the runtime handle as an external symbol. The + // runtime handle will need to be made external later, in + // AMDGPUExportOpenCLEnqueuedBlocks. The kernel itself has a hidden reference + // inside the runtime handle, and is not directly referenced. + + // TODO: We would initialize the first field by declaring F->getName() + ".kd" + // to reference the kernel descriptor. The runtime wouldn't need to bother + // setting it. We would need to have a final symbol name though. + // TODO: Can we directly use an external symbol with getGlobalIdentifier? + auto *RuntimeHandle = new llvm::GlobalVariable( + Mod, HandleTy, + /*isConstant=*/true, llvm::GlobalValue::InternalLinkage, + /*Initializer=*/RuntimeHandleInitializer, RuntimeHandleName, + /*InsertBefore=*/nullptr, llvm::GlobalValue::NotThreadLocal, + DL.getDefaultGlobalsAddressSpace(), + /*isExternallyInitialized=*/true); + + llvm::MDNode *HandleAsMD = + llvm::MDNode::get(C, llvm::ValueAsMetadata::get(RuntimeHandle)); + F->setMetadata(llvm::LLVMContext::MD_associated, HandleAsMD); + + RuntimeHandle->setSection(".amdgpu.kernel.runtime.handle"); + + CGF.CGM.addUsedGlobal(F); + CGF.CGM.addUsedGlobal(RuntimeHandle); + return RuntimeHandle; } void CodeGenModule::handleAMDGPUFlatWorkGroupSizeAttr( diff --git a/clang/lib/CodeGen/Targets/RISCV.cpp b/clang/lib/CodeGen/Targets/RISCV.cpp index 081ae8a403111..5aa10ba41f5ed 100644 --- a/clang/lib/CodeGen/Targets/RISCV.cpp +++ b/clang/lib/CodeGen/Targets/RISCV.cpp @@ -389,7 +389,7 @@ ABIArgInfo RISCVABIInfo::coerceAndExpandFPCCEligibleStruct( bool RISCVABIInfo::detectVLSCCEligibleStruct(QualType Ty, unsigned ABIVLen, llvm::Type *&VLSType) const { // No riscv_vls_cc attribute. - if (ABIVLen == 1) + if (ABIVLen == 0) return false; // Legal struct for VLS calling convention should fulfill following rules: diff --git a/clang/lib/CodeGen/VarBypassDetector.cpp b/clang/lib/CodeGen/VarBypassDetector.cpp index 6eda83dfdef2f..7b2b3542928ad 100644 --- a/clang/lib/CodeGen/VarBypassDetector.cpp +++ b/clang/lib/CodeGen/VarBypassDetector.cpp @@ -8,6 +8,7 @@ #include "VarBypassDetector.h" +#include "CodeGenModule.h" #include "clang/AST/Decl.h" #include "clang/AST/Expr.h" #include "clang/AST/Stmt.h" @@ -17,13 +18,13 @@ using namespace CodeGen; /// Clear the object and pre-process for the given statement, usually function /// body statement. -void VarBypassDetector::Init(const Stmt *Body) { +void VarBypassDetector::Init(CodeGenModule &CGM, const Stmt *Body) { FromScopes.clear(); ToScopes.clear(); Bypasses.clear(); Scopes = {{~0U, nullptr}}; unsigned ParentScope = 0; - AlwaysBypassed = !BuildScopeInformation(Body, ParentScope); + AlwaysBypassed = !BuildScopeInformation(CGM, Body, ParentScope); if (!AlwaysBypassed) Detect(); } @@ -31,7 +32,7 @@ void VarBypassDetector::Init(const Stmt *Body) { /// Build scope information for a declaration that is part of a DeclStmt. /// Returns false if we failed to build scope information and can't tell for /// which vars are being bypassed. -bool VarBypassDetector::BuildScopeInformation(const Decl *D, +bool VarBypassDetector::BuildScopeInformation(CodeGenModule &CGM, const Decl *D, unsigned &ParentScope) { const VarDecl *VD = dyn_cast(D); if (VD && VD->hasLocalStorage()) { @@ -41,7 +42,7 @@ bool VarBypassDetector::BuildScopeInformation(const Decl *D, if (const VarDecl *VD = dyn_cast(D)) if (const Expr *Init = VD->getInit()) - return BuildScopeInformation(Init, ParentScope); + return BuildScopeInformation(CGM, Init, ParentScope); return true; } @@ -50,7 +51,7 @@ bool VarBypassDetector::BuildScopeInformation(const Decl *D, /// LabelAndGotoScopes and recursively walking the AST as needed. /// Returns false if we failed to build scope information and can't tell for /// which vars are being bypassed. -bool VarBypassDetector::BuildScopeInformation(const Stmt *S, +bool VarBypassDetector::BuildScopeInformation(CodeGenModule &CGM, const Stmt *S, unsigned &origParentScope) { // If this is a statement, rather than an expression, scopes within it don't // propagate out into the enclosing scope. Otherwise we have to worry about @@ -68,12 +69,12 @@ bool VarBypassDetector::BuildScopeInformation(const Stmt *S, case Stmt::SwitchStmtClass: if (const Stmt *Init = cast(S)->getInit()) { - if (!BuildScopeInformation(Init, ParentScope)) + if (!BuildScopeInformation(CGM, Init, ParentScope)) return false; ++StmtsToSkip; } if (const VarDecl *Var = cast(S)->getConditionVariable()) { - if (!BuildScopeInformation(Var, ParentScope)) + if (!BuildScopeInformation(CGM, Var, ParentScope)) return false; ++StmtsToSkip; } @@ -86,7 +87,7 @@ bool VarBypassDetector::BuildScopeInformation(const Stmt *S, case Stmt::DeclStmtClass: { const DeclStmt *DS = cast(S); for (auto *I : DS->decls()) - if (!BuildScopeInformation(I, origParentScope)) + if (!BuildScopeInformation(CGM, I, origParentScope)) return false; return true; } @@ -126,7 +127,11 @@ bool VarBypassDetector::BuildScopeInformation(const Stmt *S, } // Recursively walk the AST. - if (!BuildScopeInformation(SubStmt, ParentScope)) + bool Result; + CGM.runWithSufficientStackSpace(S->getEndLoc(), [&] { + Result = BuildScopeInformation(CGM, SubStmt, ParentScope); + }); + if (!Result) return false; } return true; diff --git a/clang/lib/CodeGen/VarBypassDetector.h b/clang/lib/CodeGen/VarBypassDetector.h index 164e88c0b2f1b..cc4d387aeaa5b 100644 --- a/clang/lib/CodeGen/VarBypassDetector.h +++ b/clang/lib/CodeGen/VarBypassDetector.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_LIB_CODEGEN_VARBYPASSDETECTOR_H #define LLVM_CLANG_LIB_CODEGEN_VARBYPASSDETECTOR_H +#include "CodeGenModule.h" #include "clang/AST/Decl.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" @@ -50,7 +51,7 @@ class VarBypassDetector { bool AlwaysBypassed = false; public: - void Init(const Stmt *Body); + void Init(CodeGenModule &CGM, const Stmt *Body); /// Returns true if the variable declaration was by bypassed by any goto or /// switch statement. @@ -59,8 +60,10 @@ class VarBypassDetector { } private: - bool BuildScopeInformation(const Decl *D, unsigned &ParentScope); - bool BuildScopeInformation(const Stmt *S, unsigned &origParentScope); + bool BuildScopeInformation(CodeGenModule &CGM, const Decl *D, + unsigned &ParentScope); + bool BuildScopeInformation(CodeGenModule &CGM, const Stmt *S, + unsigned &origParentScope); void Detect(); void Detect(unsigned From, unsigned To); }; diff --git a/clang/lib/Driver/Action.cpp b/clang/lib/Driver/Action.cpp index 0899b8ef00152..ec09726044812 100644 --- a/clang/lib/Driver/Action.cpp +++ b/clang/lib/Driver/Action.cpp @@ -50,6 +50,8 @@ const char *Action::getClassName(ActionClass AC) { return "static-lib-linker"; case BinaryAnalyzeJobClass: return "binary-analyzer"; + case BinaryTranslatorJobClass: + return "binary-translator"; } llvm_unreachable("invalid class"); @@ -459,3 +461,9 @@ void BinaryAnalyzeJobAction::anchor() {} BinaryAnalyzeJobAction::BinaryAnalyzeJobAction(Action *Input, types::ID Type) : JobAction(BinaryAnalyzeJobClass, Input, Type) {} + +void BinaryTranslatorJobAction::anchor() {} + +BinaryTranslatorJobAction::BinaryTranslatorJobAction(Action *Input, + types::ID Type) + : JobAction(BinaryTranslatorJobClass, Input, Type) {} diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index eca96c1cce7f7..9457a19255f21 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -226,10 +226,7 @@ std::string CUIDOptions::getCUID(StringRef InputFile, else if (UseCUID == Kind::Hash) { llvm::MD5 Hasher; llvm::MD5::MD5Result Hash; - SmallString<256> RealPath; - llvm::sys::fs::real_path(InputFile, RealPath, - /*expand_tilde=*/true); - Hasher.update(RealPath); + Hasher.update(InputFile); for (auto *A : Args) { if (A->getOption().matches(options::OPT_INPUT)) continue; @@ -4672,6 +4669,16 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args, Actions.push_back(C.MakeAction( LastAction, types::TY_DX_CONTAINER)); } + if (TC.requiresBinaryTranslation(Args)) { + Action *LastAction = Actions.back(); + // Metal shader converter runs on DXIL containers, which can either be + // validated (in which case they are TY_DX_CONTAINER), or unvalidated + // (TY_OBJECT). + if (LastAction->getType() == types::TY_DX_CONTAINER || + LastAction->getType() == types::TY_Object) + Actions.push_back(C.MakeAction( + LastAction, types::TY_DX_CONTAINER)); + } } // Claim ignored clang-cl options. @@ -5212,8 +5219,14 @@ void Driver::BuildJobs(Compilation &C) const { unsigned NumOutputs = 0; unsigned NumIfsOutputs = 0; for (const Action *A : C.getActions()) { + // The actions below do not increase the number of outputs, when operating + // on DX containers. + if (A->getType() == types::TY_DX_CONTAINER && + (A->getKind() == clang::driver::Action::BinaryAnalyzeJobClass || + A->getKind() == clang::driver::Action::BinaryTranslatorJobClass)) + continue; + if (A->getType() != types::TY_Nothing && - A->getType() != types::TY_DX_CONTAINER && !(A->getKind() == Action::IfsMergeJobClass || (A->getType() == clang::driver::types::TY_IFS_CPP && A->getKind() == clang::driver::Action::CompileJobClass && @@ -6214,11 +6227,27 @@ const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA, return C.addResultFile(C.getArgs().MakeArgString(FcValue.str()), &JA); } - if (JA.getType() == types::TY_Object && - C.getArgs().hasArg(options::OPT_dxc_Fo)) { + if ((JA.getType() == types::TY_Object && + C.getArgs().hasArg(options::OPT_dxc_Fo)) || + JA.getType() == types::TY_DX_CONTAINER) { StringRef FoValue = C.getArgs().getLastArgValue(options::OPT_dxc_Fo); - // TODO: Should we use `MakeCLOutputFilename` here? If so, we can probably - // handle this as part of the SLASH_Fo handling below. + // If we are targeting DXIL and not validating or translating, we should set + // the final result file. Otherwise we should emit to a temporary. + if (C.getDefaultToolChain().getTriple().isDXIL()) { + const auto &TC = static_cast( + C.getDefaultToolChain()); + // Fo can be empty here if the validator is running for a compiler flow + // that is using Fc or just printing disassembly. + if (TC.isLastJob(C.getArgs(), JA.getKind()) && !FoValue.empty()) + return C.addResultFile(C.getArgs().MakeArgString(FoValue.str()), &JA); + StringRef Name = llvm::sys::path::filename(BaseInput); + std::pair Split = Name.split('.'); + const char *Suffix = types::getTypeTempSuffix(JA.getType(), true); + return CreateTempFile(C, Split.first, Suffix, false); + } + // We don't have SPIRV-val integrated (yet), so for now we can assume this + // is the final output. + assert(C.getDefaultToolChain().getTriple().isSPIRV()); return C.addResultFile(C.getArgs().MakeArgString(FoValue.str()), &JA); } diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp index 65acbe8a9dbea..f3bafbd01c5af 100644 --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -210,7 +210,9 @@ static void getAArch64MultilibFlags(const Driver &D, const llvm::opt::ArgList &Args, Multilib::flags_list &Result) { std::vector Features; - tools::aarch64::getAArch64TargetFeatures(D, Triple, Args, Features, false); + tools::aarch64::getAArch64TargetFeatures(D, Triple, Args, Features, + /*ForAS=*/false, + /*ForMultilib=*/true); const auto UnifiedFeatures = tools::unifyTargetFeatures(Features); llvm::DenseSet FeatureSet(UnifiedFeatures.begin(), UnifiedFeatures.end()); @@ -639,6 +641,7 @@ Tool *ToolChain::getTool(Action::ActionClass AC) const { case Action::DsymutilJobClass: case Action::VerifyDebugInfoJobClass: case Action::BinaryAnalyzeJobClass: + case Action::BinaryTranslatorJobClass: llvm_unreachable("Invalid tool kind."); case Action::CompileJobClass: diff --git a/clang/lib/Driver/ToolChains/Arch/AArch64.cpp b/clang/lib/Driver/ToolChains/Arch/AArch64.cpp index 1e2ac4e501baf..6e70effe9e325 100644 --- a/clang/lib/Driver/ToolChains/Arch/AArch64.cpp +++ b/clang/lib/Driver/ToolChains/Arch/AArch64.cpp @@ -196,7 +196,7 @@ void aarch64::getAArch64TargetFeatures(const Driver &D, const llvm::Triple &Triple, const ArgList &Args, std::vector &Features, - bool ForAS) { + bool ForAS, bool ForMultilib) { Arg *A; bool success = true; llvm::StringRef WaMArch; @@ -332,6 +332,19 @@ void aarch64::getAArch64TargetFeatures(const Driver &D, } else if (Triple.isOSOpenBSD()) Features.push_back("+strict-align"); + // Generate execute-only output (no data access to code sections). + // This only makes sense for the compiler, not for the assembler. + // It's not needed for multilib selection and may hide an unused + // argument diagnostic if the code is always run. + if (!ForAS && !ForMultilib) { + if (Arg *A = Args.getLastArg(options::OPT_mexecute_only, + options::OPT_mno_execute_only)) { + if (A->getOption().matches(options::OPT_mexecute_only)) { + Features.push_back("+execute-only"); + } + } + } + if (Args.hasArg(options::OPT_ffixed_x1)) Features.push_back("+reserve-x1"); diff --git a/clang/lib/Driver/ToolChains/Arch/AArch64.h b/clang/lib/Driver/ToolChains/Arch/AArch64.h index 6d071167bd392..2057272867a17 100644 --- a/clang/lib/Driver/ToolChains/Arch/AArch64.h +++ b/clang/lib/Driver/ToolChains/Arch/AArch64.h @@ -23,7 +23,7 @@ namespace aarch64 { void getAArch64TargetFeatures(const Driver &D, const llvm::Triple &Triple, const llvm::opt::ArgList &Args, std::vector &Features, - bool ForAS); + bool ForAS, bool ForMultilib = false); std::string getAArch64TargetCPU(const llvm::opt::ArgList &Args, const llvm::Triple &Triple, llvm::opt::Arg *&A); diff --git a/clang/lib/Driver/ToolChains/Arch/ARM.cpp b/clang/lib/Driver/ToolChains/Arch/ARM.cpp index 51454de1b9dcc..e50cb3836f2c9 100644 --- a/clang/lib/Driver/ToolChains/Arch/ARM.cpp +++ b/clang/lib/Driver/ToolChains/Arch/ARM.cpp @@ -202,13 +202,25 @@ bool arm::useAAPCSForMachO(const llvm::Triple &T) { T.getOS() == llvm::Triple::UnknownOS || isARMMProfile(T); } -// We follow GCC and support when the backend has support for the MRC/MCR +// Check whether the architecture backend has support for the MRC/MCR // instructions that are used to set the hard thread pointer ("CP15 C13 // Thread id"). +// This is not identical to ability to use the instruction, as the ARMV6K +// variants can only use it in Arm mode since they don't support Thumb2 +// encoding. bool arm::isHardTPSupported(const llvm::Triple &Triple) { int Ver = getARMSubArchVersionNumber(Triple); llvm::ARM::ArchKind AK = llvm::ARM::parseArch(Triple.getArchName()); - return Triple.isARM() || AK == llvm::ARM::ArchKind::ARMV6T2 || + return AK == llvm::ARM::ArchKind::ARMV6K || + AK == llvm::ARM::ArchKind::ARMV6KZ || + (Ver >= 7 && !isARMMProfile(Triple)); +} + +// Checks whether the architecture is capable of supporting the Thumb2 encoding +static bool supportsThumb2Encoding(const llvm::Triple &Triple) { + int Ver = arm::getARMSubArchVersionNumber(Triple); + llvm::ARM::ArchKind AK = llvm::ARM::parseArch(Triple.getArchName()); + return AK == llvm::ARM::ArchKind::ARMV6T2 || (Ver >= 7 && AK != llvm::ARM::ArchKind::ARMV8MBaseline); } @@ -240,7 +252,13 @@ arm::ReadTPMode arm::getReadTPMode(const Driver &D, const ArgList &Args, D.Diag(diag::err_drv_invalid_mtp) << A->getAsString(Args); return ReadTPMode::Invalid; } - return (isHardTPSupported(Triple) ? ReadTPMode::TPIDRURO : ReadTPMode::Soft); + // In auto mode we enable HW mode only if both the hardware supports it and + // the thumb2 encoding. For example ARMV6T2 supports thumb2, but not hardware. + // ARMV6K has HW suport, but not thumb2. Otherwise we could enable it for + // ARMV6K in thumb mode. + bool autoUseHWTPMode = + isHardTPSupported(Triple) && supportsThumb2Encoding(Triple); + return autoUseHWTPMode ? ReadTPMode::TPIDRURO : ReadTPMode::Soft; } void arm::setArchNameInTriple(const Driver &D, const ArgList &Args, diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 4ebbd241d2f0b..a6a96286e9857 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -762,8 +762,7 @@ static void addPGOAndCoverageFlags(const ToolChain &TC, Compilation &C, else { CmdArgs.push_back("-fprofile-continuous"); // Platforms that require a bias variable: - if (T.isOSBinFormatELF() || T.isOSAIX() || - T.isKnownWindowsMSVCEnvironment()) { + if (T.isOSBinFormatELF() || T.isOSAIX() || T.isOSWindows()) { CmdArgs.push_back("-mllvm"); CmdArgs.push_back("-runtime-counter-relocation"); } diff --git a/clang/lib/Driver/ToolChains/HLSL.cpp b/clang/lib/Driver/ToolChains/HLSL.cpp index ad44c2cfcd811..22498bff1f251 100644 --- a/clang/lib/Driver/ToolChains/HLSL.cpp +++ b/clang/lib/Driver/ToolChains/HLSL.cpp @@ -186,18 +186,32 @@ void tools::hlsl::Validator::ConstructJob(Compilation &C, const JobAction &JA, ArgStringList CmdArgs; assert(Inputs.size() == 1 && "Unable to handle multiple inputs."); const InputInfo &Input = Inputs[0]; - assert(Input.isFilename() && "Unexpected verify input"); - // Grabbing the output of the earlier cc1 run. CmdArgs.push_back(Input.getFilename()); - // Use the same name as output. CmdArgs.push_back("-o"); - CmdArgs.push_back(Input.getFilename()); + CmdArgs.push_back(Output.getFilename()); const char *Exec = Args.MakeArgString(DxvPath); C.addCommand(std::make_unique(JA, *this, ResponseFileSupport::None(), Exec, CmdArgs, Inputs, Input)); } +void tools::hlsl::MetalConverter::ConstructJob( + Compilation &C, const JobAction &JA, const InputInfo &Output, + const InputInfoList &Inputs, const ArgList &Args, + const char *LinkingOutput) const { + std::string MSCPath = getToolChain().GetProgramPath("metal-shaderconverter"); + ArgStringList CmdArgs; + assert(Inputs.size() == 1 && "Unable to handle multiple inputs."); + const InputInfo &Input = Inputs[0]; + CmdArgs.push_back(Input.getFilename()); + CmdArgs.push_back("-o"); + CmdArgs.push_back(Output.getFilename()); + + const char *Exec = Args.MakeArgString(MSCPath); + C.addCommand(std::make_unique(JA, *this, ResponseFileSupport::None(), + Exec, CmdArgs, Inputs, Input)); +} + /// DirectX Toolchain HLSLToolChain::HLSLToolChain(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) @@ -214,6 +228,10 @@ Tool *clang::driver::toolchains::HLSLToolChain::getTool( if (!Validator) Validator.reset(new tools::hlsl::Validator(*this)); return Validator.get(); + case Action::BinaryTranslatorJobClass: + if (!MetalConverter) + MetalConverter.reset(new tools::hlsl::MetalConverter(*this)); + return MetalConverter.get(); default: return ToolChain::getTool(AC); } @@ -301,3 +319,21 @@ bool HLSLToolChain::requiresValidation(DerivedArgList &Args) const { getDriver().Diag(diag::warn_drv_dxc_missing_dxv); return false; } + +bool HLSLToolChain::requiresBinaryTranslation(DerivedArgList &Args) const { + return Args.hasArg(options::OPT_metal) && Args.hasArg(options::OPT_dxc_Fo); +} + +bool HLSLToolChain::isLastJob(DerivedArgList &Args, + Action::ActionClass AC) const { + bool HasTranslation = requiresBinaryTranslation(Args); + bool HasValidation = requiresValidation(Args); + // If translation and validation are not required, we should only have one + // action. + if (!HasTranslation && !HasValidation) + return true; + if ((HasTranslation && AC == Action::BinaryTranslatorJobClass) || + (!HasTranslation && HasValidation && AC == Action::BinaryAnalyzeJobClass)) + return true; + return false; +} diff --git a/clang/lib/Driver/ToolChains/HLSL.h b/clang/lib/Driver/ToolChains/HLSL.h index b2a31aabab7dc..3824b4252324b 100644 --- a/clang/lib/Driver/ToolChains/HLSL.h +++ b/clang/lib/Driver/ToolChains/HLSL.h @@ -29,6 +29,19 @@ class LLVM_LIBRARY_VISIBILITY Validator : public Tool { const llvm::opt::ArgList &TCArgs, const char *LinkingOutput) const override; }; + +class LLVM_LIBRARY_VISIBILITY MetalConverter : public Tool { +public: + MetalConverter(const ToolChain &TC) + : Tool("hlsl::MetalConverter", "metal-shaderconverter", TC) {} + + bool hasIntegratedCPP() const override { return false; } + + void ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, const InputInfoList &Inputs, + const llvm::opt::ArgList &TCArgs, + const char *LinkingOutput) const override; +}; } // namespace hlsl } // namespace tools @@ -51,12 +64,15 @@ class LLVM_LIBRARY_VISIBILITY HLSLToolChain : public ToolChain { Action::OffloadKind DeviceOffloadKind) const override; static std::optional parseTargetProfile(StringRef TargetProfile); bool requiresValidation(llvm::opt::DerivedArgList &Args) const; + bool requiresBinaryTranslation(llvm::opt::DerivedArgList &Args) const; + bool isLastJob(llvm::opt::DerivedArgList &Args, Action::ActionClass AC) const; // Set default DWARF version to 4 for DXIL uses version 4. unsigned GetDefaultDwarfVersion() const override { return 4; } private: mutable std::unique_ptr Validator; + mutable std::unique_ptr MetalConverter; }; } // end namespace toolchains diff --git a/clang/lib/Driver/ToolChains/Hexagon.cpp b/clang/lib/Driver/ToolChains/Hexagon.cpp index 7ca5ab9af8810..6ea701a7882d1 100644 --- a/clang/lib/Driver/ToolChains/Hexagon.cpp +++ b/clang/lib/Driver/ToolChains/Hexagon.cpp @@ -313,6 +313,7 @@ constructHexagonLinkArgs(Compilation &C, const JobAction &JA, // handled somewhere else. Args.ClaimAllArgs(options::OPT_static_libgcc); + CmdArgs.push_back("--eh-frame-hdr"); //---------------------------------------------------------------------------- // //---------------------------------------------------------------------------- diff --git a/clang/lib/Driver/ToolChains/SPIRV.cpp b/clang/lib/Driver/ToolChains/SPIRV.cpp index 5a7894f5435fc..8ff39fe42f3aa 100644 --- a/clang/lib/Driver/ToolChains/SPIRV.cpp +++ b/clang/lib/Driver/ToolChains/SPIRV.cpp @@ -93,12 +93,6 @@ void SPIRV::Assembler::ConstructJob(Compilation &C, const JobAction &JA, constructAssembleCommand(C, *this, JA, Output, Inputs[0], {}); } -clang::driver::Tool *SPIRVToolChain::getTranslator() const { - if (!Translator) - Translator = std::make_unique(*this); - return Translator.get(); -} - clang::driver::Tool *SPIRVToolChain::getAssembler() const { if (!Assembler) Assembler = std::make_unique(*this); @@ -114,8 +108,6 @@ clang::driver::Tool *SPIRVToolChain::getTool(Action::ActionClass AC) const { switch (AC) { default: break; - case Action::BackendJobClass: - return SPIRVToolChain::getTranslator(); case Action::AssembleJobClass: return SPIRVToolChain::getAssembler(); } diff --git a/clang/lib/Driver/ToolChains/SPIRV.h b/clang/lib/Driver/ToolChains/SPIRV.h index 6223d55eca643..924eb01adcbbf 100644 --- a/clang/lib/Driver/ToolChains/SPIRV.h +++ b/clang/lib/Driver/ToolChains/SPIRV.h @@ -69,7 +69,6 @@ class LLVM_LIBRARY_VISIBILITY Assembler final : public Tool { namespace toolchains { class LLVM_LIBRARY_VISIBILITY SPIRVToolChain : public ToolChain { - mutable std::unique_ptr Translator; mutable std::unique_ptr Assembler; public: @@ -78,7 +77,7 @@ class LLVM_LIBRARY_VISIBILITY SPIRVToolChain : public ToolChain { bool useIntegratedAs() const override { return true; } - bool IsIntegratedBackendDefault() const override { return false; } + bool IsIntegratedBackendDefault() const override { return true; } bool IsNonIntegratedBackendSupported() const override { return true; } bool IsMathErrnoDefault() const override { return false; } bool isCrossCompiling() const override { return true; } @@ -97,7 +96,6 @@ class LLVM_LIBRARY_VISIBILITY SPIRVToolChain : public ToolChain { Tool *buildLinker() const override; private: - clang::driver::Tool *getTranslator() const; clang::driver::Tool *getAssembler() const; bool NativeLLVMSupport; diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index 14e984529d640..000a5105ca407 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -116,36 +116,18 @@ class LevelIndentTracker { Style.isCSharp()) { return 0; } - - auto IsAccessModifier = [&](const FormatToken &RootToken) { - if (Line.Type == LT_AccessModifier || RootToken.isObjCAccessSpecifier()) - return true; - - const auto *Next = RootToken.Next; - - // Handle Qt signals. - if (RootToken.isOneOf(Keywords.kw_signals, Keywords.kw_qsignals) && - Next && Next->is(tok::colon)) { - return true; - } - - if (Next && Next->isOneOf(Keywords.kw_slots, Keywords.kw_qslots) && - Next->Next && Next->Next->is(tok::colon)) { - return true; - } - - // Handle malformed access specifier e.g. 'private' without trailing ':'. - return !Next && RootToken.isAccessSpecifier(false); - }; - - if (IsAccessModifier(*Line.First)) { + const auto &RootToken = *Line.First; + if (Line.Type == LT_AccessModifier || + RootToken.isAccessSpecifier(/*ColonRequired=*/false) || + RootToken.isObjCAccessSpecifier() || + (RootToken.isOneOf(Keywords.kw_signals, Keywords.kw_qsignals) && + RootToken.Next && RootToken.Next->is(tok::colon))) { // The AccessModifierOffset may be overridden by IndentAccessModifiers, // in which case we take a negative value of the IndentWidth to simulate // the upper indent level. return Style.IndentAccessModifiers ? -Style.IndentWidth : Style.AccessModifierOffset; } - return 0; } diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index efb22bcdbe53f..a6e0596add5d2 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -2562,12 +2562,12 @@ bool UnwrappedLineParser::parseBracedList(bool IsAngleBracket, bool IsEnum) { /// Returns whether there is a `=` token between the parentheses. bool UnwrappedLineParser::parseParens(TokenType AmpAmpTokenType) { assert(FormatTok->is(tok::l_paren) && "'(' expected."); - auto *LeftParen = FormatTok; + auto *LParen = FormatTok; bool SeenComma = false; bool SeenEqual = false; bool MightBeFoldExpr = false; - const bool MightBeStmtExpr = Tokens->peekNextToken()->is(tok::l_brace); nextToken(); + const bool MightBeStmtExpr = FormatTok->is(tok::l_brace); do { switch (FormatTok->Tok.getKind()) { case tok::l_paren: @@ -2577,44 +2577,60 @@ bool UnwrappedLineParser::parseParens(TokenType AmpAmpTokenType) { parseChildBlock(); break; case tok::r_paren: { - auto *Prev = LeftParen->Previous; - if (!MightBeStmtExpr && !MightBeFoldExpr && !Line->InMacroBody && - Style.RemoveParentheses > FormatStyle::RPS_Leave) { - const auto *Next = Tokens->peekNextToken(); - const bool DoubleParens = - Prev && Prev->is(tok::l_paren) && Next && Next->is(tok::r_paren); - const bool CommaSeparated = - !DoubleParens && Prev && Prev->isOneOf(tok::l_paren, tok::comma) && - Next && Next->isOneOf(tok::comma, tok::r_paren); - const auto *PrevPrev = Prev ? Prev->getPreviousNonComment() : nullptr; - const bool Excluded = - PrevPrev && - (PrevPrev->isOneOf(tok::kw___attribute, tok::kw_decltype) || - SeenComma || - (SeenEqual && - (PrevPrev->isOneOf(tok::kw_if, tok::kw_while) || - PrevPrev->endsSequence(tok::kw_constexpr, tok::kw_if)))); - const bool ReturnParens = - Style.RemoveParentheses == FormatStyle::RPS_ReturnStatement && - ((NestedLambdas.empty() && !IsDecltypeAutoFunction) || - (!NestedLambdas.empty() && !NestedLambdas.back())) && - Prev && Prev->isOneOf(tok::kw_return, tok::kw_co_return) && Next && - Next->is(tok::semi); - if ((DoubleParens && !Excluded) || (CommaSeparated && !SeenComma) || - ReturnParens) { - LeftParen->Optional = true; - FormatTok->Optional = true; - } - } + auto *Prev = LParen->Previous; + auto *RParen = FormatTok; + nextToken(); if (Prev) { + auto OptionalParens = [&] { + if (MightBeStmtExpr || MightBeFoldExpr || Line->InMacroBody || + SeenComma || Style.RemoveParentheses == FormatStyle::RPS_Leave) { + return false; + } + const bool DoubleParens = + Prev->is(tok::l_paren) && FormatTok->is(tok::r_paren); + if (DoubleParens) { + const auto *PrevPrev = Prev->getPreviousNonComment(); + const bool Excluded = + PrevPrev && + (PrevPrev->isOneOf(tok::kw___attribute, tok::kw_decltype) || + (SeenEqual && + (PrevPrev->isOneOf(tok::kw_if, tok::kw_while) || + PrevPrev->endsSequence(tok::kw_constexpr, tok::kw_if)))); + if (!Excluded) + return true; + } else { + const bool CommaSeparated = + Prev->isOneOf(tok::l_paren, tok::comma) && + FormatTok->isOneOf(tok::comma, tok::r_paren); + if (CommaSeparated && + // LParen is not preceded by ellipsis, comma. + !Prev->endsSequence(tok::comma, tok::ellipsis) && + // RParen is not followed by comma, ellipsis. + !(FormatTok->is(tok::comma) && + Tokens->peekNextToken()->is(tok::ellipsis))) { + return true; + } + const bool ReturnParens = + Style.RemoveParentheses == FormatStyle::RPS_ReturnStatement && + ((NestedLambdas.empty() && !IsDecltypeAutoFunction) || + (!NestedLambdas.empty() && !NestedLambdas.back())) && + Prev->isOneOf(tok::kw_return, tok::kw_co_return) && + FormatTok->is(tok::semi); + if (ReturnParens) + return true; + } + return false; + }; if (Prev->is(TT_TypenameMacro)) { - LeftParen->setFinalizedType(TT_TypeDeclarationParen); - FormatTok->setFinalizedType(TT_TypeDeclarationParen); - } else if (Prev->is(tok::greater) && FormatTok->Previous == LeftParen) { + LParen->setFinalizedType(TT_TypeDeclarationParen); + RParen->setFinalizedType(TT_TypeDeclarationParen); + } else if (Prev->is(tok::greater) && RParen->Previous == LParen) { Prev->setFinalizedType(TT_TemplateCloser); + } else if (OptionalParens()) { + LParen->Optional = true; + RParen->Optional = true; } } - nextToken(); return SeenEqual; } case tok::r_brace: @@ -3386,75 +3402,15 @@ void UnwrappedLineParser::parseSwitch(bool IsExpr) { NestedTooDeep.pop_back(); } -// Operators that can follow a C variable. -static bool isCOperatorFollowingVar(tok::TokenKind Kind) { - switch (Kind) { - case tok::ampamp: - case tok::ampequal: - case tok::arrow: - case tok::caret: - case tok::caretequal: - case tok::comma: - case tok::ellipsis: - case tok::equal: - case tok::equalequal: - case tok::exclaim: - case tok::exclaimequal: - case tok::greater: - case tok::greaterequal: - case tok::greatergreater: - case tok::greatergreaterequal: - case tok::l_paren: - case tok::l_square: - case tok::less: - case tok::lessequal: - case tok::lessless: - case tok::lesslessequal: - case tok::minus: - case tok::minusequal: - case tok::minusminus: - case tok::percent: - case tok::percentequal: - case tok::period: - case tok::pipe: - case tok::pipeequal: - case tok::pipepipe: - case tok::plus: - case tok::plusequal: - case tok::plusplus: - case tok::question: - case tok::r_brace: - case tok::r_paren: - case tok::r_square: - case tok::semi: - case tok::slash: - case tok::slashequal: - case tok::star: - case tok::starequal: - return true; - default: - return false; - } -} - void UnwrappedLineParser::parseAccessSpecifier() { - FormatToken *AccessSpecifierCandidate = FormatTok; nextToken(); // Understand Qt's slots. if (FormatTok->isOneOf(Keywords.kw_slots, Keywords.kw_qslots)) nextToken(); // Otherwise, we don't know what it is, and we'd better keep the next token. - if (FormatTok->is(tok::colon)) { + if (FormatTok->is(tok::colon)) nextToken(); - addUnwrappedLine(); - } else if (FormatTok->isNot(tok::coloncolon) && - !isCOperatorFollowingVar(FormatTok->Tok.getKind())) { - // Not a variable name nor namespace name. - addUnwrappedLine(); - } else if (AccessSpecifierCandidate) { - // Consider the access specifier to be a C identifier. - AccessSpecifierCandidate->Tok.setKind(tok::identifier); - } + addUnwrappedLine(); } /// \brief Parses a requires, decides if it is a clause or an expression. diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index c11c857ea0606..e9e96683ca51f 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -1406,7 +1406,7 @@ static bool readASTAfterCompileModule(CompilerInstance &ImportingInstance, SourceLocation ImportLoc, SourceLocation ModuleNameLoc, Module *Module, StringRef ModuleFileName, - bool *OutOfDate) { + bool *OutOfDate, bool *Missing) { DiagnosticsEngine &Diags = ImportingInstance.getDiagnostics(); unsigned ModuleLoadCapabilities = ASTReader::ARR_Missing; @@ -1427,6 +1427,12 @@ static bool readASTAfterCompileModule(CompilerInstance &ImportingInstance, return false; } + // The caller wants to handle missing module files. + if (Missing && ReadResult == ASTReader::Missing) { + *Missing = true; + return false; + } + // The ASTReader didn't diagnose the error, so conservatively report it. if (ReadResult == ASTReader::Missing || !Diags.hasErrorOccurred()) Diags.Report(ModuleNameLoc, diag::err_module_not_built) @@ -1452,7 +1458,7 @@ static bool compileModuleAndReadASTImpl(CompilerInstance &ImportingInstance, return readASTAfterCompileModule(ImportingInstance, ImportLoc, ModuleNameLoc, Module, ModuleFileName, - /*OutOfDate=*/nullptr); + /*OutOfDate=*/nullptr, /*Missing=*/nullptr); } /// Compile a module in a separate compiler instance and read the AST, @@ -1477,29 +1483,28 @@ static bool compileModuleAndReadASTBehindLock( llvm::sys::fs::create_directories(Dir); while (true) { - llvm::LockFileManager Locked(ModuleFileName); - switch (Locked) { - case llvm::LockFileManager::LFS_Error: + llvm::LockFileManager Lock(ModuleFileName); + bool Owned; + if (llvm::Error Err = Lock.tryLock().moveInto(Owned)) { // ModuleCache takes care of correctness and locks are only necessary for // performance. Fallback to building the module in case of any lock // related errors. Diags.Report(ModuleNameLoc, diag::remark_module_lock_failure) - << Module->Name << Locked.getErrorMessage(); + << Module->Name << toString(std::move(Err)); // Clear out any potential leftover. - Locked.unsafeRemoveLockFile(); - [[fallthrough]]; - case llvm::LockFileManager::LFS_Owned: + Lock.unsafeRemoveLockFile(); + return compileModuleAndReadASTImpl(ImportingInstance, ImportLoc, + ModuleNameLoc, Module, ModuleFileName); + } + if (Owned) { // We're responsible for building the module ourselves. return compileModuleAndReadASTImpl(ImportingInstance, ImportLoc, ModuleNameLoc, Module, ModuleFileName); - - case llvm::LockFileManager::LFS_Shared: - break; // The interesting case. } // Someone else is responsible for building the module. Wait for them to // finish. - switch (Locked.waitForUnlock()) { + switch (Lock.waitForUnlock()) { case llvm::LockFileManager::Res_Success: break; // The interesting case. case llvm::LockFileManager::Res_OwnerDied: @@ -1511,21 +1516,23 @@ static bool compileModuleAndReadASTBehindLock( Diags.Report(ModuleNameLoc, diag::remark_module_lock_timeout) << Module->Name; // Clear the lock file so that future invocations can make progress. - Locked.unsafeRemoveLockFile(); + Lock.unsafeRemoveLockFile(); continue; } // Read the module that was just written by someone else. bool OutOfDate = false; + bool Missing = false; if (readASTAfterCompileModule(ImportingInstance, ImportLoc, ModuleNameLoc, - Module, ModuleFileName, &OutOfDate)) + Module, ModuleFileName, &OutOfDate, &Missing)) return true; - if (!OutOfDate) + if (!OutOfDate && !Missing) return false; - // The module may be out of date in the presence of file system races, - // or if one of its imports depends on header search paths that are not - // consistent with this ImportingInstance. Try again... + // The module may be missing or out of date in the presence of file system + // races. It may also be out of date if one of its imports depends on header + // search paths that are not consistent with this ImportingInstance. + // Try again... } } diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp index 60e103e643e27..1ea4a2e9e88cf 100644 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -403,8 +403,7 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback { } private: - static std::optional - toString(CodeSynthesisContext::SynthesisKind Kind) { + static std::string toString(CodeSynthesisContext::SynthesisKind Kind) { switch (Kind) { case CodeSynthesisContext::TemplateInstantiation: return "TemplateInstantiation"; @@ -462,10 +461,8 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback { return "TypeAliasTemplateInstantiation"; case CodeSynthesisContext::PartialOrderingTTP: return "PartialOrderingTTP"; - case CodeSynthesisContext::CheckTemplateParameter: - return std::nullopt; } - return std::nullopt; + return ""; } template @@ -473,14 +470,12 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback { const CodeSynthesisContext &Inst) { std::string YAML; { - std::optional Entry = - getTemplightEntry(TheSema, Inst); - if (!Entry) - return; llvm::raw_string_ostream OS(YAML); llvm::yaml::Output YO(OS); + TemplightEntry Entry = + getTemplightEntry(TheSema, Inst); llvm::yaml::EmptyContext Context; - llvm::yaml::yamlize(YO, *Entry, true, Context); + llvm::yaml::yamlize(YO, Entry, true, Context); } Out << "---" << YAML << "\n"; } @@ -560,13 +555,10 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback { } template - static std::optional - getTemplightEntry(const Sema &TheSema, const CodeSynthesisContext &Inst) { + static TemplightEntry getTemplightEntry(const Sema &TheSema, + const CodeSynthesisContext &Inst) { TemplightEntry Entry; - std::optional Kind = toString(Inst.Kind); - if (!Kind) - return std::nullopt; - Entry.Kind = *Kind; + Entry.Kind = toString(Inst.Kind); Entry.Event = BeginInstantiation ? "Begin" : "End"; llvm::raw_string_ostream OS(Entry.Name); printEntryName(TheSema, Inst.Entity, OS); diff --git a/clang/lib/Headers/CMakeLists.txt b/clang/lib/Headers/CMakeLists.txt index e5bf8f35f7d52..d26de236998ca 100644 --- a/clang/lib/Headers/CMakeLists.txt +++ b/clang/lib/Headers/CMakeLists.txt @@ -87,6 +87,7 @@ set(hlsl_h set(hlsl_subdir_files hlsl/hlsl_basic_types.h hlsl/hlsl_alias_intrinsics.h + hlsl/hlsl_intrinsic_helpers.h hlsl/hlsl_intrinsics.h hlsl/hlsl_detail.h ) diff --git a/clang/lib/Headers/hlsl.h b/clang/lib/Headers/hlsl.h index 6edfd949f2b97..4be68eb84a34a 100644 --- a/clang/lib/Headers/hlsl.h +++ b/clang/lib/Headers/hlsl.h @@ -16,7 +16,12 @@ #pragma clang diagnostic ignored "-Whlsl-dxc-compatability" #endif +// Basic types, type traits and type-independent templates. #include "hlsl/hlsl_basic_types.h" +#include "hlsl/hlsl_detail.h" + +// HLSL standard library function declarations/definitions. +#include "hlsl/hlsl_alias_intrinsics.h" #include "hlsl/hlsl_intrinsics.h" #if defined(__clang__) diff --git a/clang/lib/Headers/hlsl/hlsl_alias_intrinsics.h b/clang/lib/Headers/hlsl/hlsl_alias_intrinsics.h index 7573f6e024167..89dfeb475488e 100644 --- a/clang/lib/Headers/hlsl/hlsl_alias_intrinsics.h +++ b/clang/lib/Headers/hlsl/hlsl_alias_intrinsics.h @@ -2123,6 +2123,41 @@ template _HLSL_BUILTIN_ALIAS(__builtin_hlsl_select) vector select(vector, vector, vector); +/// \fn vector select(vector Conds, T TrueVal, +/// vector FalseVals) +/// \brief ternary operator for vectors. All vectors must be the same size. +/// \param Conds The Condition input values. +/// \param TrueVal The scalar value to splat from when conditions are true. +/// \param FalseVals The vector values are chosen from when conditions are +/// false. + +template +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_select) +vector select(vector, T, vector); + +/// \fn vector select(vector Conds, vector TrueVals, +/// T FalseVal) +/// \brief ternary operator for vectors. All vectors must be the same size. +/// \param Conds The Condition input values. +/// \param TrueVals The vector values are chosen from when conditions are true. +/// \param FalseVal The scalar value to splat from when conditions are false. + +template +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_select) +vector select(vector, vector, T); + +/// \fn vector select(vector Conds, vector TrueVals, +/// T FalseVal) +/// \brief ternary operator for vectors. All vectors must be the same size. +/// \param Conds The Condition input values. +/// \param TrueVal The scalar value to splat from when conditions are true. +/// \param FalseVal The scalar value to splat from when conditions are false. + +template +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_select) +__detail::enable_if_t<__detail::is_arithmetic::Value, vector> select( + vector, T, T); + //===----------------------------------------------------------------------===// // sin builtins //===----------------------------------------------------------------------===// diff --git a/clang/lib/Headers/hlsl/hlsl_detail.h b/clang/lib/Headers/hlsl/hlsl_detail.h index 39254a3cc3a0a..80c4900121dfb 100644 --- a/clang/lib/Headers/hlsl/hlsl_detail.h +++ b/clang/lib/Headers/hlsl/hlsl_detail.h @@ -1,4 +1,4 @@ -//===----- detail.h - HLSL definitions for intrinsics ----------===// +//===----- hlsl_detail.h - HLSL definitions for intrinsics ----------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,8 +9,6 @@ #ifndef _HLSL_HLSL_DETAILS_H_ #define _HLSL_HLSL_DETAILS_H_ -#include "hlsl_alias_intrinsics.h" - namespace hlsl { namespace __detail { @@ -43,59 +41,13 @@ constexpr enable_if_t bit_cast(T F) { return __builtin_bit_cast(U, F); } -constexpr vector d3d_color_to_ubyte4_impl(vector V) { - // Use the same scaling factor used by FXC, and DXC for DXIL - // (i.e., 255.001953) - // https://github.com/microsoft/DirectXShaderCompiler/blob/070d0d5a2beacef9eeb51037a9b04665716fd6f3/lib/HLSL/HLOperationLower.cpp#L666C1-L697C2 - // The DXC implementation refers to a comment on the following stackoverflow - // discussion to justify the scaling factor: "Built-in rounding, necessary - // because of truncation. 0.001953 * 256 = 0.5" - // https://stackoverflow.com/questions/52103720/why-does-d3dcolortoubyte4-multiplies-components-by-255-001953f - return V.zyxw * 255.001953f; -} - -template -constexpr enable_if_t::value || is_same::value, T> -length_impl(T X) { - return abs(X); -} - -template -constexpr enable_if_t::value || is_same::value, T> -length_vec_impl(vector X) { -#if (__has_builtin(__builtin_spirv_length)) - return __builtin_spirv_length(X); -#else - return sqrt(dot(X, X)); -#endif -} - -template -constexpr enable_if_t::value || is_same::value, T> -distance_impl(T X, T Y) { - return length_impl(X - Y); -} +template struct is_arithmetic { + static const bool Value = __is_arithmetic(T); +}; template -constexpr enable_if_t::value || is_same::value, T> -distance_vec_impl(vector X, vector Y) { - return length_vec_impl(X - Y); -} - -template -constexpr enable_if_t::value || is_same::value, T> -reflect_impl(T I, T N) { - return I - 2 * N * I * N; -} - -template -constexpr vector reflect_vec_impl(vector I, vector N) { -#if (__has_builtin(__builtin_spirv_reflect)) - return __builtin_spirv_reflect(I, N); -#else - return I - 2 * N * dot(I, N); -#endif -} +using HLSL_FIXED_VECTOR = + vector<__detail::enable_if_t<(N > 1 && N <= 4), T>, N>; } // namespace __detail } // namespace hlsl diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h b/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h new file mode 100644 index 0000000000000..87b52792447f6 --- /dev/null +++ b/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h @@ -0,0 +1,63 @@ +//===----- hlsl_intrinsic_helpers.h - HLSL helpers intrinsics -------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _HLSL_HLSL_INTRINSIC_HELPERS_H_ +#define _HLSL_HLSL_INTRINSIC_HELPERS_H_ + +namespace hlsl { +namespace __detail { + +constexpr vector d3d_color_to_ubyte4_impl(vector V) { + // Use the same scaling factor used by FXC, and DXC for DXIL + // (i.e., 255.001953) + // https://github.com/microsoft/DirectXShaderCompiler/blob/070d0d5a2beacef9eeb51037a9b04665716fd6f3/lib/HLSL/HLOperationLower.cpp#L666C1-L697C2 + // The DXC implementation refers to a comment on the following stackoverflow + // discussion to justify the scaling factor: "Built-in rounding, necessary + // because of truncation. 0.001953 * 256 = 0.5" + // https://stackoverflow.com/questions/52103720/why-does-d3dcolortoubyte4-multiplies-components-by-255-001953f + return V.zyxw * 255.001953f; +} + +template constexpr T length_impl(T X) { return abs(X); } + +template +constexpr enable_if_t::value || is_same::value, T> +length_vec_impl(vector X) { +#if (__has_builtin(__builtin_spirv_length)) + return __builtin_spirv_length(X); +#else + return sqrt(dot(X, X)); +#endif +} + +template constexpr T distance_impl(T X, T Y) { + return length_impl(X - Y); +} + +template +constexpr enable_if_t::value || is_same::value, T> +distance_vec_impl(vector X, vector Y) { + return length_vec_impl(X - Y); +} + +template constexpr T reflect_impl(T I, T N) { + return I - 2 * N * I * N; +} + +template +constexpr vector reflect_vec_impl(vector I, vector N) { +#if (__has_builtin(__builtin_spirv_reflect)) + return __builtin_spirv_reflect(I, N); +#else + return I - 2 * N * dot(I, N); +#endif +} +} // namespace __detail +} // namespace hlsl + +#endif // _HLSL_HLSL_INTRINSIC_HELPERS_H_ diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h b/clang/lib/Headers/hlsl/hlsl_intrinsics.h index fe9441080433d..47a7066b2b3e1 100644 --- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h +++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h @@ -9,7 +9,7 @@ #ifndef _HLSL_HLSL_INTRINSICS_H_ #define _HLSL_HLSL_INTRINSICS_H_ -#include "hlsl_detail.h" +#include "hlsl/hlsl_intrinsic_helpers.h" namespace hlsl { @@ -89,23 +89,31 @@ void asuint(double4, out uint4, out uint4); /// \param X The X input value. /// \param Y The Y input value. +template _HLSL_16BIT_AVAILABILITY(shadermodel, 6.2) -const inline half distance(half X, half Y) { +const inline __detail::enable_if_t<__detail::is_arithmetic::Value && + __detail::is_same::value, + T> distance(T X, T Y) { return __detail::distance_impl(X, Y); } -const inline float distance(float X, float Y) { +template +const inline __detail::enable_if_t< + __detail::is_arithmetic::Value && __detail::is_same::value, T> +distance(T X, T Y) { return __detail::distance_impl(X, Y); } template _HLSL_16BIT_AVAILABILITY(shadermodel, 6.2) -const inline half distance(vector X, vector Y) { +const inline half distance(__detail::HLSL_FIXED_VECTOR X, + __detail::HLSL_FIXED_VECTOR Y) { return __detail::distance_vec_impl(X, Y); } template -const inline float distance(vector X, vector Y) { +const inline float distance(__detail::HLSL_FIXED_VECTOR X, + __detail::HLSL_FIXED_VECTOR Y) { return __detail::distance_vec_impl(X, Y); } @@ -119,17 +127,29 @@ const inline float distance(vector X, vector Y) { /// /// Length is based on the following formula: sqrt(x[0]^2 + x[1]^2 + ...). +template _HLSL_16BIT_AVAILABILITY(shadermodel, 6.2) -const inline half length(half X) { return __detail::length_impl(X); } -const inline float length(float X) { return __detail::length_impl(X); } +const inline __detail::enable_if_t<__detail::is_arithmetic::Value && + __detail::is_same::value, + T> length(T X) { + return __detail::length_impl(X); +} + +template +const inline __detail::enable_if_t< + __detail::is_arithmetic::Value && __detail::is_same::value, T> +length(T X) { + return __detail::length_impl(X); +} template _HLSL_16BIT_AVAILABILITY(shadermodel, 6.2) -const inline half length(vector X) { +const inline half length(__detail::HLSL_FIXED_VECTOR X) { return __detail::length_vec_impl(X); } -template const inline float length(vector X) { +template +const inline float length(__detail::HLSL_FIXED_VECTOR X) { return __detail::length_vec_impl(X); } @@ -173,23 +193,33 @@ constexpr vector D3DCOLORtoUBYTE4(vector V) { /// /// Result type and the type of all operands must be the same type. +template _HLSL_16BIT_AVAILABILITY(shadermodel, 6.2) -const inline half reflect(half I, half N) { +const inline __detail::enable_if_t<__detail::is_arithmetic::Value && + __detail::is_same::value, + T> reflect(T I, T N) { return __detail::reflect_impl(I, N); } -const inline float reflect(float I, float N) { +template +const inline __detail::enable_if_t< + __detail::is_arithmetic::Value && __detail::is_same::value, T> +reflect(T I, T N) { return __detail::reflect_impl(I, N); } template _HLSL_16BIT_AVAILABILITY(shadermodel, 6.2) -const inline vector reflect(vector I, vector N) { +const inline __detail::HLSL_FIXED_VECTOR reflect( + __detail::HLSL_FIXED_VECTOR I, + __detail::HLSL_FIXED_VECTOR N) { return __detail::reflect_vec_impl(I, N); } template -const inline vector reflect(vector I, vector N) { +const inline __detail::HLSL_FIXED_VECTOR +reflect(__detail::HLSL_FIXED_VECTOR I, + __detail::HLSL_FIXED_VECTOR N) { return __detail::reflect_vec_impl(I, N); } } // namespace hlsl diff --git a/clang/lib/Headers/nvptxintrin.h b/clang/lib/Headers/nvptxintrin.h index 7af22baccb511..73eb0af8b5926 100644 --- a/clang/lib/Headers/nvptxintrin.h +++ b/clang/lib/Headers/nvptxintrin.h @@ -180,8 +180,9 @@ __gpu_shuffle_idx_u64(uint64_t __lane_mask, uint32_t __idx, uint64_t __x, _DEFAULT_FN_ATTRS static __inline__ uint64_t __gpu_match_any_u32(uint64_t __lane_mask, uint32_t __x) { // Newer targets can use the dedicated CUDA support. - if (__CUDA_ARCH__ >= 700 || __nvvm_reflect("__CUDA_ARCH") >= 700) - return __nvvm_match_any_sync_i32(__lane_mask, __x); +#if __CUDA_ARCH__ >= 700 + return __nvvm_match_any_sync_i32(__lane_mask, __x); +#endif uint32_t __match_mask = 0; bool __done = 0; @@ -201,8 +202,9 @@ __gpu_match_any_u32(uint64_t __lane_mask, uint32_t __x) { _DEFAULT_FN_ATTRS static __inline__ uint64_t __gpu_match_any_u64(uint64_t __lane_mask, uint64_t __x) { // Newer targets can use the dedicated CUDA support. - if (__CUDA_ARCH__ >= 700 || __nvvm_reflect("__CUDA_ARCH") >= 700) - return __nvvm_match_any_sync_i64(__lane_mask, __x); +#if __CUDA_ARCH__ >= 700 + return __nvvm_match_any_sync_i64(__lane_mask, __x); +#endif uint64_t __match_mask = 0; @@ -224,9 +226,10 @@ __gpu_match_any_u64(uint64_t __lane_mask, uint64_t __x) { _DEFAULT_FN_ATTRS static __inline__ uint64_t __gpu_match_all_u32(uint64_t __lane_mask, uint32_t __x) { // Newer targets can use the dedicated CUDA support. +#if __CUDA_ARCH__ >= 700 int predicate; - if (__CUDA_ARCH__ >= 700 || __nvvm_reflect("__CUDA_ARCH") >= 700) - return __nvvm_match_all_sync_i32p(__lane_mask, __x, &predicate); + return __nvvm_match_all_sync_i32p(__lane_mask, __x, &predicate); +#endif uint32_t __first = __gpu_read_first_lane_u64(__lane_mask, __x); uint64_t __ballot = __gpu_ballot(__lane_mask, __x == __first); @@ -237,9 +240,10 @@ __gpu_match_all_u32(uint64_t __lane_mask, uint32_t __x) { _DEFAULT_FN_ATTRS static __inline__ uint64_t __gpu_match_all_u64(uint64_t __lane_mask, uint64_t __x) { // Newer targets can use the dedicated CUDA support. +#if __CUDA_ARCH__ >= 700 int predicate; - if (__CUDA_ARCH__ >= 700 || __nvvm_reflect("__CUDA_ARCH") >= 700) - return __nvvm_match_all_sync_i64p(__lane_mask, __x, &predicate); + return __nvvm_match_all_sync_i64p(__lane_mask, __x, &predicate); +#endif uint64_t __first = __gpu_read_first_lane_u64(__lane_mask, __x); uint64_t __ballot = __gpu_ballot(__lane_mask, __x == __first); diff --git a/clang/lib/Interpreter/Wasm.cpp b/clang/lib/Interpreter/Wasm.cpp index aa10b160ccf84..6f584fab52ba9 100644 --- a/clang/lib/Interpreter/Wasm.cpp +++ b/clang/lib/Interpreter/Wasm.cpp @@ -73,8 +73,9 @@ llvm::Error WasmIncrementalExecutor::addModule(PartialTranslationUnit &PTU) { } llvm::TargetOptions TO = llvm::TargetOptions(); - llvm::TargetMachine *TargetMachine = Target->createTargetMachine( - PTU.TheModule->getTargetTriple(), "", "", TO, llvm::Reloc::Model::PIC_); + llvm::TargetMachine *TargetMachine = + Target->createTargetMachine(PTU.TheModule->getTargetTriple().str(), "", + "", TO, llvm::Reloc::Model::PIC_); PTU.TheModule->setDataLayout(TargetMachine->createDataLayout()); std::string ObjectFileName = PTU.TheModule->getName().str() + ".o"; std::string BinaryFileName = PTU.TheModule->getName().str() + ".wasm"; @@ -146,4 +147,4 @@ llvm::Error WasmIncrementalExecutor::cleanUp() { WasmIncrementalExecutor::~WasmIncrementalExecutor() = default; -} // namespace clang \ No newline at end of file +} // namespace clang diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp index 087c6f13aea66..c62a9f5041183 100644 --- a/clang/lib/Lex/Lexer.cpp +++ b/clang/lib/Lex/Lexer.cpp @@ -3192,23 +3192,22 @@ bool Lexer::LexEndOfFile(Token &Result, const char *CurPtr) { if (CurPtr != BufferStart && (CurPtr[-1] != '\n' && CurPtr[-1] != '\r')) { DiagnosticsEngine &Diags = PP->getDiagnostics(); SourceLocation EndLoc = getSourceLocation(BufferEnd); - unsigned DiagID; + unsigned DiagID = diag::warn_no_newline_eof; if (LangOpts.CPlusPlus11) { // C++11 [lex.phases] 2.2 p2 // Prefer the C++98 pedantic compatibility warning over the generic, // non-extension, user-requested "missing newline at EOF" warning. - if (!Diags.isIgnored(diag::warn_cxx98_compat_no_newline_eof, EndLoc)) { + if (!Diags.isIgnored(diag::warn_cxx98_compat_no_newline_eof, EndLoc)) DiagID = diag::warn_cxx98_compat_no_newline_eof; - } else { - DiagID = diag::warn_no_newline_eof; - } } else { - DiagID = diag::ext_no_newline_eof; + // This is conforming in C2y, but is an extension in earlier language + // modes. + if (!LangOpts.C2y) + DiagID = diag::ext_no_newline_eof; } - Diag(BufferEnd, DiagID) - << FixItHint::CreateInsertion(EndLoc, "\n"); + Diag(BufferEnd, DiagID) << FixItHint::CreateInsertion(EndLoc, "\n"); } BufferPtr = CurPtr; diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 7ae136af47391..82b394d5b4ca6 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -4411,6 +4411,10 @@ void Parser::ParseDeclarationSpecifiers( DiagID = diag::err_openclcxx_virtual_function; PrevSpec = Tok.getIdentifierInfo()->getNameStart(); isInvalid = true; + } else if (getLangOpts().HLSL) { + DiagID = diag::err_hlsl_virtual_function; + PrevSpec = Tok.getIdentifierInfo()->getNameStart(); + isInvalid = true; } else { isInvalid = DS.setFunctionSpecVirtual(Loc, PrevSpec, DiagID); } @@ -5652,7 +5656,7 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, Decl *D = SkipBody.CheckSameAsPrevious ? SkipBody.New : TagDecl; ParseEnumBody(StartLoc, D); if (SkipBody.CheckSameAsPrevious && - !Actions.ActOnDuplicateDefinition(TagDecl, SkipBody)) { + !Actions.ActOnDuplicateDefinition(getCurScope(), TagDecl, SkipBody)) { DS.SetTypeSpecError(); return; } diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 43db715ac6d70..9384f9ab10af0 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -2350,7 +2350,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, // Parse the definition body. ParseStructUnionBody(StartLoc, TagType, cast(D)); if (SkipBody.CheckSameAsPrevious && - !Actions.ActOnDuplicateDefinition(TagOrTempResult.get(), SkipBody)) { + !Actions.ActOnDuplicateDefinition(getCurScope(), + TagOrTempResult.get(), SkipBody)) { DS.SetTypeSpecError(); return; } @@ -2491,6 +2492,9 @@ BaseResult Parser::ParseBaseSpecifier(Decl *ClassDecl) { IsVirtual = true; } + if (getLangOpts().HLSL && IsVirtual) + Diag(Tok.getLocation(), diag::err_hlsl_virtual_inheritance); + CheckMisplacedCXX11Attribute(Attributes, StartLoc); // Parse the class-name. diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 33a90e0cb8a42..26be78ee8ca15 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -2203,8 +2203,16 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc, return ParseCXXCondition(nullptr, Loc, CK, MissingOK); } - // Parse the expression. - ExprResult Expr = ParseExpression(); // expression + ExprResult Expr = [&] { + EnterExpressionEvaluationContext Eval( + Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated, + /*LambdaContextDecl=*/nullptr, + /*ExprContext=*/Sema::ExpressionEvaluationContextRecord::EK_Other, + /*ShouldEnter=*/CK == Sema::ConditionKind::ConstexprIf); + // Parse the expression. + return ParseExpression(); // expression + }(); + if (Expr.isInvalid()) return Sema::ConditionError(); diff --git a/clang/lib/Parse/ParseOpenACC.cpp b/clang/lib/Parse/ParseOpenACC.cpp index 6ea17c97d6345..8d3501082cc27 100644 --- a/clang/lib/Parse/ParseOpenACC.cpp +++ b/clang/lib/Parse/ParseOpenACC.cpp @@ -1059,8 +1059,11 @@ Parser::OpenACCClauseParseResult Parser::ParseOpenACCClauseParams( break; } case OpenACCClauseKind::Bind: { - ExprResult BindArg = ParseOpenACCBindClauseArgument(); - if (BindArg.isInvalid()) { + ParsedClause.setBindDetails(ParseOpenACCBindClauseArgument()); + + // We can create an 'empty' bind clause in the event of an error + if (std::holds_alternative( + ParsedClause.getBindDetails())) { Parens.skipToEnd(); return OpenACCCanContinue(); } @@ -1334,7 +1337,8 @@ ExprResult Parser::ParseOpenACCIDExpression() { return getActions().CorrectDelayedTyposInExpr(Res); } -ExprResult Parser::ParseOpenACCBindClauseArgument() { +std::variant +Parser::ParseOpenACCBindClauseArgument() { // OpenACC 3.3 section 2.15: // The bind clause specifies the name to use when calling the procedure on a // device other than the host. If the name is specified as an identifier, it @@ -1343,14 +1347,21 @@ ExprResult Parser::ParseOpenACCBindClauseArgument() { // name unmodified. if (getCurToken().is(tok::r_paren)) { Diag(getCurToken(), diag::err_acc_incorrect_bind_arg); - return ExprError(); + return std::monostate{}; } - if (tok::isStringLiteral(getCurToken().getKind())) - return getActions().CorrectDelayedTyposInExpr(ParseStringLiteralExpression( - /*AllowUserDefinedLiteral=*/false, /*Unevaluated=*/true)); + if (getCurToken().is(tok::identifier)) { + IdentifierInfo *II = getCurToken().getIdentifierInfo(); + ConsumeToken(); + return II; + } - return ParseOpenACCIDExpression(); + ExprResult Res = + getActions().CorrectDelayedTyposInExpr(ParseStringLiteralExpression( + /*AllowUserDefinedLiteral=*/false, /*Unevaluated=*/true)); + if (!Res.isUsable()) + return std::monostate{}; + return cast(Res.get()); } /// OpenACC 3.3, section 1.6: diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index b791c5d5e3019..28386d2260a83 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -3353,6 +3353,20 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, ErrorFound = true; } + Clause = ParseOpenMPClause(CKind, WrongDirective); + break; + case OMPC_self_maps: + // OpenMP [6.0, self_maps clause] + if (getLangOpts().OpenMP < 60) { + Diag(Tok, diag::err_omp_expected_clause) + << getOpenMPDirectiveName(OMPD_requires); + ErrorFound = true; + } + if (!FirstClause) { + Diag(Tok, diag::err_omp_more_one_clause) + << getOpenMPDirectiveName(DKind) << getOpenMPClauseName(CKind) << 0; + ErrorFound = true; + } Clause = ParseOpenMPClause(CKind, WrongDirective); break; case OMPC_update: @@ -4333,6 +4347,20 @@ bool Parser::parseMapTypeModifiers(SemaOpenMP::OpenMPVarListDataTy &Data) { << PreMapName; } ConsumeToken(); + } else if (TypeModifier == OMPC_MAP_MODIFIER_self) { + Data.MapTypeModifiers.push_back(TypeModifier); + Data.MapTypeModifiersLoc.push_back(Tok.getLocation()); + if (PP.LookAhead(0).isNot(tok::comma) && + PP.LookAhead(0).isNot(tok::colon)) + Diag(Tok.getLocation(), diag::err_omp_missing_comma) + << "map type modifier"; + if (getLangOpts().OpenMP < 60) + Diag(Tok, diag::err_omp_unknown_map_type_modifier) + << (getLangOpts().OpenMP >= 51 + ? (getLangOpts().OpenMP >= 52 ? 2 : 1) + : 0) + << getLangOpts().OpenMPExtensions << 0; + ConsumeToken(); } else { // For the case of unknown map-type-modifier or a map-type. // Map-type is followed by a colon; the function returns when it @@ -4354,7 +4382,8 @@ bool Parser::parseMapTypeModifiers(SemaOpenMP::OpenMPVarListDataTy &Data) { Diag(Tok, diag::err_omp_unknown_map_type_modifier) << (getLangOpts().OpenMP >= 51 ? (getLangOpts().OpenMP >= 52 ? 2 : 1) : 0) - << getLangOpts().OpenMPExtensions; + << getLangOpts().OpenMPExtensions + << (getLangOpts().OpenMP >= 60 ? 1 : 0); ConsumeToken(); } if (getCurToken().is(tok::comma)) @@ -4829,7 +4858,7 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind, if (getLangOpts().OpenMP < 52) { Diag(Tok, diag::err_omp_unknown_map_type_modifier) << (getLangOpts().OpenMP >= 51 ? 1 : 0) - << getLangOpts().OpenMPExtensions; + << getLangOpts().OpenMPExtensions << 0; InvalidIterator = true; } } diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index b3fba097999f5..93a2d797679d4 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1105,9 +1105,13 @@ void Sema::ActOnStartOfTranslationUnit() { } void Sema::ActOnEndOfTranslationUnitFragment(TUFragmentKind Kind) { - // No explicit actions are required at the end of the global module fragment. - if (Kind == TUFragmentKind::Global) + if (Kind == TUFragmentKind::Global) { + // Perform Pending Instantiations at the end of global module fragment so + // that the module ownership of TU-level decls won't get messed. + llvm::TimeTraceScope TimeScope("PerformPendingInstantiations"); + PerformPendingInstantiations(); return; + } // Transfer late parsed template instantiations over to the pending template // instantiation list. During normal compilation, the late template parser diff --git a/clang/lib/Sema/SemaARM.cpp b/clang/lib/Sema/SemaARM.cpp index b4b40f293b28e..3f53fb200a93d 100644 --- a/clang/lib/Sema/SemaARM.cpp +++ b/clang/lib/Sema/SemaARM.cpp @@ -249,16 +249,16 @@ bool SemaARM::BuiltinARMSpecialReg(unsigned BuiltinID, CallExpr *TheCall, } } - SmallVector Ranges; + SmallVector FieldBitWidths; if (FiveFields) - Ranges.append({IsAArch64Builtin ? 1 : 15, 7, 15, 15, 7}); + FieldBitWidths.append({IsAArch64Builtin ? 2 : 4, 3, 4, 4, 3}); else - Ranges.append({15, 7, 15}); + FieldBitWidths.append({4, 3, 4}); for (unsigned i = 0; i < Fields.size(); ++i) { int IntField; ValidString &= !Fields[i].getAsInteger(10, IntField); - ValidString &= (IntField >= 0 && IntField <= Ranges[i]); + ValidString &= (IntField >= 0 && IntField < (1 << FieldBitWidths[i])); } if (!ValidString) diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp index c806b832dec7a..96aa65412906c 100644 --- a/clang/lib/Sema/SemaAvailability.cpp +++ b/clang/lib/Sema/SemaAvailability.cpp @@ -99,16 +99,29 @@ ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D, // For typedefs, if the typedef declaration appears available look // to the underlying type to see if it is more restrictive. while (const auto *TD = dyn_cast(D)) { - if (Result == AR_Available) { - if (const auto *TT = TD->getUnderlyingType()->getAs()) { + if (Result != AR_Available) + break; + for (const Type *T = TD->getUnderlyingType().getTypePtr(); /**/; /**/) { + if (auto *TT = dyn_cast(T)) { D = TT->getDecl(); - Result = D->getAvailability(Message); + } else if (isa(T)) { + // A Subst* node represents a use through a template. + // Any uses of the underlying declaration happened through it's template + // specialization. + goto done; + } else { + const Type *NextT = + T->getLocallyUnqualifiedSingleStepDesugaredType().getTypePtr(); + if (NextT == T) + goto done; + T = NextT; continue; } + Result = D->getAvailability(Message); + break; } - break; } - +done: // For alias templates, get the underlying declaration. if (const auto *ADecl = dyn_cast(D)) { D = ADecl->getTemplatedDecl(); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 9cac9cf5c4df7..9bcb014ab9bfc 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -2723,6 +2723,7 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, case Builtin::BI__builtin_elementwise_cosh: case Builtin::BI__builtin_elementwise_exp: case Builtin::BI__builtin_elementwise_exp2: + case Builtin::BI__builtin_elementwise_exp10: case Builtin::BI__builtin_elementwise_floor: case Builtin::BI__builtin_elementwise_log: case Builtin::BI__builtin_elementwise_log2: @@ -10620,6 +10621,42 @@ static std::optional TryGetExprRange(ASTContext &C, const Expr *E, case UO_AddrOf: // should be impossible return IntRange::forValueOfType(C, GetExprType(E)); + case UO_Minus: { + if (E->getType()->isUnsignedIntegerType()) { + return TryGetExprRange(C, UO->getSubExpr(), MaxWidth, InConstantContext, + Approximate); + } + + std::optional SubRange = TryGetExprRange( + C, UO->getSubExpr(), MaxWidth, InConstantContext, Approximate); + + if (!SubRange) + return std::nullopt; + + // If the range was previously non-negative, we need an extra bit for the + // sign bit. If the range was not non-negative, we need an extra bit + // because the negation of the most-negative value is one bit wider than + // that value. + return IntRange(SubRange->Width + 1, false); + } + + case UO_Not: { + if (E->getType()->isUnsignedIntegerType()) { + return TryGetExprRange(C, UO->getSubExpr(), MaxWidth, InConstantContext, + Approximate); + } + + std::optional SubRange = TryGetExprRange( + C, UO->getSubExpr(), MaxWidth, InConstantContext, Approximate); + + if (!SubRange) + return std::nullopt; + + // The width increments by 1 if the sub-expression cannot be negative + // since it now can be. + return IntRange(SubRange->Width + (int)SubRange->NonNegative, false); + } + default: return TryGetExprRange(C, UO->getSubExpr(), MaxWidth, InConstantContext, Approximate); diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp index 0e4f3b20c78cd..53536b0d14037 100644 --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -788,7 +788,11 @@ static bool checkSuspensionContext(Sema &S, SourceLocation Loc, // First emphasis of [expr.await]p2: must be a potentially evaluated context. // That is, 'co_await' and 'co_yield' cannot appear in subexpressions of // \c sizeof. - if (S.isUnevaluatedContext()) { + const auto ExprContext = S.currentEvaluationContext().ExprContext; + const bool BadContext = + S.isUnevaluatedContext() || + ExprContext != Sema::ExpressionEvaluationContextRecord::EK_Other; + if (BadContext) { S.Diag(Loc, diag::err_coroutine_unevaluated_context) << Keyword; return false; } @@ -798,7 +802,6 @@ static bool checkSuspensionContext(Sema &S, SourceLocation Loc, S.Diag(Loc, diag::err_coroutine_within_handler) << Keyword; return false; } - return true; } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 8c5125b8eb6d9..9c67fbd40ac71 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -61,6 +61,7 @@ #include "llvm/ADT/STLForwardCompat.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/SaveAndRestore.h" #include "llvm/TargetParser/Triple.h" #include #include @@ -350,7 +351,7 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc, Diag(QualifiedLoc, diag::warn_cxx17_compat_implicit_typename); else Diag(QualifiedLoc, diag::ext_implicit_typename) - << SS->getScopeRep() << II.getName() + << NestedNameSpecifier::Create(Context, SS->getScopeRep(), &II) << FixItHint::CreateInsertion(QualifiedLoc, "typename "); } @@ -794,9 +795,9 @@ void Sema::DiagnoseUnknownTypeName(IdentifierInfo *&II, DiagID = diag::ext_typename_missing; Diag(SS->getRange().getBegin(), DiagID) - << SS->getScopeRep() << II->getName() - << SourceRange(SS->getRange().getBegin(), IILoc) - << FixItHint::CreateInsertion(SS->getRange().getBegin(), "typename "); + << NestedNameSpecifier::Create(Context, SS->getScopeRep(), II) + << SourceRange(SS->getRange().getBegin(), IILoc) + << FixItHint::CreateInsertion(SS->getRange().getBegin(), "typename "); SuggestedType = ActOnTypenameType(S, SourceLocation(), *SS, *II, IILoc).get(); } else { @@ -2563,18 +2564,7 @@ void Sema::MergeTypedefNameDecl(Scope *S, TypedefNameDecl *New, // Make the old tag definition visible. makeMergedDefinitionVisible(Hidden); - // If this was an unscoped enumeration, yank all of its enumerators - // out of the scope. - if (isa(NewTag)) { - Scope *EnumScope = getNonFieldDeclScope(S); - for (auto *D : NewTag->decls()) { - auto *ED = cast(D); - assert(EnumScope->isDeclScope(ED)); - EnumScope->RemoveDecl(ED); - IdResolver.RemoveDecl(ED); - ED->getLexicalDeclContext()->removeDecl(ED); - } - } + CleanupMergedEnum(S, NewTag); } } @@ -2651,6 +2641,19 @@ void Sema::MergeTypedefNameDecl(Scope *S, TypedefNameDecl *New, notePreviousDefinition(Old, New->getLocation()); } +void Sema::CleanupMergedEnum(Scope *S, Decl *New) { + // If this was an unscoped enumeration, yank all of its enumerators + // out of the scope. + if (auto *ED = dyn_cast(New); ED && !ED->isScoped()) { + Scope *EnumScope = getNonFieldDeclScope(S); + for (auto *ECD : ED->enumerators()) { + assert(EnumScope->isDeclScope(ECD)); + EnumScope->RemoveDecl(ECD); + IdResolver.RemoveDecl(ECD); + } + } +} + /// DeclhasAttr - returns true if decl Declaration already has the target /// attribute. static bool DeclHasAttr(const Decl *D, const Attr *A) { @@ -8162,7 +8165,7 @@ NamedDecl *Sema::ActOnVariableDeclarator( (D.getCXXScopeSpec().isSet() && DC && DC->isRecord() && DC->isDependentContext()) ? TPC_ClassTemplateMember - : TPC_VarTemplate)) + : TPC_Other)) NewVD->setInvalidDecl(); // If we are providing an explicit specialization of a static variable @@ -12611,6 +12614,7 @@ namespace { bool isRecordType; bool isPODType; bool isReferenceType; + bool isInCXXOperatorCall; bool isInitList; llvm::SmallVector InitFieldIndex; @@ -12623,6 +12627,7 @@ namespace { isPODType = false; isRecordType = false; isReferenceType = false; + isInCXXOperatorCall = false; isInitList = false; if (ValueDecl *VD = dyn_cast(OrigDecl)) { isPODType = VD->getType().isPODType(S.Context); @@ -12810,6 +12815,7 @@ namespace { } void VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E) { + llvm::SaveAndRestore CxxOpCallScope(isInCXXOperatorCall, true); Expr *Callee = E->getCallee(); if (isa(Callee)) @@ -12820,6 +12826,19 @@ namespace { HandleValue(Arg->IgnoreParenImpCasts()); } + void VisitLambdaExpr(LambdaExpr *E) { + if (!isInCXXOperatorCall) { + Inherited::VisitLambdaExpr(E); + return; + } + + for (Expr *Init : E->capture_inits()) + if (DeclRefExpr *DRE = dyn_cast_if_present(Init)) + HandleDeclRefExpr(DRE); + else if (Init) + Visit(Init); + } + void VisitUnaryOperator(UnaryOperator *E) { // For POD record types, addresses of its own members are well-defined. if (E->getOpcode() == UO_AddrOf && isRecordType && @@ -18330,12 +18349,14 @@ void Sema::ActOnTagStartDefinition(Scope *S, Decl *TagD) { AddPushedVisibilityAttribute(Tag); } -bool Sema::ActOnDuplicateDefinition(Decl *Prev, SkipBodyInfo &SkipBody) { +bool Sema::ActOnDuplicateDefinition(Scope *S, Decl *Prev, + SkipBodyInfo &SkipBody) { if (!hasStructuralCompatLayout(Prev, SkipBody.New)) return false; // Make the previous decl visible. makeMergedDefinitionVisible(SkipBody.Previous); + CleanupMergedEnum(S, SkipBody.New); return true; } diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 1405ee5341dcf..de2696ea62e3b 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -1191,7 +1191,8 @@ static void handleTestTypestateAttr(Sema &S, Decl *D, const ParsedAttr &AL) { static void handleExtVectorTypeAttr(Sema &S, Decl *D, const ParsedAttr &AL) { // Remember this typedef decl, we will need it later for diagnostics. - S.ExtVectorDecls.push_back(cast(D)); + if (isa(D)) + S.ExtVectorDecls.push_back(cast(D)); } static void handlePackedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { @@ -2138,6 +2139,8 @@ static void handleConstructorAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (AL.getNumArgs() && !S.checkUInt32Argument(AL, AL.getArgAsExpr(0), priority)) return; + S.Diag(D->getLocation(), diag::warn_global_constructor) + << D->getSourceRange(); D->addAttr(::new (S.Context) ConstructorAttr(S.Context, AL, priority)); } @@ -2147,6 +2150,7 @@ static void handleDestructorAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (AL.getNumArgs() && !S.checkUInt32Argument(AL, AL.getArgAsExpr(0), priority)) return; + S.Diag(D->getLocation(), diag::warn_global_destructor) << D->getSourceRange(); D->addAttr(::new (S.Context) DestructorAttr(S.Context, AL, priority)); } diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index a3a028b9485d6..96aac7871db1e 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -733,20 +733,26 @@ Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D, } if (!TemplateParamLists.empty()) { - // FIXME: There's no rule against this, but there are also no rules that - // would actually make it usable, so we reject it for now. + // C++17 [temp]/1: + // A template defines a family of class, functions, or variables, or an + // alias for a family of types. + // + // Structured bindings are not included. Diag(TemplateParamLists.front()->getTemplateLoc(), diag::err_decomp_decl_template); return nullptr; } - Diag(Decomp.getLSquareLoc(), - !getLangOpts().CPlusPlus17 - ? diag::ext_decomp_decl - : D.getContext() == DeclaratorContext::Condition - ? diag::ext_decomp_decl_cond - : diag::warn_cxx14_compat_decomp_decl) - << Decomp.getSourceRange(); + unsigned DiagID; + if (!getLangOpts().CPlusPlus17) + DiagID = diag::ext_decomp_decl; + else if (D.getContext() == DeclaratorContext::Condition) + DiagID = getLangOpts().CPlusPlus26 ? diag::warn_cxx26_decomp_decl_cond + : diag::ext_decomp_decl_cond; + else + DiagID = diag::warn_cxx14_compat_decomp_decl; + + Diag(Decomp.getLSquareLoc(), DiagID) << Decomp.getSourceRange(); // The semantic context is always just the current context. DeclContext *const DC = CurContext; @@ -13583,7 +13589,7 @@ Decl *Sema::ActOnAliasDeclaration(Scope *S, AccessSpecifier AS, // Merge any previous default template arguments into our parameters, // and check the parameter list. if (CheckTemplateParameterList(TemplateParams, OldTemplateParams, - TPC_TypeAliasTemplate)) + TPC_Other)) return nullptr; TypeAliasTemplateDecl *NewDecl = diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index f896ccab53a54..a36a2f563739e 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -1748,9 +1748,14 @@ ExprResult Sema::CreateGenericSelectionExpr( // // C11 6.5.1.1p2 "The type name in a generic association shall specify a // complete object type other than a variably modified type." + // C2y removed the requirement that an expression form must + // use a complete type, though it's still as-if the type has undergone + // lvalue conversion. We support this as an extension in C23 and + // earlier because GCC does so. unsigned D = 0; if (ControllingExpr && Types[i]->getType()->isIncompleteType()) - D = diag::err_assoc_type_incomplete; + D = LangOpts.C2y ? diag::warn_c2y_compat_assoc_type_incomplete + : diag::ext_assoc_type_incomplete; else if (ControllingExpr && !Types[i]->getType()->isObjectType()) D = diag::err_assoc_type_nonobject; else if (Types[i]->getType()->isVariablyModifiedType()) @@ -2935,6 +2940,9 @@ ExprResult Sema::BuildQualifiedDeclarationNameExpr( } if (const TypeDecl *TD = R.getAsSingle()) { + QualType Ty = Context.getTypeDeclType(TD); + QualType ET = getElaboratedType(ElaboratedTypeKeyword::None, SS, Ty); + // Diagnose a missing typename if this resolved unambiguously to a type in // a dependent context. If we can recover with a type, downgrade this to // a warning in Microsoft compatibility mode. @@ -2943,8 +2951,7 @@ ExprResult Sema::BuildQualifiedDeclarationNameExpr( DiagID = diag::ext_typename_missing; SourceLocation Loc = SS.getBeginLoc(); auto D = Diag(Loc, DiagID); - D << SS.getScopeRep() << NameInfo.getName().getAsString() - << SourceRange(Loc, NameInfo.getEndLoc()); + D << ET << SourceRange(Loc, NameInfo.getEndLoc()); // Don't recover if the caller isn't expecting us to or if we're in a SFINAE // context. @@ -2955,11 +2962,9 @@ ExprResult Sema::BuildQualifiedDeclarationNameExpr( D << FixItHint::CreateInsertion(Loc, "typename "); // Recover by pretending this was an elaborated type. - QualType Ty = Context.getTypeDeclType(TD); TypeLocBuilder TLB; TLB.pushTypeSpec(Ty).setNameLoc(NameInfo.getLoc()); - QualType ET = getElaboratedType(ElaboratedTypeKeyword::None, SS, Ty); ElaboratedTypeLoc QTL = TLB.push(ET); QTL.setElaboratedKeywordLoc(SourceLocation()); QTL.setQualifierLoc(SS.getWithLocInContext(Context)); @@ -15428,7 +15433,7 @@ ExprResult Sema::BuildBinOp(Scope *S, SourceLocation OpLoc, Diag(OE->getQualifier() ? OE->getQualifierLoc().getBeginLoc() : OE->getNameLoc(), diag::err_template_kw_missing) - << OE->getName().getAsString() << ""; + << OE->getName().getAsIdentifierInfo(); return ExprError(); } } @@ -21020,18 +21025,24 @@ ExprResult Sema::CheckPlaceholderExpr(Expr *E) { NamedDecl *Temp = *ULE->decls_begin(); const bool IsTypeAliasTemplateDecl = isa(Temp); - if (NestedNameSpecifierLoc Loc = ULE->getQualifierLoc(); Loc.hasQualifier()) - Diag(NameInfo.getLoc(), diag::err_template_kw_refers_to_type_template) - << Loc.getNestedNameSpecifier() << NameInfo.getName().getAsString() - << Loc.getSourceRange() << IsTypeAliasTemplateDecl; - else - Diag(NameInfo.getLoc(), diag::err_template_kw_refers_to_type_template) - << "" << NameInfo.getName().getAsString() << ULE->getSourceRange() - << IsTypeAliasTemplateDecl; + NestedNameSpecifier *NNS = ULE->getQualifierLoc().getNestedNameSpecifier(); + TemplateName TN(dyn_cast(Temp)); + if (TN.isNull()) + TN = Context.getAssumedTemplateName(NameInfo.getName()); + TN = Context.getQualifiedTemplateName(NNS, + /*TemplateKeyword=*/true, TN); + + Diag(NameInfo.getLoc(), diag::err_template_kw_refers_to_type_template) + << TN << ULE->getSourceRange() << IsTypeAliasTemplateDecl; Diag(Temp->getLocation(), diag::note_referenced_type_template) << IsTypeAliasTemplateDecl; - return CreateRecoveryExpr(NameInfo.getBeginLoc(), NameInfo.getEndLoc(), {}); + QualType TST = + Context.getTemplateSpecializationType(TN, ULE->template_arguments()); + QualType ET = + Context.getElaboratedType(ElaboratedTypeKeyword::None, NNS, TST); + return CreateRecoveryExpr(NameInfo.getBeginLoc(), NameInfo.getEndLoc(), {}, + ET); } // Overloaded expressions. diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index d130e8b86bc56..1d9efbeb5ccb5 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -1697,7 +1697,7 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, QualType(), false); } - if (BaseType->isExtVectorBoolType()) { + if (BaseType->isPackedVectorBoolType(S.Context)) { // We disallow element access for ext_vector_type bool. There is no way to // materialize a reference to a vector element as a pointer (each element is // one bit in the vector). diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index aff349a932eec..d10acd1b7807c 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -752,7 +752,7 @@ void SemaHLSL::DiagnoseAttrStageMismatch( HLSLShaderAttr::ConvertEnvironmentTypeToStr(ST)); }); Diag(A->getLoc(), diag::err_hlsl_attr_unsupported_in_stage) - << A << llvm::Triple::getEnvironmentTypeName(Stage) + << A->getAttrName() << llvm::Triple::getEnvironmentTypeName(Stage) << (AllowedStages.size() != 1) << join(StageStrings, ", "); } @@ -2225,40 +2225,48 @@ static bool CheckBoolSelect(Sema *S, CallExpr *TheCall) { static bool CheckVectorSelect(Sema *S, CallExpr *TheCall) { assert(TheCall->getNumArgs() == 3); Expr *Arg1 = TheCall->getArg(1); + QualType Arg1Ty = Arg1->getType(); Expr *Arg2 = TheCall->getArg(2); - if (!Arg1->getType()->isVectorType()) { - S->Diag(Arg1->getBeginLoc(), diag::err_builtin_non_vector_type) - << "Second" << TheCall->getDirectCallee() << Arg1->getType() + QualType Arg2Ty = Arg2->getType(); + + QualType Arg1ScalarTy = Arg1Ty; + if (auto VTy = Arg1ScalarTy->getAs()) + Arg1ScalarTy = VTy->getElementType(); + + QualType Arg2ScalarTy = Arg2Ty; + if (auto VTy = Arg2ScalarTy->getAs()) + Arg2ScalarTy = VTy->getElementType(); + + if (!S->Context.hasSameUnqualifiedType(Arg1ScalarTy, Arg2ScalarTy)) + S->Diag(Arg1->getBeginLoc(), diag::err_hlsl_builtin_scalar_vector_mismatch) + << /* second and third */ 1 << TheCall->getCallee() << Arg1Ty << Arg2Ty; + + QualType Arg0Ty = TheCall->getArg(0)->getType(); + unsigned Arg0Length = Arg0Ty->getAs()->getNumElements(); + unsigned Arg1Length = Arg1Ty->isVectorType() + ? Arg1Ty->getAs()->getNumElements() + : 0; + unsigned Arg2Length = Arg2Ty->isVectorType() + ? Arg2Ty->getAs()->getNumElements() + : 0; + if (Arg1Length > 0 && Arg0Length != Arg1Length) { + S->Diag(TheCall->getBeginLoc(), + diag::err_typecheck_vector_lengths_not_equal) + << Arg0Ty << Arg1Ty << TheCall->getArg(0)->getSourceRange() << Arg1->getSourceRange(); return true; } - if (!Arg2->getType()->isVectorType()) { - S->Diag(Arg2->getBeginLoc(), diag::err_builtin_non_vector_type) - << "Third" << TheCall->getDirectCallee() << Arg2->getType() - << Arg2->getSourceRange(); - return true; - } - - if (!S->Context.hasSameUnqualifiedType(Arg1->getType(), Arg2->getType())) { + if (Arg2Length > 0 && Arg0Length != Arg2Length) { S->Diag(TheCall->getBeginLoc(), - diag::err_typecheck_call_different_arg_types) - << Arg1->getType() << Arg2->getType() << Arg1->getSourceRange() + diag::err_typecheck_vector_lengths_not_equal) + << Arg0Ty << Arg2Ty << TheCall->getArg(0)->getSourceRange() << Arg2->getSourceRange(); return true; } - // caller has checked that Arg0 is a vector. - // check all three args have the same length. - if (TheCall->getArg(0)->getType()->getAs()->getNumElements() != - Arg1->getType()->getAs()->getNumElements()) { - S->Diag(TheCall->getBeginLoc(), - diag::err_typecheck_vector_lengths_not_equal) - << TheCall->getArg(0)->getType() << Arg1->getType() - << TheCall->getArg(0)->getSourceRange() << Arg1->getSourceRange(); - return true; - } - TheCall->setType(Arg1->getType()); + TheCall->setType( + S->getASTContext().getExtVectorType(Arg1ScalarTy, Arg0Length)); return false; } @@ -2641,6 +2649,7 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { case Builtin::BI__builtin_elementwise_cosh: case Builtin::BI__builtin_elementwise_exp: case Builtin::BI__builtin_elementwise_exp2: + case Builtin::BI__builtin_elementwise_exp10: case Builtin::BI__builtin_elementwise_floor: case Builtin::BI__builtin_elementwise_fmod: case Builtin::BI__builtin_elementwise_log: diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index edbd4c071b563..56ec33fe37bf3 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -7271,7 +7271,7 @@ static void CheckCXX98CompatAccessibleCopy(Sema &S, void InitializationSequence::PrintInitLocationNote(Sema &S, const InitializedEntity &Entity) { - if (Entity.isParameterKind() && Entity.getDecl()) { + if (Entity.isParamOrTemplateParamKind() && Entity.getDecl()) { if (Entity.getDecl()->getLocation().isInvalid()) return; @@ -7280,8 +7280,9 @@ void InitializationSequence::PrintInitLocationNote(Sema &S, << Entity.getDecl()->getDeclName(); else S.Diag(Entity.getDecl()->getLocation(), diag::note_parameter_here); - } else if (Entity.getKind() == InitializedEntity::EK_RelatedResult && - Entity.getMethodDecl()) + } + else if (Entity.getKind() == InitializedEntity::EK_RelatedResult && + Entity.getMethodDecl()) S.Diag(Entity.getMethodDecl()->getLocation(), diag::note_method_return_type_change) << Entity.getMethodDecl()->getDeclName(); diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index 4d278bbc67d28..ceb32ee15dfa3 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -1506,13 +1506,14 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, TemplateParameterList *TemplateParams = getGenericLambdaTemplateParameterList(LSI, *this); if (TemplateParams) { - for (auto *TP : TemplateParams->asArray()) { + for (const auto *TP : TemplateParams->asArray()) { if (!TP->getIdentifier()) continue; - CheckTemplateParameterRAII CTP(*this, TP); for (const auto &Capture : Intro.Captures) { - if (Capture.Id == TP->getIdentifier()) + if (Capture.Id == TP->getIdentifier()) { Diag(Capture.Loc, diag::err_template_param_shadow) << Capture.Id; + NoteTemplateParameterLocation(*TP); + } } } } diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index f3af514596a7e..aecf8ed1b4e4d 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -1580,13 +1580,9 @@ llvm::DenseSet &Sema::getLookupModules() { unsigned N = CodeSynthesisContexts.size(); for (unsigned I = CodeSynthesisContextLookupModules.size(); I != N; ++I) { - auto &Ctx = CodeSynthesisContexts[I]; - // FIXME: Are there any other context kinds that shouldn't be looked at - // here? - if (Ctx.Kind == CodeSynthesisContext::PartialOrderingTTP || - Ctx.Kind == CodeSynthesisContext::CheckTemplateParameter) - continue; - Module *M = Ctx.Entity ? getDefiningModule(*this, Ctx.Entity) : nullptr; + Module *M = CodeSynthesisContexts[I].Entity ? + getDefiningModule(*this, CodeSynthesisContexts[I].Entity) : + nullptr; if (M && !LookupModulesCache.insert(M).second) M = nullptr; CodeSynthesisContextLookupModules.push_back(M); @@ -3707,8 +3703,7 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R, TemplateParameterList *Params = FD->getTemplateParameters(); if (Params->size() == 1) { IsTemplate = true; - NamedDecl *Param = Params->getParam(0); - if (!Param->isTemplateParameterPack() && !StringLit) { + if (!Params->getParam(0)->isTemplateParameterPack() && !StringLit) { // Implied but not stated: user-defined integer and floating literals // only ever use numeric literal operator templates, not templates // taking a parameter of class type. @@ -3721,7 +3716,6 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R, if (StringLit) { SFINAETrap Trap(*this); CheckTemplateArgumentInfo CTAI; - CheckTemplateParameterRAII CTP(*this, Param); TemplateArgumentLoc Arg(TemplateArgument(StringLit), StringLit); if (CheckTemplateArgument( Params->getParam(0), Arg, FD, R.getNameLoc(), R.getNameLoc(), diff --git a/clang/lib/Sema/SemaOpenACC.cpp b/clang/lib/Sema/SemaOpenACC.cpp index 4fe6bf5099a64..ec9e9527dca6f 100644 --- a/clang/lib/Sema/SemaOpenACC.cpp +++ b/clang/lib/Sema/SemaOpenACC.cpp @@ -1904,7 +1904,31 @@ DeclGroupRef SemaOpenACC::ActOnEndDeclDirective( Diag(DirLoc, diag::note_acc_construct_here) << OpenACCDirectiveKind::Routine; } - FD->addAttr(OpenACCRoutineAnnotAttr::Create(getASTContext(), DirLoc)); + + // OpenACC 3.3 2.15: + // A bind clause may not bind to a routine name that has a visible bind + // clause. + // TODO OpenACC: There is an exception to this rule that if these are the + // implicit function style (that is, without a name), they may have + // duplicates as long as they have the same name. + auto BindItr = llvm::find_if(Clauses, llvm::IsaPred); + if (auto *A = FD->getAttr()) { + if (BindItr != Clauses.end()) { + if (A->BindClause.isInvalid()) { + // If we have a bind clause, and the function doesn't have one + // annotated yet, set it. + A->BindClause = (*BindItr)->getBeginLoc(); + } else { + Diag((*BindItr)->getBeginLoc(), diag::err_acc_duplicate_bind); + Diag(A->BindClause, diag::note_acc_previous_clause_here); + } + } + } else { + auto *RAA = OpenACCRoutineAnnotAttr::Create(getASTContext(), DirLoc); + FD->addAttr(RAA); + if (BindItr != Clauses.end()) + RAA->BindClause = (*BindItr)->getBeginLoc(); + } } return DeclGroupRef{RoutineDecl}; diff --git a/clang/lib/Sema/SemaOpenACCClause.cpp b/clang/lib/Sema/SemaOpenACCClause.cpp index 582681f247b31..ad54e2bbe9495 100644 --- a/clang/lib/Sema/SemaOpenACCClause.cpp +++ b/clang/lib/Sema/SemaOpenACCClause.cpp @@ -483,6 +483,14 @@ bool doesClauseApplyToDirective(OpenACCDirectiveKind DirectiveKind, return false; } } + case OpenACCClauseKind::Bind: { + switch (DirectiveKind) { + case OpenACCDirectiveKind::Routine: + return true; + default: + return false; + } + } } default: @@ -509,11 +517,6 @@ bool checkAlreadyHasClauseOfKind( bool checkValidAfterDeviceType( SemaOpenACC &S, const OpenACCDeviceTypeClause &DeviceTypeClause, const SemaOpenACC::OpenACCParsedClause &NewClause) { - // This is implemented for everything but 'routine', so treat as 'fine' for - // that. - if (NewClause.getDirectiveKind() == OpenACCDirectiveKind::Routine) - return false; - // OpenACC3.3: Section 2.4: Clauses that precede any device_type clause are // default clauses. Clauses that follow a device_type clause up to the end of // the directive or up to the next device_type clause are device-specific @@ -601,6 +604,19 @@ bool checkValidAfterDeviceType( default: break; } + } else if (NewClause.getDirectiveKind() == OpenACCDirectiveKind::Routine) { + // OpenACC 3.3 section 2.15: Only the 'gang', 'worker', 'vector', 'seq', and + // 'bind' clauses may follow a device_type clause. + switch (NewClause.getClauseKind()) { + case OpenACCClauseKind::Gang: + case OpenACCClauseKind::Worker: + case OpenACCClauseKind::Vector: + case OpenACCClauseKind::Seq: + case OpenACCClauseKind::Bind: + return false; + default: + break; + } } S.Diag(NewClause.getBeginLoc(), diag::err_acc_clause_after_device_type) << NewClause.getClauseKind() << DeviceTypeClause.getClauseKind() @@ -669,10 +685,6 @@ class SemaOpenACCClauseVisitor { SemaOpenACCClauseVisitor(SemaOpenACC &S, ArrayRef ExistingClauses) : SemaRef(S), Ctx(S.getASTContext()), ExistingClauses(ExistingClauses) {} - // Once we've implemented everything, we shouldn't need this infrastructure. - // But in the meantime, we use this to help decide whether the clause was - // handled for this directive. - bool diagNotImplemented() { return NotImplemented; } OpenACCClause *Visit(SemaOpenACC::OpenACCParsedClause &Clause) { switch (Clause.getClauseKind()) { @@ -1256,10 +1268,6 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitWaitClause( OpenACCClause *SemaOpenACCClauseVisitor::VisitDeviceTypeClause( SemaOpenACC::OpenACCParsedClause &Clause) { - // Restrictions implemented properly on everything except 'routine'. - if (Clause.getDirectiveKind() == OpenACCDirectiveKind::Routine) - return isNotImplemented(); - // OpenACC 3.3 2.14.3: Two instances of the same clause may not appear on the // same directive. if (Clause.getDirectiveKind() == OpenACCDirectiveKind::Set && @@ -1981,6 +1989,17 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitCollapseClause( LoopCount.get(), Clause.getEndLoc()); } +OpenACCClause *SemaOpenACCClauseVisitor::VisitBindClause( + SemaOpenACC::OpenACCParsedClause &Clause) { + if (std::holds_alternative(Clause.getBindDetails())) + return OpenACCBindClause::Create( + Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), + std::get(Clause.getBindDetails()), Clause.getEndLoc()); + return OpenACCBindClause::Create( + Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), + std::get(Clause.getBindDetails()), Clause.getEndLoc()); +} + // Return true if the two vars refer to the same variable, for the purposes of // equality checking. bool areVarsEqual(Expr *VarExpr1, Expr *VarExpr2) { @@ -2056,11 +2075,8 @@ SemaOpenACC::ActOnClause(ArrayRef ExistingClauses, return nullptr; } - if (const auto *DevTypeClause = - llvm::find_if(ExistingClauses, - [&](const OpenACCClause *C) { - return isa(C); - }); + if (const auto *DevTypeClause = llvm::find_if( + ExistingClauses, llvm::IsaPred); DevTypeClause != ExistingClauses.end()) { if (checkValidAfterDeviceType( *this, *cast(*DevTypeClause), Clause)) @@ -2072,12 +2088,7 @@ SemaOpenACC::ActOnClause(ArrayRef ExistingClauses, assert((!Result || Result->getClauseKind() == Clause.getClauseKind()) && "Created wrong clause?"); - if (Visitor.diagNotImplemented()) - Diag(Clause.getBeginLoc(), diag::warn_acc_clause_unimplemented) - << Clause.getClauseKind(); - return Result; - } /// OpenACC 3.3 section 2.5.15: diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index 616296027d811..e2c1b2aa81ce5 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -6626,6 +6626,7 @@ StmtResult SemaOpenMP::ActOnOpenMPExecutableDirective( case OMPC_reverse_offload: case OMPC_dynamic_allocators: case OMPC_atomic_default_mem_order: + case OMPC_self_maps: case OMPC_device_type: case OMPC_match: case OMPC_when: @@ -15537,6 +15538,7 @@ OMPClause *SemaOpenMP::ActOnOpenMPSingleExprClause(OpenMPClauseKind Kind, case OMPC_reverse_offload: case OMPC_dynamic_allocators: case OMPC_atomic_default_mem_order: + case OMPC_self_maps: case OMPC_device_type: case OMPC_match: case OMPC_nontemporal: @@ -16192,6 +16194,7 @@ OMPClause *SemaOpenMP::ActOnOpenMPSimpleClause( case OMPC_unified_shared_memory: case OMPC_reverse_offload: case OMPC_dynamic_allocators: + case OMPC_self_maps: case OMPC_device_type: case OMPC_match: case OMPC_nontemporal: @@ -16664,6 +16667,7 @@ OMPClause *SemaOpenMP::ActOnOpenMPSingleExprWithArgClause( case OMPC_reverse_offload: case OMPC_dynamic_allocators: case OMPC_atomic_default_mem_order: + case OMPC_self_maps: case OMPC_device_type: case OMPC_match: case OMPC_nontemporal: @@ -16871,6 +16875,9 @@ OMPClause *SemaOpenMP::ActOnOpenMPClause(OpenMPClauseKind Kind, case OMPC_dynamic_allocators: Res = ActOnOpenMPDynamicAllocatorsClause(StartLoc, EndLoc); break; + case OMPC_self_maps: + Res = ActOnOpenMPSelfMapsClause(StartLoc, EndLoc); + break; case OMPC_destroy: Res = ActOnOpenMPDestroyClause(/*InteropVar=*/nullptr, StartLoc, /*LParenLoc=*/SourceLocation(), @@ -17081,6 +17088,11 @@ SemaOpenMP::ActOnOpenMPDynamicAllocatorsClause(SourceLocation StartLoc, return new (getASTContext()) OMPDynamicAllocatorsClause(StartLoc, EndLoc); } +OMPClause *SemaOpenMP::ActOnOpenMPSelfMapsClause(SourceLocation StartLoc, + SourceLocation EndLoc) { + return new (getASTContext()) OMPSelfMapsClause(StartLoc, EndLoc); +} + StmtResult SemaOpenMP::ActOnOpenMPInteropDirective(ArrayRef Clauses, SourceLocation StartLoc, @@ -17553,6 +17565,7 @@ OMPClause *SemaOpenMP::ActOnOpenMPVarListClause(OpenMPClauseKind Kind, case OMPC_reverse_offload: case OMPC_dynamic_allocators: case OMPC_atomic_default_mem_order: + case OMPC_self_maps: case OMPC_device_type: case OMPC_match: case OMPC_order: @@ -21961,7 +21974,8 @@ OMPClause *SemaOpenMP::ActOnOpenMPMapClause( OpenMPMapModifierKind Modifiers[] = { OMPC_MAP_MODIFIER_unknown, OMPC_MAP_MODIFIER_unknown, OMPC_MAP_MODIFIER_unknown, OMPC_MAP_MODIFIER_unknown, - OMPC_MAP_MODIFIER_unknown, OMPC_MAP_MODIFIER_unknown}; + OMPC_MAP_MODIFIER_unknown, OMPC_MAP_MODIFIER_unknown, + OMPC_MAP_MODIFIER_unknown}; SourceLocation ModifiersLoc[NumberOfOMPMapClauseModifiers]; if (IteratorModifier && !IteratorModifier->getType()->isSpecificBuiltinType( diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index d3c0534b4dd0b..990f2659eb8fa 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -8147,17 +8147,14 @@ void Sema::AddConversionCandidate( ExprValueKind VK = Expr::getValueKindForType(ConversionType); - // Note that it is safe to allocate CallExpr on the stack here because - // there are 0 arguments (i.e., nothing is allocated using ASTContext's - // allocator). QualType CallResultType = ConversionType.getNonLValueExprType(Context); - alignas(CallExpr) char Buffer[sizeof(CallExpr) + sizeof(Stmt *)]; - CallExpr *TheTemporaryCall = CallExpr::CreateTemporary( - Buffer, &ConversionFn, CallResultType, VK, From->getBeginLoc()); + // Introduce a temporary expression with the right type and value category + // that we can use for deduction purposes. + OpaqueValueExpr FakeCall(From->getBeginLoc(), CallResultType, VK); ImplicitConversionSequence ICS = - TryCopyInitialization(*this, TheTemporaryCall, ToType, + TryCopyInitialization(*this, &FakeCall, ToType, /*SuppressUserConversions=*/true, /*InOverloadResolution=*/false, /*AllowObjCWritebackConversion=*/false); diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 0a193b5299bcc..944e3300c83fc 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -4052,9 +4052,9 @@ StmtResult Sema::BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp, Diag(ReturnLoc, D) << CurDecl << isa(CurDecl) << RetValExp->getSourceRange(); } - // return (some void expression); is legal in C++. + // return (some void expression); is legal in C++ and C2y. else if (D != diag::ext_return_has_void_expr || - !getLangOpts().CPlusPlus) { + (!getLangOpts().CPlusPlus && !getLangOpts().C2y)) { NamedDecl *CurDecl = getCurFunctionOrMethodDecl(); int FunctionKind = 0; diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 0caabc6573361..64aabb1fcdc35 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -363,12 +363,12 @@ bool Sema::DiagnoseUnknownTemplateName(const IdentifierInfo &II, // The code is missing a 'template' keyword prior to the dependent template // name. - NestedNameSpecifier *Qualifier = (NestedNameSpecifier*)SS->getScopeRep(); - Diag(IILoc, diag::err_template_kw_missing) - << Qualifier << II.getName() - << FixItHint::CreateInsertion(IILoc, "template "); + NestedNameSpecifier *Qualifier = (NestedNameSpecifier *)SS->getScopeRep(); SuggestedTemplate = TemplateTy::make(Context.getDependentTemplateName(Qualifier, &II)); + Diag(IILoc, diag::err_template_kw_missing) + << SuggestedTemplate.get() + << FixItHint::CreateInsertion(IILoc, "template "); SuggestedKind = TNK_Dependent_template_name; return true; } @@ -660,7 +660,7 @@ void Sema::diagnoseExprIntendedAsTemplateName(Scope *S, ExprResult TemplateName, // was missing. if (MissingTemplateKeyword) { Diag(NameInfo.getBeginLoc(), diag::err_template_kw_missing) - << "" << NameInfo.getName().getAsString() << SourceRange(Less, Greater); + << NameInfo.getName() << SourceRange(Less, Greater); return; } @@ -869,11 +869,9 @@ void Sema::DiagnoseTemplateParameterShadow(SourceLocation Loc, Decl *PrevDecl, ? diag::ext_template_param_shadow : (SupportedForCompatibility ? diag::ext_compat_template_param_shadow : diag::err_template_param_shadow); - auto *ND = cast(PrevDecl); - CheckTemplateParameterRAII CTP(*this, ND); - // FIXME: Don't put the name in the diagnostic, unless there is no source - // location. + const auto *ND = cast(PrevDecl); Diag(Loc, DiagId) << ND->getDeclName(); + NoteTemplateParameterLocation(*ND); } TemplateDecl *Sema::AdjustDeclIfTemplate(Decl *&D) { @@ -1593,8 +1591,16 @@ NamedDecl *Sema::ActOnTemplateTemplateParameter( assert(S->isTemplateParamScope() && "Template template parameter not in template parameter scope!"); - // Construct the parameter object. bool IsParameterPack = EllipsisLoc.isValid(); + + bool Invalid = false; + if (CheckTemplateParameterList( + Params, + /*OldParams=*/nullptr, + IsParameterPack ? TPC_TemplateTemplateParameterPack : TPC_Other)) + Invalid = true; + + // Construct the parameter object. TemplateTemplateParmDecl *Param = TemplateTemplateParmDecl::Create( Context, Context.getTranslationUnitDecl(), NameLoc.isInvalid() ? TmpLoc : NameLoc, Depth, Position, IsParameterPack, @@ -1617,9 +1623,12 @@ NamedDecl *Sema::ActOnTemplateTemplateParameter( if (Params->size() == 0) { Diag(Param->getLocation(), diag::err_template_template_parm_no_parms) << SourceRange(Params->getLAngleLoc(), Params->getRAngleLoc()); - Param->setInvalidDecl(); + Invalid = true; } + if (Invalid) + Param->setInvalidDecl(); + // C++0x [temp.param]p9: // A default template-argument may be specified for any kind of // template-parameter that is not a template parameter pack. @@ -2068,7 +2077,7 @@ DeclResult Sema::CheckClassTemplate( SemanticContext->isDependentContext()) ? TPC_ClassTemplateMember : TUK == TagUseKind::Friend ? TPC_FriendClassTemplate - : TPC_ClassTemplate, + : TPC_Other, SkipBody)) Invalid = true; @@ -2210,9 +2219,8 @@ static bool DiagnoseDefaultTemplateArgument(Sema &S, SourceLocation ParamLoc, SourceRange DefArgRange) { switch (TPC) { - case Sema::TPC_ClassTemplate: - case Sema::TPC_VarTemplate: - case Sema::TPC_TypeAliasTemplate: + case Sema::TPC_Other: + case Sema::TPC_TemplateTemplateParameterPack: return false; case Sema::TPC_FunctionTemplate: @@ -2385,8 +2393,11 @@ bool Sema::CheckTemplateParameterList(TemplateParameterList *NewParams, MissingDefaultArg = true; } else if (NonTypeTemplateParmDecl *NewNonTypeParm = dyn_cast(*NewParam)) { - // Check for unexpanded parameter packs. - if (!NewNonTypeParm->isParameterPack() && + // Check for unexpanded parameter packs, except in a template template + // parameter pack, as in those any unexpanded packs should be expanded + // along with the parameter itself. + if (TPC != TPC_TemplateTemplateParameterPack && + !NewNonTypeParm->isParameterPack() && DiagnoseUnexpandedParameterPack(NewNonTypeParm->getLocation(), NewNonTypeParm->getTypeSourceInfo(), UPPC_NonTypeTemplateParameterType)) { @@ -2494,8 +2505,7 @@ bool Sema::CheckTemplateParameterList(TemplateParameterList *NewParams, // If a template parameter of a primary class template or alias template // is a template parameter pack, it shall be the last template parameter. if (SawParameterPack && (NewParam + 1) != NewParamEnd && - (TPC == TPC_ClassTemplate || TPC == TPC_VarTemplate || - TPC == TPC_TypeAliasTemplate)) { + (TPC == TPC_Other || TPC == TPC_TemplateTemplateParameterPack)) { Diag((*NewParam)->getLocation(), diag::err_template_param_pack_must_be_last_template_parameter); Invalid = true; @@ -2528,8 +2538,8 @@ bool Sema::CheckTemplateParameterList(TemplateParameterList *NewParams, << PrevModuleName; Invalid = true; } else if (MissingDefaultArg && - (TPC == TPC_ClassTemplate || TPC == TPC_FriendClassTemplate || - TPC == TPC_VarTemplate || TPC == TPC_TypeAliasTemplate)) { + (TPC == TPC_Other || TPC == TPC_TemplateTemplateParameterPack || + TPC == TPC_FriendClassTemplate)) { // C++ 23[temp.param]p14: // If a template-parameter of a class template, variable template, or // alias template has a default template argument, each subsequent @@ -3764,16 +3774,17 @@ TypeResult Sema::ActOnTemplateIdType( // elaborated-type-specifier (7.1.5.3). if (!LookupCtx && isDependentScopeSpecifier(SS)) { // C++2a relaxes some of those restrictions in [temp.res]p5. + NestedNameSpecifier *NNS = + NestedNameSpecifier::Create(Context, SS.getScopeRep(), TemplateII); if (AllowImplicitTypename == ImplicitTypenameContext::Yes) { if (getLangOpts().CPlusPlus20) Diag(SS.getBeginLoc(), diag::warn_cxx17_compat_implicit_typename); else Diag(SS.getBeginLoc(), diag::ext_implicit_typename) - << SS.getScopeRep() << TemplateII->getName() + << NNS << FixItHint::CreateInsertion(SS.getBeginLoc(), "typename "); } else - Diag(SS.getBeginLoc(), diag::err_typename_missing_template) - << SS.getScopeRep() << TemplateII->getName(); + Diag(SS.getBeginLoc(), diag::err_typename_missing_template) << NNS; // FIXME: This is not quite correct recovery as we don't transform SS // into the corresponding dependent form (and we don't diagnose missing @@ -4826,7 +4837,7 @@ TemplateNameKind Sema::ActOnTemplateName(Scope *S, } bool Sema::CheckTemplateTypeArgument( - TemplateArgumentLoc &AL, + TemplateTypeParmDecl *Param, TemplateArgumentLoc &AL, SmallVectorImpl &SugaredConverted, SmallVectorImpl &CanonicalConverted) { const TemplateArgument &Arg = AL.getArgument(); @@ -4882,6 +4893,7 @@ bool Sema::CheckTemplateTypeArgument( ? diag::ext_ms_template_type_arg_missing_typename : diag::err_template_arg_must_be_type_suggest) << FixItHint::CreateInsertion(Loc, "typename "); + NoteTemplateParameterLocation(*Param); // Recover by synthesizing a type using the location information that we // already have. @@ -4919,6 +4931,7 @@ bool Sema::CheckTemplateTypeArgument( // is not a type. SourceRange SR = AL.getSourceRange(); Diag(SR.getBegin(), diag::err_template_arg_must_be_type) << SR; + NoteTemplateParameterLocation(*Param); return true; } @@ -5208,8 +5221,8 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, TemplateArgumentLoc &ArgLoc, CheckTemplateArgumentInfo &CTAI, CheckTemplateArgumentKind CTAK) { // Check template type parameters. - if (isa(Param)) - return CheckTemplateTypeArgument(ArgLoc, CTAI.SugaredConverted, + if (TemplateTypeParmDecl *TTP = dyn_cast(Param)) + return CheckTemplateTypeArgument(TTP, ArgLoc, CTAI.SugaredConverted, CTAI.CanonicalConverted); const TemplateArgument &Arg = ArgLoc.getArgument(); @@ -5354,6 +5367,8 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, TemplateArgumentLoc &ArgLoc, // therefore cannot be a non-type template argument. Diag(ArgLoc.getLocation(), diag::err_template_arg_must_be_expr) << ArgLoc.getSourceRange(); + NoteTemplateParameterLocation(*Param); + return true; case TemplateArgument::Type: { @@ -5373,6 +5388,7 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, TemplateArgumentLoc &ArgLoc, Diag(SR.getBegin(), diag::err_template_arg_nontype_ambig) << SR << T; else Diag(SR.getBegin(), diag::err_template_arg_must_be_expr) << SR; + NoteTemplateParameterLocation(*Param); return true; } @@ -5463,11 +5479,11 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, TemplateArgumentLoc &ArgLoc, } /// Diagnose a missing template argument. -template +template static bool diagnoseMissingArgument(Sema &S, SourceLocation Loc, - TemplateDecl *TD, const TemplateParmDecl *D, - TemplateArgumentListInfo &Args, - bool MatchingTTP) { + TemplateDecl *TD, + const TemplateParmDecl *D, + TemplateArgumentListInfo &Args) { // Dig out the most recent declaration of the template parameter; there may be // declarations of the template that are more recent than TD. D = cast(cast(TD->getMostRecentDecl()) @@ -5485,12 +5501,16 @@ static bool diagnoseMissingArgument(Sema &S, SourceLocation Loc, return true; } - SourceLocation DiagLoc = Args.getRAngleLoc(); // FIXME: If there's a more recent default argument that *is* visible, // diagnose that it was declared too late. - S.Diag(DiagLoc.isValid() ? DiagLoc : Loc, - MatchingTTP ? diag::err_template_template_param_missing_param - : diag::err_template_param_missing_arg); + + TemplateParameterList *Params = TD->getTemplateParameters(); + + S.Diag(Loc, diag::err_template_arg_list_different_arity) + << /*not enough args*/0 + << (int)S.getTemplateNameKindForDiagnostics(TemplateName(TD)) + << TD; + S.NoteTemplateLocation(*TD, Params->getSourceRange()); return true; } @@ -5529,8 +5549,6 @@ bool Sema::CheckTemplateArgumentList( Param = ParamBegin; Param != ParamEnd; /* increment in loop */) { - CheckTemplateParameterRAII CTP1(*this, *Param); - if (size_t ParamIdx = Param - ParamBegin; DefaultArgs && ParamIdx >= DefaultArgs.StartPos) { // All written arguments should have been consumed by this point. @@ -5567,9 +5585,11 @@ bool Sema::CheckTemplateArgumentList( continue; } else if (ArgIdx == NumArgs && !PartialTemplateArgs) { // Not enough arguments for this parameter pack. - Diag(RAngleLoc, CTAI.MatchingTTP - ? diag::err_template_template_param_missing_param - : diag::err_template_param_missing_arg); + Diag(TemplateLoc, diag::err_template_arg_list_different_arity) + << /*not enough args*/0 + << (int)getTemplateNameKindForDiagnostics(TemplateName(Template)) + << Template; + NoteTemplateLocation(*Template, Params->getSourceRange()); return true; } } @@ -5582,10 +5602,8 @@ bool Sema::CheckTemplateArgumentList( if (ArgIsExpansion && CTAI.MatchingTTP) { SmallVector Args(ParamEnd - Param); - CTP1.Clear(); // Will continue processing parameters below. for (TemplateParameterList::iterator First = Param; Param != ParamEnd; ++Param) { - CheckTemplateParameterRAII CTP2(*this, *Param); TemplateArgument &Arg = Args[Param - First]; Arg = ArgLoc.getArgument(); if (!(*Param)->isTemplateParameterPack() || @@ -5626,6 +5644,7 @@ bool Sema::CheckTemplateArgumentList( diag::err_template_expansion_into_fixed_list) << (isa(Template) ? 1 : 0) << ArgLoc.getSourceRange(); + NoteTemplateParameterLocation(**Param); return true; } } @@ -5732,14 +5751,14 @@ bool Sema::CheckTemplateArgumentList( if (!HasDefaultArg) { if (TemplateTypeParmDecl *TTP = dyn_cast(*Param)) return diagnoseMissingArgument(*this, TemplateLoc, Template, TTP, - NewArgs, CTAI.MatchingTTP); + NewArgs); if (NonTypeTemplateParmDecl *NTTP = dyn_cast(*Param)) return diagnoseMissingArgument(*this, TemplateLoc, Template, NTTP, - NewArgs, CTAI.MatchingTTP); + NewArgs); return diagnoseMissingArgument(*this, TemplateLoc, Template, cast(*Param), - NewArgs, CTAI.MatchingTTP); + NewArgs); } return true; } @@ -5795,7 +5814,8 @@ bool Sema::CheckTemplateArgumentList( // If we have any leftover arguments, then there were too many arguments. // Complain and fail. if (ArgIdx < NumArgs) { - Diag(TemplateLoc, diag::err_template_too_many_args) + Diag(TemplateLoc, diag::err_template_arg_list_different_arity) + << /*too many args*/1 << (int)getTemplateNameKindForDiagnostics(TemplateName(Template)) << Template << SourceRange(NewArgs[ArgIdx].getLocation(), NewArgs.getRAngleLoc()); @@ -6220,6 +6240,8 @@ isNullPointerValueTemplateArgument(Sema &S, NonTypeTemplateParmDecl *Param, << Arg->getType() << Arg->getSourceRange(); for (unsigned I = 0, N = Notes.size(); I != N; ++I) S.Diag(Notes[I].first, Notes[I].second); + + S.NoteTemplateParameterLocation(*Param); return NPV_Error; } @@ -6244,7 +6266,8 @@ isNullPointerValueTemplateArgument(Sema &S, NonTypeTemplateParmDecl *Param, // The types didn't match, but we know we got a null pointer; complain, // then recover as if the types were correct. S.Diag(Arg->getExprLoc(), diag::err_template_arg_wrongtype_null_constant) - << Arg->getType() << ParamType << Arg->getSourceRange(); + << Arg->getType() << ParamType << Arg->getSourceRange(); + S.NoteTemplateParameterLocation(*Param); return NPV_NullPointer; } @@ -6253,7 +6276,8 @@ isNullPointerValueTemplateArgument(Sema &S, NonTypeTemplateParmDecl *Param, // We could just return NPV_NotNullPointer, but we can print a better // message with the information we have here. S.Diag(Arg->getExprLoc(), diag::err_template_arg_invalid) - << EvalResult.Val.getAsString(S.Context, ParamType); + << EvalResult.Val.getAsString(S.Context, ParamType); + S.NoteTemplateParameterLocation(*Param); return NPV_Error; } @@ -6265,6 +6289,7 @@ isNullPointerValueTemplateArgument(Sema &S, NonTypeTemplateParmDecl *Param, << ParamType << FixItHint::CreateInsertion(Arg->getBeginLoc(), Code) << FixItHint::CreateInsertion(S.getLocForEndOfToken(Arg->getEndLoc()), ")"); + S.NoteTemplateParameterLocation(*Param); return NPV_NullPointer; } @@ -6305,6 +6330,7 @@ static bool CheckTemplateArgumentIsCompatibleWithParameter( S.Diag(Arg->getBeginLoc(), diag::err_template_arg_ref_bind_ignores_quals) << ParamType << Arg->getType() << Arg->getSourceRange(); + S.NoteTemplateParameterLocation(*Param); return true; } } @@ -6322,6 +6348,7 @@ static bool CheckTemplateArgumentIsCompatibleWithParameter( else S.Diag(Arg->getBeginLoc(), diag::err_template_arg_not_convertible) << ArgIn->getType() << ParamType << Arg->getSourceRange(); + S.NoteTemplateParameterLocation(*Param); return true; } } @@ -6464,6 +6491,7 @@ static bool CheckTemplateArgumentAddressOfObjectOrFunction( if (!Entity) { S.Diag(Arg->getBeginLoc(), diag::err_template_arg_not_decl_ref) << Arg->getSourceRange(); + S.NoteTemplateParameterLocation(*Param); return true; } @@ -6471,6 +6499,7 @@ static bool CheckTemplateArgumentAddressOfObjectOrFunction( if (isa(Entity) || isa(Entity)) { S.Diag(Arg->getBeginLoc(), diag::err_template_arg_field) << Entity << Arg->getSourceRange(); + S.NoteTemplateParameterLocation(*Param); return true; } @@ -6479,6 +6508,7 @@ static bool CheckTemplateArgumentAddressOfObjectOrFunction( if (!Method->isStatic()) { S.Diag(Arg->getBeginLoc(), diag::err_template_arg_method) << Method << Arg->getSourceRange(); + S.NoteTemplateParameterLocation(*Param); return true; } } @@ -6518,6 +6548,7 @@ static bool CheckTemplateArgumentAddressOfObjectOrFunction( if (Var->getType()->isReferenceType()) { S.Diag(Arg->getBeginLoc(), diag::err_template_arg_reference_var) << Var->getType() << Arg->getSourceRange(); + S.NoteTemplateParameterLocation(*Param); return true; } @@ -6537,12 +6568,15 @@ static bool CheckTemplateArgumentAddressOfObjectOrFunction( if (!S.Context.hasSameUnqualifiedType(Entity->getType(), ParamType.getNonReferenceType())) { S.Diag(AddrOpLoc, diag::err_template_arg_address_of_non_pointer) - << ParamType; + << ParamType; + S.NoteTemplateParameterLocation(*Param); return true; } S.Diag(AddrOpLoc, diag::err_template_arg_address_of_non_pointer) - << ParamType << FixItHint::CreateRemoval(AddrOpLoc); + << ParamType + << FixItHint::CreateRemoval(AddrOpLoc); + S.NoteTemplateParameterLocation(*Param); ArgType = Entity->getType(); } @@ -6563,12 +6597,15 @@ static bool CheckTemplateArgumentAddressOfObjectOrFunction( ArgType = S.Context.getPointerType(Entity->getType()); if (!S.Context.hasSameUnqualifiedType(ArgType, ParamType)) { S.Diag(Arg->getBeginLoc(), diag::err_template_arg_not_address_of) - << ParamType; + << ParamType; + S.NoteTemplateParameterLocation(*Param); return true; } S.Diag(Arg->getBeginLoc(), diag::err_template_arg_not_address_of) - << ParamType << FixItHint::CreateInsertion(Arg->getBeginLoc(), "&"); + << ParamType << FixItHint::CreateInsertion(Arg->getBeginLoc(), "&"); + + S.NoteTemplateParameterLocation(*Param); } } @@ -6684,6 +6721,7 @@ CheckTemplateArgumentPointerToMember(Sema &S, NonTypeTemplateParmDecl *Param, // We can't perform this conversion. S.Diag(ResultArg->getBeginLoc(), diag::err_template_arg_not_convertible) << ResultArg->getType() << ParamType << ResultArg->getSourceRange(); + S.NoteTemplateParameterLocation(*Param); return true; } @@ -6789,6 +6827,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, diag::err_non_type_template_parm_type_deduction_failure) << Param->getDeclName() << Param->getType() << Arg->getType() << Arg->getSourceRange(); + NoteTemplateParameterLocation(*Param); return ExprError(); } } @@ -6797,8 +6836,10 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, // declaration, but here we'll pass the argument location because that's // where the parameter type is deduced. ParamType = CheckNonTypeTemplateParameterType(ParamType, Arg->getExprLoc()); - if (ParamType.isNull()) + if (ParamType.isNull()) { + NoteTemplateParameterLocation(*Param); return ExprError(); + } } // We should have already dropped all cv-qualifiers by now. @@ -6830,7 +6871,9 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, // not the type of the template argument deduced from A, against the // template parameter type. Diag(StartLoc, diag::err_deduced_non_type_template_arg_type_mismatch) - << Arg->getType() << ParamType.getUnqualifiedType(); + << Arg->getType() + << ParamType.getUnqualifiedType(); + NoteTemplateParameterLocation(*Param); return ExprError(); } @@ -6925,8 +6968,10 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, Arg, ParamType, PartialOrderingTTP ? CCEK_InjectedTTP : CCEK_TemplateArg, Param); assert(!ArgResult.isUnset()); - if (ArgResult.isInvalid()) + if (ArgResult.isInvalid()) { + NoteTemplateParameterLocation(*Param); return ExprError(); + } } else { ArgResult = Arg; } @@ -7073,6 +7118,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, if (!ArgType->isIntegralOrEnumerationType()) { Diag(Arg->getBeginLoc(), diag::err_template_arg_not_integral_or_enumeral) << ArgType << Arg->getSourceRange(); + NoteTemplateParameterLocation(*Param); return ExprError(); } else if (!Arg->isValueDependent()) { class TmplArgICEDiagnoser : public VerifyICEDiagnoser { @@ -7110,6 +7156,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, // We can't perform this conversion. Diag(Arg->getBeginLoc(), diag::err_template_arg_not_convertible) << Arg->getType() << ParamType << Arg->getSourceRange(); + NoteTemplateParameterLocation(*Param); return ExprError(); } @@ -7155,6 +7202,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, Diag(Arg->getBeginLoc(), diag::warn_template_arg_negative) << toString(OldValue, 10) << toString(Value, 10) << Param->getType() << Arg->getSourceRange(); + NoteTemplateParameterLocation(*Param); } // Complain if we overflowed the template parameter's type. @@ -7165,10 +7213,12 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, RequiredBits = OldValue.getActiveBits() + 1; else RequiredBits = OldValue.getSignificantBits(); - if (RequiredBits > AllowedBits) + if (RequiredBits > AllowedBits) { Diag(Arg->getBeginLoc(), diag::warn_template_arg_too_large) << toString(OldValue, 10) << toString(Value, 10) << Param->getType() << Arg->getSourceRange(); + NoteTemplateParameterLocation(*Param); + } } QualType T = ParamType->isEnumeralType() ? ParamType : IntegerType; @@ -7293,7 +7343,8 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, switch (isNullPointerValueTemplateArgument(*this, Param, ParamType, Arg)) { case NPV_NotNullPointer: Diag(Arg->getExprLoc(), diag::err_template_arg_not_convertible) - << Arg->getType() << ParamType; + << Arg->getType() << ParamType; + NoteTemplateParameterLocation(*Param); return ExprError(); case NPV_Error: @@ -7321,7 +7372,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, static void DiagnoseTemplateParameterListArityMismatch( Sema &S, TemplateParameterList *New, TemplateParameterList *Old, - Sema::TemplateParameterListEqualKind Kind); + Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc); bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, TemplateParameterList *Params, @@ -7391,6 +7442,7 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, Diag(Arg.getLocation(), diag::err_template_template_parameter_not_at_least_as_constrained) << Template << Param << Arg.getSourceRange(); + Diag(Param->getLocation(), diag::note_entity_declared_at) << Param; Diag(Template->getLocation(), diag::note_entity_declared_at) << Template; MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Param, ParamsAC, Template, TemplateAC); @@ -7399,24 +7451,25 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, return false; } -SmallString<128> Sema::toTerseString(const NamedDecl &D) const { +static Sema::SemaDiagnosticBuilder noteLocation(Sema &S, const NamedDecl &Decl, + unsigned HereDiagID, + unsigned ExternalDiagID) { + if (Decl.getLocation().isValid()) + return S.Diag(Decl.getLocation(), HereDiagID); + SmallString<128> Str; llvm::raw_svector_ostream Out(Str); - PrintingPolicy PP = getPrintingPolicy(); + PrintingPolicy PP = S.getPrintingPolicy(); PP.TerseOutput = 1; - D.print(Out, PP); - return Str; + Decl.print(Out, PP); + return S.Diag(Decl.getLocation(), ExternalDiagID) << Out.str(); } -// FIXME: Transform this into a context note. void Sema::NoteTemplateLocation(const NamedDecl &Decl, std::optional ParamRange) { - bool HasLoc = Decl.getLocation().isValid(); SemaDiagnosticBuilder DB = - Diag(Decl.getLocation(), HasLoc ? diag::note_template_decl_here - : diag::note_template_decl_external); - if (!HasLoc) - DB << toTerseString(Decl).str(); + noteLocation(*this, Decl, diag::note_template_decl_here, + diag::note_template_decl_external); if (ParamRange && ParamRange->isValid()) { assert(Decl.getLocation().isValid() && "Parameter range has location when Decl does not"); @@ -7424,6 +7477,11 @@ void Sema::NoteTemplateLocation(const NamedDecl &Decl, } } +void Sema::NoteTemplateParameterLocation(const NamedDecl &Decl) { + noteLocation(*this, Decl, diag::note_template_param_here, + diag::note_template_param_external); +} + ExprResult Sema::BuildExpressionFromDeclTemplateArgument( const TemplateArgument &Arg, QualType ParamType, SourceLocation Loc, NamedDecl *TemplateParam) { @@ -7700,17 +7758,21 @@ Sema::BuildExpressionFromNonTypeTemplateArgument(const TemplateArgument &Arg, } /// Match two template parameters within template parameter lists. -static bool -MatchTemplateParameterKind(Sema &S, NamedDecl *New, - const Sema::TemplateCompareNewDeclInfo &NewInstFrom, - NamedDecl *Old, const NamedDecl *OldInstFrom, - bool Complain, - Sema::TemplateParameterListEqualKind Kind) { +static bool MatchTemplateParameterKind( + Sema &S, NamedDecl *New, + const Sema::TemplateCompareNewDeclInfo &NewInstFrom, NamedDecl *Old, + const NamedDecl *OldInstFrom, bool Complain, + Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc) { // Check the actual kind (type, non-type, template). if (Old->getKind() != New->getKind()) { if (Complain) { - S.Diag(New->getLocation(), diag::err_template_param_different_kind) - << (Kind != Sema::TPL_TemplateMatch); + unsigned NextDiag = diag::err_template_param_different_kind; + if (TemplateArgLoc.isValid()) { + S.Diag(TemplateArgLoc, diag::err_template_arg_template_params_mismatch); + NextDiag = diag::note_template_param_different_kind; + } + S.Diag(New->getLocation(), NextDiag) + << (Kind != Sema::TPL_TemplateMatch); S.Diag(Old->getLocation(), diag::note_template_prev_declaration) << (Kind != Sema::TPL_TemplateMatch); } @@ -7724,11 +7786,18 @@ MatchTemplateParameterKind(Sema &S, NamedDecl *New, // a parameter pack where the template template argument does not. if (Old->isTemplateParameterPack() != New->isTemplateParameterPack()) { if (Complain) { + unsigned NextDiag = diag::err_template_parameter_pack_non_pack; + if (TemplateArgLoc.isValid()) { + S.Diag(TemplateArgLoc, + diag::err_template_arg_template_params_mismatch); + NextDiag = diag::note_template_parameter_pack_non_pack; + } + unsigned ParamKind = isa(New)? 0 : isa(New)? 1 : 2; - S.Diag(New->getLocation(), diag::err_template_parameter_pack_non_pack) - << ParamKind << New->isParameterPack(); + S.Diag(New->getLocation(), NextDiag) + << ParamKind << New->isParameterPack(); S.Diag(Old->getLocation(), diag::note_template_parameter_pack_here) << ParamKind << Old->isParameterPack(); } @@ -7749,8 +7818,13 @@ MatchTemplateParameterKind(Sema &S, NamedDecl *New, QualType NewType = S.Context.getUnconstrainedType(NewNTTP->getType()); if (!S.Context.hasSameType(OldType, NewType)) { if (Complain) { - S.Diag(NewNTTP->getLocation(), - diag::err_template_nontype_parm_different_type) + unsigned NextDiag = diag::err_template_nontype_parm_different_type; + if (TemplateArgLoc.isValid()) { + S.Diag(TemplateArgLoc, + diag::err_template_arg_template_params_mismatch); + NextDiag = diag::note_template_nontype_parm_different_type; + } + S.Diag(NewNTTP->getLocation(), NextDiag) << NewNTTP->getType() << (Kind != Sema::TPL_TemplateMatch); S.Diag(OldNTTP->getLocation(), diag::note_template_nontype_parm_prev_declaration) @@ -7771,7 +7845,8 @@ MatchTemplateParameterKind(Sema &S, NamedDecl *New, OldTTP->getTemplateParameters(), Complain, (Kind == Sema::TPL_TemplateMatch ? Sema::TPL_TemplateTemplateParmMatch - : Kind))) + : Kind), + TemplateArgLoc)) return false; } @@ -7822,12 +7897,21 @@ MatchTemplateParameterKind(Sema &S, NamedDecl *New, /// Diagnose a known arity mismatch when comparing template argument /// lists. -static void DiagnoseTemplateParameterListArityMismatch( - Sema &S, TemplateParameterList *New, TemplateParameterList *Old, - Sema::TemplateParameterListEqualKind Kind) { - S.Diag(New->getTemplateLoc(), diag::err_template_param_list_different_arity) - << (New->size() > Old->size()) << (Kind != Sema::TPL_TemplateMatch) - << SourceRange(New->getTemplateLoc(), New->getRAngleLoc()); +static +void DiagnoseTemplateParameterListArityMismatch(Sema &S, + TemplateParameterList *New, + TemplateParameterList *Old, + Sema::TemplateParameterListEqualKind Kind, + SourceLocation TemplateArgLoc) { + unsigned NextDiag = diag::err_template_param_list_different_arity; + if (TemplateArgLoc.isValid()) { + S.Diag(TemplateArgLoc, diag::err_template_arg_template_params_mismatch); + NextDiag = diag::note_template_param_list_different_arity; + } + S.Diag(New->getTemplateLoc(), NextDiag) + << (New->size() > Old->size()) + << (Kind != Sema::TPL_TemplateMatch) + << SourceRange(New->getTemplateLoc(), New->getRAngleLoc()); S.Diag(Old->getTemplateLoc(), diag::note_template_prev_declaration) << (Kind != Sema::TPL_TemplateMatch) << SourceRange(Old->getTemplateLoc(), Old->getRAngleLoc()); @@ -7836,10 +7920,11 @@ static void DiagnoseTemplateParameterListArityMismatch( bool Sema::TemplateParameterListsAreEqual( const TemplateCompareNewDeclInfo &NewInstFrom, TemplateParameterList *New, const NamedDecl *OldInstFrom, TemplateParameterList *Old, bool Complain, - TemplateParameterListEqualKind Kind) { + TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc) { if (Old->size() != New->size()) { if (Complain) - DiagnoseTemplateParameterListArityMismatch(*this, New, Old, Kind); + DiagnoseTemplateParameterListArityMismatch(*this, New, Old, Kind, + TemplateArgLoc); return false; } @@ -7857,18 +7942,21 @@ bool Sema::TemplateParameterListsAreEqual( OldParm != OldParmEnd; ++OldParm, ++NewParm) { if (NewParm == NewParmEnd) { if (Complain) - DiagnoseTemplateParameterListArityMismatch(*this, New, Old, Kind); + DiagnoseTemplateParameterListArityMismatch(*this, New, Old, Kind, + TemplateArgLoc); return false; } if (!MatchTemplateParameterKind(*this, *NewParm, NewInstFrom, *OldParm, - OldInstFrom, Complain, Kind)) + OldInstFrom, Complain, Kind, + TemplateArgLoc)) return false; } // Make sure we exhausted all of the arguments. if (NewParm != NewParmEnd) { if (Complain) - DiagnoseTemplateParameterListArityMismatch(*this, New, Old, Kind); + DiagnoseTemplateParameterListArityMismatch(*this, New, Old, Kind, + TemplateArgLoc); return false; } @@ -8166,6 +8254,7 @@ static bool CheckNonTypeTemplatePartialSpecializationArgs( S.Diag(IsDefaultArgument ? TemplateNameLoc : ArgExpr->getBeginLoc(), diag::err_dependent_typed_non_type_arg_in_partial_spec) << Param->getType(); + S.NoteTemplateParameterLocation(*Param); return true; } } @@ -8189,7 +8278,6 @@ bool Sema::CheckTemplatePartialSpecializationArgs( if (!Param) continue; - CheckTemplateParameterRAII CTP(*this, Param); if (CheckNonTypeTemplatePartialSpecializationArgs(*this, TemplateNameLoc, Param, &TemplateArgs[I], 1, I >= NumExplicit)) diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index b1a1f86c5cfe1..e6ec4a7178e81 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -3006,7 +3006,7 @@ ConvertDeducedTemplateArgument(Sema &S, NamedDecl *Param, // arguments). S.Diag(Param->getLocation(), diag::err_template_arg_deduced_incomplete_pack) - << Arg << Param; + << Arg << Param; return true; } if (ConvertArg(InnerArg, SugaredPackedArgsBuilder.size())) @@ -3072,7 +3072,7 @@ static TemplateDeductionResult ConvertDeducedTemplateArguments( for (unsigned I = 0, N = TemplateParams->size(); I != N; ++I) { NamedDecl *Param = TemplateParams->getParam(I); - Sema::CheckTemplateParameterRAII CTP(S, Param); + // C++0x [temp.arg.explicit]p3: // A trailing template parameter pack (14.5.3) not otherwise deduced will // be deduced to an empty sequence of template arguments. @@ -3427,9 +3427,9 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( if (!P.isPackExpansion() && !A.isPackExpansion()) { Info.Param = makeTemplateParameter(Template->getTemplateParameters()->getParam( - (PsStack.empty() ? TemplateArgs.end() - : PsStack.front().begin()) - - TemplateArgs.begin())); + (AsStack.empty() ? CTAI.CanonicalConverted.end() + : AsStack.front().begin()) - + 1 - CTAI.CanonicalConverted.begin())); Info.FirstArg = P; Info.SecondArg = A; return TemplateDeductionResult::NonDeducedMismatch; @@ -6642,17 +6642,19 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( TemplateDeductionResult TDK; runWithSufficientStackSpace(Info.getLocation(), [&] { - TDK = ::FinishTemplateArgumentDeduction( - *this, AArg, /*IsPartialOrdering=*/true, PArgs, Deduced, Info); + TDK = ::FinishTemplateArgumentDeduction(*this, AArg, PartialOrdering, PArgs, + Deduced, Info); }); switch (TDK) { case TemplateDeductionResult::Success: return true; // It doesn't seem possible to get a non-deduced mismatch when partial - // ordering TTPs. + // ordering TTPs, except with an invalid template parameter list which has + // a parameter after a pack. case TemplateDeductionResult::NonDeducedMismatch: - llvm_unreachable("Unexpected NonDeducedMismatch"); + assert(PArg->isInvalidDecl() && "Unexpected NonDeducedMismatch"); + return false; // Substitution failures should have already been diagnosed. case TemplateDeductionResult::AlreadyDiagnosed: diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 847057a3f70ea..19c27a76b182c 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -575,7 +575,6 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const { case BuildingDeductionGuides: case TypeAliasTemplateInstantiation: case PartialOrderingTTP: - case CheckTemplateParameter: return false; // This function should never be called when Kind's value is Memoization. @@ -810,16 +809,7 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( Sema &SemaRef, SourceLocation ArgLoc, PartialOrderingTTP, TemplateDecl *PArg, SourceRange InstantiationRange) : InstantiatingTemplate(SemaRef, CodeSynthesisContext::PartialOrderingTTP, - ArgLoc, SourceRange(), PArg) {} - -Sema::InstantiatingTemplate::InstantiatingTemplate(Sema &SemaRef, - CheckTemplateParameter, - NamedDecl *Param) - : InstantiatingTemplate( - SemaRef, CodeSynthesisContext::CheckTemplateParameter, - Param->getLocation(), Param->getSourceRange(), Param) { - assert(Param->isTemplateParameter()); -} + ArgLoc, InstantiationRange, PArg) {} void Sema::pushCodeSynthesisContext(CodeSynthesisContext Ctx) { Ctx.SavedInNonInstantiationSFINAEContext = InNonInstantiationSFINAEContext; @@ -1261,18 +1251,12 @@ void Sema::PrintInstantiationStack(InstantiationContextDiagFuncRef DiagFunc) { case CodeSynthesisContext::PartialOrderingTTP: DiagFunc(Active->PointOfInstantiation, PDiag(diag::note_template_arg_template_params_mismatch)); + if (SourceLocation ParamLoc = Active->Entity->getLocation(); + ParamLoc.isValid()) + DiagFunc(ParamLoc, PDiag(diag::note_template_prev_declaration) + << /*isTemplateTemplateParam=*/true + << Active->InstantiationRange); break; - case CodeSynthesisContext::CheckTemplateParameter: { - const auto &ND = *cast(Active->Entity); - if (SourceLocation Loc = ND.getLocation(); Loc.isValid()) { - DiagFunc(Loc, PDiag(diag::note_template_param_here) - << ND.getSourceRange()); - break; - } - DiagFunc(SourceLocation(), PDiag(diag::note_template_param_external) - << toTerseString(ND).str()); - break; - } } } } @@ -1316,7 +1300,6 @@ std::optional Sema::isSFINAEContext() const { case CodeSynthesisContext::DefaultTemplateArgumentChecking: case CodeSynthesisContext::RewritingOperatorAsSpaceship: case CodeSynthesisContext::PartialOrderingTTP: - case CodeSynthesisContext::CheckTemplateParameter: // A default template argument instantiation and substitution into // template parameters with arguments for prior parameters may or may // not be a SFINAE context; look further up the stack. @@ -2365,7 +2348,6 @@ TemplateInstantiator::TransformSubstNonTypeTemplateParmPackExpr( ExprResult TemplateInstantiator::TransformSubstNonTypeTemplateParmExpr( SubstNonTypeTemplateParmExpr *E) { - Sema::CheckTemplateParameterRAII CTP(SemaRef, E->getParameter()); ExprResult SubstReplacement = E->getReplacement(); if (!isa(SubstReplacement.get())) SubstReplacement = TransformExpr(E->getReplacement()); diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index f3e33078c55c7..170c0b0d39f86 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1056,7 +1056,6 @@ CLAUSE_NOT_ON_DECLS(Delete) CLAUSE_NOT_ON_DECLS(Detach) CLAUSE_NOT_ON_DECLS(Device) CLAUSE_NOT_ON_DECLS(DeviceNum) -CLAUSE_NOT_ON_DECLS(DeviceType) CLAUSE_NOT_ON_DECLS(Finalize) CLAUSE_NOT_ON_DECLS(FirstPrivate) CLAUSE_NOT_ON_DECLS(Host) @@ -1116,6 +1115,15 @@ void OpenACCDeclClauseInstantiator::VisitNoHostClause( ParsedClause.getEndLoc()); } +void OpenACCDeclClauseInstantiator::VisitDeviceTypeClause( + const OpenACCDeviceTypeClause &C) { + // Nothing to transform here, just create a new version of 'C'. + NewClause = OpenACCDeviceTypeClause::Create( + SemaRef.getASTContext(), C.getClauseKind(), ParsedClause.getBeginLoc(), + ParsedClause.getLParenLoc(), C.getArchitectures(), + ParsedClause.getEndLoc()); +} + void OpenACCDeclClauseInstantiator::VisitWorkerClause( const OpenACCWorkerClause &C) { assert(!C.hasIntExpr() && "Int Expr not allowed on routine 'worker' clause"); @@ -1241,6 +1249,21 @@ void OpenACCDeclClauseInstantiator::VisitDevicePtrClause( ParsedClause.getEndLoc()); } +void OpenACCDeclClauseInstantiator::VisitBindClause( + const OpenACCBindClause &C) { + // Nothing to instantiate, we support only string literal or identifier. + if (C.isStringArgument()) + NewClause = OpenACCBindClause::Create( + SemaRef.getASTContext(), ParsedClause.getBeginLoc(), + ParsedClause.getLParenLoc(), C.getStringArgument(), + ParsedClause.getEndLoc()); + else + NewClause = OpenACCBindClause::Create( + SemaRef.getASTContext(), ParsedClause.getBeginLoc(), + ParsedClause.getLParenLoc(), C.getIdentifierArgument(), + ParsedClause.getEndLoc()); +} + llvm::SmallVector InstantiateOpenACCClauseList( Sema &S, const MultiLevelTemplateArgumentList &MLTAL, OpenACCDirectiveKind DK, ArrayRef ClauseList) { @@ -2168,7 +2191,7 @@ Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) { // Do some additional validation, then merge default arguments // from the existing declarations. if (SemaRef.CheckTemplateParameterList(InstParams, PrevParams, - Sema::TPC_ClassTemplate)) + Sema::TPC_Other)) return nullptr; Inst->setAccess(PrevClassTemplate->getAccess()); diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 9591fd4cfcc1c..a09f623ff8fbf 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -10835,6 +10835,12 @@ OMPClause *TreeTransform::TransformOMPAtomicDefaultMemOrderClause( "atomic_default_mem_order clause cannot appear in dependent context"); } +template +OMPClause * +TreeTransform::TransformOMPSelfMapsClause(OMPSelfMapsClause *C) { + llvm_unreachable("self_maps clause cannot appear in dependent context"); +} + template OMPClause *TreeTransform::TransformOMPAtClause(OMPAtClause *C) { return getDerived().RebuildOMPAtClause(C->getAtKind(), C->getAtKindKwLoc(), @@ -11878,7 +11884,12 @@ void OpenACCClauseTransform::VisitDeviceResidentClause( template void OpenACCClauseTransform::VisitNoHostClause( const OpenACCNoHostClause &C) { - llvm_unreachable("device_resident clause not valid unless a decl transform"); + llvm_unreachable("nohost clause not valid unless a decl transform"); +} +template +void OpenACCClauseTransform::VisitBindClause( + const OpenACCBindClause &C) { + llvm_unreachable("bind clause not valid unless a decl transform"); } template diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 2ac9754f02eed..d0a1a9221ce6f 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -11094,6 +11094,9 @@ OMPClause *OMPClauseReader::readClause() { case llvm::omp::OMPC_atomic_default_mem_order: C = new (Context) OMPAtomicDefaultMemOrderClause(); break; + case llvm::omp::OMPC_self_maps: + C = new (Context) OMPSelfMapsClause(); + break; case llvm::omp::OMPC_at: C = new (Context) OMPAtClause(); break; @@ -11575,6 +11578,8 @@ void OMPClauseReader::VisitOMPAtomicDefaultMemOrderClause( C->setAtomicDefaultMemOrderKindKwLoc(Record.readSourceLocation()); } +void OMPClauseReader::VisitOMPSelfMapsClause(OMPSelfMapsClause *) {} + void OMPClauseReader::VisitOMPAtClause(OMPAtClause *C) { C->setAtKind(static_cast(Record.readInt())); C->setLParenLoc(Record.readSourceLocation()); @@ -12797,7 +12802,15 @@ OpenACCClause *ASTRecordReader::readOpenACCClause() { LParenLoc, VarList, EndLoc); } - case OpenACCClauseKind::Bind: + case OpenACCClauseKind::Bind: { + SourceLocation LParenLoc = readSourceLocation(); + bool IsString = readBool(); + if (IsString) + return OpenACCBindClause::Create(getContext(), BeginLoc, LParenLoc, + cast(readExpr()), EndLoc); + return OpenACCBindClause::Create(getContext(), BeginLoc, LParenLoc, + readIdentifier(), EndLoc); + } case OpenACCClauseKind::Invalid: llvm_unreachable("Clause serialization not yet implemented"); } diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 0aa115ecadf8e..0f53d9e137910 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -1753,7 +1753,7 @@ void ASTWriter::WriteControlBlock(Preprocessor &PP, StringRef isysroot) { AddFileID(SourceMgr.getMainFileID(), Record); Stream.EmitRecord(ORIGINAL_FILE_ID, Record); - WriteInputFiles(SourceMgr, PP.getHeaderSearchInfo().getHeaderSearchOpts()); + WriteInputFiles(SourceMgr); Stream.ExitBlock(); } @@ -1786,8 +1786,7 @@ SourceLocation ASTWriter::getAffectingIncludeLoc(const SourceManager &SourceMgr, return IncludeLoc; } -void ASTWriter::WriteInputFiles(SourceManager &SourceMgr, - HeaderSearchOptions &HSOpts) { +void ASTWriter::WriteInputFiles(SourceManager &SourceMgr) { using namespace llvm; Stream.EnterSubblock(INPUT_FILES_BLOCK_ID, 4); @@ -8430,6 +8429,8 @@ void OMPClauseWriter::VisitOMPAtomicDefaultMemOrderClause( Record.AddSourceLocation(C->getAtomicDefaultMemOrderKindKwLoc()); } +void OMPClauseWriter::VisitOMPSelfMapsClause(OMPSelfMapsClause *) {} + void OMPClauseWriter::VisitOMPAtClause(OMPAtClause *C) { Record.push_back(C->getAtKind()); Record.AddSourceLocation(C->getLParenLoc()); @@ -8844,7 +8845,17 @@ void ASTRecordWriter::writeOpenACCClause(const OpenACCClause *C) { return; } - case OpenACCClauseKind::Bind: + case OpenACCClauseKind::Bind: { + const auto *BC = cast(C); + writeSourceLocation(BC->getLParenLoc()); + writeBool(BC->isStringArgument()); + if (BC->isStringArgument()) + AddStmt(const_cast(BC->getStringArgument())); + else + AddIdentifierRef(BC->getIdentifierArgument()); + + return; + } case OpenACCClauseKind::Invalid: llvm_unreachable("Clause serialization not yet implemented"); } diff --git a/clang/lib/Serialization/GlobalModuleIndex.cpp b/clang/lib/Serialization/GlobalModuleIndex.cpp index 4b920fccecac3..1e2272c48bd04 100644 --- a/clang/lib/Serialization/GlobalModuleIndex.cpp +++ b/clang/lib/Serialization/GlobalModuleIndex.cpp @@ -849,22 +849,21 @@ GlobalModuleIndex::writeIndex(FileManager &FileMgr, // Coordinate building the global index file with other processes that might // try to do the same. - llvm::LockFileManager Locked(IndexPath); - switch (Locked) { - case llvm::LockFileManager::LFS_Error: + llvm::LockFileManager Lock(IndexPath); + bool Owned; + if (llvm::Error Err = Lock.tryLock().moveInto(Owned)) { + llvm::consumeError(std::move(Err)); return llvm::createStringError(std::errc::io_error, "LFS error"); - - case llvm::LockFileManager::LFS_Owned: - // We're responsible for building the index ourselves. Do so below. - break; - - case llvm::LockFileManager::LFS_Shared: + } + if (!Owned) { // Someone else is responsible for building the index. We don't care // when they finish, so we're done. return llvm::createStringError(std::errc::device_or_resource_busy, "someone else is building the index"); } + // We're responsible for building the index ourselves. + // The module index builder. GlobalModuleIndexBuilder Builder(FileMgr, PCHContainerRdr); diff --git a/clang/lib/StaticAnalyzer/Checkers/AssumeModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/AssumeModeling.cpp new file mode 100644 index 0000000000000..1e3adb4f266ca --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/AssumeModeling.cpp @@ -0,0 +1,86 @@ +//=== AssumeModeling.cpp --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This checker evaluates the builting assume functions. +// This checker also sinks execution paths leaving [[assume]] attributes with +// false assumptions. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/AttrIterator.h" +#include "clang/Basic/Builtins.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "llvm/ADT/STLExtras.h" + +using namespace clang; +using namespace ento; + +namespace { +class AssumeModelingChecker + : public Checker> { +public: + void checkPostStmt(const AttributedStmt *A, CheckerContext &C) const; + bool evalCall(const CallEvent &Call, CheckerContext &C) const; +}; +} // namespace + +void AssumeModelingChecker::checkPostStmt(const AttributedStmt *A, + CheckerContext &C) const { + if (!hasSpecificAttr(A->getAttrs())) + return; + + for (const auto *Attr : getSpecificAttrs(A->getAttrs())) { + SVal AssumptionVal = C.getSVal(Attr->getAssumption()); + + // The assumption is not evaluated at all if it had sideffects; skip them. + if (AssumptionVal.isUnknown()) + continue; + + const auto *Assumption = AssumptionVal.getAsInteger(); + assert(Assumption && "We should know the exact outcome of an assume expr"); + if (Assumption && Assumption->isZero()) { + C.addSink(); + } + } +} + +bool AssumeModelingChecker::evalCall(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + const auto *FD = dyn_cast_or_null(Call.getDecl()); + if (!FD) + return false; + + if (!llvm::is_contained({Builtin::BI__builtin_assume, Builtin::BI__assume}, + FD->getBuiltinID())) { + return false; + } + + assert(Call.getNumArgs() > 0); + SVal Arg = Call.getArgSVal(0); + if (Arg.isUndef()) + return true; // Return true to model purity. + + State = State->assume(Arg.castAs(), true); + if (!State) { + C.addSink(); + return true; + } + + C.addTransition(State); + return true; +} + +void ento::registerAssumeModeling(CheckerManager &Mgr) { + Mgr.registerChecker(); +} + +bool ento::shouldRegisterAssumeModeling(const CheckerManager &) { return true; } diff --git a/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp index cfdd3c9faa360..b1a11442dec53 100644 --- a/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp @@ -23,7 +23,6 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" using namespace clang; @@ -288,25 +287,6 @@ bool BuiltinFunctionChecker::evalCall(const CallEvent &Call, handleOverflowBuiltin(Call, C, BO_Add, getOverflowBuiltinResultType(Call, C, BI)); return true; - case Builtin::BI__builtin_assume: - case Builtin::BI__assume: { - assert (Call.getNumArgs() > 0); - SVal Arg = Call.getArgSVal(0); - if (Arg.isUndef()) - return true; // Return true to model purity. - - state = state->assume(Arg.castAs(), true); - // FIXME: do we want to warn here? Not right now. The most reports might - // come from infeasible paths, thus being false positives. - if (!state) { - C.generateSink(C.getState(), C.getPredecessor()); - return true; - } - - C.addTransition(state); - return true; - } - case Builtin::BI__builtin_unpredictable: case Builtin::BI__builtin_expect: case Builtin::BI__builtin_expect_with_probability: diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt index 5910043440987..878b48197cd58 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -8,6 +8,7 @@ add_clang_library(clangStaticAnalyzerCheckers AnalysisOrderChecker.cpp AnalyzerStatsChecker.cpp ArrayBoundChecker.cpp + AssumeModeling.cpp BasicObjCFoundationChecks.cpp BitwiseShiftChecker.cpp BlockInCriticalSectionChecker.cpp @@ -128,14 +129,14 @@ add_clang_library(clangStaticAnalyzerCheckers VLASizeChecker.cpp ValistChecker.cpp VirtualCallChecker.cpp - WebKit/RawPtrRefMemberChecker.cpp WebKit/ASTUtils.cpp WebKit/MemoryUnsafeCastChecker.cpp WebKit/PtrTypesSemantics.cpp WebKit/RefCntblBaseVirtualDtorChecker.cpp WebKit/RawPtrRefCallArgsChecker.cpp - WebKit/UncountedLambdaCapturesChecker.cpp + WebKit/RawPtrRefLambdaCapturesChecker.cpp WebKit/RawPtrRefLocalVarsChecker.cpp + WebKit/RawPtrRefMemberChecker.cpp LINK_LIBS clangAST diff --git a/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp index 0b6c5163a1637..6035e2d34c2b3 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -430,9 +430,8 @@ void ExprInspectionChecker::analyzerHashDump(const CallExpr *CE, const LangOptions &Opts = C.getLangOpts(); const SourceManager &SM = C.getSourceManager(); FullSourceLoc FL(CE->getArg(0)->getBeginLoc(), SM); - std::string HashContent = - getIssueString(FL, getCheckerName().getName(), "Category", - C.getLocationContext()->getDecl(), Opts); + std::string HashContent = getIssueString( + FL, getName(), "Category", C.getLocationContext()->getDecl(), Opts); reportBug(HashContent, C); } diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 0d1213ddf8b01..1c4293c30abdb 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -3885,7 +3885,7 @@ void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State, Out << " : "; Data.dump(Out); if (CheckKind) - Out << " (" << CheckNames[*CheckKind].getName() << ")"; + Out << " (" << CheckNames[*CheckKind] << ")"; Out << NL; } } diff --git a/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp index 3da571adfa44c..1e114149debf5 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp @@ -60,7 +60,7 @@ class ReturnValueChecker : public Checker { }; } // namespace -static std::string getName(const CallEvent &Call) { +static std::string getFunctionName(const CallEvent &Call) { std::string Name; if (const auto *MD = dyn_cast(Call.getDecl())) if (const CXXRecordDecl *RD = MD->getParent()) @@ -84,7 +84,7 @@ void ReturnValueChecker::checkPostCall(const CallEvent &Call, if (ProgramStateRef StTrue = State->assume(*ReturnV, true)) { // The return value can be true, so transition to a state where it's true. std::string Msg = - formatv("'{0}' returns true (by convention)", getName(Call)); + formatv("'{0}' returns true (by convention)", getFunctionName(Call)); C.addTransition(StTrue, C.getNoteTag(Msg, /*IsPrunable=*/true)); return; } @@ -94,7 +94,7 @@ void ReturnValueChecker::checkPostCall(const CallEvent &Call, // Note that this checker is 'hidden' so it cannot produce a bug report. std::string Msg = formatv("'{0}' returned false, breaking the convention " "that it always returns true", - getName(Call)); + getFunctionName(Call)); C.addTransition(State, C.getNoteTag(Msg, /*IsPrunable=*/true)); } diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp index 356d63e3e8b80..fef19b4547555 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp @@ -1797,7 +1797,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( auto IsNull = [&](ArgNo ArgN) { return std::make_shared(ArgN, false); }; - auto NotNullBuffer = [&](ArgNo ArgN, ArgNo SizeArg1N, ArgNo SizeArg2N) { + auto NotNullBuffer = [&](ArgNo ArgN, ArgNo SizeArg1N, + std::optional SizeArg2N = std::nullopt) { return std::make_shared(ArgN, SizeArg1N, SizeArg2N); }; @@ -3365,7 +3366,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Summary(NoEvalCall) .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) - .ArgConstraint(NotNull(ArgNo(3))) + .ArgConstraint(NotNullBuffer(ArgNo(3), ArgNo(4))) .ArgConstraint( BufferSize(/*Buffer=*/ArgNo(3), /*BufSize=*/ArgNo(4))) .ArgConstraint( diff --git a/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp index 28320f46f237a..bd2f88c7b1bcc 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp @@ -272,12 +272,12 @@ void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists, if (!BT_leakedvalist) { // FIXME: maybe creating a new check name for this type of bug is a better // solution. - BT_leakedvalist.reset( - new BugType(CheckNames[CK_Unterminated].getName().empty() - ? CheckNames[CK_Uninitialized] - : CheckNames[CK_Unterminated], - "Leaked va_list", categories::MemoryError, - /*SuppressOnSink=*/true)); + BT_leakedvalist.reset(new BugType( + static_cast(CheckNames[CK_Unterminated]).empty() + ? CheckNames[CK_Uninitialized] + : CheckNames[CK_Unterminated], + "Leaked va_list", categories::MemoryError, + /*SuppressOnSink=*/true)); } const ExplodedNode *StartNode = getStartCallSite(N, Reg); diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index 58020ec4e084d..5e67cb29d08e4 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -43,6 +43,10 @@ bool tryToFindPtrOrigin( break; } } + if (auto *TempExpr = dyn_cast(E)) { + if (isSafePtrType(TempExpr->getTypeAsWritten())) + return callback(TempExpr, true); + } if (auto *POE = dyn_cast(E)) { if (auto *RF = POE->getResultExpr()) { E = RF; @@ -87,9 +91,17 @@ bool tryToFindPtrOrigin( } if (auto *operatorCall = dyn_cast(E)) { - if (operatorCall->getNumArgs() == 1) { - E = operatorCall->getArg(0); - continue; + if (auto *Callee = operatorCall->getDirectCallee()) { + auto ClsName = safeGetName(Callee->getParent()); + if (isRefType(ClsName) || isCheckedPtr(ClsName) || + isRetainPtr(ClsName) || ClsName == "unique_ptr" || + ClsName == "UniqueRef" || ClsName == "WeakPtr" || + ClsName == "WeakRef") { + if (operatorCall->getNumArgs() == 1) { + E = operatorCall->getArg(0); + continue; + } + } } } @@ -211,7 +223,7 @@ bool EnsureFunctionAnalysis::isACallToEnsureFn(const clang::Expr *E) const { if (!Callee) return false; auto *Body = Callee->getBody(); - if (!Body) + if (!Body || Callee->isVirtualAsWritten()) return false; auto [CacheIt, IsNew] = Cache.insert(std::make_pair(Callee, false)); if (IsNew) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index 7899b19854806..419d9c2325412 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -162,13 +162,14 @@ static bool isPtrOfType(const clang::QualType T, Predicate Pred) { type = elaboratedT->desugar(); continue; } - auto *SpecialT = type->getAs(); - if (!SpecialT) - return false; - auto *Decl = SpecialT->getTemplateName().getAsTemplateDecl(); - if (!Decl) - return false; - return Pred(Decl->getNameAsString()); + if (auto *SpecialT = type->getAs()) { + auto *Decl = SpecialT->getTemplateName().getAsTemplateDecl(); + return Decl && Pred(Decl->getNameAsString()); + } else if (auto *DTS = type->getAs()) { + auto *Decl = DTS->getTemplateName().getAsTemplateDecl(); + return Decl && Pred(Decl->getNameAsString()); + } else + break; } return false; } @@ -479,6 +480,10 @@ class TrivialFunctionAnalysisVisitor TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {} bool IsFunctionTrivial(const Decl *D) { + if (auto *FnDecl = dyn_cast(D)) { + if (FnDecl->isVirtualAsWritten()) + return false; + } return WithCachedResult(D, [&]() { if (auto *CtorDecl = dyn_cast(D)) { for (auto *CtorInit : CtorDecl->inits()) { diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h index fcc1a41dba78b..323d473665888 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h @@ -131,6 +131,9 @@ bool isRefType(const std::string &Name); /// \returns true if \p Name is CheckedRef or CheckedPtr, false if not. bool isCheckedPtr(const std::string &Name); +/// \returns true if \p Name is RetainPtr or its variant, false if not. +bool isRetainPtr(const std::string &Name); + /// \returns true if \p M is getter of a ref-counted class, false if not. std::optional isGetterOfSafePtr(const clang::CXXMethodDecl *Method); diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp index d633fbcbd798b..9d07c65da88af 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp @@ -172,7 +172,7 @@ class RawPtrRefCallArgsChecker if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc())) return true; - if (Callee && TFA.isTrivial(Callee)) + if (Callee && TFA.isTrivial(Callee) && !Callee->isVirtualAsWritten()) return true; if (CE->getNumArgs() == 0) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp similarity index 76% rename from clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp rename to clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp index 506442f352288..4a297a6c0ab91 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp @@ -21,15 +21,23 @@ using namespace clang; using namespace ento; namespace { -class UncountedLambdaCapturesChecker +class RawPtrRefLambdaCapturesChecker : public Checker> { private: - BugType Bug{this, "Lambda capture of uncounted variable", - "WebKit coding guidelines"}; + BugType Bug; mutable BugReporter *BR = nullptr; TrivialFunctionAnalysis TFA; +protected: + mutable std::optional RTC; + public: + RawPtrRefLambdaCapturesChecker(const char *description) + : Bug(this, description, "WebKit coding guidelines") {} + + virtual std::optional isUnsafePtr(QualType) const = 0; + virtual const char *ptrKind(QualType QT) const = 0; + void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, BugReporter &BRArg) const { BR = &BRArg; @@ -38,7 +46,7 @@ class UncountedLambdaCapturesChecker // visit template instantiations or lambda classes. We // want to visit those, so we make our own RecursiveASTVisitor. struct LocalVisitor : DynamicRecursiveASTVisitor { - const UncountedLambdaCapturesChecker *Checker; + const RawPtrRefLambdaCapturesChecker *Checker; llvm::DenseSet DeclRefExprsToIgnore; llvm::DenseSet LambdasToIgnore; llvm::DenseSet ProtectedThisDecls; @@ -46,7 +54,7 @@ class UncountedLambdaCapturesChecker QualType ClsType; - explicit LocalVisitor(const UncountedLambdaCapturesChecker *Checker) + explicit LocalVisitor(const RawPtrRefLambdaCapturesChecker *Checker) : Checker(Checker) { assert(Checker); ShouldVisitTemplateInstantiations = true; @@ -60,16 +68,23 @@ class UncountedLambdaCapturesChecker return DynamicRecursiveASTVisitor::TraverseCXXMethodDecl(CXXMD); } + bool VisitTypedefDecl(TypedefDecl *TD) override { + if (Checker->RTC) + Checker->RTC->visitTypedef(TD); + return true; + } + bool shouldCheckThis() { auto result = - !ClsType.isNull() ? isUnsafePtr(ClsType, false) : std::nullopt; + !ClsType.isNull() ? Checker->isUnsafePtr(ClsType) : std::nullopt; return result && *result; } bool VisitLambdaExpr(LambdaExpr *L) override { if (LambdasToIgnore.contains(L)) return true; - Checker->visitLambdaExpr(L, shouldCheckThis() && !hasProtectedThis(L)); + Checker->visitLambdaExpr(L, shouldCheckThis() && !hasProtectedThis(L), + ClsType); return true; } @@ -97,7 +112,8 @@ class UncountedLambdaCapturesChecker if (!L) return true; LambdasToIgnore.insert(L); - Checker->visitLambdaExpr(L, shouldCheckThis() && !hasProtectedThis(L)); + Checker->visitLambdaExpr(L, shouldCheckThis() && !hasProtectedThis(L), + ClsType); return true; } @@ -122,8 +138,8 @@ class UncountedLambdaCapturesChecker if (auto *L = findLambdaInArg(Arg)) { LambdasToIgnore.insert(L); if (!Param->hasAttr()) - Checker->visitLambdaExpr(L, shouldCheckThis() && - !hasProtectedThis(L)); + Checker->visitLambdaExpr( + L, shouldCheckThis() && !hasProtectedThis(L), ClsType); } ++ArgIndex; } @@ -143,8 +159,8 @@ class UncountedLambdaCapturesChecker if (auto *L = findLambdaInArg(Arg)) { LambdasToIgnore.insert(L); if (!Param->hasAttr() && !TreatAllArgsAsNoEscape) - Checker->visitLambdaExpr(L, shouldCheckThis() && - !hasProtectedThis(L)); + Checker->visitLambdaExpr( + L, shouldCheckThis() && !hasProtectedThis(L), ClsType); } ++ArgIndex; } @@ -169,14 +185,22 @@ class UncountedLambdaCapturesChecker auto *CtorArg = CE->getArg(0)->IgnoreParenCasts(); if (!CtorArg) return nullptr; - if (auto *Lambda = dyn_cast(CtorArg)) { + auto *InnerCE = dyn_cast_or_null(CtorArg); + if (InnerCE && InnerCE->getNumArgs()) + CtorArg = InnerCE->getArg(0)->IgnoreParenCasts(); + auto updateIgnoreList = [&] { ConstructToIgnore.insert(CE); + if (InnerCE) + ConstructToIgnore.insert(InnerCE); + }; + if (auto *Lambda = dyn_cast(CtorArg)) { + updateIgnoreList(); return Lambda; } if (auto *TempExpr = dyn_cast(CtorArg)) { E = TempExpr->getSubExpr()->IgnoreParenCasts(); if (auto *Lambda = dyn_cast(E)) { - ConstructToIgnore.insert(CE); + updateIgnoreList(); return Lambda; } } @@ -189,10 +213,14 @@ class UncountedLambdaCapturesChecker auto *Init = VD->getInit(); if (!Init) return nullptr; + if (auto *Lambda = dyn_cast(Init)) { + updateIgnoreList(); + return Lambda; + } TempExpr = dyn_cast(Init->IgnoreParenCasts()); if (!TempExpr) return nullptr; - ConstructToIgnore.insert(CE); + updateIgnoreList(); return dyn_cast_or_null(TempExpr->getSubExpr()); } @@ -226,7 +254,7 @@ class UncountedLambdaCapturesChecker DeclRefExprsToIgnore.insert(ArgRef); LambdasToIgnore.insert(L); Checker->visitLambdaExpr(L, shouldCheckThis() && !hasProtectedThis(L), - /* ignoreParamVarDecl */ true); + ClsType, /* ignoreParamVarDecl */ true); } bool hasProtectedThis(LambdaExpr *L) { @@ -293,10 +321,12 @@ class UncountedLambdaCapturesChecker }; LocalVisitor visitor(this); + if (RTC) + RTC->visitTranslationUnitDecl(TUD); visitor.TraverseDecl(const_cast(TUD)); } - void visitLambdaExpr(LambdaExpr *L, bool shouldCheckThis, + void visitLambdaExpr(LambdaExpr *L, bool shouldCheckThis, const QualType T, bool ignoreParamVarDecl = false) const { if (TFA.isTrivial(L->getBody())) return; @@ -306,13 +336,13 @@ class UncountedLambdaCapturesChecker if (ignoreParamVarDecl && isa(CapturedVar)) continue; QualType CapturedVarQualType = CapturedVar->getType(); - auto IsUncountedPtr = isUnsafePtr(CapturedVar->getType(), false); + auto IsUncountedPtr = isUnsafePtr(CapturedVar->getType()); if (IsUncountedPtr && *IsUncountedPtr) reportBug(C, CapturedVar, CapturedVarQualType); } else if (C.capturesThis() && shouldCheckThis) { if (ignoreParamVarDecl) // this is always a parameter to this function. continue; - reportBugOnThisPtr(C); + reportBugOnThisPtr(C, T); } } } @@ -321,6 +351,9 @@ class UncountedLambdaCapturesChecker const QualType T) const { assert(CapturedVar); + if (isa(CapturedVar) && !Capture.getLocation().isValid()) + return; // Ignore implicit captruing of self. + SmallString<100> Buf; llvm::raw_svector_ostream Os(Buf); @@ -329,22 +362,22 @@ class UncountedLambdaCapturesChecker } else { Os << "Implicitly captured "; } - if (T->isPointerType()) { + if (isa(T) || isa(T)) { Os << "raw-pointer "; } else { - assert(T->isReferenceType()); Os << "reference "; } - printQuotedQualifiedName(Os, Capture.getCapturedVar()); - Os << " to ref-counted type or CheckedPtr-capable type is unsafe."; + printQuotedQualifiedName(Os, CapturedVar); + Os << " to " << ptrKind(T) << " type is unsafe."; PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager()); auto Report = std::make_unique(Bug, Os.str(), BSLoc); BR->emitReport(std::move(Report)); } - void reportBugOnThisPtr(const LambdaCapture &Capture) const { + void reportBugOnThisPtr(const LambdaCapture &Capture, + const QualType T) const { SmallString<100> Buf; llvm::raw_svector_ostream Os(Buf); @@ -354,14 +387,54 @@ class UncountedLambdaCapturesChecker Os << "Implicitly captured "; } - Os << "raw-pointer 'this' to ref-counted type or CheckedPtr-capable type " - "is unsafe."; + Os << "raw-pointer 'this' to " << ptrKind(T) << " type is unsafe."; PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager()); auto Report = std::make_unique(Bug, Os.str(), BSLoc); BR->emitReport(std::move(Report)); } }; + +class UncountedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker { +public: + UncountedLambdaCapturesChecker() + : RawPtrRefLambdaCapturesChecker("Lambda capture of uncounted or " + "unchecked variable") {} + + std::optional isUnsafePtr(QualType QT) const final { + auto result1 = isUncountedPtr(QT); + auto result2 = isUncheckedPtr(QT); + if (result1 && *result1) + return true; + if (result2 && *result2) + return true; + if (result1) + return *result1; + return result2; + } + + const char *ptrKind(QualType QT) const final { + if (isUncounted(QT)) + return "uncounted"; + return "unchecked"; + } +}; + +class UnretainedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker { +public: + UnretainedLambdaCapturesChecker() + : RawPtrRefLambdaCapturesChecker("Lambda capture of unretained " + "variables") { + RTC = RetainTypeChecker(); + } + + std::optional isUnsafePtr(QualType QT) const final { + return RTC->isUnretained(QT); + } + + const char *ptrKind(QualType QT) const final { return "unretained"; } +}; + } // namespace void ento::registerUncountedLambdaCapturesChecker(CheckerManager &Mgr) { @@ -372,3 +445,12 @@ bool ento::shouldRegisterUncountedLambdaCapturesChecker( const CheckerManager &mgr) { return true; } + +void ento::registerUnretainedLambdaCapturesChecker(CheckerManager &Mgr) { + Mgr.registerChecker(); +} + +bool ento::shouldRegisterUnretainedLambdaCapturesChecker( + const CheckerManager &mgr) { + return true; +} diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp index 963f59831c8ed..593e6e3663639 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp @@ -31,12 +31,16 @@ class RawPtrRefMemberChecker BugType Bug; mutable BugReporter *BR; +protected: + mutable std::optional RTC; + public: RawPtrRefMemberChecker(const char *description) : Bug(this, description, "WebKit coding guidelines") {} virtual std::optional - isPtrCompatible(const clang::CXXRecordDecl *) const = 0; + isPtrCompatible(const clang::QualType, + const clang::CXXRecordDecl *R) const = 0; virtual bool isPtrCls(const clang::CXXRecordDecl *) const = 0; virtual const char *typeName() const = 0; virtual const char *invariant() const = 0; @@ -57,6 +61,12 @@ class RawPtrRefMemberChecker ShouldVisitImplicitCode = false; } + bool VisitTypedefDecl(const TypedefDecl *TD) override { + if (Checker->RTC) + Checker->RTC->visitTypedef(TD); + return true; + } + bool VisitRecordDecl(const RecordDecl *RD) override { Checker->visitRecordDecl(RD); return true; @@ -69,6 +79,8 @@ class RawPtrRefMemberChecker }; LocalVisitor visitor(this); + if (RTC) + RTC->visitTranslationUnitDecl(TUD); visitor.TraverseDecl(TUD); } @@ -77,16 +89,22 @@ class RawPtrRefMemberChecker return; for (auto *Member : RD->fields()) { - const Type *MemberType = Member->getType().getTypePtrOrNull(); + auto QT = Member->getType(); + const Type *MemberType = QT.getTypePtrOrNull(); if (!MemberType) continue; if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) { - // If we don't see the definition we just don't know. - if (MemberCXXRD->hasDefinition()) { - std::optional isRCAble = isPtrCompatible(MemberCXXRD); - if (isRCAble && *isRCAble) - reportBug(Member, MemberType, MemberCXXRD, RD); + std::optional IsCompatible = isPtrCompatible(QT, MemberCXXRD); + if (IsCompatible && *IsCompatible) + reportBug(Member, MemberType, MemberCXXRD, RD); + } else { + std::optional IsCompatible = isPtrCompatible(QT, nullptr); + auto *PointeeType = MemberType->getPointeeType().getTypePtrOrNull(); + if (IsCompatible && *IsCompatible) { + auto *Desugared = PointeeType->getUnqualifiedDesugaredType(); + if (auto *ObjCType = dyn_cast_or_null(Desugared)) + reportBug(Member, MemberType, ObjCType->getDecl(), RD); } } } @@ -107,11 +125,12 @@ class RawPtrRefMemberChecker void visitIvarDecl(const ObjCContainerDecl *CD, const ObjCIvarDecl *Ivar) const { - const Type *IvarType = Ivar->getType().getTypePtrOrNull(); + auto QT = Ivar->getType(); + const Type *IvarType = QT.getTypePtrOrNull(); if (!IvarType) return; if (auto *IvarCXXRD = IvarType->getPointeeCXXRecordDecl()) { - std::optional IsCompatible = isPtrCompatible(IvarCXXRD); + std::optional IsCompatible = isPtrCompatible(QT, IvarCXXRD); if (IsCompatible && *IsCompatible) reportBug(Ivar, IvarType, IvarCXXRD, CD); } @@ -151,13 +170,13 @@ class RawPtrRefMemberChecker return false; } - template + template void reportBug(const DeclType *Member, const Type *MemberType, - const CXXRecordDecl *MemberCXXRD, + const PointeeType *Pointee, const ParentDeclType *ClassCXXRD) const { assert(Member); assert(MemberType); - assert(MemberCXXRD); + assert(Pointee); SmallString<100> Buf; llvm::raw_svector_ostream Os(Buf); @@ -169,10 +188,13 @@ class RawPtrRefMemberChecker printQuotedName(Os, Member); Os << " in "; printQuotedQualifiedName(Os, ClassCXXRD); - Os << " is a " - << (isa(MemberType) ? "raw pointer" : "reference") << " to " - << typeName() << " "; - printQuotedQualifiedName(Os, MemberCXXRD); + Os << " is a "; + if (printPointer(Os, MemberType) == PrintDeclKind::Pointer) { + auto Typedef = MemberType->getAs(); + assert(Typedef); + printQuotedQualifiedName(Os, Typedef->getDecl()); + } else + printQuotedQualifiedName(Os, Pointee); Os << "; " << invariant() << "."; PathDiagnosticLocation BSLoc(Member->getSourceRange().getBegin(), @@ -181,6 +203,15 @@ class RawPtrRefMemberChecker Report->addRange(Member->getSourceRange()); BR->emitReport(std::move(Report)); } + + enum class PrintDeclKind { Pointee, Pointer }; + virtual PrintDeclKind printPointer(llvm::raw_svector_ostream &Os, + const Type *T) const { + T = T->getUnqualifiedDesugaredType(); + bool IsPtr = isa(T) || isa(T); + Os << (IsPtr ? "raw pointer" : "reference") << " to " << typeName() << " "; + return PrintDeclKind::Pointee; + } }; class NoUncountedMemberChecker final : public RawPtrRefMemberChecker { @@ -190,8 +221,9 @@ class NoUncountedMemberChecker final : public RawPtrRefMemberChecker { "reference-countable type") {} std::optional - isPtrCompatible(const clang::CXXRecordDecl *R) const final { - return isRefCountable(R); + isPtrCompatible(const clang::QualType, + const clang::CXXRecordDecl *R) const final { + return R && isRefCountable(R); } bool isPtrCls(const clang::CXXRecordDecl *R) const final { @@ -212,8 +244,9 @@ class NoUncheckedPtrMemberChecker final : public RawPtrRefMemberChecker { "checked-pointer capable type") {} std::optional - isPtrCompatible(const clang::CXXRecordDecl *R) const final { - return isCheckedPtrCapable(R); + isPtrCompatible(const clang::QualType, + const clang::CXXRecordDecl *R) const final { + return R && isCheckedPtrCapable(R); } bool isPtrCls(const clang::CXXRecordDecl *R) const final { @@ -228,6 +261,40 @@ class NoUncheckedPtrMemberChecker final : public RawPtrRefMemberChecker { } }; +class NoUnretainedMemberChecker final : public RawPtrRefMemberChecker { +public: + NoUnretainedMemberChecker() + : RawPtrRefMemberChecker("Member variable is a raw-pointer/reference to " + "retainable type") { + RTC = RetainTypeChecker(); + } + + std::optional + isPtrCompatible(const clang::QualType QT, + const clang::CXXRecordDecl *) const final { + return RTC->isUnretained(QT); + } + + bool isPtrCls(const clang::CXXRecordDecl *R) const final { + return isRetainPtr(R); + } + + const char *typeName() const final { return "retainable type"; } + + const char *invariant() const final { + return "member variables must be a RetainPtr"; + } + + PrintDeclKind printPointer(llvm::raw_svector_ostream &Os, + const Type *T) const final { + if (!isa(T) && T->getAs()) { + Os << typeName() << " "; + return PrintDeclKind::Pointer; + } + return RawPtrRefMemberChecker::printPointer(Os, T); + } +}; + } // namespace void ento::registerNoUncountedMemberChecker(CheckerManager &Mgr) { @@ -246,3 +313,11 @@ bool ento::shouldRegisterNoUncheckedPtrMemberChecker( const CheckerManager &Mgr) { return true; } + +void ento::registerNoUnretainedMemberChecker(CheckerManager &Mgr) { + Mgr.registerChecker(); +} + +bool ento::shouldRegisterNoUnretainedMemberChecker(const CheckerManager &Mgr) { + return true; +} diff --git a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp index 7101b1eb2c7f5..a4f9e092e8205 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -3418,8 +3418,8 @@ void BugReporter::EmitBasicReport(const Decl *DeclWithIssue, PathDiagnosticLocation Loc, ArrayRef Ranges, ArrayRef Fixits) { - EmitBasicReport(DeclWithIssue, Checker->getCheckerName(), Name, Category, Str, - Loc, Ranges, Fixits); + EmitBasicReport(DeclWithIssue, Checker->getName(), Name, Category, Str, Loc, + Ranges, Fixits); } void BugReporter::EmitBasicReport(const Decl *DeclWithIssue, @@ -3442,8 +3442,8 @@ void BugReporter::EmitBasicReport(const Decl *DeclWithIssue, BugType *BugReporter::getBugTypeForName(CheckerNameRef CheckName, StringRef name, StringRef category) { SmallString<136> fullDesc; - llvm::raw_svector_ostream(fullDesc) << CheckName.getName() << ":" << name - << ":" << category; + llvm::raw_svector_ostream(fullDesc) + << CheckName << ":" << name << ":" << category; std::unique_ptr &BT = StrBugTypes[fullDesc]; if (!BT) BT = std::make_unique(CheckName, name, category); diff --git a/clang/lib/StaticAnalyzer/Core/Checker.cpp b/clang/lib/StaticAnalyzer/Core/Checker.cpp index bc1c8964b3ee4..d93db6ddfc3bf 100644 --- a/clang/lib/StaticAnalyzer/Core/Checker.cpp +++ b/clang/lib/StaticAnalyzer/Core/Checker.cpp @@ -18,11 +18,9 @@ using namespace ento; int ImplicitNullDerefEvent::Tag; -StringRef CheckerBase::getTagDescription() const { - return getCheckerName().getName(); -} +StringRef CheckerBase::getTagDescription() const { return getName(); } -CheckerNameRef CheckerBase::getCheckerName() const { return Name; } +CheckerNameRef CheckerBase::getName() const { return Name; } CheckerProgramPointTag::CheckerProgramPointTag(StringRef CheckerName, StringRef Msg) @@ -30,10 +28,10 @@ CheckerProgramPointTag::CheckerProgramPointTag(StringRef CheckerName, CheckerProgramPointTag::CheckerProgramPointTag(const CheckerBase *Checker, StringRef Msg) - : SimpleProgramPointTag(Checker->getCheckerName().getName(), Msg) {} + : SimpleProgramPointTag(Checker->getName(), Msg) {} raw_ostream& clang::ento::operator<<(raw_ostream &Out, const CheckerBase &Checker) { - Out << Checker.getCheckerName().getName(); + Out << Checker.getName(); return Out; } diff --git a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp index 53929d370e2fe..56b1e44acc1fd 100644 --- a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -139,7 +139,7 @@ std::string checkerScopeName(StringRef Name, const CheckerBase *Checker) { if (!llvm::timeTraceProfilerEnabled()) return ""; StringRef CheckerName = - Checker ? Checker->getCheckerName().getName() : ""; + Checker ? static_cast(Checker->getName()) : ""; return (Name + ":" + CheckerName).str(); } @@ -721,13 +721,13 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, "The '{0}' call has been already evaluated by the {1} checker, " "while the {2} checker also tried to evaluate the same call. At " "most one checker supposed to evaluate a call.", - toString(Call), evaluatorChecker->getName(), - EvalCallChecker.Checker->getCheckerName()); + toString(Call), evaluatorChecker, + EvalCallChecker.Checker->getName()); llvm_unreachable(AssertionMessage.c_str()); } #endif if (evaluated) { - evaluatorChecker = EvalCallChecker.Checker->getCheckerName(); + evaluatorChecker = EvalCallChecker.Checker->getName(); Dst.insert(checkDst); #ifdef NDEBUG break; // on release don't check that no other checker also evals. @@ -798,9 +798,8 @@ void CheckerManager::runCheckersForPrintStateJson(raw_ostream &Out, if (TempBuf.empty()) continue; - Indent(Out, Space, IsDot) - << "{ \"checker\": \"" << CT.second->getCheckerName().getName() - << "\", \"messages\": [" << NL; + Indent(Out, Space, IsDot) << "{ \"checker\": \"" << CT.second->getName() + << "\", \"messages\": [" << NL; Indent(Out, InnerSpace, IsDot) << '\"' << TempBuf.str().trim() << '\"' << NL; Indent(Out, Space, IsDot) << "]}"; diff --git a/clang/test/AST/ByteCode/builtin-constant-p.cpp b/clang/test/AST/ByteCode/builtin-constant-p.cpp index 62899b60064c2..ed9e606ed16aa 100644 --- a/clang/test/AST/ByteCode/builtin-constant-p.cpp +++ b/clang/test/AST/ByteCode/builtin-constant-p.cpp @@ -1,6 +1,7 @@ -// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify=expected,both %s -// RUN: %clang_cc1 -verify=ref,both %s +// RUN: %clang_cc1 -std=c++20 -fexperimental-new-constant-interpreter -verify=expected,both %s +// RUN: %clang_cc1 -std=c++20 -verify=ref,both %s +using intptr_t = __INTPTR_TYPE__; static_assert(__builtin_constant_p(12), ""); static_assert(__builtin_constant_p(1.0), ""); @@ -18,3 +19,105 @@ constexpr int foo(int &a) { return __builtin_constant_p(a); } static_assert(!foo(z)); + +static_assert(__builtin_constant_p(__builtin_constant_p(1))); + +constexpr bool nested(int& a) { + return __builtin_constant_p(__builtin_constant_p(a)); +} +static_assert(nested(z)); + +constexpr bool Local() { + int z = 10; + return __builtin_constant_p(z); +} +static_assert(Local()); + +constexpr bool Local2() { + int z = 10; + return __builtin_constant_p(&z); +} +static_assert(!Local2()); + +constexpr bool Parameter(int a) { + return __builtin_constant_p(a); +} +static_assert(Parameter(10)); + +constexpr bool InvalidLocal() { + int *z; + { + int b = 10; + z = &b; + } + return __builtin_constant_p(z); +} +static_assert(!InvalidLocal()); + +template constexpr bool bcp(T t) { + return __builtin_constant_p(t); +} + +constexpr intptr_t ptr_to_int(const void *p) { + return __builtin_constant_p(1) ? (intptr_t)p : (intptr_t)p; +} + +static_assert(bcp(ptr_to_int("foo"))); + +constexpr bool AndFold(const int &a, const int &b) { + return __builtin_constant_p(a && b); +} + +static_assert(AndFold(10, 20)); +static_assert(!AndFold(z, 10)); +static_assert(!AndFold(10, z)); + + +struct F { + int a; +}; + +constexpr F f{12}; +static_assert(__builtin_constant_p(f.a)); + +constexpr bool Member() { + F f; + return __builtin_constant_p(f.a); +} +static_assert(!Member()); + +constexpr bool Discard() { + (void)__builtin_constant_p(10); + return true; +} +static_assert(Discard()); + +static_assert(__builtin_constant_p((int*)123)); + +constexpr void func() {} +static_assert(!__builtin_constant_p(func)); + +/// This is from SemaCXX/builtin-constant-p and GCC agrees with the bytecode interpreter. +constexpr int mutate1() { + int n = 1; + int m = __builtin_constant_p(++n); + return n * 10 + m; +} +static_assert(mutate1() == 21); // ref-error {{static assertion failed}} \ + // ref-note {{evaluates to '10 == 21'}} + +/// Similar for this. GCC agrees with the bytecode interpreter. +constexpr int mutate_param(bool mutate, int ¶m) { + mutate = mutate; // Mutation of internal state is OK + if (mutate) + ++param; + return param; +} +constexpr int mutate6(bool mutate) { + int n = 1; + int m = __builtin_constant_p(mutate_param(mutate, n)); + return n * 10 + m; +} +static_assert(mutate6(false) == 11); +static_assert(mutate6(true) == 21); // ref-error {{static assertion failed}} \ + // ref-note {{evaluates to '10 == 21'}} diff --git a/clang/test/AST/ByteCode/builtin-functions.cpp b/clang/test/AST/ByteCode/builtin-functions.cpp index 75380f99901a2..29812553af9f0 100644 --- a/clang/test/AST/ByteCode/builtin-functions.cpp +++ b/clang/test/AST/ByteCode/builtin-functions.cpp @@ -18,6 +18,8 @@ extern "C" { typedef decltype(sizeof(int)) size_t; extern size_t wcslen(const wchar_t *p); + extern void *memchr(const void *s, int c, size_t n); + extern char *strchr(const char *s, int c); } namespace strcmp { @@ -1350,4 +1352,135 @@ namespace Memcmp { static_assert(__builtin_wmemcmp(L"abab\0banana", L"abab\0canada", 7) == -1); static_assert(__builtin_wmemcmp(L"abab\0banana", L"abab\0canada", 6) == -1); static_assert(__builtin_wmemcmp(L"abab\0banana", L"abab\0canada", 5) == 0); + +#if __cplusplus >= 202002L + constexpr bool f() { + char *c = new char[12]; + c[0] = 'b'; + + char n = 'a'; + bool b = __builtin_memcmp(c, &n, 1) == 0; + + delete[] c; + return !b; + } + static_assert(f()); +#endif + +} + +namespace Memchr { + constexpr const char *kStr = "abca\xff\0d"; + constexpr char kFoo[] = {'f', 'o', 'o'}; + + static_assert(__builtin_memchr(kStr, 'a', 0) == nullptr); + static_assert(__builtin_memchr(kStr, 'a', 1) == kStr); + static_assert(__builtin_memchr(kStr, '\0', 5) == nullptr); + static_assert(__builtin_memchr(kStr, '\0', 6) == kStr + 5); + static_assert(__builtin_memchr(kStr, '\xff', 8) == kStr + 4); + static_assert(__builtin_memchr(kStr, '\xff' + 256, 8) == kStr + 4); + static_assert(__builtin_memchr(kStr, '\xff' - 256, 8) == kStr + 4); + static_assert(__builtin_memchr(kFoo, 'x', 3) == nullptr); + static_assert(__builtin_memchr(kFoo, 'x', 4) == nullptr); // both-error {{not an integral constant}} \ + // both-note {{dereferenced one-past-the-end}} + static_assert(__builtin_memchr(nullptr, 'x', 3) == nullptr); // both-error {{not an integral constant}} \ + // both-note {{dereferenced null}} + static_assert(__builtin_memchr(nullptr, 'x', 0) == nullptr); + + +#if defined(CHAR8_T) + constexpr const char8_t *kU8Str = u8"abca\xff\0d"; + constexpr char8_t kU8Foo[] = {u8'f', u8'o', u8'o'}; + static_assert(__builtin_memchr(kU8Str, u8'a', 0) == nullptr); + static_assert(__builtin_memchr(kU8Str, u8'a', 1) == kU8Str); + static_assert(__builtin_memchr(kU8Str, u8'\0', 5) == nullptr); + static_assert(__builtin_memchr(kU8Str, u8'\0', 6) == kU8Str + 5); + static_assert(__builtin_memchr(kU8Str, u8'\xff', 8) == kU8Str + 4); + static_assert(__builtin_memchr(kU8Str, u8'\xff' + 256, 8) == kU8Str + 4); + static_assert(__builtin_memchr(kU8Str, u8'\xff' - 256, 8) == kU8Str + 4); + static_assert(__builtin_memchr(kU8Foo, u8'x', 3) == nullptr); + static_assert(__builtin_memchr(kU8Foo, u8'x', 4) == nullptr); // both-error {{not an integral constant}} \ + // both-note {{dereferenced one-past-the-end}} + static_assert(__builtin_memchr(nullptr, u8'x', 3) == nullptr); // both-error {{not an integral constant}} \ + // both-note {{dereferenced null}} + static_assert(__builtin_memchr(nullptr, u8'x', 0) == nullptr); +#endif + + extern struct Incomplete incomplete; + static_assert(__builtin_memchr(&incomplete, 0, 0u) == nullptr); + static_assert(__builtin_memchr(&incomplete, 0, 1u) == nullptr); // both-error {{not an integral constant}} \ + // ref-note {{read of incomplete type 'struct Incomplete'}} + + const unsigned char &u1 = 0xf0; + auto &&i1 = (const signed char []){-128}; + static_assert(__builtin_memchr(&u1, -(0x0f + 1), 1) == &u1); + static_assert(__builtin_memchr(i1, 0x80, 1) == i1); + + enum class E : unsigned char {}; + struct EPair { E e, f; }; + constexpr EPair ee{E{240}}; + static_assert(__builtin_memchr(&ee.e, 240, 1) == &ee.e); // both-error {{constant}} \ + // both-note {{not supported}} + + constexpr bool kBool[] = {false, true, false}; + constexpr const bool *const kBoolPastTheEndPtr = kBool + 3; + static_assert(sizeof(bool) != 1u || __builtin_memchr(kBoolPastTheEndPtr - 3, 1, 99) == kBool + 1); // both-error {{constant}} \ + // both-note {{not supported}} + static_assert(sizeof(bool) != 1u || __builtin_memchr(kBool + 1, 0, 99) == kBoolPastTheEndPtr - 1); // both-error {{constant}} \ + // both-note {{not supported}} + static_assert(sizeof(bool) != 1u || __builtin_memchr(kBoolPastTheEndPtr - 3, -1, 3) == nullptr); // both-error {{constant}} \ + // both-note {{not supported}} + static_assert(sizeof(bool) != 1u || __builtin_memchr(kBoolPastTheEndPtr, 0, 1) == nullptr); // both-error {{constant}} \ + // both-note {{not supported}} + + static_assert(__builtin_char_memchr(kStr, 'a', 0) == nullptr); + static_assert(__builtin_char_memchr(kStr, 'a', 1) == kStr); + static_assert(__builtin_char_memchr(kStr, '\0', 5) == nullptr); + static_assert(__builtin_char_memchr(kStr, '\0', 6) == kStr + 5); + static_assert(__builtin_char_memchr(kStr, '\xff', 8) == kStr + 4); + static_assert(__builtin_char_memchr(kStr, '\xff' + 256, 8) == kStr + 4); + static_assert(__builtin_char_memchr(kStr, '\xff' - 256, 8) == kStr + 4); + static_assert(__builtin_char_memchr(kFoo, 'x', 3) == nullptr); + static_assert(__builtin_char_memchr(kFoo, 'x', 4) == nullptr); // both-error {{not an integral constant}} \ + // both-note {{dereferenced one-past-the-end}} + static_assert(__builtin_char_memchr(nullptr, 'x', 3) == nullptr); // both-error {{not an integral constant}} \ + // both-note {{dereferenced null}} + static_assert(__builtin_char_memchr(nullptr, 'x', 0) == nullptr); + + static_assert(*__builtin_char_memchr(kStr, '\xff', 8) == '\xff'); + constexpr bool char_memchr_mutable() { + char buffer[] = "mutable"; + *__builtin_char_memchr(buffer, 't', 8) = 'r'; + *__builtin_char_memchr(buffer, 'm', 8) = 'd'; + return __builtin_strcmp(buffer, "durable") == 0; + } + static_assert(char_memchr_mutable()); + + constexpr bool b = !memchr("hello", 'h', 3); // both-error {{constant expression}} \ + // both-note {{non-constexpr function 'memchr' cannot be used in a constant expression}} + +} + +namespace Strchr { + constexpr const char *kStr = "abca\xff\0d"; + constexpr char kFoo[] = {'f', 'o', 'o'}; + static_assert(__builtin_strchr(kStr, 'a') == kStr); + static_assert(__builtin_strchr(kStr, 'b') == kStr + 1); + static_assert(__builtin_strchr(kStr, 'c') == kStr + 2); + static_assert(__builtin_strchr(kStr, 'd') == nullptr); + static_assert(__builtin_strchr(kStr, 'e') == nullptr); + static_assert(__builtin_strchr(kStr, '\0') == kStr + 5); + static_assert(__builtin_strchr(kStr, 'a' + 256) == nullptr); + static_assert(__builtin_strchr(kStr, 'a' - 256) == nullptr); + static_assert(__builtin_strchr(kStr, '\xff') == kStr + 4); + static_assert(__builtin_strchr(kStr, '\xff' + 256) == nullptr); + static_assert(__builtin_strchr(kStr, '\xff' - 256) == nullptr); + static_assert(__builtin_strchr(kFoo, 'o') == kFoo + 1); + static_assert(__builtin_strchr(kFoo, 'x') == nullptr); // both-error {{not an integral constant}} \ + // both-note {{dereferenced one-past-the-end}} + static_assert(__builtin_strchr(nullptr, 'x') == nullptr); // both-error {{not an integral constant}} \ + // both-note {{dereferenced null}} + + constexpr bool a = !strchr("hello", 'h'); // both-error {{constant expression}} \ + // both-note {{non-constexpr function 'strchr' cannot be used in a constant expression}} } diff --git a/clang/test/AST/ByteCode/cxx1z.cpp b/clang/test/AST/ByteCode/cxx1z.cpp index ca5f10f6567b4..57f99235a2b20 100644 --- a/clang/test/AST/ByteCode/cxx1z.cpp +++ b/clang/test/AST/ByteCode/cxx1z.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++17 -verify=expected,both %s // RUN: %clang_cc1 -std=c++17 -verify=ref,both %s -template struct A {}; // both-note 6{{template parameter is declared here}} +template struct A {}; namespace Temp { struct S { int n; }; constexpr S &addr(S &&s) { return s; } diff --git a/clang/test/AST/ByteCode/cxx20.cpp b/clang/test/AST/ByteCode/cxx20.cpp index d39c2281ec146..06501de64916a 100644 --- a/clang/test/AST/ByteCode/cxx20.cpp +++ b/clang/test/AST/ByteCode/cxx20.cpp @@ -897,7 +897,7 @@ namespace VirtDtor { } namespace TemporaryInNTTP { - template struct B { /* ... */ }; // both-note {{template parameter is declared here}} + template struct B { /* ... */ }; struct J1 { J1 *self=this; }; diff --git a/clang/test/AST/ByteCode/cxx98.cpp b/clang/test/AST/ByteCode/cxx98.cpp index 9af668029f8a3..c17049b01c1da 100644 --- a/clang/test/AST/ByteCode/cxx98.cpp +++ b/clang/test/AST/ByteCode/cxx98.cpp @@ -6,7 +6,7 @@ namespace IntOrEnum { const int k = 0; const int &p = k; // both-note {{declared here}} - template struct S {}; // both-note {{template parameter is declared here}} + template struct S {}; S

s; // both-error {{not an integral constant expression}} \ // both-note {{read of variable 'p' of non-integral, non-enumeration type 'const int &'}} } diff --git a/clang/test/AST/ByteCode/if.cpp b/clang/test/AST/ByteCode/if.cpp index c48b2b8d378c8..909e08a22a283 100644 --- a/clang/test/AST/ByteCode/if.cpp +++ b/clang/test/AST/ByteCode/if.cpp @@ -54,7 +54,7 @@ namespace InitDecl { constexpr char g(char const (&x)[2]) { return 'x'; if (auto [a, b] = x) // both-error {{an array type is not allowed here}} \ - // both-warning {{ISO C++17 does not permit structured binding declaration in a condition}} + // both-warning {{structured binding declaration in a condition is a C++2c extenstion}} ; } static_assert(g("x") == 'x'); diff --git a/clang/test/AST/ByteCode/libcxx/memcmp-pointer.cpp b/clang/test/AST/ByteCode/libcxx/memcmp-pointer.cpp new file mode 100644 index 0000000000000..ec4caefd4af85 --- /dev/null +++ b/clang/test/AST/ByteCode/libcxx/memcmp-pointer.cpp @@ -0,0 +1,120 @@ +// RUN: %clang_cc1 -std=c++2c -fexperimental-new-constant-interpreter -verify=expected,both %s +// RUN: %clang_cc1 -std=c++2c -verify=ref,both %s + +// both-no-diagnostics + +namespace std { +inline namespace { +template struct integral_constant { + static const int value = __v; +}; +template using __enable_if_t = int; +char *addressof(char &); +struct pointer_traits { + template using rebind = _Up *; +}; +} // namespace +} // namespace std +void *operator new(decltype(sizeof(int)), void *); +namespace std { +inline namespace { +template using __make_unsigned_t = __make_unsigned(_Tp); +template class> +using __detected_or_t = _Default; +template using __pointer_member = _Tp; +template +using __pointer = __detected_or_t<_Tp *, __pointer_member>; +template using __size_type_member = _Tp; +template +using __size_type = + __detected_or_t<__make_unsigned_t<_DiffType>, __size_type_member>; +struct allocation_result { + char *ptr; + unsigned long count; +}; +template struct allocator_traits { + using allocator_type = _Alloc; + using pointer = + __pointer; + using const_pointer = pointer_traits::rebind; + using size_type = + __size_type(nullptr) - + static_cast(nullptr))>; + template + static constexpr allocation_result allocate_at_least(_Ap __alloc, + size_type __n) { + return {__alloc.allocate(__n), (unsigned long)__n}; + } +}; +template +constexpr auto __allocate_at_least(_Alloc __alloc, decltype(sizeof(int)) __n) { + return allocator_traits<_Alloc>::allocate_at_least(__alloc, __n); +} +template struct allocator { + typedef char value_type; + constexpr char *allocate(decltype(sizeof(int)) __n) { + return static_cast(operator new(__n)); + } + constexpr void deallocate(char *__p) { operator delete(__p); } +}; +struct __long { + allocator_traits>::size_type __is_long_; + allocator_traits>::size_type __size_; + allocator_traits>::pointer __data_; +}; +allocator __alloc_; +struct basic_string { + __long __l; + constexpr basic_string(basic_string &__str) { + allocator_traits>::size_type __trans_tmp_1 = + __str.__get_long_size(); + auto __allocation = __allocate_at_least(__alloc_, __trans_tmp_1); + for (allocator_traits>::size_type __i = 0; + __i != __allocation.count; ++__i) { + char *__trans_tmp_9 = addressof(__allocation.ptr[__i]); + new (__trans_tmp_9) char(); + } + __l.__data_ = __allocation.ptr; + __l.__is_long_ = __l.__size_ = __trans_tmp_1; + } + template <__enable_if_t::value, int> = 0> + constexpr basic_string(const char *__s, allocator) { + decltype(sizeof(int)) __trans_tmp_11, __i = 0; + for (; __s[__i]; ++__i) + __trans_tmp_11 = __i; + auto __allocation = __allocate_at_least(__alloc_, 1); + __l.__data_ = __allocation.ptr; + __l.__size_ = __trans_tmp_11; + } + constexpr ~basic_string() { + allocator __a; + __a.deallocate(__l.__data_); + } + constexpr allocator_traits>::size_type size() { + return __l.__is_long_; + } + constexpr char *data() { + allocator_traits>::const_pointer __trans_tmp_6 = + __l.__is_long_ ? __l.__data_ : 0; + return __trans_tmp_6; + } + constexpr allocator_traits>::size_type __get_long_size() { + return __l.__size_; + } +}; +constexpr void operator==(basic_string __lhs, basic_string __rhs) { + decltype(sizeof(int)) __lhs_sz = __lhs.size(); + char *__trans_tmp_10 = __rhs.data(), *__trans_tmp_15 = __lhs.data(); + __builtin_memcmp(__trans_tmp_15, __trans_tmp_10, __lhs_sz); +} +} // namespace +} // namespace std +constexpr void test(std::basic_string s0) { + std::basic_string s1 = s0, s2(s0); + s2 == s1; +} +constexpr bool test() { + test(std::basic_string("2345678901234567890", std::allocator())); + return true; +} +static_assert(test()); diff --git a/clang/test/AST/ByteCode/placement-new.cpp b/clang/test/AST/ByteCode/placement-new.cpp index 7a4fc89a27dac..c353162a7aab0 100644 --- a/clang/test/AST/ByteCode/placement-new.cpp +++ b/clang/test/AST/ByteCode/placement-new.cpp @@ -339,6 +339,30 @@ namespace PR48606 { static_assert(f()); } +/// This used to crash because of an assertion in the implementation +/// of the This instruction. +namespace ExplicitThisOnArrayElement { + struct S { + int a = 12; + constexpr S(int a) { + this->a = a; + } + }; + + template + constexpr void construct_at(_Tp *__location, _Args &&...__args) { + new (__location) _Tp(__args...); + } + + constexpr bool foo() { + auto *M = std::allocator().allocate(13); // both-note {{allocation performed here was not deallocated}} + construct_at(M, 12); + return true; + } + + static_assert(foo()); // both-error {{not an integral constant expression}} +} + #ifdef BYTECODE constexpr int N = [] // expected-error {{must be initialized by a constant expression}} \ // expected-note {{assignment to dereferenced one-past-the-end pointer is not allowed in a constant expression}} \ diff --git a/clang/test/AST/ByteCode/records.cpp b/clang/test/AST/ByteCode/records.cpp index d125fb0bcf1b4..42b6d82d7190b 100644 --- a/clang/test/AST/ByteCode/records.cpp +++ b/clang/test/AST/ByteCode/records.cpp @@ -413,7 +413,7 @@ namespace DeriveFailures { constexpr Derived(int i) : OtherVal(i) {} // ref-error {{never produces a constant expression}} \ // both-note {{non-constexpr constructor 'Base' cannot be used in a constant expression}} \ - // ref-note {{non-constexpr constructor 'Base' cannot be used in a constant expression}} + // ref-note {{non-constexpr constructor 'Base' cannot be used in a constant expression}} }; constexpr Derived D(12); // both-error {{must be initialized by a constant expression}} \ @@ -1740,9 +1740,9 @@ namespace CtorOfInvalidClass { // both-error {{must be initialized by a constant expression}} #if __cplusplus >= 202002L - template // both-note {{template parameter is declared here}} + template concept ReferenceOf = Q; - /// This calls a valid and constexpr copy constructor of InvalidCtor, + /// This calls a valid and constexpr copy constructor of InvalidCtor, /// but should still be rejected. template auto R, typename Rep> int F; // both-error {{non-type template argument is not a constant expression}} #endif diff --git a/clang/test/AST/ast-dump-cxx2b-deducing-this.cpp b/clang/test/AST/ast-dump-cxx2b-deducing-this.cpp index abe9d6a5b5bc6..fc86aeb3e5ec3 100644 --- a/clang/test/AST/ast-dump-cxx2b-deducing-this.cpp +++ b/clang/test/AST/ast-dump-cxx2b-deducing-this.cpp @@ -26,3 +26,12 @@ struct S { // CHECK-NEXT: `-DeclRefExpr 0x{{[^ ]*}} 'S' lvalue ParmVar 0x{{[^ ]*}} 's' 'S' }; } + +namespace GH130272 { +struct A {}; +struct B { + operator A(this B); +}; +A a = A(B{}); +// CHECK: CallExpr 0x{{[^ ]*}} 'A':'GH130272::A' +} diff --git a/clang/test/AST/ast-print-openacc-routine-construct.cpp b/clang/test/AST/ast-print-openacc-routine-construct.cpp index 739718724f179..1d21e2ddee9a1 100644 --- a/clang/test/AST/ast-print-openacc-routine-construct.cpp +++ b/clang/test/AST/ast-print-openacc-routine-construct.cpp @@ -1,11 +1,16 @@ // RUN: %clang_cc1 -fopenacc -ast-print %s -o - | FileCheck %s auto Lambda = [](){}; -// CHECK: #pragma acc routine(Lambda) worker -#pragma acc routine(Lambda) worker +// CHECK: #pragma acc routine(Lambda) worker bind(identifier) +#pragma acc routine(Lambda) worker bind(identifier) int function(); -// CHECK: #pragma acc routine(function) vector nohost -#pragma acc routine (function) vector nohost +// CHECK: #pragma acc routine(function) vector nohost bind("string") +#pragma acc routine (function) vector nohost bind("string") + +// CHECK: #pragma acc routine(function) device_type(Something) seq +#pragma acc routine(function) device_type(Something) seq +// CHECK: #pragma acc routine(function) dtype(Something) seq +#pragma acc routine(function) dtype(Something) seq namespace NS { int NSFunc(); @@ -70,22 +75,27 @@ struct DepS { #pragma acc routine(DepS::MemFunc) seq nohost // CHECK: #pragma acc routine(DepS::StaticMemFunc) nohost worker #pragma acc routine(DepS::StaticMemFunc) nohost worker + +// CHECK: #pragma acc routine(MemFunc) worker dtype(*) +#pragma acc routine (MemFunc) worker dtype(*) +// CHECK: #pragma acc routine(MemFunc) device_type(Lambda) vector +#pragma acc routine (MemFunc) device_type(Lambda) vector }; -// CHECK: #pragma acc routine(DepS::Lambda) gang -#pragma acc routine(DepS::Lambda) gang +// CHECK: #pragma acc routine(DepS::Lambda) gang bind("string") +#pragma acc routine(DepS::Lambda) gang bind("string") // CHECK: #pragma acc routine(DepS::MemFunc) gang(dim: 1) #pragma acc routine(DepS::MemFunc) gang(dim:1) -// CHECK: #pragma acc routine(DepS::StaticMemFunc) vector -#pragma acc routine(DepS::StaticMemFunc) vector +// CHECK: #pragma acc routine(DepS::StaticMemFunc) vector bind(identifier) +#pragma acc routine(DepS::StaticMemFunc) vector bind(identifier) template void TemplFunc() { // CHECK: #pragma acc routine(T::MemFunc) gang(dim: T::SomethingElse()) #pragma acc routine(T::MemFunc) gang(dim:T::SomethingElse()) -// CHECK: #pragma acc routine(T::StaticMemFunc) worker nohost -#pragma acc routine(T::StaticMemFunc) worker nohost -// CHECK: #pragma acc routine(T::Lambda) nohost seq -#pragma acc routine(T::Lambda) nohost seq +// CHECK: #pragma acc routine(T::StaticMemFunc) worker nohost bind(identifier) +#pragma acc routine(T::StaticMemFunc) worker nohost bind(identifier) +// CHECK: #pragma acc routine(T::Lambda) nohost seq bind("string") +#pragma acc routine(T::Lambda) nohost seq bind("string") } diff --git a/clang/test/Analysis/out-of-bounds-notes.c b/clang/test/Analysis/ArrayBound/assumption-reporting.c similarity index 88% rename from clang/test/Analysis/out-of-bounds-notes.c rename to clang/test/Analysis/ArrayBound/assumption-reporting.c index 7256beb7e893e..d687886ada1ae 100644 --- a/clang/test/Analysis/out-of-bounds-notes.c +++ b/clang/test/Analysis/ArrayBound/assumption-reporting.c @@ -1,6 +1,16 @@ // RUN: %clang_analyze_cc1 -Wno-array-bounds -analyzer-output=text \ // RUN: -analyzer-checker=core,security.ArrayBound,unix.Malloc,optin.taint -verify %s +// When the checker security.ArrayBound encounters an array subscript operation +// that _may be_ in bounds, it assumes that indexing _is_ in bound. These +// assumptions will be reported to the user if the execution path leads to a +// bug report (made by any checker) and the symbol which was constrainted by +// the assumption is marked as interesting (with `markInteresting` or +// indirectly via `trackExpressionValue`) in that bug report. +// +// This test file validates the content of these note tags which describe the +// assumptions for the user. + int TenElements[10]; int irrelevantAssumptions(int arg) { @@ -197,3 +207,14 @@ int *extentInterestingness(int arg) { // expected-warning@-1 {{Out of bound access to memory after the end of the heap area}} // expected-note@-2 {{Access of 'int' element in the heap area at index 12}} } + +int triggeredByAnyReport(int arg) { + // Verify that note tags explaining the assumptions made by ArrayBound are + // not limited to ArrayBound reports but will appear on any bug report (that + // marks the relevant symbol as interesting). + TenElements[arg + 10] = 8; + // expected-note@-1 {{Assuming index is non-negative and less than 10, the number of 'int' elements in 'TenElements'}} + return 1024 >> arg; + // expected-warning@-1 {{Right operand is negative in right shift}} + // expected-note@-2 {{The result of right shift is undefined because the right operand is negative}} +} diff --git a/clang/test/Analysis/out-of-bounds-constraint-check.c b/clang/test/Analysis/ArrayBound/assumptions.c similarity index 100% rename from clang/test/Analysis/out-of-bounds-constraint-check.c rename to clang/test/Analysis/ArrayBound/assumptions.c diff --git a/clang/test/Analysis/out-of-bounds.c b/clang/test/Analysis/ArrayBound/brief-tests.c similarity index 96% rename from clang/test/Analysis/out-of-bounds.c rename to clang/test/Analysis/ArrayBound/brief-tests.c index 2174dafc0021b..f4811efd8d8b6 100644 --- a/clang/test/Analysis/out-of-bounds.c +++ b/clang/test/Analysis/ArrayBound/brief-tests.c @@ -1,5 +1,10 @@ // RUN: %clang_analyze_cc1 -Wno-array-bounds -analyzer-checker=core,security.ArrayBound,debug.ExprInspection -verify %s +// Miscellaneous tests for `security.ArrayBound` where we only test the +// presence or absence of a bug report. If a test doesn't fit in a more +// specific file and doesn't need to verify the details of 'note' diagnostics, +// then it should be placed here. + void clang_analyzer_value(int); // Tests doing an out-of-bounds access after the end of an array using: diff --git a/clang/test/Analysis/out-of-bounds-new.cpp b/clang/test/Analysis/ArrayBound/cplusplus.cpp similarity index 58% rename from clang/test/Analysis/out-of-bounds-new.cpp rename to clang/test/Analysis/ArrayBound/cplusplus.cpp index 9ae371819714d..680fbf4817c30 100644 --- a/clang/test/Analysis/out-of-bounds-new.cpp +++ b/clang/test/Analysis/ArrayBound/cplusplus.cpp @@ -1,10 +1,6 @@ -// RUN: %clang_analyze_cc1 -std=c++11 -Wno-array-bounds -verify %s \ -// RUN: -triple=x86_64-unknown-linux-gnu \ -// RUN: -analyzer-checker=unix,core,security.ArrayBound,debug.ExprInspection +// RUN: %clang_analyze_cc1 -std=c++11 -Wno-array-bounds -analyzer-checker=unix,core,security.ArrayBound -verify %s -void clang_analyzer_eval(bool); -void clang_analyzer_value(int); -void clang_analyzer_dump(int); +// Test the interactions of `security.ArrayBound` with C++ features. // Tests doing an out-of-bounds access after the end of an array using: // - constant integer index @@ -156,15 +152,13 @@ void test_dynamic_size(int s) { } //Tests complex arithmetic //in new expression -void test_dynamic_size2(unsigned m,unsigned n){ +void test_dynamic_size2(unsigned m, unsigned n){ unsigned *U = nullptr; U = new unsigned[m + n + 1]; } //Test creating invalid references, which break the invariant that a reference //is always holding a value, and could lead to nasty runtime errors. -//(This is not related to operator new, but placed in this file because the -//other test files are not C++.) int array[10] = {0}; void test_after_the_end_reference() { @@ -185,60 +179,3 @@ int test_reference_that_might_be_after_the_end(int idx) { return -1; return ref; } - -// From: https://github.com/llvm/llvm-project/issues/100762 -extern int arrOf10[10]; -void using_builtin(int x) { - __builtin_assume(x > 101); // CallExpr - arrOf10[x] = 404; // expected-warning {{Out of bound access to memory}} -} - -void using_assume_attr(int ax) { - [[assume(ax > 100)]]; // NullStmt with an "assume" attribute. - arrOf10[ax] = 405; // expected-warning {{Out of bound access to memory}} -} - -void using_many_assume_attr(int yx) { - [[assume(yx > 104), assume(yx > 200), assume(yx < 300)]]; // NullStmt with an attribute - arrOf10[yx] = 406; // expected-warning{{Out of bound access to memory}} -} - -int using_assume_attr_has_no_sideeffects(int y) { - int orig_y = y; - clang_analyzer_value(y); // expected-warning {{32s:{ [-2147483648, 2147483647] }}} - clang_analyzer_value(orig_y); // expected-warning {{32s:{ [-2147483648, 2147483647] }}} - clang_analyzer_dump(y); // expected-warning-re {{{{^}}reg_${{[0-9]+}} [debug.ExprInspection]{{$}}}} - clang_analyzer_dump(orig_y); // expected-warning-re {{{{^}}reg_${{[0-9]+}} [debug.ExprInspection]{{$}}}} - - // We should not apply sideeffects of the argument of [[assume(...)]]. - // "y" should not get incremented; - [[assume(++y == 43)]]; // expected-warning {{assumption is ignored because it contains (potential) side-effects}} - - clang_analyzer_dump(y); // expected-warning-re {{{{^}}reg_${{[0-9]+}} [debug.ExprInspection]{{$}}}} - clang_analyzer_dump(orig_y); // expected-warning-re {{{{^}}reg_${{[0-9]+}} [debug.ExprInspection]{{$}}}} - clang_analyzer_value(y); // expected-warning {{32s:{ [-2147483648, 2147483647] }}} - clang_analyzer_value(orig_y); // expected-warning {{32s:{ [-2147483648, 2147483647] }}} - clang_analyzer_eval(y == orig_y); // expected-warning {{TRUE}} Good. - - return y; -} - -int using_builtin_assume_has_no_sideeffects(int y) { - int orig_y = y; - clang_analyzer_value(y); // expected-warning {{32s:{ [-2147483648, 2147483647] }}} - clang_analyzer_value(orig_y); // expected-warning {{32s:{ [-2147483648, 2147483647] }}} - clang_analyzer_dump(y); // expected-warning-re {{{{^}}reg_${{[0-9]+}} [debug.ExprInspection]{{$}}}} - clang_analyzer_dump(orig_y); // expected-warning-re {{{{^}}reg_${{[0-9]+}} [debug.ExprInspection]{{$}}}} - - // We should not apply sideeffects of the argument of __builtin_assume(...) - // "u" should not get incremented; - __builtin_assume(++y == 43); // expected-warning {{assumption is ignored because it contains (potential) side-effects}} - - clang_analyzer_dump(y); // expected-warning-re {{{{^}}reg_${{[0-9]+}} [debug.ExprInspection]{{$}}}} - clang_analyzer_dump(orig_y); // expected-warning-re {{{{^}}reg_${{[0-9]+}} [debug.ExprInspection]{{$}}}} - clang_analyzer_value(y); // expected-warning {{32s:{ [-2147483648, 2147483647] }}} - clang_analyzer_value(orig_y); // expected-warning {{32s:{ [-2147483648, 2147483647] }}} - clang_analyzer_eval(y == orig_y); // expected-warning {{TRUE}} Good. - - return y; -} diff --git a/clang/test/Analysis/out-of-bounds-diagnostics.c b/clang/test/Analysis/ArrayBound/verbose-tests.c similarity index 98% rename from clang/test/Analysis/out-of-bounds-diagnostics.c rename to clang/test/Analysis/ArrayBound/verbose-tests.c index 524fa4e2aaaf7..84d238ed1a2a4 100644 --- a/clang/test/Analysis/out-of-bounds-diagnostics.c +++ b/clang/test/Analysis/ArrayBound/verbose-tests.c @@ -1,6 +1,11 @@ // RUN: %clang_analyze_cc1 -Wno-array-bounds -analyzer-output=text \ // RUN: -analyzer-checker=core,security.ArrayBound,unix.Malloc,optin.taint -verify %s +// Miscellaneous tests for `security.ArrayBound` where we also verify the +// content of the 'note' diagnostics. This makes the tests sensitive to textual +// changes in the diagnostics, so prefer adding new tests to `brief-tests.c` +// unless they need to verify the correctness of 'note' diagnostics. + int TenElements[10]; void arrayUnderflow(void) { diff --git a/clang/test/Analysis/Checkers/WebKit/call-args.cpp b/clang/test/Analysis/Checkers/WebKit/call-args.cpp index b4613d5090f29..e7afd9798da3e 100644 --- a/clang/test/Analysis/Checkers/WebKit/call-args.cpp +++ b/clang/test/Analysis/Checkers/WebKit/call-args.cpp @@ -359,6 +359,41 @@ namespace call_with_ptr_on_ref { } } +namespace call_with_explicit_construct_from_auto { + + struct Impl { + void ref() const; + void deref() const; + + static Ref create(); + }; + + template + struct ArgObj { + T* t; + }; + + struct Object { + Object(); + Object(Ref&&); + + Impl* impl() const { return m_impl.get(); } + + static Object create(ArgObj&) { return Impl::create(); } + static void bar(Impl&); + + private: + RefPtr m_impl; + }; + + template void foo() + { + auto result = Object::create(ArgObj { }); + Object::bar(Ref { *result.impl() }); + } + +} + namespace call_with_explicit_temporary_obj { void foo() { Ref { *provide() }->method(); diff --git a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h index 7bb33bcb6cf44..9b13810d0c5c9 100644 --- a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h +++ b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h @@ -1,30 +1,94 @@ @class NSString; @class NSArray; @class NSMutableArray; +#define nil ((id)0) #define CF_BRIDGED_TYPE(T) __attribute__((objc_bridge(T))) +#define CF_BRIDGED_MUTABLE_TYPE(T) __attribute__((objc_bridge_mutable(T))) typedef CF_BRIDGED_TYPE(id) void * CFTypeRef; +typedef signed char BOOL; typedef signed long CFIndex; -typedef const struct __CFAllocator * CFAllocatorRef; +typedef unsigned long NSUInteger; +typedef const struct CF_BRIDGED_TYPE(id) __CFAllocator * CFAllocatorRef; typedef const struct CF_BRIDGED_TYPE(NSString) __CFString * CFStringRef; typedef const struct CF_BRIDGED_TYPE(NSArray) __CFArray * CFArrayRef; -typedef struct CF_BRIDGED_TYPE(NSMutableArray) __CFArray * CFMutableArrayRef; +typedef struct CF_BRIDGED_MUTABLE_TYPE(NSMutableArray) __CFArray * CFMutableArrayRef; +typedef struct CF_BRIDGED_MUTABLE_TYPE(CFRunLoopRef) __CFRunLoop * CFRunLoopRef; extern const CFAllocatorRef kCFAllocatorDefault; +typedef struct _NSZone NSZone; CFMutableArrayRef CFArrayCreateMutable(CFAllocatorRef allocator, CFIndex capacity); extern void CFArrayAppendValue(CFMutableArrayRef theArray, const void *value); CFArrayRef CFArrayCreate(CFAllocatorRef allocator, const void **values, CFIndex numValues); CFIndex CFArrayGetCount(CFArrayRef theArray); +CFRunLoopRef CFRunLoopGetCurrent(void); +CFRunLoopRef CFRunLoopGetMain(void); extern CFTypeRef CFRetain(CFTypeRef cf); extern void CFRelease(CFTypeRef cf); +#define CFSTR(cStr) ((CFStringRef) __builtin___CFStringMakeConstantString ("" cStr "")) +extern Class NSClassFromString(NSString *aClassName); __attribute__((objc_root_class)) @interface NSObject + (instancetype) alloc; ++ (Class) class; ++ (Class) superclass; - (instancetype) init; - (instancetype)retain; - (void)release; +- (BOOL)isKindOfClass:(Class)aClass; +@end + +@protocol NSCopying +- (id)copyWithZone:(NSZone *)zone; +@end + +@protocol NSFastEnumeration +- (int)countByEnumeratingWithState:(void *)state objects:(id *)objects count:(unsigned)count; +- (void)protocolMethod; +@end + +@interface NSEnumerator +@end + +@interface NSDictionary : NSObject +- (NSUInteger)count; +- (id)objectForKey:(id)aKey; +- (id)objectForKeyedSubscript:(id)aKey; +- (NSEnumerator *)keyEnumerator; ++ (id)dictionary; ++ (id)dictionaryWithObject:(id)object forKey:(id )key; ++ (instancetype)dictionaryWithObjects:(const id [])objects forKeys:(const id [])keys count:(NSUInteger)cnt; +@end + +@interface NSArray : NSObject +- (NSUInteger)count; +- (NSEnumerator *)objectEnumerator; ++ (NSArray *)arrayWithObjects:(const id [])objects count:(NSUInteger)count; +@end + +@interface NSString : NSObject +- (NSUInteger)length; +- (NSString *)stringByAppendingString:(NSString *)aString; +- ( const char *)UTF8String; +- (id)initWithUTF8String:(const char *)nullTerminatedCString; ++ (id)stringWithUTF8String:(const char *)nullTerminatedCString; +@end + +@interface NSMutableString : NSString +@end + +@interface NSValue : NSObject +- (void)getValue:(void *)value; +@end + +@interface NSNumber : NSValue +- (char)charValue; +- (id)initWithInt:(int)value; ++ (NSNumber *)numberWithInt:(int)value; @end @interface SomeObj : NSObject +- (SomeObj *)mutableCopy; +- (SomeObj *)copyWithValue:(int)value; - (void)doWork; - (SomeObj *)other; - (SomeObj *)next; @@ -57,28 +121,34 @@ template struct RetainPtr { PtrType t; RetainPtr() : t(nullptr) { } - RetainPtr(PtrType t) : t(t) { if (t) - CFRetain(t); + CFRetain(toCFTypeRef(t)); } - RetainPtr(RetainPtr&& o) - : RetainPtr(o.t) - { - o.t = nullptr; - } - RetainPtr(const RetainPtr& o) + RetainPtr(RetainPtr&&); + RetainPtr(const RetainPtr&); + template + RetainPtr(const RetainPtr& o) : RetainPtr(o.t) + {} + RetainPtr operator=(const RetainPtr& o) { + if (t) + CFRelease(toCFTypeRef(t)); + t = o.t; + if (t) + CFRetain(toCFTypeRef(t)); + return *this; } - RetainPtr operator=(const RetainPtr& o) + template + RetainPtr operator=(const RetainPtr& o) { if (t) - CFRelease(t); + CFRelease(toCFTypeRef(t)); t = o.t; if (t) - CFRetain(t); + CFRetain(toCFTypeRef(t)); return *this; } ~RetainPtr() { @@ -86,7 +156,7 @@ template struct RetainPtr { } void clear() { if (t) - CFRelease(t); + CFRelease(toCFTypeRef(t)); t = nullptr; } void swap(RetainPtr& o) { @@ -102,10 +172,19 @@ template struct RetainPtr { swap(o); return *this; } + PtrType leakRef() + { + PtrType s = t; + t = nullptr; + return s; + } operator PtrType() const { return t; } operator bool() const { return t; } private: + CFTypeRef toCFTypeRef(id ptr) { return (__bridge CFTypeRef)ptr; } + CFTypeRef toCFTypeRef(const void* ptr) { return (CFTypeRef)ptr; } + template friend RetainPtr adoptNS(U*); template friend RetainPtr adoptCF(U); @@ -113,9 +192,26 @@ template struct RetainPtr { RetainPtr(PtrType t, AdoptTag) : t(t) { } }; +template +RetainPtr::RetainPtr(RetainPtr&& o) + : RetainPtr(o.t) +{ + o.t = nullptr; +} + +template +RetainPtr::RetainPtr(const RetainPtr& o) + : RetainPtr(o.t) +{ +} + template RetainPtr adoptNS(T* t) { +#if __has_feature(objc_arc) + return t; +#else return RetainPtr(t, RetainPtr::Adopt); +#endif } template @@ -123,9 +219,31 @@ RetainPtr adoptCF(T t) { return RetainPtr(t, RetainPtr::Adopt); } +template inline RetainPtr retainPtr(T ptr) +{ + return ptr; +} + +template inline RetainPtr retainPtr(T* ptr) +{ + return ptr; +} + +inline NSObject *bridge_cast(CFTypeRef object) +{ + return (__bridge NSObject *)object; +} + +inline CFTypeRef bridge_cast(NSObject *object) +{ + return (__bridge CFTypeRef)object; +} + } using WTF::RetainPtr; using WTF::adoptNS; using WTF::adoptCF; +using WTF::retainPtr; using WTF::downcast; +using WTF::bridge_cast; \ No newline at end of file diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures-decl-protects-this-crash.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures-decl-protects-this-crash.cpp index 840433db5133a..0c10c69a97eda 100644 --- a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures-decl-protects-this-crash.cpp +++ b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures-decl-protects-this-crash.cpp @@ -28,9 +28,9 @@ struct Obj { void foo(Foo foo) { bar([this](auto baz) { - // expected-warning@-1{{Captured raw-pointer 'this' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured raw-pointer 'this' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} bar([this, foo = *baz, foo2 = !baz](auto&&) { - // expected-warning@-1{{Captured raw-pointer 'this' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured raw-pointer 'this' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} someFunction(); }); }); diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp index 82058bf13d137..1746d2b93d469 100644 --- a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp +++ b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp @@ -94,22 +94,22 @@ void callAsync(const WTF::Function&); void raw_ptr() { RefCountable* ref_countable = make_obj(); auto foo1 = [ref_countable](){ - // expected-warning@-1{{Captured raw-pointer 'ref_countable' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured raw-pointer 'ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} ref_countable->method(); }; auto foo2 = [&ref_countable](){ - // expected-warning@-1{{Captured raw-pointer 'ref_countable' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured raw-pointer 'ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} ref_countable->method(); }; auto foo3 = [&](){ ref_countable->method(); - // expected-warning@-1{{Implicitly captured raw-pointer 'ref_countable' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured raw-pointer 'ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} ref_countable = nullptr; }; auto foo4 = [=](){ ref_countable->method(); - // expected-warning@-1{{Implicitly captured raw-pointer 'ref_countable' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured raw-pointer 'ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} }; call(foo1); @@ -128,13 +128,13 @@ void references() { RefCountable automatic; RefCountable& ref_countable_ref = automatic; auto foo1 = [ref_countable_ref](){ ref_countable_ref.constMethod(); }; - // expected-warning@-1{{Captured reference 'ref_countable_ref' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured reference 'ref_countable_ref' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} auto foo2 = [&ref_countable_ref](){ ref_countable_ref.method(); }; - // expected-warning@-1{{Captured reference 'ref_countable_ref' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured reference 'ref_countable_ref' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} auto foo3 = [&](){ ref_countable_ref.method(); }; - // expected-warning@-1{{Implicitly captured reference 'ref_countable_ref' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured reference 'ref_countable_ref' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} auto foo4 = [=](){ ref_countable_ref.constMethod(); }; - // expected-warning@-1{{Implicitly captured reference 'ref_countable_ref' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured reference 'ref_countable_ref' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} call(foo1); call(foo2); @@ -187,7 +187,7 @@ void noescape_lambda() { otherObj->method(); }, [&](RefCountable& obj) { otherObj->method(); - // expected-warning@-1{{Implicitly captured raw-pointer 'otherObj' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured raw-pointer 'otherObj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} }); ([&] { someObj->method(); @@ -217,7 +217,7 @@ struct RefCountableWithLambdaCapturingThis { void method_captures_this_unsafe() { auto lambda = [&]() { nonTrivial(); - // expected-warning@-1{{Implicitly captured raw-pointer 'this' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured raw-pointer 'this' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} }; call(lambda); } @@ -225,7 +225,7 @@ struct RefCountableWithLambdaCapturingThis { void method_captures_this_unsafe_capture_local_var_explicitly() { RefCountable* x = make_obj(); call([this, protectedThis = RefPtr { this }, x]() { - // expected-warning@-1{{Captured raw-pointer 'x' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured raw-pointer 'x' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} nonTrivial(); x->method(); }); @@ -234,7 +234,7 @@ struct RefCountableWithLambdaCapturingThis { void method_captures_this_with_other_protected_var() { RefCountable* x = make_obj(); call([this, protectedX = RefPtr { x }]() { - // expected-warning@-1{{Captured raw-pointer 'this' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured raw-pointer 'this' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} nonTrivial(); protectedX->method(); }); @@ -243,7 +243,7 @@ struct RefCountableWithLambdaCapturingThis { void method_captures_this_unsafe_capture_local_var_explicitly_with_deref() { RefCountable* x = make_obj(); call([this, protectedThis = Ref { *this }, x]() { - // expected-warning@-1{{Captured raw-pointer 'x' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured raw-pointer 'x' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} nonTrivial(); x->method(); }); @@ -252,7 +252,7 @@ struct RefCountableWithLambdaCapturingThis { void method_captures_this_unsafe_local_var_via_vardecl() { RefCountable* x = make_obj(); auto lambda = [this, protectedThis = Ref { *this }, x]() { - // expected-warning@-1{{Captured raw-pointer 'x' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured raw-pointer 'x' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} nonTrivial(); x->method(); }; @@ -330,7 +330,7 @@ struct RefCountableWithLambdaCapturingThis { void method_nested_lambda3() { callAsync([this, protectedThis = RefPtr { this }] { callAsync([this] { - // expected-warning@-1{{Captured raw-pointer 'this' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured raw-pointer 'this' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} nonTrivial(); }); }); @@ -380,10 +380,10 @@ void lambda_converted_to_function(RefCountable* obj) { callFunction([&]() { obj->method(); - // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} }); callFunctionOpaque([&]() { obj->method(); - // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} }); } diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-local-vars.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-local-vars.cpp index 52854cd10f68c..07b6de21df80f 100644 --- a/clang/test/Analysis/Checkers/WebKit/uncounted-local-vars.cpp +++ b/clang/test/Analysis/Checkers/WebKit/uncounted-local-vars.cpp @@ -464,4 +464,17 @@ namespace local_var_for_singleton { RefCountable* bar = singleton(); RefCountable* baz = otherSingleton(); } +} + +namespace virtual_function { + struct SomeObject { + virtual RefCountable* provide() { return nullptr; } + virtual RefCountable* operator&() { return nullptr; } + }; + void foo(SomeObject* obj) { + auto* bar = obj->provide(); + // expected-warning@-1{{Local variable 'bar' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker]}} + auto* baz = &*obj; + // expected-warning@-1{{Local variable 'baz' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker]}} + } } \ No newline at end of file diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp index fe7ce158eb8ba..0279e2c68ec6d 100644 --- a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp +++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp @@ -196,6 +196,10 @@ class ComplexNumber { ComplexNumber& operator+(); const Number& real() const { return realPart; } + const Number& complex() const; + + void ref() const; + void deref() const; private: Number realPart; @@ -240,6 +244,11 @@ class SomeType : public BaseType { using BaseType::BaseType; }; +struct OtherObj { + unsigned v { 0 }; + OtherObj* children[4] { nullptr }; +}; + void __libcpp_verbose_abort(const char *__format, ...); class RefCounted { @@ -375,7 +384,7 @@ class RefCounted { double y; }; void trivial68() { point pt = { 1.0 }; } - unsigned trivial69() { return offsetof(RefCounted, children); } + unsigned trivial69() { return offsetof(OtherObj, children); } DerivedNumber* trivial70() { [[clang::suppress]] return static_cast(number); } static RefCounted& singleton() { @@ -467,6 +476,8 @@ class RefCounted { unsigned nonTrivial22() { return ComplexNumber(123, "456").real().value(); } unsigned nonTrivial23() { return DerivedNumber("123").value(); } SomeType nonTrivial24() { return SomeType("123"); } + virtual void nonTrivial25() { } + virtual ComplexNumber* operator->() { return nullptr; } static unsigned s_v; unsigned v { 0 }; @@ -642,6 +653,10 @@ class UnrelatedClass { // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} getFieldTrivial().nonTrivial24(); // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} + getFieldTrivial().nonTrivial25(); + // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} + getFieldTrivial()->complex(); + // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} } void setField(RefCounted*); diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures-arc.mm b/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures-arc.mm new file mode 100644 index 0000000000000..4e3d9c2708d96 --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures-arc.mm @@ -0,0 +1,273 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UnretainedLambdaCapturesChecker -fobjc-arc -verify %s + +#include "objc-mock-types.h" + +namespace std { + +template +class unique_ptr { +private: + T *t; + +public: + unique_ptr() : t(nullptr) { } + unique_ptr(T *t) : t(t) { } + ~unique_ptr() { + if (t) + delete t; + } + template unique_ptr(unique_ptr&& u) + : t(u.t) + { + u.t = nullptr; + } + T *get() const { return t; } + T *operator->() const { return t; } + T &operator*() const { return *t; } + unique_ptr &operator=(T *) { return *this; } + explicit operator bool() const { return !!t; } +}; + +}; + +namespace WTF { + +namespace Detail { + +template +class CallableWrapperBase { +public: + virtual ~CallableWrapperBase() { } + virtual Out call(In...) = 0; +}; + +template class CallableWrapper; + +template +class CallableWrapper : public CallableWrapperBase { +public: + explicit CallableWrapper(CallableType& callable) + : m_callable(callable) { } + Out call(In... in) final { return m_callable(in...); } + +private: + CallableType m_callable; +}; + +} // namespace Detail + +template class Function; + +template Function adopt(Detail::CallableWrapperBase*); + +template +class Function { +public: + using Impl = Detail::CallableWrapperBase; + + Function() = default; + + template + Function(FunctionType f) + : m_callableWrapper(new Detail::CallableWrapper(f)) { } + + Out operator()(In... in) const { return m_callableWrapper->call(in...); } + explicit operator bool() const { return !!m_callableWrapper; } + +private: + enum AdoptTag { Adopt }; + Function(Impl* impl, AdoptTag) + : m_callableWrapper(impl) + { + } + + friend Function adopt(Impl*); + + std::unique_ptr m_callableWrapper; +}; + +template Function adopt(Detail::CallableWrapperBase* impl) +{ + return Function(impl, Function::Adopt); +} + +template +class HashMap { +public: + HashMap(); + HashMap([[clang::noescape]] const Function&); + void ensure(const KeyType&, [[clang::noescape]] const Function&); + bool operator+([[clang::noescape]] const Function&) const; + static void ifAny(HashMap, [[clang::noescape]] const Function&); + +private: + ValueType* m_table { nullptr }; +}; + +} // namespace WTF + +struct A { + static void b(); +}; + +SomeObj* make_obj(); +CFMutableArrayRef make_cf(); + +void someFunction(); +template void call(Callback callback) { + someFunction(); + callback(); +} +void callAsync(const WTF::Function&); + +void raw_ptr() { + SomeObj* obj = make_obj(); + auto foo1 = [obj](){ + [obj doWork]; + }; + call(foo1); + + auto foo2 = [&obj](){ + [obj doWork]; + }; + auto foo3 = [&](){ + [obj doWork]; + obj = nullptr; + }; + auto foo4 = [=](){ + [obj doWork]; + }; + + auto cf = make_cf(); + auto bar1 = [cf](){ + // expected-warning@-1{{Captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + CFArrayAppendValue(cf, nullptr); + }; + auto bar2 = [&cf](){ + // expected-warning@-1{{Captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + CFArrayAppendValue(cf, nullptr); + }; + auto bar3 = [&](){ + CFArrayAppendValue(cf, nullptr); + // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + cf = nullptr; + }; + auto bar4 = [=](){ + CFArrayAppendValue(cf, nullptr); + // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + }; + + call(foo1); + call(foo2); + call(foo3); + call(foo4); + + call(bar1); + call(bar2); + call(bar3); + call(bar4); +} + +void quiet() { +// This code is not expected to trigger any warnings. + SomeObj *obj; + + auto foo3 = [&]() {}; + auto foo4 = [=]() {}; + + call(foo3); + call(foo4); + + obj = nullptr; +} + +template +void map(SomeObj* start, [[clang::noescape]] Callback&& callback) +{ + while (start) { + callback(start); + start = [start next]; + } +} + +template +void doubleMap(SomeObj* start, [[clang::noescape]] Callback1&& callback1, Callback2&& callback2) +{ + while (start) { + callback1(start); + callback2(start); + start = [start next]; + } +} + +template +void get_count_cf(CFArrayRef array, [[clang::noescape]] Callback1&& callback1, Callback2&& callback2) +{ + auto count = CFArrayGetCount(array); + callback1(count); + callback2(count); +} + +void noescape_lambda() { + SomeObj* someObj = make_obj(); + SomeObj* otherObj = make_obj(); + map(make_obj(), [&](SomeObj *obj) { + [otherObj doWork]; + }); + doubleMap(make_obj(), [&](SomeObj *obj) { + [otherObj doWork]; + }, [&](SomeObj *obj) { + [otherObj doWork]; + }); + ([&] { + [someObj doWork]; + })(); + + CFMutableArrayRef someCF = make_cf(); + get_count_cf(make_cf(), [&](CFIndex count) { + CFArrayAppendValue(someCF, nullptr); + }, [&](CFIndex count) { + CFArrayAppendValue(someCF, nullptr); + // expected-warning@-1{{Implicitly captured reference 'someCF' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + }); +} + +void callFunctionOpaque(WTF::Function&&); +void callFunction(WTF::Function&& function) { + someFunction(); + function(); +} + +void lambda_converted_to_function(SomeObj* obj, CFMutableArrayRef cf) +{ + callFunction([&]() { + [obj doWork]; + CFArrayAppendValue(cf, nullptr); + // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + }); + callFunctionOpaque([&]() { + [obj doWork]; + CFArrayAppendValue(cf, nullptr); + // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + }); +} + +@interface ObjWithSelf : NSObject { + RetainPtr delegate; +} +-(void)doWork; +-(void)run; +@end + +@implementation ObjWithSelf +-(void)doWork { + auto doWork = [&] { + someFunction(); + [delegate doWork]; + }; + callFunctionOpaque(doWork); +} +-(void)run { + someFunction(); +} +@end \ No newline at end of file diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures.mm b/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures.mm new file mode 100644 index 0000000000000..073eff9386baa --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures.mm @@ -0,0 +1,296 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UnretainedLambdaCapturesChecker -verify %s + +#include "objc-mock-types.h" + +namespace std { + +template +class unique_ptr { +private: + T *t; + +public: + unique_ptr() : t(nullptr) { } + unique_ptr(T *t) : t(t) { } + ~unique_ptr() { + if (t) + delete t; + } + template unique_ptr(unique_ptr&& u) + : t(u.t) + { + u.t = nullptr; + } + T *get() const { return t; } + T *operator->() const { return t; } + T &operator*() const { return *t; } + unique_ptr &operator=(T *) { return *this; } + explicit operator bool() const { return !!t; } +}; + +}; + +namespace WTF { + +namespace Detail { + +template +class CallableWrapperBase { +public: + virtual ~CallableWrapperBase() { } + virtual Out call(In...) = 0; +}; + +template class CallableWrapper; + +template +class CallableWrapper : public CallableWrapperBase { +public: + explicit CallableWrapper(CallableType& callable) + : m_callable(callable) { } + Out call(In... in) final { return m_callable(in...); } + +private: + CallableType m_callable; +}; + +} // namespace Detail + +template class Function; + +template Function adopt(Detail::CallableWrapperBase*); + +template +class Function { +public: + using Impl = Detail::CallableWrapperBase; + + Function() = default; + + template + Function(FunctionType f) + : m_callableWrapper(new Detail::CallableWrapper(f)) { } + + Out operator()(In... in) const { return m_callableWrapper->call(in...); } + explicit operator bool() const { return !!m_callableWrapper; } + +private: + enum AdoptTag { Adopt }; + Function(Impl* impl, AdoptTag) + : m_callableWrapper(impl) + { + } + + friend Function adopt(Impl*); + + std::unique_ptr m_callableWrapper; +}; + +template Function adopt(Detail::CallableWrapperBase* impl) +{ + return Function(impl, Function::Adopt); +} + +template +class HashMap { +public: + HashMap(); + HashMap([[clang::noescape]] const Function&); + void ensure(const KeyType&, [[clang::noescape]] const Function&); + bool operator+([[clang::noescape]] const Function&) const; + static void ifAny(HashMap, [[clang::noescape]] const Function&); + +private: + ValueType* m_table { nullptr }; +}; + +} // namespace WTF + +struct A { + static void b(); +}; + +SomeObj* make_obj(); +CFMutableArrayRef make_cf(); + +void someFunction(); +template void call(Callback callback) { + someFunction(); + callback(); +} +void callAsync(const WTF::Function&); + +void raw_ptr() { + SomeObj* obj = make_obj(); + auto foo1 = [obj](){ + // expected-warning@-1{{Captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + [obj doWork]; + }; + call(foo1); + + auto foo2 = [&obj](){ + // expected-warning@-1{{Captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + [obj doWork]; + }; + auto foo3 = [&](){ + [obj doWork]; + // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + obj = nullptr; + }; + auto foo4 = [=](){ + [obj doWork]; + // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + }; + + auto cf = make_cf(); + auto bar1 = [cf](){ + // expected-warning@-1{{Captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + CFArrayAppendValue(cf, nullptr); + }; + auto bar2 = [&cf](){ + // expected-warning@-1{{Captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + CFArrayAppendValue(cf, nullptr); + }; + auto bar3 = [&](){ + CFArrayAppendValue(cf, nullptr); + // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + cf = nullptr; + }; + auto bar4 = [=](){ + CFArrayAppendValue(cf, nullptr); + // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + }; + + call(foo1); + call(foo2); + call(foo3); + call(foo4); + + call(bar1); + call(bar2); + call(bar3); + call(bar4); + + // Confirm that the checker respects [[clang::suppress]]. + SomeObj* suppressed_obj = nullptr; + [[clang::suppress]] auto foo5 = [suppressed_obj](){ + [suppressed_obj doWork]; + }; + // no warning. + call(foo5); + + // Confirm that the checker respects [[clang::suppress]]. + CFMutableArrayRef suppressed_cf = nullptr; + [[clang::suppress]] auto bar5 = [suppressed_cf](){ + CFArrayAppendValue(suppressed_cf, nullptr); + }; + // no warning. + call(bar5); +} + +void quiet() { +// This code is not expected to trigger any warnings. + SomeObj *obj; + + auto foo3 = [&]() {}; + auto foo4 = [=]() {}; + + call(foo3); + call(foo4); + + obj = nullptr; +} + +template +void map(SomeObj* start, [[clang::noescape]] Callback&& callback) +{ + while (start) { + callback(start); + start = [start next]; + } +} + +template +void doubleMap(SomeObj* start, [[clang::noescape]] Callback1&& callback1, Callback2&& callback2) +{ + while (start) { + callback1(start); + callback2(start); + start = [start next]; + } +} + +template +void get_count_cf(CFArrayRef array, [[clang::noescape]] Callback1&& callback1, Callback2&& callback2) +{ + auto count = CFArrayGetCount(array); + callback1(count); + callback2(count); +} + +void noescape_lambda() { + SomeObj* someObj = make_obj(); + SomeObj* otherObj = make_obj(); + map(make_obj(), [&](SomeObj *obj) { + [otherObj doWork]; + }); + doubleMap(make_obj(), [&](SomeObj *obj) { + [otherObj doWork]; + }, [&](SomeObj *obj) { + [otherObj doWork]; + // expected-warning@-1{{Implicitly captured raw-pointer 'otherObj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + }); + ([&] { + [someObj doWork]; + })(); + + CFMutableArrayRef someCF = make_cf(); + get_count_cf(make_cf(), [&](CFIndex count) { + CFArrayAppendValue(someCF, nullptr); + }, [&](CFIndex count) { + CFArrayAppendValue(someCF, nullptr); + // expected-warning@-1{{Implicitly captured reference 'someCF' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + }); +} + +void callFunctionOpaque(WTF::Function&&); +void callFunction(WTF::Function&& function) { + someFunction(); + function(); +} + +void lambda_converted_to_function(SomeObj* obj, CFMutableArrayRef cf) +{ + callFunction([&]() { + [obj doWork]; + // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + CFArrayAppendValue(cf, nullptr); + // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + }); + callFunctionOpaque([&]() { + [obj doWork]; + // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + CFArrayAppendValue(cf, nullptr); + // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + }); +} + +@interface ObjWithSelf : NSObject { + RetainPtr delegate; +} +-(void)doWork; +-(void)run; +@end + +@implementation ObjWithSelf +-(void)doWork { + auto doWork = [&] { + someFunction(); + [delegate doWork]; + }; + callFunctionOpaque(doWork); +} +-(void)run { + someFunction(); +} +@end \ No newline at end of file diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm b/clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm new file mode 100644 index 0000000000000..9820c875b87c0 --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm @@ -0,0 +1,39 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.NoUnretainedMemberChecker -fobjc-arc -verify %s + +#include "objc-mock-types.h" + +namespace members { + + struct Foo { + private: + SomeObj* a = nullptr; + + [[clang::suppress]] + SomeObj* a_suppressed = nullptr; + + protected: + RetainPtr b; + + public: + SomeObj* c = nullptr; + RetainPtr d; + + CFMutableArrayRef e = nullptr; +// expected-warning@-1{{Member variable 'e' in 'members::Foo' is a retainable type 'CFMutableArrayRef'}} + }; + + template + struct FooTmpl { + T* x; + S y; +// expected-warning@-1{{Member variable 'y' in 'members::FooTmpl' is a raw pointer to retainable type}} + }; + + void forceTmplToInstantiate(FooTmpl) {} + + struct [[clang::suppress]] FooSuppressed { + private: + SomeObj* a = nullptr; + }; + +} diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-members.mm b/clang/test/Analysis/Checkers/WebKit/unretained-members.mm new file mode 100644 index 0000000000000..e068a583c18c5 --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/unretained-members.mm @@ -0,0 +1,60 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.NoUnretainedMemberChecker -verify %s + +#include "objc-mock-types.h" + +namespace members { + + struct Foo { + private: + SomeObj* a = nullptr; +// expected-warning@-1{{Member variable 'a' in 'members::Foo' is a raw pointer to retainable type}} + + [[clang::suppress]] + SomeObj* a_suppressed = nullptr; +// No warning. + + protected: + RetainPtr b; +// No warning. + + public: + SomeObj* c = nullptr; +// expected-warning@-1{{Member variable 'c' in 'members::Foo' is a raw pointer to retainable type}} + RetainPtr d; + + CFMutableArrayRef e = nullptr; +// expected-warning@-1{{Member variable 'e' in 'members::Foo' is a retainable type 'CFMutableArrayRef'}} + }; + + template + struct FooTmpl { + T* a; +// expected-warning@-1{{Member variable 'a' in 'members::FooTmpl' is a raw pointer to retainable type}} + S b; +// expected-warning@-1{{Member variable 'b' in 'members::FooTmpl' is a raw pointer to retainable type}} + }; + + void forceTmplToInstantiate(FooTmpl) {} + + struct [[clang::suppress]] FooSuppressed { + private: + SomeObj* a = nullptr; +// No warning. + }; + +} + +namespace ignore_unions { + union Foo { + SomeObj* a; + RetainPtr b; + CFMutableArrayRef c; + }; + + template + union RefPtr { + T* a; + }; + + void forceTmplToInstantiate(RefPtr) {} +} diff --git a/clang/test/Analysis/analyzer-enabled-checkers.c b/clang/test/Analysis/analyzer-enabled-checkers.c index c70aeb21ab045..e5d0acb84a039 100644 --- a/clang/test/Analysis/analyzer-enabled-checkers.c +++ b/clang/test/Analysis/analyzer-enabled-checkers.c @@ -24,6 +24,7 @@ // CHECK-NEXT: core.StackAddressEscape // CHECK-NEXT: core.UndefinedBinaryOperatorResult // CHECK-NEXT: core.VLASize +// CHECK-NEXT: core.builtin.AssumeModeling // CHECK-NEXT: core.builtin.BuiltinFunctions // CHECK-NEXT: core.builtin.NoReturnFunctions // CHECK-NEXT: core.uninitialized.ArraySubscript diff --git a/clang/test/Analysis/builtin_assume.cpp b/clang/test/Analysis/builtin_assume.cpp new file mode 100644 index 0000000000000..7158306be2b82 --- /dev/null +++ b/clang/test/Analysis/builtin_assume.cpp @@ -0,0 +1,64 @@ +// RUN: %clang_analyze_cc1 -std=c++11 -verify %s \ +// RUN: -triple=x86_64-unknown-linux-gnu \ +// RUN: -analyzer-checker=core,security.ArrayBound,debug.ExprInspection + +void clang_analyzer_eval(bool); +void clang_analyzer_value(int); +void clang_analyzer_dump(int); + +// From: https://github.com/llvm/llvm-project/issues/100762 +extern int arrOf10[10]; +void using_builtin(int x) { + __builtin_assume(x > 101); // CallExpr + arrOf10[x] = 404; // expected-warning {{Out of bound access to memory}} +} + +void using_assume_attr(int ax) { + [[assume(ax > 100)]]; // NullStmt with an "assume" attribute. + arrOf10[ax] = 405; // expected-warning {{Out of bound access to memory}} +} + +void using_many_assume_attr(int yx) { + [[assume(yx > 104), assume(yx > 200), assume(yx < 300)]]; // NullStmt with an attribute + arrOf10[yx] = 406; // expected-warning{{Out of bound access to memory}} +} + +int using_assume_attr_has_no_sideeffects(int y) { + int orig_y = y; + clang_analyzer_value(y); // expected-warning {{32s:{ [-2147483648, 2147483647] }}} + clang_analyzer_value(orig_y); // expected-warning {{32s:{ [-2147483648, 2147483647] }}} + clang_analyzer_dump(y); // expected-warning-re {{{{^}}reg_${{[0-9]+}} [debug.ExprInspection]{{$}}}} + clang_analyzer_dump(orig_y); // expected-warning-re {{{{^}}reg_${{[0-9]+}} [debug.ExprInspection]{{$}}}} + + // We should not apply sideeffects of the argument of [[assume(...)]]. + // "y" should not get incremented; + [[assume(++y == 43)]]; // expected-warning {{assumption is ignored because it contains (potential) side-effects}} + + clang_analyzer_dump(y); // expected-warning-re {{{{^}}reg_${{[0-9]+}} [debug.ExprInspection]{{$}}}} + clang_analyzer_dump(orig_y); // expected-warning-re {{{{^}}reg_${{[0-9]+}} [debug.ExprInspection]{{$}}}} + clang_analyzer_value(y); // expected-warning {{32s:{ [-2147483648, 2147483647] }}} + clang_analyzer_value(orig_y); // expected-warning {{32s:{ [-2147483648, 2147483647] }}} + clang_analyzer_eval(y == orig_y); // expected-warning {{TRUE}} Good. + + return y; +} + +int using_builtin_assume_has_no_sideeffects(int y) { + int orig_y = y; + clang_analyzer_value(y); // expected-warning {{32s:{ [-2147483648, 2147483647] }}} + clang_analyzer_value(orig_y); // expected-warning {{32s:{ [-2147483648, 2147483647] }}} + clang_analyzer_dump(y); // expected-warning-re {{{{^}}reg_${{[0-9]+}} [debug.ExprInspection]{{$}}}} + clang_analyzer_dump(orig_y); // expected-warning-re {{{{^}}reg_${{[0-9]+}} [debug.ExprInspection]{{$}}}} + + // We should not apply sideeffects of the argument of __builtin_assume(...) + // "u" should not get incremented; + __builtin_assume(++y == 43); // expected-warning {{assumption is ignored because it contains (potential) side-effects}} + + clang_analyzer_dump(y); // expected-warning-re {{{{^}}reg_${{[0-9]+}} [debug.ExprInspection]{{$}}}} + clang_analyzer_dump(orig_y); // expected-warning-re {{{{^}}reg_${{[0-9]+}} [debug.ExprInspection]{{$}}}} + clang_analyzer_value(y); // expected-warning {{32s:{ [-2147483648, 2147483647] }}} + clang_analyzer_value(orig_y); // expected-warning {{32s:{ [-2147483648, 2147483647] }}} + clang_analyzer_eval(y == orig_y); // expected-warning {{TRUE}} Good. + + return y; +} diff --git a/clang/test/Analysis/cxx23-assume-attribute.cpp b/clang/test/Analysis/cxx23-assume-attribute.cpp index ee049af9f13aa..86e7662cd2af9 100644 --- a/clang/test/Analysis/cxx23-assume-attribute.cpp +++ b/clang/test/Analysis/cxx23-assume-attribute.cpp @@ -1,6 +1,7 @@ // RUN: %clang_analyze_cc1 -std=c++23 -triple x86_64-pc-linux-gnu \ // RUN: -analyzer-checker=core,debug.ExprInspection -verify %s +void clang_analyzer_warnIfReached(); template void clang_analyzer_dump(T); template void clang_analyzer_value(T); @@ -27,33 +28,22 @@ int ternary_in_builtin_assume(int a, int b) { // From: https://github.com/llvm/llvm-project/pull/116462#issuecomment-2517853226 int ternary_in_assume(int a, int b) { - // FIXME notes - // Currently, if this test is run without the core.builtin.Builtin checker, the above function with the __builtin_assume behaves identically to the following test - // i.e. calls to `clang_analyzer_dump` result in "extraneous" prints of the SVal(s) `reg ...` - // as opposed to 4 or 10 - // which likely implies the Program State(s) did not get narrowed. - // A new checker is likely needed to be implemented to properly handle the expressions within `[[assume]]` to eliminate the states where `b` is not narrowed. - [[assume(a > 10 ? b == 4 : b == 10)]]; clang_analyzer_value(a); // expected-warning@-1 {{[-2147483648, 10]}} // expected-warning@-2 {{[11, 2147483647]}} clang_analyzer_dump(b); // expected-warning {{4}} expected-warning {{10}} - // expected-warning-re@-1 {{reg_${{[0-9]+}}}} FIXME: We shouldn't have this dump. if (a > 20) { clang_analyzer_dump(b + 100); // expected-warning {{104}} - // expected-warning-re@-1 {{(reg_${{[0-9]+}}) + 100}} FIXME: We shouldn't have this dump. return 2; } if (a > 10) { clang_analyzer_dump(b + 200); // expected-warning {{204}} - // expected-warning-re@-1 {{(reg_${{[0-9]+}}) + 200}} FIXME: We shouldn't have this dump. return 1; } clang_analyzer_dump(b + 300); // expected-warning {{310}} - // expected-warning-re@-1 {{(reg_${{[0-9]+}}) + 300}} FIXME: We shouldn't have this dump. return 0; } @@ -70,8 +60,12 @@ int assume_and_fallthrough_at_the_same_attrstmt(int a, int b) { return b; } } + // This code should be unreachable. + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + [[assume(false)]]; // This should definitely make it so. - clang_analyzer_dump(33); // expected-warning {{33 S32b}} + clang_analyzer_warnIfReached(); // no-warning + return 0; } diff --git a/clang/test/Analysis/std-c-library-functions-POSIX.c b/clang/test/Analysis/std-c-library-functions-POSIX.c index b53f3132b8687..f6d88e6c1502d 100644 --- a/clang/test/Analysis/std-c-library-functions-POSIX.c +++ b/clang/test/Analysis/std-c-library-functions-POSIX.c @@ -237,3 +237,14 @@ void test_readlinkat_bufsize_zero(int fd, char *Buf, size_t Bufsize) { else clang_analyzer_eval(Bufsize == 0); // expected-warning{{UNKNOWN}} } + +void test_setsockopt_bufptr_null(int x) { + char buf[10] = {0}; + + setsockopt(1, 2, 3, 0, 0); + setsockopt(1, 2, 3, buf, 10); + if (x) + setsockopt(1, 2, 3, buf, 11); // expected-warning{{The 4th argument to 'setsockopt' is a buffer with size 10 but should be a buffer with size equal to or greater than the value of the 5th argument (which is 11)}} + else + setsockopt(1, 2, 3, 0, 10); // expected-warning{{The 4th argument to 'setsockopt' is NULL but should not be NULL}} +} diff --git a/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c b/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c index faf0a8f19d919..d2900c6a42fff 100644 --- a/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c +++ b/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c @@ -32,6 +32,7 @@ // CHECK-NEXT: core.StackAddressEscape // CHECK-NEXT: core.UndefinedBinaryOperatorResult // CHECK-NEXT: core.VLASize +// CHECK-NEXT: core.builtin.AssumeModeling // CHECK-NEXT: core.builtin.BuiltinFunctions // CHECK-NEXT: core.builtin.NoReturnFunctions // CHECK-NEXT: core.uninitialized.ArraySubscript diff --git a/clang/test/C/C2y/n3409.c b/clang/test/C/C2y/n3409.c new file mode 100644 index 0000000000000..01be716132b11 --- /dev/null +++ b/clang/test/C/C2y/n3409.c @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -verify -std=c2y -pedantic %s +// RUN: %clang_cc1 -verify=pre-c2y -std=c2y -Wpre-c2y-compat %s +// RUN: %clang_cc1 -verify=ext -std=c23 -pedantic %s +// expected-no-diagnostics + +/* WG14 N3409: Clang 21 + * Slay Some Earthly Demons X + * + * Removes the requirement that an expression with type void cannot be used in + * any way. This was making it UB to use a void expression in a _Generic + * selection expression for no good reason, as well as making it UB to cast a + * void expression to void, etc. + */ + +extern void x; +void foo() { + // FIXME: this is technically an extension before C2y and should be diagnosed + // under -pedantic. + (void)(void)1; + // FIXME: same with this. + x; + _Generic(x, void: 1); /* pre-c2y-warning {{use of incomplete type 'void' in a '_Generic' association is incompatible with C standards before C2y}} + ext-warning {{incomplete type 'void' in a '_Generic' association is a C2y extension}} + */ + _Generic(x, typeof(x): 1); /* pre-c2y-warning {{use of incomplete type 'typeof (x)' (aka 'void') in a '_Generic' association is incompatible with C standards before C2y}} + ext-warning {{incomplete type 'typeof (x)' (aka 'void') in a '_Generic' association is a C2y extension}} + */ + (void)_Generic(void, default : 1); /* pre-c2y-warning {{passing a type argument as the first operand to '_Generic' is incompatible with C standards before C2y}} + ext-warning {{passing a type argument as the first operand to '_Generic' is a C2y extension}} + */ + + // This is not sufficiently important of an extension to warrant a "not + // compatible with standards before C2y" warning, but it is an extension in + // C23 and earlier. + return x; // ext-warning {{void function 'foo' should not return void expression}} +} diff --git a/clang/test/C/C2y/n3410.c b/clang/test/C/C2y/n3410.c new file mode 100644 index 0000000000000..e1cb41f375b82 --- /dev/null +++ b/clang/test/C/C2y/n3410.c @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -verify -std=c2y -Wall -pedantic -Wno-unused %s + +/* WG14 N3410: No + * Slay Some Earthly Demons XI + * + * It is now ill-formed for the same identifier within a TU to have both + * internal and external linkage. + */ + +void func1() { + extern int a; // #a +} + +// This 'a' is the same as the one declared extern above. +static int a; /* expected-error {{static declaration of 'a' follows non-static declaration}} + expected-note@#a {{previous declaration is here}} + */ + +static int b; +void func2() { + // This 'b' is the same as the one declaraed static above, but this is not + // ill-formed because of C2y 6.2.2p4, which gives this variable internal + // linkage because the previous declaration had internal linkage. + extern int b; // Ok +} + +static int c, d; +void func3() { + int c; // no linkage, different object from the one declared above. + for (int d;;) { + // This 'c' is the same as the one declared at file scope, but because of + // the local scope 'c', the file scope 'c' is not visible. + // FIXME: This should be diagnosed under N3410. + extern int c; + // This 'd' is the same as the one declared at file scope as well, but + // because of the 'd' declared within the for loop, the file scope 'd' is + // also not visible, same as with 'c'. + // FIXME: This should be diagnosed under N3410. + extern int d; + } + for (static int e;;) { + extern int e; // Ok for the same reason as 'b' above. + } +} + diff --git a/clang/test/C/C2y/n3411.c b/clang/test/C/C2y/n3411.c new file mode 100644 index 0000000000000..934a7c70fa67e --- /dev/null +++ b/clang/test/C/C2y/n3411.c @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -verify=good -std=c2y -Wall -pedantic %s +// RUN: %clang_cc1 -verify -Wnewline-eof -std=c2y -Wall -pedantic %s +// RUN: %clang_cc1 -verify -std=c23 -Wall -pedantic %s +// RUN: %clang_cc1 -verify=good -std=c23 %s + +/* WG14 N3411: Yes + * Slay Some Earthly Demons XII + * + * Allow a non-empty source file to end without a final newline character. Note + * that this file intentionally does not end with a trailing newline. + */ +// good-no-diagnostics + +int x; // Ensure the file contains at least one declaration. +// expected-warning {{no newline at end of file}} \ No newline at end of file diff --git a/clang/test/C/C2y/n3451.c b/clang/test/C/C2y/n3451.c new file mode 100644 index 0000000000000..e6727f6cbe591 --- /dev/null +++ b/clang/test/C/C2y/n3451.c @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 -verify -std=c2y -Wall -pedantic -Wno-gnu-folding-constant %s +// RUN: %clang_cc1 -verify -std=c23 -Wall -pedantic -Wno-gnu-folding-constant %s + +/* WG14 N3451: Yes + * Initialization of anonymous structures and unions + * + * This paper allows initialization of anonymous structure and union members + * within the containing object. + */ +// expected-no-diagnostics + +constexpr struct { + int a : 10; + int : 12; + long b; +} s = { 1, 2 }; +static_assert(s.a == 1 && s.b == 2); + +constexpr union { + int : 16; + char c; +} u = {3}; +static_assert(u.c == 3); + +constexpr struct { + union { + float a; + int b; + void *p; + }; + char c; +} t = {{.b = 1}, 2}; +static_assert(t.b == 1 && t.c == 2); + +constexpr struct { + union { + float a; + int b; + void *p; + }; + char c; +} v = {.b = 1, 2}; +static_assert(v.b == 1 && v.c == 2); diff --git a/clang/test/C/C2y/n3478.c b/clang/test/C/C2y/n3478.c new file mode 100644 index 0000000000000..9d1d8f97fa962 --- /dev/null +++ b/clang/test/C/C2y/n3478.c @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -verify -std=c2y %s +// RUN: %clang_cc1 -verify -std=c23 %s + +/* WG14 N3478: Yes + * Slay Some Earthly Demons XIII + * + * It was previously UB to end a source file with a partial preprocessing token + * or a partial comment. Clang has always diagnosed these. + */ + +// expected-error@+1 {{unterminated /* comment}} +/* \ No newline at end of file diff --git a/clang/test/C/C2y/n3481.c b/clang/test/C/C2y/n3481.c new file mode 100644 index 0000000000000..1088b62dd8085 --- /dev/null +++ b/clang/test/C/C2y/n3481.c @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -verify -std=c2y %s +// RUN: %clang_cc1 -verify -std=c23 %s + +/* WG14 N3481: Yes + * Slay Some Earthly Demons XVI + * + * It was previously UB to use a non-array lvalue with an incomplete type in a + * context which required the value of the object. Clang has always diagnosed + * this as an error, except when the incomplete type is void. Then we allow the + * dereference, but not for a value computation. + */ + +struct f *p; // expected-note {{forward declaration of 'struct f'}} +void g(void) { + (void)*p; // expected-error {{incomplete type 'struct f' where a complete type is required}} +} + +void h(void *ptr) { + (void)*ptr; // expected-warning {{ISO C does not allow indirection on operand of type 'void *'}} + (*ptr)++; /* expected-warning {{ISO C does not allow indirection on operand of type 'void *'}} + expected-error {{cannot increment value of type 'void'}} + */ +} diff --git a/clang/test/C/C2y/n3496.c b/clang/test/C/C2y/n3496.c new file mode 100644 index 0000000000000..74b8880b8ca19 --- /dev/null +++ b/clang/test/C/C2y/n3496.c @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -verify -std=c2y -ffreestanding %s +// RUN: %clang_cc1 -verify -std=c23 -ffreestanding %s + +/* WG14 N3496: Clang 20 + * Clarify the specification of the width macros + * + * C23 N2412 mandated a two's complement sign representation for integers, and + * added *_WIDTH macros for all of the various integer types. There was + * confusion as to whether BOOL_WIDTH specified the minimum number of bits or + * an exact number of bits. N3496 clarified that it's an exact number of bits. + */ +// expected-no-diagnostics + +#include + +static_assert(BOOL_WIDTH == 1); + diff --git a/clang/test/C/C2y/n3505.c b/clang/test/C/C2y/n3505.c new file mode 100644 index 0000000000000..7f307390e09ca --- /dev/null +++ b/clang/test/C/C2y/n3505.c @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -verify -std=c2y -Wall -pedantic %s +// RUN: %clang_cc1 -verify -std=c23 -Wall -pedantic %s +// RUN: %clang_cc1 -verify -std=c11 -Wall -pedantic %s +// RUN: %clang_cc1 -verify -std=c99 -Wall -pedantic %s + +/* WG14 N3505: Yes + * Preprocessor integer expressions, v. 2 + * + * This introduces a constraint that preprocessing tokens must be an integer + * literal, character literal, punctuator, or some other implementation-defined + * sequence of tokens (to support builtins that insert odd tokens into the + * parsing stream). + */ + +// This is technically an integer constant expression, but it does not match +// the new constraints and thus needs to be diagnosed. +#if 1 ? 1 : (""[0] += 5) // expected-error {{invalid token at start of a preprocessor expression}} +#endif + +// But with a character literal, it is fine. +#if 1 ? 1 : ('a' + 5) // Ok +#endif + +// This doesn't mean that all punctuators are fine, however. +#if 1 ? 1 : ('a' += 5) // expected-error {{token is not a valid binary operator in a preprocessor subexpression}} +#endif + +// But some are. +#if 1 ? 1 : ~('a') // Ok +#endif + +int x; // Needs a declaration to avoid a pedantic warning diff --git a/clang/test/CIR/CodeGen/basic.cpp b/clang/test/CIR/CodeGen/basic.cpp index 6a2faa725a34d..ef922cc2b46fc 100644 --- a/clang/test/CIR/CodeGen/basic.cpp +++ b/clang/test/CIR/CodeGen/basic.cpp @@ -1,9 +1,4 @@ -// RUN: not %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - 2>&1 | FileCheck %s - -// This error is caused by the "const int i = 2" line in f2(). When -// initaliziers are implemented, the checks there should be updated -// and the "not" should be removed from the run line. -// CHECK: error: ClangIR code gen Not Yet Implemented: emitAutoVarInit +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - 2>&1 | FileCheck %s int f1() { int i; @@ -22,13 +17,15 @@ int f2() { } // CHECK: cir.func @f2() -> !cir.int -// CHECK: %[[I_PTR:.*]] = cir.alloca !cir.int, !cir.ptr>, ["i", const] {alignment = 4 : i64} +// CHECK: %[[I_PTR:.*]] = cir.alloca !cir.int, !cir.ptr>, ["i", init, const] {alignment = 4 : i64} +// CHECK: %[[TWO:.*]] = cir.const #cir.int<2> : !cir.int +// CHECK: cir.store %[[TWO]], %[[I_PTR]] : !cir.int, !cir.ptr> // CHECK: %[[I:.*]] = cir.load %[[I_PTR]] : !cir.ptr>, !cir.int // CHECK: cir.return %[[I]] : !cir.int int f3(int i) { - return i; - } + return i; +} // CHECK: cir.func @f3(%[[ARG:.*]]: !cir.int loc({{.*}})) -> !cir.int // CHECK: %[[ARG_ALLOCA:.*]] = cir.alloca !cir.int, !cir.ptr>, ["i", init] {alignment = 4 : i64} diff --git a/clang/test/CIR/CodeGen/local-vars.cpp b/clang/test/CIR/CodeGen/local-vars.cpp new file mode 100644 index 0000000000000..14be8f4da902b --- /dev/null +++ b/clang/test/CIR/CodeGen/local-vars.cpp @@ -0,0 +1,65 @@ +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - 2>&1 | FileCheck %s + +void test() { + int i = 1; + long l = 2l; + float f = 3.0f; + double d = 4.0; + bool b1 = true; + bool b2 = false; + const int ci = 1; + const long cl = 2l; + const float cf = 3.0f; + const double cd = 4.0; + const bool cb1 = true; + const bool cb2 = false; + int uii; + long uil; + float uif; + double uid; + bool uib; +} + +// CHECK: module +// CHECK: cir.func @test() +// CHECK: %[[I_PTR:.*]] = cir.alloca !cir.int, !cir.ptr>, ["i", init] {alignment = 4 : i64} +// CHECK: %[[L_PTR:.*]] = cir.alloca !cir.int, !cir.ptr>, ["l", init] {alignment = 8 : i64} +// CHECK: %[[F_PTR:.*]] = cir.alloca !cir.float, !cir.ptr, ["f", init] {alignment = 4 : i64} +// CHECK: %[[D_PTR:.*]] = cir.alloca !cir.double, !cir.ptr, ["d", init] {alignment = 8 : i64} +// CHECK: %[[B1_PTR:.*]] = cir.alloca !cir.bool, !cir.ptr, ["b1", init] {alignment = 1 : i64} +// CHECK: %[[B2_PTR:.*]] = cir.alloca !cir.bool, !cir.ptr, ["b2", init] {alignment = 1 : i64} +// CHECK: %[[CI_PTR:.*]] = cir.alloca !cir.int, !cir.ptr>, ["ci", init, const] {alignment = 4 : i64} +// CHECK: %[[CL_PTR:.*]] = cir.alloca !cir.int, !cir.ptr>, ["cl", init, const] {alignment = 8 : i64} +// CHECK: %[[CF_PTR:.*]] = cir.alloca !cir.float, !cir.ptr, ["cf", init, const] {alignment = 4 : i64} +// CHECK: %[[CD_PTR:.*]] = cir.alloca !cir.double, !cir.ptr, ["cd", init, const] {alignment = 8 : i64} +// CHECK: %[[CB1_PTR:.*]] = cir.alloca !cir.bool, !cir.ptr, ["cb1", init, const] {alignment = 1 : i64} +// CHECK: %[[CB2_PTR:.*]] = cir.alloca !cir.bool, !cir.ptr, ["cb2", init, const] {alignment = 1 : i64} +// CHECK: %[[UII_PTR:.*]] = cir.alloca !cir.int, !cir.ptr>, ["uii"] {alignment = 4 : i64} +// CHECK: %[[UIL_PTR:.*]] = cir.alloca !cir.int, !cir.ptr>, ["uil"] {alignment = 8 : i64} +// CHECK: %[[UIF_PTR:.*]] = cir.alloca !cir.float, !cir.ptr, ["uif"] {alignment = 4 : i64} +// CHECK: %[[UID_PTR:.*]] = cir.alloca !cir.double, !cir.ptr, ["uid"] {alignment = 8 : i64} +// CHECK: %[[UIB_PTR:.*]] = cir.alloca !cir.bool, !cir.ptr, ["uib"] {alignment = 1 : i64} +// CHECK: %[[ONE:.*]] = cir.const #cir.int<1> : !cir.int +// CHECK: cir.store %[[ONE]], %[[I_PTR]] : !cir.int, !cir.ptr> +// CHECK: %[[TWO:.*]] = cir.const #cir.int<2> : !cir.int +// CHECK: cir.store %[[TWO]], %[[L_PTR]] : !cir.int, !cir.ptr> +// CHECK: %[[THREE:.*]] = cir.const #cir.fp<3.0{{.*}}> : !cir.float +// CHECK: cir.store %[[THREE]], %[[F_PTR]] : !cir.float, !cir.ptr +// CHECK: %[[FOUR:.*]] = cir.const #cir.fp<4.0{{.*}}> : !cir.double +// CHECK: cir.store %[[FOUR]], %[[D_PTR]] : !cir.double, !cir.ptr +// CHECK: %[[TRUE:.*]] = cir.const #true +// CHECK: cir.store %[[TRUE]], %[[B1_PTR]] : !cir.bool, !cir.ptr +// CHECK: %[[FALSE:.*]] = cir.const #false +// CHECK: cir.store %[[FALSE]], %[[B2_PTR]] : !cir.bool, !cir.ptr +// CHECK: %[[ONEC:.*]] = cir.const #cir.int<1> : !cir.int +// CHECK: cir.store %[[ONEC]], %[[CI_PTR]] : !cir.int, !cir.ptr> +// CHECK: %[[TWOC:.*]] = cir.const #cir.int<2> : !cir.int +// CHECK: cir.store %[[TWOC]], %[[CL_PTR]] : !cir.int, !cir.ptr> +// CHECK: %[[THREEC:.*]] = cir.const #cir.fp<3.0{{.*}}> : !cir.float +// CHECK: cir.store %[[THREEC]], %[[CF_PTR]] : !cir.float, !cir.ptr +// CHECK: %[[FOURC:.*]] = cir.const #cir.fp<4.0{{.*}}> : !cir.double +// CHECK: cir.store %[[FOURC]], %[[CD_PTR]] : !cir.double, !cir.ptr +// CHECK: %[[TRUEC:.*]] = cir.const #true +// CHECK: cir.store %[[TRUEC]], %[[CB1_PTR]] : !cir.bool, !cir.ptr +// CHECK: %[[FALSEC:.*]] = cir.const #false +// CHECK: cir.store %[[FALSEC]], %[[CB2_PTR]] : !cir.bool, !cir.ptr diff --git a/clang/test/CIR/Lowering/basic.cpp b/clang/test/CIR/Lowering/basic.cpp index 2c29368bd5835..d1dc343a068a8 100644 --- a/clang/test/CIR/Lowering/basic.cpp +++ b/clang/test/CIR/Lowering/basic.cpp @@ -1,9 +1,4 @@ -// RUN: not %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - 2>&1 | FileCheck %s - -// This error is caused by the "const int i = 2" line in f2(). When -// initaliziers are implemented, the checks there should be updated -// and the "not" should be removed from the run line. -// CHECK: error: ClangIR code gen Not Yet Implemented: emitAutoVarInit +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - 2>&1 | FileCheck %s int f1() { int i; @@ -22,6 +17,7 @@ int f2() { // CHECK: define{{.*}} i32 @f2() { // CHECK: %[[I_PTR:.*]] = alloca i32, i64 1, align 4 +// CHECK: store i32 2, ptr %[[I_PTR]], align 4 // CHECK: %[[I:.*]] = load i32, ptr %[[I_PTR]], align 4 // CHECK: ret i32 %[[I]] diff --git a/clang/test/CIR/Lowering/func-simple.cpp b/clang/test/CIR/Lowering/func-simple.cpp index 1429ec270774b..32d75cdd2c15d 100644 --- a/clang/test/CIR/Lowering/func-simple.cpp +++ b/clang/test/CIR/Lowering/func-simple.cpp @@ -13,6 +13,26 @@ int intfunc() { return 42; } // CHECK: define{{.*}} i32 @intfunc() // CHECK: ret i32 42 +int scopes() { + { + { + return 99; + } + } +} +// CHECK: define{{.*}} i32 @scopes() { +// CHECK: br label %[[LABEL1:.*]] +// CHECK: [[LABEL1]]: +// CHECK: br label %[[LABEL2:.*]] +// CHECK: [[LABEL2]]: +// CHECK: ret i32 99 +// CHECK: [[LABEL3:.*]]: +// CHECK: br label %[[LABEL4:.*]] +// CHECK: [[LABEL4]]: +// CHECK: call void @llvm.trap() +// CHECK: unreachable +// CHECK: } + long longfunc() { return 42l; } // CHECK: define{{.*}} i64 @longfunc() { // CHECK: ret i64 42 diff --git a/clang/test/CIR/Lowering/local-vars.cpp b/clang/test/CIR/Lowering/local-vars.cpp new file mode 100644 index 0000000000000..bd47ed14065c6 --- /dev/null +++ b/clang/test/CIR/Lowering/local-vars.cpp @@ -0,0 +1,55 @@ +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - 2>&1 | FileCheck %s + +void test() { + int i = 1; + long l = 2l; + float f = 3.0f; + double d = 4.0; + bool b1 = true; + bool b2 = false; + const int ci = 1; + const long cl = 2l; + const float cf = 3.0f; + const double cd = 4.0; + const bool cb1 = true; + const bool cb2 = false; + int uii; + long uil; + float uif; + double uid; + bool uib; +} + +// Note: The alignment of i64 stores below is wrong. That should be fixed +// when we add alignment attributes to the load/store ops. + +// CHECK: define{{.*}} void @test() +// CHECK: %[[I_PTR:.*]] = alloca i32, i64 1, align 4 +// CHECK: %[[L_PTR:.*]] = alloca i64, i64 1, align 8 +// CHECK: %[[F_PTR:.*]] = alloca float, i64 1, align 4 +// CHECK: %[[D_PTR:.*]] = alloca double, i64 1, align 8 +// CHECK: %[[B1_PTR:.*]] = alloca i8, i64 1, align 1 +// CHECK: %[[B2_PTR:.*]] = alloca i8, i64 1, align 1 +// CHECK: %[[CI_PTR:.*]] = alloca i32, i64 1, align 4 +// CHECK: %[[CL_PTR:.*]] = alloca i64, i64 1, align 8 +// CHECK: %[[CF_PTR:.*]] = alloca float, i64 1, align 4 +// CHECK: %[[CD_PTR:.*]] = alloca double, i64 1, align 8 +// CHECK: %[[CB1_PTR:.*]] = alloca i8, i64 1, align 1 +// CHECK: %[[CB2_PTR:.*]] = alloca i8, i64 1, align 1 +// CHECK: %[[UII_PTR:.*]] = alloca i32, i64 1, align 4 +// CHECK: %[[UIL_PTR:.*]] = alloca i64, i64 1, align 8 +// CHECK: %[[UIF_PTR:.*]] = alloca float, i64 1, align 4 +// CHECK: %[[UID_PTR:.*]] = alloca double, i64 1, align 8 +// CHECK: %[[UIB_PTR:.*]] = alloca i8, i64 1, align 1 +// CHECK: store i32 1, ptr %[[I_PTR]], align 4 +// CHECK: store i64 2, ptr %[[L_PTR]], align 4 +// CHECK: store float 3.000000e+00, ptr %[[F_PTR]], align 4 +// CHECK: store double 4.000000e+00, ptr %[[D_PTR]], align 8 +// CHECK: store i8 1, ptr %[[B1_PTR]], align 1 +// CHECK: store i8 0, ptr %[[B2_PTR]], align 1 +// CHECK: store i32 1, ptr %[[CI_PTR]], align 4 +// CHECK: store i64 2, ptr %[[CL_PTR]], align 4 +// CHECK: store float 3.000000e+00, ptr %[[CF_PTR]], align 4 +// CHECK: store double 4.000000e+00, ptr %[[CD_PTR]], align 8 +// CHECK: store i8 1, ptr %[[CB1_PTR]], align 1 +// CHECK: store i8 0, ptr %[[CB2_PTR]], align 1 diff --git a/clang/test/CMakeLists.txt b/clang/test/CMakeLists.txt index 1c93e2b0d9844..51fd4820d7edb 100644 --- a/clang/test/CMakeLists.txt +++ b/clang/test/CMakeLists.txt @@ -136,6 +136,7 @@ if( NOT CLANG_BUILT_STANDALONE ) llvm-dis llvm-dwarfdump llvm-ifs + llvm-link llvm-lto2 llvm-mc llvm-modextract diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp index 0fa98ad101f6c..5c281ac806836 100644 --- a/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp +++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp @@ -190,7 +190,7 @@ namespace InhCtor { } struct DerivedFromNS : NS::NS { // No special case unless the NNS names a class. - using InhCtor::NS::NS; // expected-error {{using declaration in class refers into 'InhCtor::NS::', which is not a class}} + using InhCtor::NS::NS; // expected-error {{using declaration in class refers into 'InhCtor::NS', which is not a class}} }; diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.unqual/p7.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.unqual/p7.cpp index fc4e359666ba9..9632fda296aa1 100644 --- a/clang/test/CXX/basic/basic.lookup/basic.lookup.unqual/p7.cpp +++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.unqual/p7.cpp @@ -33,6 +33,5 @@ namespace test1 { // specifiers. namespace test2 { template struct bar {}; - // expected-note@-1 {{template parameter is declared here}} template struct foo : bar {}; // expected-error {{use of class template 'foo' requires template arguments}} expected-note {{template is declared here}} } diff --git a/clang/test/CXX/class.access/class.access.dcl/p1.cpp b/clang/test/CXX/class.access/class.access.dcl/p1.cpp index 118ab9e52d0a1..fdb1373dd9b12 100644 --- a/clang/test/CXX/class.access/class.access.dcl/p1.cpp +++ b/clang/test/CXX/class.access/class.access.dcl/p1.cpp @@ -331,7 +331,7 @@ namespace test4 { // expected-warning@-2 {{access declarations are deprecated; use using declarations instead}} #else // expected-error@-4 {{ISO C++11 does not allow access declarations; use using declarations instead}} - // expected-error@-5 {{using declaration refers into 'Subclass::', which is not a base class of 'C'}} + // expected-error@-5 {{using declaration refers into 'Subclass', which is not a base class of 'C'}} #endif int bar(); diff --git a/clang/test/CXX/class.access/class.friend/p3-cxx0x.cpp b/clang/test/CXX/class.access/class.friend/p3-cxx0x.cpp index 9aabdbe540a66..f7216ea7eb7b0 100644 --- a/clang/test/CXX/class.access/class.friend/p3-cxx0x.cpp +++ b/clang/test/CXX/class.access/class.friend/p3-cxx0x.cpp @@ -36,7 +36,7 @@ class A { public: class foo {}; static int y; - template friend class B::ty; // expected-warning {{dependent nested name specifier 'B::' for friend class declaration is not supported}} + template friend class B::ty; // expected-warning {{dependent nested name specifier 'B' for friend class declaration is not supported}} }; template class B { typedef int ty; }; diff --git a/clang/test/CXX/class.access/class.friend/p6.cpp b/clang/test/CXX/class.access/class.friend/p6.cpp index 47104e29dc6b3..da6d212b78868 100644 --- a/clang/test/CXX/class.access/class.friend/p6.cpp +++ b/clang/test/CXX/class.access/class.friend/p6.cpp @@ -8,11 +8,11 @@ struct X { struct Y { friend void ::f1() { } // expected-error{{friend function definition cannot be qualified with '::'}} - friend void X::f2() { } // expected-error{{friend function definition cannot be qualified with 'X::'}} + friend void X::f2() { } // expected-error{{friend function definition cannot be qualified with 'X'}} }; template struct Z { - friend void T::f() {} // expected-error{{friend function definition cannot be qualified with 'T::'}} + friend void T::f() {} // expected-error{{friend function definition cannot be qualified with 'T'}} }; void local() { @@ -32,6 +32,6 @@ namespace N { template struct A { friend void f3(T) {} friend void f3(T) {} // expected-error{{friend function specialization cannot be defined}} - friend void N::f4(T) {} // expected-error{{friend function definition cannot be qualified with 'N::'}} - friend void N::f4(T) {} // expected-error{{friend function definition cannot be qualified with 'N::'}} + friend void N::f4(T) {} // expected-error{{friend function definition cannot be qualified with 'N'}} + friend void N::f4(T) {} // expected-error{{friend function definition cannot be qualified with 'N'}} }; diff --git a/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3.cpp b/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3.cpp index 3351666525374..657695657e0f7 100644 --- a/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3.cpp +++ b/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3.cpp @@ -28,7 +28,7 @@ class D2 : public B { using B::E; using B::e; using B::x; - using C::g; // expected-error{{using declaration refers into 'C::', which is not a base class of 'D2'}} + using C::g; // expected-error{{using declaration refers into 'C', which is not a base class of 'D2'}} // These are valid in C++98 but not in C++11. using D::f2; @@ -36,10 +36,10 @@ class D2 : public B { using D::e2; using D::x2; #if __cplusplus >= 201103L - // expected-error@-5 {{using declaration refers into 'D::', which is not a base class of 'D2'}} - // expected-error@-5 {{using declaration refers into 'D::', which is not a base class of 'D2'}} - // expected-error@-5 {{using declaration refers into 'D::', which is not a base class of 'D2'}} - // expected-error@-5 {{using declaration refers into 'D::', which is not a base class of 'D2'}} + // expected-error@-5 {{using declaration refers into 'D', which is not a base class of 'D2'}} + // expected-error@-5 {{using declaration refers into 'D', which is not a base class of 'D2'}} + // expected-error@-5 {{using declaration refers into 'D', which is not a base class of 'D2'}} + // expected-error@-5 {{using declaration refers into 'D', which is not a base class of 'D2'}} #endif using B::EC; diff --git a/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p4.cpp b/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p4.cpp index 65ccb751b9aa5..973cd77279f19 100644 --- a/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p4.cpp +++ b/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p4.cpp @@ -210,7 +210,7 @@ namespace test4 { using Subclass::foo; // legal in C++03 #if __cplusplus >= 201103L // expected-error@-3 {{refers to its own class}} - // expected-error@-3 {{refers into 'Subclass::', which is not a base class}} + // expected-error@-3 {{refers into 'Subclass', which is not a base class}} #endif int bar(); diff --git a/clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp index 57c99212f4c69..2bceb3e267790 100644 --- a/clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp +++ b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp @@ -251,5 +251,5 @@ void P1957R2(void *a, int *b, Agg *c, int Agg::*d) { Agg tc = {c}; // expected-error {{cannot be narrowed}} expected-note {{}} Agg td = {d}; // expected-error {{cannot be narrowed}} expected-note {{}} } -template struct BoolParam {}; // expected-note {{template parameter is declared here}} +template struct BoolParam {}; BoolParam<&P1957R2> bp; // expected-error {{not allowed in a converted constant expression}} diff --git a/clang/test/CXX/dcl.decl/dcl.meaning/p1-0x.cpp b/clang/test/CXX/dcl.decl/dcl.meaning/p1-0x.cpp index 13be079a40bc3..4475bd787c74d 100644 --- a/clang/test/CXX/dcl.decl/dcl.meaning/p1-0x.cpp +++ b/clang/test/CXX/dcl.decl/dcl.meaning/p1-0x.cpp @@ -18,9 +18,9 @@ class tfoo { }; template -int decltype(tfoo())::i; // expected-error{{nested name specifier 'decltype(tfoo())::' for declaration does not refer into a class, class template or class template partial specialization}} +int decltype(tfoo())::i; // expected-error{{nested name specifier 'decltype(tfoo())' for declaration does not refer into a class, class template or class template partial specialization}} template -void decltype(tfoo())::func() { // expected-error{{nested name specifier 'decltype(tfoo())::' for declaration does not refer into a class, class template or class template partial specialization}} +void decltype(tfoo())::func() { // expected-error{{nested name specifier 'decltype(tfoo())' for declaration does not refer into a class, class template or class template partial specialization}} } // An init-declarator named with a qualified-id can refer to an element of the diff --git a/clang/test/CXX/drs/cwg0xx.cpp b/clang/test/CXX/drs/cwg0xx.cpp index c035aa3f4ec61..282e71bbf3bda 100644 --- a/clang/test/CXX/drs/cwg0xx.cpp +++ b/clang/test/CXX/drs/cwg0xx.cpp @@ -806,7 +806,6 @@ namespace cwg49 { // cwg49: 2.8 // since-cxx17-error@#cwg49-c {{non-type template argument is not a constant expression}} // since-cxx17-note@#cwg49-c {{read of non-constexpr variable 'q' is not allowed in a constant expression}} // since-cxx17-note@#cwg49-q {{declared here}} - // since-cxx17-note@#cwg49-A {{template parameter is declared here}} } // namespace cwg49 namespace cwg50 { // cwg50: 2.7 @@ -1019,9 +1018,9 @@ namespace cwg62 { // cwg62: 2.9 struct A { struct { int n; } b; }; - template struct X {}; // #cwg62-X - template T get() { return get(); } // #cwg62-get - template int take(T) { return 0; } // #cwg62-take + template struct X {}; + template T get() { return get(); } + template int take(T) { return 0; } X x1; A a = get(); @@ -1035,27 +1034,22 @@ namespace cwg62 { // cwg62: 2.9 X x2; // cxx98-error@-1 {{template argument uses unnamed type}} - // cxx98-note@#cwg62-X {{template parameter is declared here}} // cxx98-note@#cwg62-unnamed {{unnamed type used in template argument was declared here}} X x3; // cxx98-error@-1 {{template argument uses unnamed type}} - // cxx98-note@#cwg62-X {{template parameter is declared here}} // cxx98-note@#cwg62-unnamed {{unnamed type used in template argument was declared here}} NoNameForLinkagePtr p1 = get(); // cxx98-error@-1 {{template argument uses unnamed type}} - // cxx98-note@#cwg62-get {{template parameter is declared here}} // cxx98-note@#cwg62-unnamed {{unnamed type used in template argument was declared here}} - // cxx98-note@-4 {{while substituting explicitly-specified template arguments}} + // cxx98-note@-3 {{while substituting explicitly-specified template arguments}} NoNameForLinkagePtr p2 = get(); // cxx98-error@-1 {{template argument uses unnamed type}} - // cxx98-note@#cwg62-get {{template parameter is declared here}} // cxx98-note@#cwg62-unnamed {{unnamed type used in template argument was declared here}} - // cxx98-note@-4 {{while substituting explicitly-specified template arguments}} + // cxx98-note@-3 {{while substituting explicitly-specified template arguments}} int n1 = take(noNameForLinkagePtr); // cxx98-error@-1 {{template argument uses unnamed type}} - // cxx98-note@#cwg62-take {{template parameter is declared here}} // cxx98-note@#cwg62-unnamed {{unnamed type used in template argument was declared here}} - // cxx98-note@-4 {{while substituting deduced template arguments}} + // cxx98-note@-3 {{while substituting deduced template arguments}} X x4; @@ -1063,24 +1057,18 @@ namespace cwg62 { // cwg62: 2.9 struct NoLinkage {}; X a; // cxx98-error@-1 {{template argument uses local type }} - // cxx98-note@#cwg62-X {{template parameter is declared here}} X b; // cxx98-error@-1 {{template argument uses local type }} - // cxx98-note@#cwg62-X {{template parameter is declared here}} get(); // cxx98-error@-1 {{template argument uses local type }} - // cxx98-note@#cwg62-get {{template parameter is declared here}} - // cxx98-note@-3 {{while substituting explicitly-specified template arguments}} + // cxx98-note@-2 {{while substituting explicitly-specified template arguments}} get(); // cxx98-error@-1 {{template argument uses local type }} - // cxx98-note@#cwg62-get {{template parameter is declared here}} - // cxx98-note@-3 {{while substituting explicitly-specified template arguments}} + // cxx98-note@-2 {{while substituting explicitly-specified template arguments}} X c; // cxx98-error@-1 {{template argument uses local type }} - // cxx98-note@#cwg62-X {{template parameter is declared here}} X d; // cxx98-error@-1 {{template argument uses local type }} - // cxx98-note@#cwg62-X {{template parameter is declared here}} } } // namespace cwg62 @@ -1147,11 +1135,10 @@ namespace cwg69 { // cwg69: 9 extern template void f(); // cxx98-error@-1 {{extern templates are a C++11 extension}} // expected-error@-2 {{explicit instantiation declaration of 'f' with internal linkage}} - template struct Q {}; // #cwg69-Q + template struct Q {}; Q<&f > q; // cxx98-error@-1 {{non-type template argument referring to function 'f' with internal linkage is a C++11 extension}} // cxx98-note@#cwg69-f {{non-type template argument refers to function here}} - // cxx98-note@#cwg69-Q {{template parameter is declared here}} } // namespace cwg69 namespace cwg70 { // cwg70: 2.7 diff --git a/clang/test/CXX/drs/cwg10xx.cpp b/clang/test/CXX/drs/cwg10xx.cpp index 10ee50558c7e8..c5b96c4ab8ffc 100644 --- a/clang/test/CXX/drs/cwg10xx.cpp +++ b/clang/test/CXX/drs/cwg10xx.cpp @@ -43,7 +43,6 @@ namespace cwg1004 { // cwg1004: 5 template class U = T::template A> struct Third { }; // expected-error@-1 {{is a constructor name}} // expected-note@#cwg1004-t {{in instantiation of default argument}} - // expected-note@-3 {{template parameter is declared here}} Third > t; // #cwg1004-t } // namespace cwg1004 diff --git a/clang/test/CXX/drs/cwg13xx.cpp b/clang/test/CXX/drs/cwg13xx.cpp index 8a9f285673699..9c72fefb5b65c 100644 --- a/clang/test/CXX/drs/cwg13xx.cpp +++ b/clang/test/CXX/drs/cwg13xx.cpp @@ -180,7 +180,6 @@ namespace cwg1315 { // cwg1315: partial // dependent type of T::value is not the same as 'int'. // A core issue will be opened to decide what is supposed to happen here. template struct C; - // expected-note@-1 {{template parameter is declared here}} template struct C; // expected-error@-1 {{type of specialized non-type template argument depends on a template parameter of the partial specialization}} } // namespace cwg1315 diff --git a/clang/test/CXX/drs/cwg14xx.cpp b/clang/test/CXX/drs/cwg14xx.cpp index 759a402b5f9d6..26febd9715184 100644 --- a/clang/test/CXX/drs/cwg14xx.cpp +++ b/clang/test/CXX/drs/cwg14xx.cpp @@ -660,14 +660,14 @@ namespace cwg1487 { // cwg1487: 3.3 struct A { // #cwg1482-A struct B { using A::A; - // since-cxx11-error@-1 {{using declaration refers into 'A::', which is not a base class of 'B'}} + // since-cxx11-error@-1 {{using declaration refers into 'A', which is not a base class of 'B'}} }; struct C : A { // since-cxx11-error@-1 {{base class has incomplete type}} // since-cxx11-note@#cwg1482-A {{definition of 'cwg1487::A' is not complete until the closing '}'}} using A::A; - // since-cxx11-error@-1 {{using declaration refers into 'A::', which is not a base class of 'C'}} + // since-cxx11-error@-1 {{using declaration refers into 'A', which is not a base class of 'C'}} }; struct D; diff --git a/clang/test/CXX/drs/cwg18xx.cpp b/clang/test/CXX/drs/cwg18xx.cpp index 5f40fe469b01b..5b4551ba0143b 100644 --- a/clang/test/CXX/drs/cwg18xx.cpp +++ b/clang/test/CXX/drs/cwg18xx.cpp @@ -26,7 +26,6 @@ S V; // #cwg1801-S-i // cxx98-14-error@-1 {{non-type template argument does not refer to any declaration}} // cxx98-14-note@#cwg1801-S {{template parameter is declared here}} // cxx17-error@#cwg1801-S-i {{non-type template argument refers to subobject '.i'}} -// cxx17-note@#cwg1801-S {{template parameter is declared here}} } // namespace cwg1801 namespace cwg1802 { // cwg1802: 3.1 @@ -421,25 +420,25 @@ class C { template friend struct A::B; - // expected-warning@-1 {{dependent nested name specifier 'A::' for friend class declaration is not supported; turning off access control for 'C'}} + // expected-warning@-1 {{dependent nested name specifier 'A' for friend class declaration is not supported; turning off access control for 'C'}} template friend void A::f(); - // expected-warning@-1 {{dependent nested name specifier 'A::' for friend class declaration is not supported; turning off access control for 'C'}} + // expected-warning@-1 {{dependent nested name specifier 'A' for friend class declaration is not supported; turning off access control for 'C'}} // FIXME: this is ill-formed, because A​::​D does not end with a simple-template-id template friend void A::D::g(); - // expected-warning@-1 {{dependent nested name specifier 'A::D::' for friend class declaration is not supported; turning off access control for 'C'}} + // expected-warning@-1 {{dependent nested name specifier 'A::D' for friend class declaration is not supported; turning off access control for 'C'}} template friend int *A::h(); - // expected-warning@-1 {{dependent nested name specifier 'A::' for friend class declaration is not supported; turning off access control for 'C'}} + // expected-warning@-1 {{dependent nested name specifier 'A' for friend class declaration is not supported; turning off access control for 'C'}} template template friend T A::i(); - // expected-warning@-1 {{dependent nested name specifier 'A::' for friend class declaration is not supported; turning off access control for 'C'}} + // expected-warning@-1 {{dependent nested name specifier 'A' for friend class declaration is not supported; turning off access control for 'C'}} }; C c; diff --git a/clang/test/CXX/drs/cwg19xx.cpp b/clang/test/CXX/drs/cwg19xx.cpp index 55a7c7cbba66f..15ad3921cb1be 100644 --- a/clang/test/CXX/drs/cwg19xx.cpp +++ b/clang/test/CXX/drs/cwg19xx.cpp @@ -18,7 +18,7 @@ struct A { }; } int N::f() { return 0; } -int N::g() { return 0; } +int N::g() { return 0; } // expected-error@-1 {{out-of-line definition of 'g' does not match any declaration in namespace 'cwg1900::N'}} } // namespace cwg1900 @@ -84,7 +84,7 @@ namespace cwg1909 { // cwg1909: 3.7 }; struct C { template static int C; - // expected-error@-1 {{member 'C' has the same name as its class}} + // expected-error@-1 {{member 'C' has the same name as its class}} // cxx98-11-error@-2 {{variable templates are a C++14 extension}} }; struct D { @@ -105,7 +105,7 @@ class X { // FIXME: this is ill-formed, because A::B::C does not end with a simple-template-id template friend class A::B::C; - // expected-warning@-1 {{dependent nested name specifier 'A::B::' for friend class declaration is not supported; turning off access control for 'X'}} + // expected-warning@-1 {{dependent nested name specifier 'A::B' for friend class declaration is not supported; turning off access control for 'X'}} }; template<> struct A { typedef struct Q B; @@ -170,7 +170,7 @@ class X { // FIXME: this is ill-formed, because A::B::C does not end with a simple-template-id template friend class A::B::C; - // expected-warning@-1 {{dependent nested name specifier 'A::B::' for friend class declaration is not supported; turning off access control for 'X'}} + // expected-warning@-1 {{dependent nested name specifier 'A::B' for friend class declaration is not supported; turning off access control for 'X'}} }; } // namespace cwg1945 diff --git a/clang/test/CXX/drs/cwg1xx.cpp b/clang/test/CXX/drs/cwg1xx.cpp index 51f320fc43cfa..4a5394be146bc 100644 --- a/clang/test/CXX/drs/cwg1xx.cpp +++ b/clang/test/CXX/drs/cwg1xx.cpp @@ -26,22 +26,18 @@ namespace cwg100 { // cwg100: 2.7 // cxx98-14-error@#cwg100-a {{non-type template argument does not refer to any declaration}} // cxx98-14-note@#cwg100-A {{template parameter is declared here}} // since-cxx17-error@#cwg100-a {{pointer to string literal is not allowed in a template argument}} - // since-cxx17-note@#cwg100-A {{template parameter is declared here}} B<"bar"> b; // #cwg100-b // cxx98-14-error@#cwg100-b {{non-type template argument does not refer to any declaration}} // cxx98-14-note@#cwg100-B {{template parameter is declared here}} // since-cxx17-error@#cwg100-b {{reference to string literal is not allowed in a template argument}} - // since-cxx17-note@#cwg100-B {{template parameter is declared here}} C<"baz"> c; // #cwg100-c // cxx98-14-error@#cwg100-c {{non-type template argument does not refer to any declaration}} // cxx98-14-note@#cwg100-C {{template parameter is declared here}} // since-cxx17-error@#cwg100-c {{pointer to subobject of string literal is not allowed in a template argument}} - // since-cxx17-note@#cwg100-C {{template parameter is declared here}} D<*"quux"> d; // #cwg100-d // cxx98-14-error@#cwg100-d {{non-type template argument does not refer to any declaration}} // cxx98-14-note@#cwg100-D {{template parameter is declared here}} // since-cxx17-error@#cwg100-d {{reference to subobject of string literal is not allowed in a template argument}} - // since-cxx17-note@#cwg100-D {{template parameter is declared here}} } // namespace cwg100 namespace cwg101 { // cwg101: 3.5 @@ -100,7 +96,7 @@ namespace cwg108 { // cwg108: 2.9 template struct A { struct B { typedef int X; }; B::X x; - // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name B::X; implicit 'typename' is a C++20 extension}} + // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name 'B::X'; implicit 'typename' is a C++20 extension}} struct C : B { X x; }; // expected-error@-1 {{unknown type name 'X'}} }; @@ -156,17 +152,15 @@ namespace cwg112 { // cwg112: 3.1 volatile T a2[1] = {}; const Arr a3 = {}; // #cwg112-a3 volatile Arr a4 = {}; - template struct X {}; // #cwg112-X + template struct X {}; // FIXME: Test this somehow in C++11 and on. X x1; // cxx98-error@-1 {{non-type template argument referring to object 'a1' with internal linkage is a C++11 extension}} // cxx98-note@#cwg112-a1 {{non-type template argument refers to object here}} - // cxx98-note@#cwg112-X {{template parameter is declared here}} X x2; X x3; // cxx98-error@-1 {{non-type template argument referring to object 'a3' with internal linkage is a C++11 extension}} // cxx98-note@#cwg112-a3 {{non-type template argument refers to object here}} - // cxx98-note@#cwg112-X {{template parameter is declared here}} X x4; } // namespace cwg112 @@ -327,7 +321,7 @@ namespace cwg121 { // cwg121: 2.7 X::Y x; T::Y y; // expected-error@-1 {{use 'template' keyword to treat 'Y' as a dependent template name}} - // cxx98-17-error@-2 {{missing 'typename' prior to dependent type name T::Y; implicit 'typename' is a C++20 extension}} + // cxx98-17-error@-2 {{missing 'typename' prior to dependent type name 'T::Y'; implicit 'typename' is a C++20 extension}} }; Z z; } // namespace cwg121 @@ -640,7 +634,7 @@ namespace example3 { struct Base { private: static const int i = 10; // #cwg138-ex3-Base-i - + public: struct Data; // Elaborated type specifier is not the sole constituent of declaration, @@ -654,7 +648,7 @@ struct Base { }; }; struct Data { - void f() { + void f() { int i2 = Base::i; // expected-error@-1 {{'i' is a private member of 'cwg138::example3::Base'}} // expected-note@#cwg138-ex3-Base-i {{declared private here}} @@ -1315,8 +1309,8 @@ namespace cwg184 { // cwg184: 2.7 template class T> void A::f() { // #cwg184-T T<> t; - // expected-error@-1 {{missing template argument for template parameter}} - // expected-note@#cwg184-T {{template parameter is declared here}} + // expected-error@-1 {{too few template arguments for template template parameter 'T'}} + // expected-note@#cwg184-T {{template is declared here}} } template class T> void A::g() { diff --git a/clang/test/CXX/drs/cwg20xx.cpp b/clang/test/CXX/drs/cwg20xx.cpp index fd31a51f79ec5..141a1012aef93 100644 --- a/clang/test/CXX/drs/cwg20xx.cpp +++ b/clang/test/CXX/drs/cwg20xx.cpp @@ -27,7 +27,7 @@ int b = __builtin_addressof(b2)->foo; // cwg2009: na namespace cwg2026 { // cwg2026: 11 - template struct X {}; // #cwg2026-X + template struct X {}; const int a = a + 1; // #cwg2026-a // expected-warning@-1 {{variable 'a' is uninitialized when used within its own initialization}} @@ -35,11 +35,9 @@ namespace cwg2026 { // cwg2026: 11 // cxx98-error@-1 {{non-type template argument of type 'int' is not an integral constant expression}} // cxx98-note@-2 {{initializer of 'a' is not a constant expression}} // cxx98-note@#cwg2026-a {{declared here}} - // cxx98-note@#cwg2026-X {{template parameter is declared here}} // since-cxx11-error@#cwg2026-xa {{non-type template argument is not a constant expression}} // since-cxx11-note@#cwg2026-xa {{initializer of 'a' is not a constant expression}} // since-cxx11-note@#cwg2026-a {{declared here}} - // since-cxx11-note@#cwg2026-X {{template parameter is declared here}} #if __cplusplus >= 201103L constexpr int b = b; @@ -67,11 +65,9 @@ namespace cwg2026 { // cwg2026: 11 // cxx98-error@-1 {{non-type template argument of type 'int' is not an integral constant expression}} // cxx98-note@-2 {{initializer of 'e' is not a constant expression}} // cxx98-note@#cwg2026-e {{declared here}} - // cxx98-note@#cwg2026-X {{template parameter is declared here}} // since-cxx11-error@#cwg2026-xe {{non-type template argument is not a constant expression}} // since-cxx11-note@#cwg2026-xe {{initializer of 'e' is not a constant expression}} // since-cxx11-note@#cwg2026-e {{declared here}} - // since-cxx11-note@#cwg2026-X {{template parameter is declared here}} #if __cplusplus >= 201103L static constexpr int f = f; @@ -153,7 +149,7 @@ namespace cwg2076 { // cwg2076: 13 operator string_view() const; }; - void foo(const string &); // #cwg2076-foo + void foo(const string &); // #cwg2076-foo void bar(string_view); // #cwg2076-bar void func(const string &arg) { @@ -433,7 +429,7 @@ int f() return 0; } } // namespace GH42233 -} // namespace cwg2091 +} // namespace cwg2091 namespace cwg2094 { // cwg2094: 5 struct A { int n; }; diff --git a/clang/test/CXX/drs/cwg21xx.cpp b/clang/test/CXX/drs/cwg21xx.cpp index 97bf320658759..42a7c4d7bbded 100644 --- a/clang/test/CXX/drs/cwg21xx.cpp +++ b/clang/test/CXX/drs/cwg21xx.cpp @@ -24,7 +24,7 @@ namespace std { } namespace cwg2100 { // cwg2100: 12 - template struct X {}; // #cwg2100-X + template struct X {}; template struct A { static const int n = 1; int f() { @@ -35,7 +35,6 @@ namespace cwg2100 { // cwg2100: 12 return X<&n>::n; // ok, value-dependent // cxx98-14-error@-1 {{non-type template argument refers to object 'n' that does not have linkage}} // cxx98-14-note@#cwg2100-n {{non-type template argument refers to object here}} - // cxx98-14-note@#cwg2100-X {{template parameter is declared here}} } }; template struct X

{ diff --git a/clang/test/CXX/drs/cwg22xx.cpp b/clang/test/CXX/drs/cwg22xx.cpp index d93070ef3804d..8c8ad9f7f74ee 100644 --- a/clang/test/CXX/drs/cwg22xx.cpp +++ b/clang/test/CXX/drs/cwg22xx.cpp @@ -1,10 +1,10 @@ // RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify=expected -fexceptions -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors namespace cwg2211 { // cwg2211: 8 @@ -196,6 +196,17 @@ void g() { #endif } // namespace cwg2277 +namespace cwg2285 { // cwg2285: 4 +// Note: Clang 4 implements this DR but it set a wrong value of `__cplusplus` +#if __cplusplus >= 201703L + void test() { + using T = int[1]; + auto [a] = T{a}; + // since-cxx17-error@-1 {{binding 'a' cannot appear in the initializer of its own decomposition declaration}} + } +#endif +} // namespace cwg2285 + namespace cwg2292 { // cwg2292: 9 #if __cplusplus >= 201103L template using id = T; diff --git a/clang/test/CXX/drs/cwg28xx.cpp b/clang/test/CXX/drs/cwg28xx.cpp index caa9b0f1f5058..b32e649374893 100644 --- a/clang/test/CXX/drs/cwg28xx.cpp +++ b/clang/test/CXX/drs/cwg28xx.cpp @@ -150,7 +150,7 @@ struct A { // FIXME: The index of the pack-index-specifier is printed as a memory address in the diagnostic. template friend struct Ts...[0]::C; - // since-cxx26-warning@-1 {{dependent nested name specifier 'Ts...[0]::' for friend template declaration is not supported; ignoring this friend declaration}} + // since-cxx26-warning@-1 {{dependent nested name specifier 'Ts...[0]' for friend template declaration is not supported; ignoring this friend declaration}} }; #endif diff --git a/clang/test/CXX/drs/cwg2xx.cpp b/clang/test/CXX/drs/cwg2xx.cpp index 0d644bae78382..4f383aacd4532 100644 --- a/clang/test/CXX/drs/cwg2xx.cpp +++ b/clang/test/CXX/drs/cwg2xx.cpp @@ -426,7 +426,7 @@ namespace cwg224 { // cwg224: 16 A::type a; A::type b; A::type c; - // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name A::type; implicit 'typename' is a C++20 extension}} + // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name 'A::type'; implicit 'typename' is a C++20 extension}} ::cwg224::example1::A::type d; class B { @@ -435,13 +435,13 @@ namespace cwg224 { // cwg224: 16 A::type a; A::type b; A::type c; - // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name A::type; implicit 'typename' is a C++20 extension}} + // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name 'A::type'; implicit 'typename' is a C++20 extension}} ::cwg224::example1::A::type d; B::type e; A::B::type f; A::B::type g; - // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name A::B::type; implicit 'typename' is a C++20 extension}} + // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name 'A::B::type'; implicit 'typename' is a C++20 extension}} typename A::B::type h; }; }; @@ -450,25 +450,25 @@ namespace cwg224 { // cwg224: 16 typedef int type; A::type a; A::type b; - // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name A::type; implicit 'typename' is a C++20 extension}} + // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name 'A::type'; implicit 'typename' is a C++20 extension}} }; template struct B { typedef int type; B::type b1; B::type b2; - // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name B::type; implicit 'typename' is a C++20 extension}} + // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name 'B::type'; implicit 'typename' is a C++20 extension}} typedef T1 my_T1; static const int my_I = I; static const int my_I2 = I+0; static const int my_I3 = my_I; B::type b3; - // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name B::type; implicit 'typename' is a C++20 extension}} + // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name 'B::type'; implicit 'typename' is a C++20 extension}} B::type b4; - // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name B::type; implicit 'typename' is a C++20 extension}} + // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name 'B::type'; implicit 'typename' is a C++20 extension}} B::type b5; - // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name B::type; implicit 'typename' is a C++20 extension}} + // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name 'B::type'; implicit 'typename' is a C++20 extension}} }; } @@ -480,7 +480,7 @@ namespace cwg224 { // cwg224: 16 X::type x; X::i, double>::type y; X::i, long>::type z; - // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name X::i, long>::type; implicit 'typename' is a C++20 extension}} + // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name 'X::i, long>::type'; implicit 'typename' is a C++20 extension}} int f(); }; template int A::f() { @@ -867,7 +867,7 @@ namespace cwg252 { // cwg252: 3.1 struct E { void operator delete(void*, int); void operator delete(void*) = delete; // #cwg252-E - // cxx98-error@-1 {{deleted function definitions are a C++11 extension}} + // cxx98-error@-1 {{deleted function definitions are a C++11 extension}} virtual ~E(); }; E::~E() {} diff --git a/clang/test/CXX/drs/cwg3xx.cpp b/clang/test/CXX/drs/cwg3xx.cpp index a616120b82063..164cc26ae585a 100644 --- a/clang/test/CXX/drs/cwg3xx.cpp +++ b/clang/test/CXX/drs/cwg3xx.cpp @@ -317,20 +317,18 @@ namespace cwg319 { // cwg319: no typedef int (*pa)[n1]; pa parr; // ok, type has linkage despite using 'n1' - template struct X {}; // #cwg319-X + template struct X {}; void f() { struct A { int n; }; extern A a; // FIXME: ill-formed X xa; // cxx98-error@-1 {{template argument uses local type 'A'}} - // cxx98-note@#cwg319-X {{template parameter is declared here}} typedef A B; extern B b; // FIXME: ill-formed X xb; // cxx98-error@-1 {{template argument uses local type 'A'}} - // cxx98-note@#cwg319-X {{template parameter is declared here}} const int n = 1; typedef int (*C)[n]; @@ -605,7 +603,7 @@ namespace cwg336 { // cwg336: 2.7 // expected-error@-1 {{out-of-line definition of 'mf1' does not match any declaration in 'cwg336::Pre::A::B'}} // expected-note@#cwg336-B {{defined here}} template template<> void A::B::mf2() {} - // expected-error@-1 {{nested name specifier 'A::B::' for declaration does not refer into a class, class template or class template partial specialization}} + // expected-error@-1 {{nested name specifier 'A::B' for declaration does not refer into a class, class template or class template partial specialization}} } namespace Post { template class A { @@ -620,7 +618,7 @@ namespace cwg336 { // cwg336: 2.7 template<> template<> template void A::B::mf1(T t) {} // FIXME: This diagnostic isn't very good. template template<> void A::B::mf2() {} - // expected-error@-1 {{nested name specifier 'A::B::' for declaration does not refer into a class, class template or class template partial specialization}} + // expected-error@-1 {{nested name specifier 'A::B' for declaration does not refer into a class, class template or class template partial specialization}} } } // namespace cwg336 @@ -999,7 +997,6 @@ namespace cwg354 { // cwg354: 3.1 c++11 // since-cxx17-note@#cwg354-ptr_mem {{template parameter is declared here}} ptr_mem<(int S::*)0> m1; // cxx98-error@-1 {{non-type template argument is not a pointer to member constant}} - // cxx98-note@#cwg354-ptr_mem {{template parameter is declared here}} ptr_mem<(float S::*)0> m2; // #cwg354-m2 // cxx98-error@#cwg354-m2 {{non-type template argument of type 'float S::*' cannot be converted to a value of type 'int S::*'}} // cxx98-note@#cwg354-ptr_mem {{template parameter is declared here}} @@ -1505,7 +1502,7 @@ namespace cwg389 { // cwg389: no typedef struct {} const C; // #cwg389-C typedef enum {} const D; // #cwg389-D }; - template struct T {}; // #cwg389-T + template struct T {}; struct WithLinkage1 {}; enum WithLinkage2 {}; @@ -1546,23 +1543,18 @@ namespace cwg389 { // cwg389: no typedef T BadArg1; // expected-error@-1 {{template argument uses unnamed type}} - // expected-note@#cwg389-T {{template parameter is declared here}} // expected-note@#cwg389-no-link-1 {{unnamed type used in template argument was declared here}} typedef T BadArg2; // expected-error@-1 {{template argument uses unnamed type}} - // expected-note@#cwg389-T {{template parameter is declared here}} // expected-note@#cwg389-no-link-2 {{unnamed type used in template argument was declared here}} typedef T BadArg3; // expected-error@-1 {{template argument uses unnamed type}} - // expected-note@#cwg389-T {{template parameter is declared here}} // expected-note@#cwg389-C {{unnamed type used in template argument was declared here}} typedef T BadArg4; // expected-error@-1 {{template argument uses unnamed type}} - // expected-note@#cwg389-T {{template parameter is declared here}} // expected-note@#cwg389-D {{unnamed type used in template argument was declared here}} typedef T BadArg5; // expected-error@-1 {{template argument uses unnamed type}} - // expected-note@#cwg389-T {{template parameter is declared here}} // expected-note@#cwg389-C {{unnamed type used in template argument was declared here}} #endif diff --git a/clang/test/CXX/drs/cwg4xx.cpp b/clang/test/CXX/drs/cwg4xx.cpp index 78b03b3df2323..d76c6012b9195 100644 --- a/clang/test/CXX/drs/cwg4xx.cpp +++ b/clang/test/CXX/drs/cwg4xx.cpp @@ -43,15 +43,12 @@ namespace cwg401 { // cwg401: 2.8 // expected-error@#cwg401-A {{'type' is a private member of 'cwg401::C'}} // expected-note@#cwg402-friend-A-C {{in instantiation of default argument for 'A' required here}} // expected-note@#cwg402-C-type {{implicitly declared private here}} - // expected-note@#cwg401-A {{template parameter is declared here}} // expected-error@#cwg401-A {{'type' is a protected member of 'cwg401::B'}} // expected-note@#cwg402-b {{in instantiation of default argument for 'A' required here}} // expected-note@#cwg402-B-type {{declared protected here}} - // expected-note@#cwg401-A {{template parameter is declared here}} // expected-error@#cwg401-A {{'type' is a private member of 'cwg401::D'}} // expected-note@#cwg402-d {{in instantiation of default argument for 'A' required here}} // expected-note@#cwg402-D-type {{implicitly declared private here}} - // expected-note@#cwg401-A {{template parameter is declared here}} class B { protected: typedef int type; // #cwg402-B-type @@ -83,9 +80,8 @@ namespace cwg401 { // cwg401: 2.8 // to not treat the default template argument as a SFINAE context in C++98. template void f(T) {} // #cwg402-f // cxx98-error@-1 {{default template arguments for a function template are a C++11 extension}} - // cxx98-note@-2 {{template parameter is declared here}} - // cxx98-error@-3 {{'type' is a protected member of 'cwg401::B'}} - // cxx98-note@-4 {{in instantiation of default argument for 'f' required here}} + // cxx98-error@-2 {{'type' is a protected member of 'cwg401::B'}} + // cxx98-note@-3 {{in instantiation of default argument for 'f' required here}} // cxx98-note@#cwg402-f-b {{while substituting deduced template arguments into function template 'f' [with T = B, U = (no value)]}} // cxx98-note@#cwg402-B-type {{declared protected here}} void g(B b) { f(b); } // #cwg402-f-b @@ -261,7 +257,7 @@ namespace cwg409 { // cwg409: 2.7 A::B b2; A::B b3; A::B b4; - // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name A::B; implicit 'typename' is a C++20 extension}} + // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name 'A::B'; implicit 'typename' is a C++20 extension}} }; } // namespace cwg409 @@ -640,17 +636,15 @@ namespace cwg431 { // cwg431: 2.8 } // namespace cwg431 namespace cwg432 { // cwg432: 3.0 - template struct A {}; // #cwg432-A + template struct A {}; template struct B : A {}; // expected-error@-1 {{use of class template 'B' requires template arguments}} - // expected-note@#cwg432-A {{template parameter is declared here}} - // expected-note@-3 {{template is declared here}} + // expected-note@-2 {{template is declared here}} template struct C : A > {}; #if __cplusplus >= 201103L template struct D : decltype(A()) {}; // since-cxx11-error@-1 {{use of class template 'D' requires template arguments}} - // since-cxx11-note@#cwg432-A {{template parameter is declared here}} - // since-cxx11-note@-3 {{template is declared here}} + // since-cxx11-note@-2 {{template is declared here}} #endif } // namespace cwg432 @@ -1383,7 +1377,6 @@ namespace cwg487 { // cwg487: 2.7 namespace cwg488 { // cwg488: 2.9 c++11 template void f(T); - // cxx98-note@-1 {{template parameter is declared here}} void f(int); void g() { // FIXME: It seems CWG thought this should be a SFINAE failure prior to diff --git a/clang/test/CXX/drs/cwg5xx.cpp b/clang/test/CXX/drs/cwg5xx.cpp index 1fdfe5785c5c4..0825b52653b4d 100644 --- a/clang/test/CXX/drs/cwg5xx.cpp +++ b/clang/test/CXX/drs/cwg5xx.cpp @@ -254,9 +254,9 @@ namespace cwg526 { // cwg526: 2.7 typedef int type; X::type v1; X<(N)>::type v2; - // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name X<(N)>::type; implicit 'typename' is a C++20 extension}} + // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name 'X<(N)>::type'; implicit 'typename' is a C++20 extension}} X<+N>::type v3; - // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name X<+N>::type; implicit 'typename' is a C++20 extension}} + // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name 'X<+N>::type'; implicit 'typename' is a C++20 extension}} }; } // namespace cwg526 @@ -783,7 +783,7 @@ struct Outer { }; template Outer::Inner* Outer::Inner::self() { return this; } -// cxx98-17-error@-1 {{missing 'typename' prior to dependent type name Outer::Inner; implicit 'typename' is a C++20 extension}} +// cxx98-17-error@-1 {{missing 'typename' prior to dependent type name 'Outer::Inner'; implicit 'typename' is a C++20 extension}} } // namespace cwg560 diff --git a/clang/test/CXX/drs/cwg6xx.cpp b/clang/test/CXX/drs/cwg6xx.cpp index 11a77575c70c1..e2eb009508b52 100644 --- a/clang/test/CXX/drs/cwg6xx.cpp +++ b/clang/test/CXX/drs/cwg6xx.cpp @@ -79,11 +79,10 @@ namespace cwg602 { // cwg602: 2.7 } // namespace cwg602 namespace cwg603 { // cwg603: 3.1 - template struct S {}; // #cwg603-S + template struct S {}; typedef S<'\001'> S1; typedef S<(1ul << __CHAR_BIT__) + 1> S1; // since-cxx11-error@-1 {{non-type template argument evaluates to 257, which cannot be narrowed to type 'unsigned char'}} - // since-cxx11-note@#cwg603-S {{template parameter is declared here}} } // namespace cwg603 // cwg604: na @@ -409,13 +408,13 @@ namespace cwg638 { // cwg638: no class X { typedef int type; template friend struct A::B; - // expected-warning@-1 {{dependent nested name specifier 'A::' for friend class declaration is not supported; turning off access control for 'X'}} + // expected-warning@-1 {{dependent nested name specifier 'A' for friend class declaration is not supported; turning off access control for 'X'}} template friend void A::f(); - // expected-warning@-1 {{dependent nested name specifier 'A::' for friend class declaration is not supported; turning off access control for 'X'}} + // expected-warning@-1 {{dependent nested name specifier 'A' for friend class declaration is not supported; turning off access control for 'X'}} template friend void A::g(); - // expected-warning@-1 {{dependent nested name specifier 'A::' for friend class declaration is not supported; turning off access control for 'X'}} + // expected-warning@-1 {{dependent nested name specifier 'A' for friend class declaration is not supported; turning off access control for 'X'}} template friend void A::C::h(); - // expected-warning@-1 {{dependent nested name specifier 'A::C::' for friend class declaration is not supported; turning off access control for 'X'}} + // expected-warning@-1 {{dependent nested name specifier 'A::C' for friend class declaration is not supported; turning off access control for 'X'}} }; template<> struct A { diff --git a/clang/test/CXX/expr/expr.const/p3-0x.cpp b/clang/test/CXX/expr/expr.const/p3-0x.cpp index f40e1af14d111..3eedef3cf7712 100644 --- a/clang/test/CXX/expr/expr.const/p3-0x.cpp +++ b/clang/test/CXX/expr/expr.const/p3-0x.cpp @@ -4,7 +4,7 @@ // A converted constant expression of type T is a core constant expression, int nonconst = 8; // expected-note 3 {{here}} enum NonConstE : unsigned char { NCE = nonconst }; // expected-error {{enumerator value is not a constant expression}} expected-note {{read of non-const}} -template struct NonConstT {}; // expected-error {{non-type template argument is not a constant expression}} expected-note {{read of non-const}} expected-note {{template parameter is declared here}} +template struct NonConstT {}; // expected-error {{non-type template argument is not a constant expression}} expected-note {{read of non-const}} void NonConstF() { switch (nonconst) { case nonconst: // expected-error {{case value is not a constant expression}} expected-note {{read of non-const}} @@ -66,7 +66,7 @@ enum class EEE : unsigned short { e = 123456, // expected-error {{enumerator value evaluates to 123456, which cannot be narrowed to type 'unsigned short'}} f = -3 // expected-error {{enumerator value evaluates to -3, which cannot be narrowed to type 'unsigned short'}} }; -template using A = int; // expected-note 4{{template parameter is declared here}} +template using A = int; // cxx17-note 2{{template parameter is declared here}} using Int = A; using Int = A; // expected-error {{not implicitly convertible}} @@ -79,8 +79,7 @@ using Int = A<-3>; // expected-error {{template argument evaluates to -3, which // integral conversions as well as boolean conversions. // FIXME: Per core issue 1407, this is not correct. template struct Val { static constexpr T value = v; }; -// cxx17-note@-1 1{{template parameter is declared here}} -// expected-note@-2 2{{template parameter is declared here}} +// cxx17-note@-1 2{{template parameter is declared here}} static_assert(Val::value == 1, ""); // ok static_assert(Val::value == 0, ""); // ok static_assert(Val::value == 1, ""); // ok diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp index 2572e766cb263..5433cfb21955d 100644 --- a/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp @@ -77,7 +77,7 @@ using r2i3 = r2; // expected-error{{constraints not satisfied for clas namespace ns2 { template struct identity {}; - template requires requires { typename identity; } // expected-note 2{{because 'typename identity' would be invalid: missing template argument for template parameter}} + template requires requires { typename identity; } // expected-note 2{{because 'typename identity' would be invalid: too few template arguments for class template 'identity'}} struct r4 {}; using r4i1 = r4; // expected-error{{constraints not satisfied for class template 'r4' [with Ts = ]}} diff --git a/clang/test/CXX/special/class.inhctor/elsewhere.cpp b/clang/test/CXX/special/class.inhctor/elsewhere.cpp index f86f4b86faa7d..9aa310bb7f6ab 100644 --- a/clang/test/CXX/special/class.inhctor/elsewhere.cpp +++ b/clang/test/CXX/special/class.inhctor/elsewhere.cpp @@ -33,14 +33,14 @@ struct D1 : I1 { template struct A {}; template struct B : A, A { - using A::A; // expected-error {{'A::', which is not a base class of 'B'}} + using A::A; // expected-error {{'A', which is not a base class of 'B'}} }; B bb; B bc; B bd; // expected-note {{here}} template struct C : A { - using A::A; // expected-error {{'A::', which is not a base class of 'C'}} + using A::A; // expected-error {{'A', which is not a base class of 'C'}} }; C cb; C cc; // expected-note {{here}} diff --git a/clang/test/CXX/temp/temp.arg/temp.arg.nontype/p1-11.cpp b/clang/test/CXX/temp/temp.arg/temp.arg.nontype/p1-11.cpp index 332f69bacb69e..692958ef565cf 100644 --- a/clang/test/CXX/temp/temp.arg/temp.arg.nontype/p1-11.cpp +++ b/clang/test/CXX/temp/temp.arg/temp.arg.nontype/p1-11.cpp @@ -9,7 +9,7 @@ template struct IP { // expected-note 6 {{template parameter is declar IP *ip2; }; -template struct IR {}; // expected-note {{template parameter is declared here}} +template struct IR {}; constexpr std::nullptr_t get_nullptr() { return nullptr; } diff --git a/clang/test/CXX/temp/temp.arg/temp.arg.nontype/p1.cpp b/clang/test/CXX/temp/temp.arg/temp.arg.nontype/p1.cpp index e979051e23419..629000d88acc3 100644 --- a/clang/test/CXX/temp/temp.arg/temp.arg.nontype/p1.cpp +++ b/clang/test/CXX/temp/temp.arg/temp.arg.nontype/p1.cpp @@ -31,16 +31,16 @@ namespace non_type_tmpl_param { // omitted if the name refers to a function or array and shall be omitted // if the corresopnding template-parameter is a reference; or namespace addr_of_obj_or_func { - template struct X0 { }; // expected-note 5{{here}} cxx17-note 2{{here}} + template struct X0 { }; // expected-note 5{{here}} #if __cplusplus >= 201103L // precxx17-note@-2 2{{template parameter is declared here}} #endif - template struct X1 { }; // cxx17-note {{here}} precxx17-note{{here}} + template struct X1 { }; // cxx17-note {{here}} #if __cplusplus <= 199711L // precxx17-note@-2 {{here}} #endif - template struct X2 { }; // expected-note 5{{here}} + template struct X2 { }; // expected-note 4{{here}} template struct X2k { }; // expected-note {{here}} template struct X3 { }; // expected-note 4{{here}} @@ -180,7 +180,6 @@ namespace addr_of_obj_or_func { namespace bad_args { template struct X0 { }; // precxx17-note 4{{template parameter is declared here}} - // cxx17-note@-1 3{{template parameter is declared here}} int i = 42; X0<&i + 2> x0a; // precxx17-error{{non-type template argument does not refer to any declaration}} \ cxx17-error {{non-type template argument is not a constant expression}} \ diff --git a/clang/test/CXX/temp/temp.arg/temp.arg.nontype/p5.cpp b/clang/test/CXX/temp/temp.arg/temp.arg.nontype/p5.cpp index bb7512a95ed3b..034ad49d0715c 100644 --- a/clang/test/CXX/temp/temp.arg/temp.arg.nontype/p5.cpp +++ b/clang/test/CXX/temp/temp.arg/temp.arg.nontype/p5.cpp @@ -48,9 +48,9 @@ namespace pointer_to_object_parameters { X(int, int); operator int() const; }; - - template struct A2; // expected-note 1-2{{template parameter is declared here}} - + + template struct A2; // expected-note 0-1{{template parameter is declared here}} + X *X_ptr; // expected-note 0-1{{declared here}} X an_X; X array_of_Xs[10]; @@ -131,16 +131,16 @@ namespace reference_parameters { S3 s3v; S3 s3cv; } - + namespace PR6250 { template void inc() { ref++; // expected-error{{read-only variable is not assignable}} } - + template void bind() { T &ref2 = ref; // expected-error{{drops 'const' qualifier}} } - + int counter; void test() { inc(); // expected-note{{instantiation of}} @@ -213,7 +213,7 @@ namespace reference_to_function { // (13.4). namespace pointer_to_member_function { struct X { }; - struct Y : X { + struct Y : X { int f(int); int g(int); int g(float); diff --git a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp index 2638bef8f20a0..3caed045c6688 100644 --- a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp +++ b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp @@ -20,39 +20,39 @@ eval> eE; // expected-error{{implicit instantiation of undefined t template< template // expected-error {{cannot be narrowed from type 'int' to 'short'}} // expected-error@-1 {{conversion from 'int' to 'void *' is not allowed in a converted constant expression}} - class TT // expected-note 2{{template parameter is declared here}} + class TT // expected-note 2{{previous template template parameter is here}} > struct X0 { }; template struct X0a; template struct X0b; template struct X0c; -template struct X0d; // expected-note {{template parameter is declared here}} -template struct X0e; // expected-note {{template parameter is declared here}} +template struct X0d; +template struct X0e; // expected-note{{template parameter is declared here}} X0 inst_x0a; X0 inst_x0b; X0 inst_x0c; -X0 inst_x0d; // expected-note {{template template argument is incompatible}} -X0 inst_x0e; // expected-note {{template template argument is incompatible}} +X0 inst_x0d; // expected-note {{has different template parameters}} +X0 inst_x0e; // expected-note{{template template argument has different template parameters than its corresponding template template parameter}} template // expected-error {{conversion from 'short' to 'void *' is not allowed in a converted constant expression}} // expected-error@-1 {{cannot be narrowed from type 'int' to 'short'}} - class TT // expected-note 2{{template parameter is declared here}} + class TT // expected-note 2{{previous template template parameter is here}} > struct X1 { }; template struct X1a; template struct X1b; template struct X1c; -template struct X1d; // expected-note {{template parameter is declared here}} -template struct X1e; // expected-note {{template parameter is declared here}} +template struct X1d; +template struct X1e; // expected-note{{template parameter is declared here}} X1 inst_x1a; X1 inst_x1b; X1 inst_x1c; X1 inst_sx1d; -X1 inst_ix1d; // expected-note {{template template argument is incompatible}} -X1 inst_x1e; // expected-note {{template template argument is incompatible}} +X1 inst_ix1d; // expected-note {{has different template parameters}} +X1 inst_x1e; // expected-note {{has different template parameters}} template class X2; // expected-note{{template is declared here}} \ // expected-note{{template is declared here}} diff --git a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp index 5570a7f9d75de..342ffba53dbfa 100644 --- a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp +++ b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp @@ -13,12 +13,12 @@ template struct W { }; // #W S1 s11; S1 s12; // expected-error@-1 {{template template argument 'Y' is more constrained than template template parameter 'P'}} -// expected-note@#S1 {{template parameter is declared here}} +// expected-note@#S1 {{'P' declared here}} // expected-note@#Y {{'Y' declared here}} S1 s13; S1 s14; // expected-error@-1 {{template template argument 'W' is more constrained than template template parameter 'P'}} -// expected-note@#S1 {{template parameter is declared here}} +// expected-note@#S1 {{'P' declared here}} // expected-note@#W {{'W' declared here}} // expected-note@#F 1-2{{similar constraint expressions not considered equivalent}} // expected-note@#C 1-2{{similar constraint}} @@ -43,12 +43,12 @@ template requires C class P> struct S4 { }; // #S4 S4 s41; S4 s42; // expected-error@-1 {{template template argument 'Y' is more constrained than template template parameter 'P'}} -// expected-note@#S4 {{template parameter is declared here}} +// expected-note@#S4 {{'P' declared here}} // expected-note@#Y {{'Y' declared here}} S4 s43; S4 s44; // expected-error@-1 {{template template argument 'W' is more constrained than template template parameter 'P'}} -// expected-note@#S4 {{template parameter is declared here}} +// expected-note@#S4 {{'P' declared here}} // expected-note@#W {{'W' declared here}} template requires C typename U> struct S5 { diff --git a/clang/test/CXX/temp/temp.arg/temp.arg.type/p2.cpp b/clang/test/CXX/temp/temp.arg/temp.arg.type/p2.cpp index 65f3e178ac963..650f8585b115a 100644 --- a/clang/test/CXX/temp/temp.arg/temp.arg.type/p2.cpp +++ b/clang/test/CXX/temp/temp.arg/temp.arg.type/p2.cpp @@ -1,5 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -verify -Wvla %s -// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx98 -std=c++98 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s // RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s template struct A { @@ -13,8 +13,7 @@ template struct B { }; B b; // expected-note{{instantiation of}} -template // #f0-temphead -int f0(void *, const T&); // expected-note{{candidate template ignored: substitution failure}} +template int f0(void *, const T&); // expected-note{{candidate template ignored: substitution failure}} enum {e}; // expected-note@-1 {{unnamed type used in template argument was declared here}} @@ -23,7 +22,6 @@ void test_f0(int n) { // #here #if __cplusplus <= 199711L // expected-warning@-2 {{template argument uses unnamed type}} // expected-note@-3 {{while substituting deduced template arguments}} - // expected-note@#f0-temphead {{template parameter is declared here}} #endif int vla[n]; // expected-warning {{variable length arrays in C++ are a Clang extension}} @@ -35,9 +33,9 @@ void test_f0(int n) { // #here } namespace N0 { - template void f0(R (*)(A1)); // #f0 - template int f1(T); // #f1-1 - template int f1(T, U); // #f1-2 + template void f0(R (*)(A1)); + template int f1(T); + template int f1(T, U); enum {e1}; #if __cplusplus <= 199711L // expected-note@-2 2{{unnamed type used in template argument was declared here}} @@ -53,7 +51,7 @@ namespace N0 { // expected-note@-2 {{unnamed type used in template argument was declared here}} #endif - template struct X; // cxx98-note {{template parameter is declared here}} + template struct X; template struct X { }; void f() { @@ -61,28 +59,24 @@ namespace N0 { #if __cplusplus <= 199711L // expected-warning@-2 {{template argument uses unnamed type}} // expected-note@-3 {{while substituting deduced template arguments}} - // expected-note@#f0 {{template parameter is declared here}} #endif &f1<__typeof__(e1)>); #if __cplusplus <= 199711L // expected-warning@-2 {{template argument uses unnamed type}} // expected-note@-3 {{while substituting explicitly-specified template arguments}} - // expected-note@#f1-1 {{template parameter is declared here}} #endif int (*fp1)(int, __typeof__(e2)) = f1; #if __cplusplus <= 199711L // expected-warning@-2 {{template argument uses unnamed type}} // expected-note@-3 {{while substituting deduced template arguments}} - // expected-note@#f1-2 {{template parameter is declared here}} #endif f1(e2); #if __cplusplus <= 199711L // expected-warning@-2 {{template argument uses unnamed type}} // expected-note@-3 {{while substituting deduced template arguments}} - // expected-note@#f1-1 {{template parameter is declared here}} #endif f1(e2); diff --git a/clang/test/CXX/temp/temp.decls/temp.class.spec/p8-1y.cpp b/clang/test/CXX/temp/temp.decls/temp.class.spec/p8-1y.cpp index cf01e406c14ad..388a80ee765c8 100644 --- a/clang/test/CXX/temp/temp.decls/temp.class.spec/p8-1y.cpp +++ b/clang/test/CXX/temp/temp.decls/temp.class.spec/p8-1y.cpp @@ -25,7 +25,7 @@ template struct X { template static int y; // expected-error 3{{cannot be deduced}} expected-note 3{{'Inner'}} template static int y; // expected-error {{does not specialize}} - template static int z; // expected-note {{template parameter is declared here}} + template static int z; template static int z; // expected-error {{not implicitly convertible}} }; template template int X::y; // expected-error {{cannot be deduced}} expected-note {{'Inner'}} @@ -33,4 +33,4 @@ template template int X::y; // exp template<> template int X::y; // expected-error {{does not specialize}} expected-note {{instantiation of}} X xi; -X xf; // expected-note 2{{instantiation of}} +X xf; // expected-note {{instantiation of}} diff --git a/clang/test/CXX/temp/temp.decls/temp.variadic/fixed-expansion.cpp b/clang/test/CXX/temp/temp.decls/temp.variadic/fixed-expansion.cpp index cab5c967b0c4f..ab4c663d24c7d 100644 --- a/clang/test/CXX/temp/temp.decls/temp.variadic/fixed-expansion.cpp +++ b/clang/test/CXX/temp/temp.decls/temp.variadic/fixed-expansion.cpp @@ -14,10 +14,10 @@ struct is_same { }; namespace ExpandIntoFixed { - template, - typename W = V*> + template, + typename W = V*> class X0 { }; template @@ -26,24 +26,24 @@ namespace ExpandIntoFixed { typedef X0 type; }; - static_assert(is_same::type, + static_assert(is_same::type, X0, pair*>>::value, "fails with two default arguments"); - static_assert(is_same::type, + static_assert(is_same::type, X0>::value, "fails with one default argument"); - static_assert(is_same::type, + static_assert(is_same::type, X0>::value, "fails with no default arguments"); } namespace ExpandIntoFixedShifted { - template, - typename W = V*> + template, + typename W = V*> class X0 { }; template @@ -52,15 +52,15 @@ namespace ExpandIntoFixedShifted { typedef X0 type; }; - static_assert(is_same::type, + static_assert(is_same::type, X0, pair*>>::value, "fails with two default arguments"); - static_assert(is_same::type, + static_assert(is_same::type, X0>::value, "fails with one default argument"); - static_assert(is_same::type, + static_assert(is_same::type, X0>::value, "fails with no default arguments"); } @@ -76,11 +76,11 @@ namespace Deduction { } namespace PR9021a { - template + template struct A { }; template - struct B { + struct B { A a1; }; @@ -93,9 +93,9 @@ namespace PR9021b { template struct t2 { - + }; - + template class M> struct m { @@ -107,14 +107,14 @@ namespace PR9021b { } namespace PartialSpecialization { - template // expected-note {{template parameter is declared here}} - struct X0; // expected-note {{template is declared here}} + template + struct X0; // expected-note 2{{template is declared here}} template struct X0 { // expected-error {{class template partial specialization is not more specialized than the primary template}} }; - X0 x0i; // expected-error{{missing template argument for template parameter}} + X0 x0i; // expected-error{{too few template arguments for class template 'X0'}} X0 x0if; X0 x0ifd; } diff --git a/clang/test/CXX/temp/temp.decls/temp.variadic/multi-level-substitution.cpp b/clang/test/CXX/temp/temp.decls/temp.variadic/multi-level-substitution.cpp index c6e6038fb6ddf..30e7c65dac91c 100644 --- a/clang/test/CXX/temp/temp.decls/temp.variadic/multi-level-substitution.cpp +++ b/clang/test/CXX/temp/temp.decls/temp.variadic/multi-level-substitution.cpp @@ -48,7 +48,7 @@ namespace PacksAtDifferentLevels { int check1[X::Inner, pair, pair> - >::value == 1? 1 : -1]; + >::value == 1? 1 : -1]; template struct unsigned_tuple { }; template @@ -99,7 +99,7 @@ namespace PacksAtDifferentLevels { int check5[X2::Inner, pair, pair) - >::value == 1? 1 : -1]; + >::value == 1? 1 : -1]; template struct some_function_object { @@ -217,8 +217,8 @@ namespace ExpandingNonTypeTemplateParameters { template struct tuple_of_values { template // expected-error{{a non-type template parameter cannot have type 'float'}} \ - // expected-note 2{{template parameter is declared here}} - struct apply { // expected-note {{template is declared here}} + // expected-note{{template parameter is declared here}} + struct apply { // expected-note 2{{template is declared here}} typedef tuple...> type; }; }; @@ -236,7 +236,7 @@ namespace ExpandingNonTypeTemplateParameters { tuple_of_values::apply::type tv2; // expected-error{{non-type template parameter of reference type 'float &' cannot bind to template argument of type 'int'}} - tuple_of_values::apply::type tv3; // expected-error{{missing template argument for template parameter}} + tuple_of_values::apply::type tv3; // expected-error{{too few template arguments for class template 'apply'}} tuple_of_values::apply::type tv4; // expected-error{{too many template arguments for class template 'apply'}} } diff --git a/clang/test/CXX/temp/temp.deduct/p9.cpp b/clang/test/CXX/temp/temp.deduct/p9.cpp index 5f9ea27234c81..7b661c275211b 100644 --- a/clang/test/CXX/temp/temp.deduct/p9.cpp +++ b/clang/test/CXX/temp/temp.deduct/p9.cpp @@ -15,14 +15,13 @@ void test_f() { } template -// expected-note@-1 {{template parameter is declared here}} void g(T); void g(...); void test_g() { - g(0); // expected-error@-5 {{type 'int' cannot be used prior to '::'}} + g(0); // expected-error@-4 {{type 'int' cannot be used prior to '::'}} // expected-note@-4 {{in instantiation of default argument}} // expected-note@-2 {{while substituting deduced template arguments}} - // expected-note@-8 {{while substituting into a lambda expression here}} + // expected-note@-7 {{while substituting into a lambda expression here}} } template diff --git a/clang/test/CXX/temp/temp.param/p1.cpp b/clang/test/CXX/temp/temp.param/p1.cpp index e2eecdf0d4554..e9a978961769c 100644 --- a/clang/test/CXX/temp/temp.param/p1.cpp +++ b/clang/test/CXX/temp/temp.param/p1.cpp @@ -5,9 +5,8 @@ template class C> class D; // expected-error{{template template param struct A {}; -template // expected-error{{template parameter missing a default argument}} -class X0 {}; -X0 x0; // expected-error{{missing template argument for template parameter}} +class X0 {}; // expected-note{{template is declared here}} +X0 x0; // expected-error{{too few template arguments for class template 'X0'}} diff --git a/clang/test/CXX/temp/temp.param/p12.cpp b/clang/test/CXX/temp/temp.param/p12.cpp index e3dfb65d29461..8317e7f24152c 100644 --- a/clang/test/CXX/temp/temp.param/p12.cpp +++ b/clang/test/CXX/temp/temp.param/p12.cpp @@ -32,9 +32,9 @@ template class =// expected-note {{template parameter is declared here}} +template class =// expected-note {{previous template template parameter is here}} Y1> // expected-error{{too many template arguments for class template 'Y1'}} - // expected-note@-1 {{template template argument is incompatible}} + // expected-note@-1 {{template template argument has different template parameters than its corresponding template template parameter}} class C1 {}; C1<> c1; // expected-note{{while checking a default template argument}} diff --git a/clang/test/CXX/temp/temp.param/p15-cxx0x.cpp b/clang/test/CXX/temp/temp.param/p15-cxx0x.cpp index 402a205f1ea63..83144a494937b 100644 --- a/clang/test/CXX/temp/temp.param/p15-cxx0x.cpp +++ b/clang/test/CXX/temp/temp.param/p15-cxx0x.cpp @@ -77,9 +77,9 @@ template struct wrap { template struct takedrop_impl; template struct takedrop_impl> { - template class ...Take, // expected-note 2{{template parameter is declared here}} + template class ...Take, template class ...Drop> - struct inner { + struct inner { // expected-note 2{{declared}} typedef types::type...> take; typedef types::type...> drop; }; @@ -87,11 +87,11 @@ template struct takedrop_impl> { template struct take { using type = typename takedrop_impl::type>:: - template inner::template inner...>::take; // expected-error {{missing template argument}} + template inner::template inner...>::take; // expected-error {{too few template arguments}} }; template struct drop { using type = typename takedrop_impl::type>:: - template inner::template inner...>::drop; // expected-error {{missing template argument}} + template inner::template inner...>::drop; // expected-error {{too few template arguments}} }; using T1 = take<3, int, char, double, long>::type; // expected-note {{previous}} @@ -118,7 +118,7 @@ using D3 = drop<5, int, char, double, long>::type; // expected-note {{in instant // implicitly a pack expansion. template struct DefArg { template class ...Classes> struct Inner { // expected-error {{default argument contains unexpanded parameter pack}} expected-note {{here}} - Inner(Classes<>...); // expected-error {{missing template argument}} + Inner(Classes<>...); // expected-error {{too few}} }; }; template struct vector {}; diff --git a/clang/test/CXX/temp/temp.param/p8-cxx20.cpp b/clang/test/CXX/temp/temp.param/p8-cxx20.cpp index aa1666f6306d0..a3478c0669661 100644 --- a/clang/test/CXX/temp/temp.param/p8-cxx20.cpp +++ b/clang/test/CXX/temp/temp.param/p8-cxx20.cpp @@ -59,7 +59,7 @@ namespace ConstDestruction { f(); } - template struct Z {}; // expected-note {{template parameter is declared here}} + template struct Z {}; Z z1; Z z2; // expected-error {{non-type template argument is not a constant expression}} expected-note-re {{in call to '{{.*}}.~D()'}} } diff --git a/clang/test/CXX/temp/temp.res/temp.dep/p3.cpp b/clang/test/CXX/temp/temp.res/temp.dep/p3.cpp index 583fb4b3cc05d..a5cbcfc0fcafa 100644 --- a/clang/test/CXX/temp/temp.res/temp.dep/p3.cpp +++ b/clang/test/CXX/temp/temp.res/temp.dep/p3.cpp @@ -10,13 +10,13 @@ template struct B0: A0 { }; namespace E1 { - typedef double A; + typedef double A; template class B { - typedef int A; + typedef int A; }; - template + template struct X : B { A* blarg(double *dp) { return dp; @@ -25,20 +25,20 @@ namespace E1 { } namespace E2 { - struct A { + struct A { struct B; int *a; int Y; }; - + int a; - template struct Y : T { + template struct Y : T { struct B { /* ... */ }; - B b; - void f(int i) { a = i; } + B b; + void f(int i) { a = i; } Y* p; - }; - + }; + Y ya; } @@ -56,7 +56,7 @@ namespace PR14402 { }; struct D { - using A::n; // expected-error {{using declaration refers into 'A::', which is not a base class of 'D'}} + using A::n; // expected-error {{using declaration refers into 'A', which is not a base class of 'D'}} int g() { return f(); } // expected-error {{call to non-static member function 'f' of 'A' from nested type 'D'}} }; diff --git a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.constexpr/p2.cpp b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.constexpr/p2.cpp index cb048ecdf7001..ecb82372bcb47 100644 --- a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.constexpr/p2.cpp +++ b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.constexpr/p2.cpp @@ -2,7 +2,7 @@ // RUN: %clang_cc1 -std=c++11 -verify=cxx11 %s // cxx11-no-diagnostics -template struct S; // cxx98-note {{template parameter is declared here}} +template struct S; template struct T { T() { diff --git a/clang/test/CXX/temp/temp.spec/cxx1y-variable-template-no-body.cpp b/clang/test/CXX/temp/temp.spec/cxx1y-variable-template-no-body.cpp index 749b45524f9e6..741ebc5de41fc 100644 --- a/clang/test/CXX/temp/temp.spec/cxx1y-variable-template-no-body.cpp +++ b/clang/test/CXX/temp/temp.spec/cxx1y-variable-template-no-body.cpp @@ -3,27 +3,27 @@ // RUN: not %clang_cc1 --std=c++1y -x c++ -fixit %t -DFIXING // RUN: %clang_cc1 --std=c++1y -x c++ %t -DFIXING -template // expected-note {{template parameter is declared here}} -T pi = T(3.1415926535897932385); +template +T pi = T(3.1415926535897932385); // expected-note {{template is declared here}} template int pi; #ifndef FIXING -template float pi<>; // expected-error {{missing template argument for template parameter}} +template float pi<>; // expected-error {{too few template arguments for variable template 'pi'}} template double pi_var0; // expected-error {{explicit instantiation of 'pi_var0' does not refer to a function template, variable template, member function, member class, or static data member}} #endif // Should recover as if definition template double pi_var = 5; // expected-error {{variable cannot be defined in an explicit instantiation; if this declaration is meant to be a variable definition, remove the 'template' keyword}} #ifndef FIXING -template +template T pi0 = T(3.1415926535897932385); // expected-note {{previous definition is here}} template int pi0 = 10; // expected-error {{variable cannot be defined in an explicit instantiation; if this declaration is meant to be a variable definition, remove the 'template' keyword}} \ expected-error{{redefinition of 'pi0' as different kind of symbol}} #endif -template +template T pi1 = T(3.1415926535897932385); // expected-note 0-2 {{here}} // Should recover as if specialization diff --git a/clang/test/CXX/temp/temp.spec/part.spec.cpp b/clang/test/CXX/temp/temp.spec/part.spec.cpp index 3923d160ef5cf..4b0fdb902633a 100644 --- a/clang/test/CXX/temp/temp.spec/part.spec.cpp +++ b/clang/test/CXX/temp/temp.spec/part.spec.cpp @@ -250,7 +250,7 @@ template class PCT1 {}; template class PCT2 {}; template class PCT3 {}; template class PCT4 {}; -template class PCT5 {}; // expected-note {{template parameter is declared here}} +template class PCT5 {}; template class PCT6 { // expected-note@+1 3{{implicitly declared private here}} template class NPCT1 {}; @@ -416,7 +416,7 @@ template class PCTT1 {}; template class PCTT2 {}; template class PCTT3 {}; template class PCTT4 {}; -template class PCTT5 {}; // expected-note {{template parameter is declared here}} +template class PCTT5 {}; template class PCTT6 { template class NCT1 {}; template class NCT2; // forward declaration diff --git a/clang/test/CXX/temp/temp.spec/temp.expl.spec/p20.cpp b/clang/test/CXX/temp/temp.spec/temp.expl.spec/p20.cpp index 63fd997add4ec..0283dba63b110 100644 --- a/clang/test/CXX/temp/temp.spec/temp.expl.spec/p20.cpp +++ b/clang/test/CXX/temp/temp.spec/temp.expl.spec/p20.cpp @@ -2,13 +2,13 @@ template void f(T); -template // expected-note {{template parameter is declared here}} -struct A { }; +template +struct A { }; // expected-note{{template is declared here}} struct X { template<> friend void f(int); // expected-error{{in a friend}} template<> friend class A; // expected-error{{cannot be a friend}} - + friend void f(float); // okay friend class A; // okay }; @@ -18,6 +18,6 @@ struct PR41792 { template <> friend void f<>(int); // expected-error@+2{{template specialization declaration cannot be a friend}} - // expected-error@+1{{missing template argument for template parameter}} + // expected-error@+1{{too few template arguments for class template 'A'}} template <> friend class A<>; }; diff --git a/clang/test/CodeGen/AArch64/sve-vector-bits-codegen.c b/clang/test/CodeGen/AArch64/sve-vector-bits-codegen.c index 1391a1b09fbd1..0ed14b4b3b793 100644 --- a/clang/test/CodeGen/AArch64/sve-vector-bits-codegen.c +++ b/clang/test/CodeGen/AArch64/sve-vector-bits-codegen.c @@ -13,9 +13,12 @@ void func(int *restrict a, int *restrict b) { // CHECK-LABEL: func -// CHECK256-COUNT-8: str -// CHECK512-COUNT-4: str -// CHECK1024-COUNT-2: str +// CHECK256-COUNT-1: str +// CHECK256-COUNT-7: st1w +// CHECK512-COUNT-1: str +// CHECK512-COUNT-3: st1w +// CHECK1024-COUNT-1: str +// CHECK1024-COUNT-1: st1w // CHECK2048-COUNT-1: st1w #pragma clang loop vectorize(enable) for (int i = 0; i < 64; ++i) diff --git a/clang/test/CodeGen/PowerPC/builtins-ppc-p9vector.c b/clang/test/CodeGen/PowerPC/builtins-ppc-p9vector.c index b55a522ed2608..68d32ee14c8fa 100644 --- a/clang/test/CodeGen/PowerPC/builtins-ppc-p9vector.c +++ b/clang/test/CodeGen/PowerPC/builtins-ppc-p9vector.c @@ -853,10 +853,16 @@ vector unsigned char test73(void) { vector unsigned short test74(void) { // CHECK-BE: @llvm.ppc.vsx.xvcvsphp(<4 x float> // CHECK-BE: @llvm.ppc.vsx.xvcvsphp(<4 x float> -// CHECK-BE: @llvm.ppc.altivec.vperm +// CHECK-BE: [[REG0:%[0-9]+]] = call <4 x i32> @llvm.ppc.altivec.vperm +// CHECK-BE-NEXT: [[REG1:%[0-9]+]] = bitcast <4 x i32> [[REG0]] to <4 x float> +// CHECK-BE-NEXT: [[REG2:%[0-9]+]] = bitcast <4 x float> [[REG1]] to <8 x i16> +// CHECK-BE-NEXT: ret <8 x i16> [[REG2]] // CHECK: @llvm.ppc.vsx.xvcvsphp(<4 x float> // CHECK: @llvm.ppc.vsx.xvcvsphp(<4 x float> -// CHECK: @llvm.ppc.altivec.vperm +// CHECK: [[REG0:%[0-9]+]] = call <4 x i32> @llvm.ppc.altivec.vperm +// CHECK-NEXT: [[REG1:%[0-9]+]] = bitcast <4 x i32> [[REG0]] to <4 x float> +// CHECK-NEXT: [[REG2:%[0-9]+]] = bitcast <4 x float> [[REG1]] to <8 x i16> +// CHECK-NEXT: ret <8 x i16> [[REG2]] return vec_pack_to_short_fp32(vfa, vfb); } vector unsigned int test75(void) { diff --git a/clang/test/CodeGen/RISCV/pr129995.cc b/clang/test/CodeGen/RISCV/pr129995.cc new file mode 100644 index 0000000000000..590c6b9fbdf96 --- /dev/null +++ b/clang/test/CodeGen/RISCV/pr129995.cc @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 triple riscv64 -emit-llvm -target-feature +m -target-feature +v -target-abi lp64d -o /dev/null %s + +struct a { + using b = char __attribute__((vector_size(sizeof(char)))); +}; +class c { + using d = a::b; + d e; + +public: + static c f(); +}; +class g { +public: + template g(h); + friend g operator^(g, g) { c::f; } + friend g operator^=(g i, g j) { i ^ j; } +}; +template using k = g; +template using m = k; +void n() { + void o(); + m p = o ^= p; +} diff --git a/clang/test/CodeGen/aarch64-execute-only.c b/clang/test/CodeGen/aarch64-execute-only.c new file mode 100644 index 0000000000000..19fb29fcfcf21 --- /dev/null +++ b/clang/test/CodeGen/aarch64-execute-only.c @@ -0,0 +1,19 @@ +// RUN: %clang -target aarch64 -### %s 2>&1 | \ +// RUN: FileCheck %s -check-prefix CHECK-NO-EXECUTE-ONLY +// RUN: %clang -target aarch64 -### -mexecute-only %s 2>&1 | \ +// RUN: FileCheck %s -check-prefix CHECK-EXECUTE-ONLY +// RUN: %clang -target aarch64 -### -mexecute-only -mno-execute-only %s 2>&1 | \ +// RUN: FileCheck %s -check-prefix CHECK-NO-EXECUTE-ONLY + +/// -mpure-code flag for GCC compatibility +// RUN: %clang -target aarch64 -### %s 2>&1 | \ +// RUN: FileCheck %s -check-prefix CHECK-NO-EXECUTE-ONLY +// RUN: %clang -target aarch64 -### -mpure-code %s 2>&1 | \ +// RUN: FileCheck %s -check-prefix CHECK-EXECUTE-ONLY +// RUN: %clang -target aarch64 -### -mpure-code -mno-pure-code %s 2>&1 | \ +// RUN: FileCheck %s -check-prefix CHECK-NO-EXECUTE-ONLY + +// CHECK-NO-EXECUTE-ONLY-NOT: "+execute-only" +// CHECK-EXECUTE-ONLY: "+execute-only" + +void a() {} diff --git a/clang/test/CodeGen/attr-counted-by.c b/clang/test/CodeGen/attr-counted-by.c index e85f3db1121af..dfdf06587f0e2 100644 --- a/clang/test/CodeGen/attr-counted-by.c +++ b/clang/test/CodeGen/attr-counted-by.c @@ -333,7 +333,7 @@ size_t test3_bdos(struct annotated *p) { // SANITIZE-WITH-ATTR-NEXT: [[COUNT50:%.*]] = sext i32 [[DOTCOUNTED_BY_LOAD44]] to i64 // SANITIZE-WITH-ATTR-NEXT: [[TMP10:%.*]] = sub nsw i64 [[COUNT50]], [[IDXPROM42]] // SANITIZE-WITH-ATTR-NEXT: [[TMP11:%.*]] = tail call i64 @llvm.smax.i64(i64 [[TMP10]], i64 0) -// SANITIZE-WITH-ATTR-NEXT: [[DOTTR:%.*]] = trunc nuw i64 [[TMP11]] to i32 +// SANITIZE-WITH-ATTR-NEXT: [[DOTTR:%.*]] = trunc nuw nsw i64 [[TMP11]] to i32 // SANITIZE-WITH-ATTR-NEXT: [[CONV54:%.*]] = shl i32 [[DOTTR]], 2 // SANITIZE-WITH-ATTR-NEXT: [[CONV55:%.*]] = and i32 [[CONV54]], 252 // SANITIZE-WITH-ATTR-NEXT: store i32 [[CONV55]], ptr [[ARRAYIDX65]], align 4, !tbaa [[TBAA4]] diff --git a/clang/test/CodeGen/builtin-constant-p.c b/clang/test/CodeGen/builtin-constant-p.c index 8e35cb2b2ef83..07288069a5e3a 100644 --- a/clang/test/CodeGen/builtin-constant-p.c +++ b/clang/test/CodeGen/builtin-constant-p.c @@ -1,5 +1,8 @@ // RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -disable-llvm-optzns -o - %s -O2 | FileCheck %s // RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -disable-llvm-optzns -o - %s -O0 | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -disable-llvm-optzns -o - %s -O2 -fexperimental-new-constant-interpreter | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -disable-llvm-optzns -o - %s -O0 -fexperimental-new-constant-interpreter | FileCheck %s + int a = 42; diff --git a/clang/test/CodeGen/builtins-elementwise-math.c b/clang/test/CodeGen/builtins-elementwise-math.c index 5661bbc6008a0..ee8345ff51e5e 100644 --- a/clang/test/CodeGen/builtins-elementwise-math.c +++ b/clang/test/CodeGen/builtins-elementwise-math.c @@ -710,6 +710,21 @@ void test_builtin_elementwise_exp2(float f1, float f2, double d1, double d2, vf2 = __builtin_elementwise_exp2(vf1); } +void test_builtin_elementwise_exp10(float f1, float f2, double d1, double d2, + float4 vf1, float4 vf2) { + // CHECK-LABEL: define void @test_builtin_elementwise_exp10( + // CHECK: [[F1:%.+]] = load float, ptr %f1.addr, align 4 + // CHECK-NEXT: call float @llvm.exp10.f32(float [[F1]]) + f2 = __builtin_elementwise_exp10(f1); + + // CHECK: [[D1:%.+]] = load double, ptr %d1.addr, align 8 + // CHECK-NEXT: call double @llvm.exp10.f64(double [[D1]]) + d2 = __builtin_elementwise_exp10(d1); + + // CHECK: [[VF1:%.+]] = load <4 x float>, ptr %vf1.addr, align 16 + // CHECK-NEXT: call <4 x float> @llvm.exp10.v4f32(<4 x float> [[VF1]]) + vf2 = __builtin_elementwise_exp10(vf1); +} void test_builtin_elementwise_floor(float f1, float f2, double d1, double d2, float4 vf1, float4 vf2) { diff --git a/clang/test/CodeGen/openacc-noop-decl.c b/clang/test/CodeGen/openacc-noop-decl.c new file mode 100644 index 0000000000000..41f756d835aa5 --- /dev/null +++ b/clang/test/CodeGen/openacc-noop-decl.c @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 %s -emit-llvm -o - | FileCheck %s + +// Check to ensure these are no-ops/don't really do anything. +void name(); + +void foo() { +#pragma acc declare + // CHECK-NOT: declare +#pragma acc routine(name) worker + // CHECK-NOT: routine +} +#pragma acc declare + // CHECK-NOT: declare +#pragma acc routine(name) worker + // CHECK-NOT: routine diff --git a/clang/test/CodeGen/p0963r3.cpp b/clang/test/CodeGen/p0963r3.cpp new file mode 100644 index 0000000000000..4a5e6c3f5d751 --- /dev/null +++ b/clang/test/CodeGen/p0963r3.cpp @@ -0,0 +1,209 @@ +// RUN: %clang_cc1 -std=c++2c -verify -emit-llvm -triple=x86_64-pc-linux-gnu %s -o - | FileCheck %s +// RUN: %clang_cc1 -std=c++2c -verify -fsyntax-only -fexperimental-new-constant-interpreter -triple=x86_64-pc-linux-gnu %s +// expected-no-diagnostics + +namespace std { + +template struct tuple_size; + +template struct tuple_element; + +} // namespace std + +namespace Case1 { + +struct S { + int a, b; + bool flag = false; + + constexpr explicit operator bool() { + flag = true; + return a != b; + } + + constexpr operator int() { + flag = true; + return a * b; + } + + constexpr bool operator==(S rhs) const { + return a == rhs.a && b == rhs.b; + } + + template + constexpr int& get() { + if (!flag) + return a = a + b; + return I == 0 ? a : b; + } +}; + +} // namespace Case1 + +template <> struct std::tuple_size { + static const int value = 2; +}; + +template struct std::tuple_element { + using type = int; +}; + +namespace Case1 { + +void foo() { + if (S s(1, 2); auto [a, b] = s) { + __builtin_assume(a == 1); + __builtin_assume(b == 2); + } +// CHECK: %[[call:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv +// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv +// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv +// CHECK: br i1 %[[call]], label {{.*}}, label {{.*}} + + if (auto [a, b] = S(1, 2)) { + __builtin_assume(a == 1); + __builtin_assume(b == 2); + } +// CHECK: %[[call2:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv +// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv +// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv +// CHECK: br i1 %[[call2]], label {{.*}}, label {{.*}} + + if (S s(3, 4); auto& [a, b] = s) { + __builtin_assume(a == 3); + __builtin_assume(b == 4); + } +// CHECK: %[[call3:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv +// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv +// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv +// CHECK: br i1 %[[call3]], label {{.*}}, label {{.*}} + + while (auto [i, j] = S(5, 6)) + break; + +// CHECK: while.cond{{.*}}: +// CHECK: %[[call4:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv +// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv +// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv +// CHECK: br i1 %[[call4]], label {{.*}}, label {{.*}} + + S s(7, 8); + while (auto& [i, j] = s) + break; + +// CHECK: while.cond{{.*}}: +// CHECK: %[[call5:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv +// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv +// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv +// CHECK: br i1 %[[call5]], label {{.*}}, label {{.*}} + + for (int k = 0; auto [i, j] = S(24, 42); ++k) + break; + +// CHECK: for.cond{{.*}}: +// CHECK: %[[call6:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv +// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv +// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv +// CHECK: br i1 %[[call6]], label {{.*}}, label {{.*}} + + for (S s(114, 514); auto& [i, j] = s; ++i) + break; + +// CHECK: for.cond{{.*}}: +// CHECK: %[[call7:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv +// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv +// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv +// CHECK: br i1 %[[call7]], label {{.*}}, label {{.*}} + + switch (S s(10, 11); auto& [i, j] = s) { + case 10 * 11: + __builtin_assume(i == 10); + __builtin_assume(j == 11); + break; + default: + break; + } + +// CHECK: %[[call8:.+]] = call {{.*}} i32 @_ZN5Case11ScviEv +// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv +// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv +// CHECK: switch i32 %[[call8]], label {{.*}} + +} + +constexpr int bar(auto) { + constexpr auto value = [] { + if (S s(1, 2); auto [i, j] = s) + return S(i, j); + return S(0, 0); + }(); + static_assert(value == S(1, 2)); + + constexpr auto value2 = [] { + if (auto [a, b] = S(1, 2)) + return S(a, b); + return S(0, 0); + }(); + static_assert(value2 == S(1, 2)); + + constexpr auto value3 = [] { + if (auto&& [a, b] = S(3, 4)) + return S(a, b); + return S(0, 0); + }(); + static_assert(value3 == S(3, 4)); + + constexpr auto value4 = [] { + S s(7, 8); + int cnt = 0; + while (auto& [i, j] = s) { + s.flag = false; + ++i, ++j; + if (++cnt == 10) + break; + } + return s; + }(); + static_assert(value4 == S(17, 18)); + + constexpr auto value5 = [] { + S s(3, 4); + for (int cnt = 0; auto& [x, y] = s; s.flag = false, ++cnt) { + if (cnt == 3) + break; + ++x, ++y; + } + return s; + }(); + static_assert(value5 == S(6, 7)); + + constexpr auto value6 = [] { + switch (auto [x, y] = S(3, 4)) { + case 3 * 4: + return S(x, y); + default: + return S(y, x); + } + }(); + static_assert(value6 == S(3, 4)); + + return 42; +} + +constexpr int value = bar(1); + +#if 0 + +// FIXME: This causes clang to ICE, though this is not a regression. +constexpr int ice(auto) { + if constexpr (S s(1, 2); auto [i, j] = s) { + static_assert(i == 1); + } + return 42; +} + +constexpr int value2 = ice(1); + +#endif + +} // namespace Case1 diff --git a/clang/test/CodeGen/strictfp-elementwise-bulitins.cpp b/clang/test/CodeGen/strictfp-elementwise-builtins.cpp similarity index 96% rename from clang/test/CodeGen/strictfp-elementwise-bulitins.cpp rename to clang/test/CodeGen/strictfp-elementwise-builtins.cpp index 175ad22601839..b250512efc5c7 100644 --- a/clang/test/CodeGen/strictfp-elementwise-bulitins.cpp +++ b/clang/test/CodeGen/strictfp-elementwise-builtins.cpp @@ -127,6 +127,16 @@ float4 strict_elementwise_exp2(float4 a) { return __builtin_elementwise_exp2(a); } +// CHECK-LABEL: define dso_local noundef <4 x float> @_Z24strict_elementwise_exp10Dv4_f +// CHECK-SAME: (<4 x float> noundef [[A:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ELT_EXP10:%.*]] = tail call <4 x float> @llvm.exp10.v4f32(<4 x float> [[A]]) #[[ATTR4]] +// CHECK-NEXT: ret <4 x float> [[ELT_EXP10]] +// +float4 strict_elementwise_exp10(float4 a) { + return __builtin_elementwise_exp10(a); +} + // CHECK-LABEL: define dso_local noundef <4 x float> @_Z24strict_elementwise_floorDv4_f // CHECK-SAME: (<4 x float> noundef [[A:%.*]]) local_unnamed_addr #[[ATTR2]] { // CHECK-NEXT: entry: @@ -340,9 +350,8 @@ float4 strict_elementwise_pow(float4 a, float4 b) { // CHECK-LABEL: define dso_local noundef <4 x float> @_Z23strict_elementwise_fmodDv4_fS_ // CHECK-SAME: (<4 x float> noundef [[A:%.*]], <4 x float> noundef [[B:%.*]]) local_unnamed_addr #[[ATTR0]] { // CHECK-NEXT: entry: -// CHECK-NEXT: [[TMP0:%.*]] = tail call <4 x float> @llvm.experimental.constrained.frem.v4f32(<4 x float> [[A]], <4 x float> [[B]], -// CHECK-SAME: metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR4]] -// CHECK-NEXT: ret <4 x float> [[TMP0]] +// CHECK-NEXT: [[FMOD:%.*]] = tail call <4 x float> @llvm.experimental.constrained.frem.v4f32(<4 x float> [[A]], <4 x float> [[B]], metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR4]] +// CHECK-NEXT: ret <4 x float> [[FMOD]] // float4 strict_elementwise_fmod(float4 a, float4 b) { return __builtin_elementwise_fmod(a, b); diff --git a/clang/test/CodeGen/vector.c b/clang/test/CodeGen/vector.c index c20e320463056..5d677aaf16948 100644 --- a/clang/test/CodeGen/vector.c +++ b/clang/test/CodeGen/vector.c @@ -8,6 +8,10 @@ void test1(void) { __v4hi x = {1,2,3}; __v4hi y = {1,2,3,4}; + +// CHECK: @z = local_unnamed_addr global <8 x float> zeroinitializer +float z __attribute__((ext_vector_type(8))); + typedef int vty __attribute((vector_size(16))); int test2(void) { vty b; return b[2LL]; } @@ -18,9 +22,6 @@ void test3 ( vec4* a, char b, float c ) { (*a)[b] = c; } - - - #include int test4(int argc, char *argv[]) { diff --git a/clang/test/CodeGenCUDA/amdgpu-bf16.cu b/clang/test/CodeGenCUDA/amdgpu-bf16.cu index 4610b4ae3cbe5..f9b067d3fe0d3 100644 --- a/clang/test/CodeGenCUDA/amdgpu-bf16.cu +++ b/clang/test/CodeGenCUDA/amdgpu-bf16.cu @@ -111,19 +111,15 @@ __device__ __bf16 test_call( __bf16 in) { // CHECK-NEXT: ret void // __device__ void test_vec_assign() { - typedef __attribute__((ext_vector_type(2))) __bf16 bf16_x2; - bf16_x2 vec2_a, vec2_b; + __bf16 [[clang::ext_vector_type(2)]] vec2_a, vec2_b; vec2_a = vec2_b; - typedef __attribute__((ext_vector_type(4))) __bf16 bf16_x4; - bf16_x4 vec4_a, vec4_b; + __bf16 __attribute__((ext_vector_type(4))) vec4_a, vec4_b; vec4_a = vec4_b; - typedef __attribute__((ext_vector_type(8))) __bf16 bf16_x8; - bf16_x8 vec8_a, vec8_b; + __bf16 [[clang::ext_vector_type(8)]] vec8_a, vec8_b; vec8_a = vec8_b; - typedef __attribute__((ext_vector_type(16))) __bf16 bf16_x16; - bf16_x16 vec16_a, vec16_b; + __bf16 __attribute__((ext_vector_type(16))) vec16_a, vec16_b; vec16_a = vec16_b; } diff --git a/clang/test/CodeGenCXX/builtin-constant-p.cpp b/clang/test/CodeGenCXX/builtin-constant-p.cpp index 866faa5ec9761..39416b94375fe 100644 --- a/clang/test/CodeGenCXX/builtin-constant-p.cpp +++ b/clang/test/CodeGenCXX/builtin-constant-p.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -o - %s -fexperimental-new-constant-interpreter | FileCheck %s // Don't crash if the argument to __builtin_constant_p isn't scalar. template diff --git a/clang/test/CodeGenCXX/cxx23-p2280r4.cpp b/clang/test/CodeGenCXX/cxx23-p2280r4.cpp index d5409be451df0..53b00695d9d6d 100644 --- a/clang/test/CodeGenCXX/cxx23-p2280r4.cpp +++ b/clang/test/CodeGenCXX/cxx23-p2280r4.cpp @@ -4,7 +4,7 @@ extern int& s; -// CHECK: @_Z4testv() +// CHECK-LABEL: @_Z4testv() // CHECK-NEXT: entry: // CHECK-NEXT: [[I:%.*]] = alloca ptr, align {{.*}} // CHECK-NEXT: [[X:%.*]] = load ptr, ptr @s, align {{.*}} @@ -13,3 +13,16 @@ int& test() { auto &i = s; return i; } + +// CHECK-LABEL: @_Z1fv( +// CHECK: [[X1:%.*]] = load ptr, ptr @x, align {{.*}} +// CHECK-NEXT: store ptr [[X1]] +// CHECK: [[X2:%.*]] = load ptr, ptr @x, align {{.*}} +// CHECK-NEXT: store ptr [[X2]] +// CHECK: [[X3:%.*]] = load ptr, ptr @x, align {{.*}} +// CHECK-NEXT: store ptr [[X3]] +int &ff(); +int &x = ff(); +struct A { int& x; }; +struct B { A x[20]; }; +B f() { return {x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x}; } diff --git a/clang/test/CodeGenCXX/wasm-eh.cpp b/clang/test/CodeGenCXX/wasm-eh.cpp index e8797794e7c1e..faff764878f5d 100644 --- a/clang/test/CodeGenCXX/wasm-eh.cpp +++ b/clang/test/CodeGenCXX/wasm-eh.cpp @@ -17,7 +17,7 @@ struct Cleanup { }; // Multiple catch clauses w/o catch-all -void test0() { +void multiple_catches_wo_catch_all() { try { may_throw(); } catch (int) { @@ -27,7 +27,7 @@ void test0() { } } -// CHECK-LABEL: define void @_Z5test0v() {{.*}} personality ptr @__gxx_wasm_personality_v0 +// CHECK-LABEL: define void @_Z29multiple_catches_wo_catch_allv() {{.*}} personality ptr @__gxx_wasm_personality_v0 // CHECK: %[[INT_ALLOCA:.*]] = alloca i32 // CHECK: invoke void @_Z9may_throwv() @@ -73,7 +73,7 @@ void test0() { // CHECK-NEXT: unreachable // Single catch-all -void test1() { +void single_catch_all() { try { may_throw(); } catch (...) { @@ -81,7 +81,7 @@ void test1() { } } -// CATCH-LABEL: @_Z5test1v() +// CATCH-LABEL: @_Z16single_catch_allv() // CHECK: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind to caller @@ -93,7 +93,7 @@ void test1() { // CHECK: catchret from %[[CATCHPAD]] to label // Multiple catch clauses w/ catch-all -void test2() { +void multiple_catches_w_catch_all() { try { may_throw(); } catch (int) { @@ -103,7 +103,7 @@ void test2() { } } -// CHECK-LABEL: @_Z5test2v() +// CHECK-LABEL: @_Z28multiple_catches_w_catch_allv() // CHECK: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind to caller @@ -118,12 +118,12 @@ void test2() { // CHECK: catchret from %[[CATCHPAD]] to label // Cleanup -void test3() { +void cleanup() { Cleanup c; may_throw(); } -// CHECK-LABEL: @_Z5test3v() +// CHECK-LABEL: @_Z7cleanupv() // CHECK: invoke void @_Z9may_throwv() // CHECK-NEXT: to label {{.*}} unwind label %[[EHCLEANUP_BB:.*]] @@ -134,7 +134,7 @@ void test3() { // CHECK-NEXT: cleanupret from %[[CLEANUPPAD]] unwind to caller // Possibly throwing function call within a catch -void test4() { +void catch_int() { try { may_throw(); } catch (int) { @@ -142,7 +142,7 @@ void test4() { } } -// CHECK-LABEL: @_Z5test4v() +// CHECK-LABEL: @_Z9catch_intv() // CHECK: %[[CATCHSWITCH]] = catchswitch within none [label %[[CATCHSTART_BB]]] unwind to caller @@ -162,7 +162,7 @@ void test4() { // CHECK-NEXT: cleanupret from %[[CLEANUPPAD]] unwind to caller // Possibly throwing function call within a catch-all -void test5() { +void catch_all() { try { may_throw(); } catch (...) { @@ -170,7 +170,7 @@ void test5() { } } -// CHECK-LABEL: @_Z5test5v() +// CHECK-LABEL: @_Z9catch_allv() // CHECK: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB]]] unwind to caller @@ -198,7 +198,7 @@ void test5() { // CHECK-NEXT: unreachable // Try-catch with cleanups -void test6() { +void try_catch_w_cleanups() { Cleanup c1; try { Cleanup c2; @@ -209,7 +209,7 @@ void test6() { } } -// CHECK-LABEL: @_Z5test6v() +// CHECK-LABEL: @_Z20try_catch_w_cleanupsv() // CHECK: invoke void @_Z9may_throwv() // CHECK-NEXT: to label %{{.*}} unwind label %[[EHCLEANUP_BB0:.*]] @@ -254,7 +254,7 @@ void test6() { // CHECK-NEXT: unreachable // Nested try-catches within a try with cleanups -void test7() { +void nested_try_catches_with_cleanups() { Cleanup c1; may_throw(); try { @@ -275,7 +275,7 @@ void test7() { } } -// CHECK-LABEL: @_Z5test7v() +// CHECK-LABEL: @_Z32nested_try_catches_with_cleanupsv() // CHECK: invoke void @_Z9may_throwv() // CHECK: invoke void @_Z9may_throwv() @@ -340,7 +340,7 @@ void test7() { // CHECK: unreachable // Nested try-catches within a catch -void test8() { +void nested_try_catch_within_catch() { try { may_throw(); } catch (int) { @@ -352,7 +352,7 @@ void test8() { } } -// CHECK-LABEL: @_Z5test8v() +// CHECK-LABEL: @_Z29nested_try_catch_within_catchv() // CHECK: invoke void @_Z9may_throwv() // CHECK: %[[CATCHSWITCH0:.*]] = catchswitch within none @@ -402,19 +402,19 @@ void noexcept_throw() noexcept { // This is controlled by -Wwasm-exception-spec, which is on by default. This // warning can be suppressed with -Wno-wasm-exception-spec. Checks if a warning // message is correctly printed or not printed depending on the options. -void test9() throw(int) { +void exception_spec_warning() throw(int) { } // WARNING-DEFAULT: warning: dynamic exception specifications with types are currently ignored in wasm // WARNING-ON: warning: dynamic exception specifications with types are currently ignored in wasm // WARNING-OFF-NOT: warning: dynamic exception specifications with types are currently ignored in wasm // EM-EH-WARNING: warning: dynamic exception specifications with types are currently ignored in wasm -// Wasm curremtly treats 'throw()' in the same way as 'noexept'. Check if the +// Wasm currently treats 'throw()' in the same way as 'noexcept'. Check if the // same warning message is printed as if when a 'noexcept' function throws. -void test10() throw() { +void exception_spec_throw_empty() throw() { throw 3; } -// WARNING-DEFAULT: warning: 'test10' has a non-throwing exception specification but can still throw +// WARNING-DEFAULT: warning: 'exception_spec_throw_empty' has a non-throwing exception specification but can still throw // WARNING-DEFAULT: function declared non-throwing here // Here we only check if the command enables wasm exception handling in the diff --git a/clang/test/CodeGenHLSL/BasicFeatures/standard_conversion_sequences.hlsl b/clang/test/CodeGenHLSL/BasicFeatures/standard_conversion_sequences.hlsl index 1665a0260ab05..6770efefe94fe 100644 --- a/clang/test/CodeGenHLSL/BasicFeatures/standard_conversion_sequences.hlsl +++ b/clang/test/CodeGenHLSL/BasicFeatures/standard_conversion_sequences.hlsl @@ -91,13 +91,12 @@ void l4_to_i2() { // CHECK-LABEL: i2_to_b2 // CHECK: [[l2:%.*]] = alloca <2 x i32> -// CHECK: [[b2:%.*]] = alloca i8 +// CHECK: [[b2:%.*]] = alloca <2 x i32> // CHECK: store <2 x i32> splat (i32 8), ptr [[i2]] // CHECK: [[veci2:%.*]] = load <2 x i32>, ptr [[i2]] // CHECK: [[vecb2:%.*]] = icmp ne <2 x i32> [[veci2]], zeroinitializer -// CHECK: [[vecb8:%.*]] = shufflevector <2 x i1> [[vecb2]], <2 x i1> poison, <8 x i32> -// CHECK: [[i8:%.*]] = bitcast <8 x i1> [[vecb8]] to i8 -// CHECK: store i8 [[i8]], ptr [[b2]] +// CHECK: [[vecb8:%.*]] = zext <2 x i1> [[vecb2]] to <2 x i32> +// CHECK: store <2 x i32> [[vecb8]], ptr [[b2]] void i2_to_b2() { vector i2 = 8; vector b2 = i2; @@ -105,14 +104,13 @@ void i2_to_b2() { // CHECK-LABEL: d4_to_b2 // CHECK: [[d4:%.*]] = alloca <4 x double> -// CHECK: [[b2:%.*]] = alloca i8 +// CHECK: [[b2:%.*]] = alloca <2 x i32> // CHECK: store <4 x double> splat (double 9.000000e+00), ptr [[d4]] // CHECK: [[vecd4:%.*]] = load <4 x double>, ptr [[d4]] // CHECK: [[vecb4:%.*]] = fcmp reassoc nnan ninf nsz arcp afn une <4 x double> [[vecd4]], zeroinitializer // CHECK: [[vecd2:%.*]] = shufflevector <4 x i1> [[vecb4]], <4 x i1> poison, <2 x i32> -// CHECK: [[vecb8:%.*]] = shufflevector <2 x i1> [[vecd2]], <2 x i1> poison, <8 x i32> -// CHECK: [[i8:%.*]] = bitcast <8 x i1> [[vecb8]] to i8 -// CHECK: store i8 [[i8]], ptr [[b2]] +// CHECK: [[vecb8:%.*]] = zext <2 x i1> [[vecd2]] to <2 x i32> +// CHECK: store <2 x i32> [[vecb8]], ptr [[b2]] void d4_to_b2() { vector d4 = 9.0; vector b2 = d4; diff --git a/clang/test/CodeGenHLSL/BoolVector.hlsl b/clang/test/CodeGenHLSL/BoolVector.hlsl new file mode 100644 index 0000000000000..5e889d50be98d --- /dev/null +++ b/clang/test/CodeGenHLSL/BoolVector.hlsl @@ -0,0 +1,111 @@ +// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-library -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s + +// CHECK: %struct.S = type { <2 x i32>, float } +// CHECK: [[ConstS:@.*]] = private unnamed_addr constant %struct.S { <2 x i32> splat (i32 1), float 1.000000e+00 }, align 8 +// CHECK: [[ConstArr:.*]] = private unnamed_addr constant [2 x <2 x i32>] [<2 x i32> splat (i32 1), <2 x i32> zeroinitializer], align 8 + +struct S { + bool2 bv; + float f; +}; + +// CHECK-LABEL: define noundef i1 {{.*}}fn1{{.*}} +// CHECK: [[B:%.*]] = alloca <2 x i32>, align 8 +// CHECK-NEXT: store <2 x i32> splat (i32 1), ptr [[B]], align 8 +// CHECK-NEXT: [[BoolVec:%.*]] = load <2 x i32>, ptr [[B]], align 8 +// CHECK-NEXT: [[L:%.*]] = trunc <2 x i32> [[BoolVec:%.*]] to <2 x i1> +// CHECK-NEXT: [[VecExt:%.*]] = extractelement <2 x i1> [[L]], i32 0 +// CHECK-NEXT: ret i1 [[VecExt]] +bool fn1() { + bool2 B = {true,true}; + return B[0]; +} + +// CHECK-LABEL: define noundef <2 x i1> {{.*}}fn2{{.*}} +// CHECK: [[VAddr:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[A:%.*]] = alloca <2 x i32>, align 8 +// CHECK-NEXT: [[StoreV:%.*]] = zext i1 {{.*}} to i32 +// CHECK-NEXT: store i32 [[StoreV]], ptr [[VAddr]], align 4 +// CHECK-NEXT: [[L:%.*]] = load i32, ptr [[VAddr]], align 4 +// CHECK-NEXT: [[LoadV:%.*]] = trunc i32 [[L]] to i1 +// CHECK-NEXT: [[Vec:%.*]] = insertelement <2 x i1> poison, i1 [[LoadV]], i32 0 +// CHECK-NEXT: [[Vec1:%.*]] = insertelement <2 x i1> [[Vec]], i1 true, i32 1 +// CHECK-NEXT: [[Z:%.*]] = zext <2 x i1> [[Vec1]] to <2 x i32> +// CHECK-NEXT: store <2 x i32> [[Z]], ptr [[A]], align 8 +// CHECK-NEXT: [[LoadBV:%.*]] = load <2 x i32>, ptr [[A]], align 8 +// CHECK-NEXT: [[LoadV2:%.*]] = trunc <2 x i32> [[LoadBV]] to <2 x i1> +// CHECK-NEXT: ret <2 x i1> [[LoadV2]] +bool2 fn2(bool V) { + bool2 A = {V,true}; + return A; +} + +// CHECK-LABEL: define noundef i1 {{.*}}fn3{{.*}} +// CHECK: [[s:%.*]] = alloca %struct.S, align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 8 [[s]], ptr align 8 [[ConstS]], i32 16, i1 false) +// CHECK-NEXT: [[BV:%.*]] = getelementptr inbounds nuw %struct.S, ptr [[s]], i32 0, i32 0 +// CHECK-NEXT: [[LBV:%.*]] = load <2 x i32>, ptr [[BV]], align 8 +// CHECK-NEXT: [[LV:%.*]] = trunc <2 x i32> [[LBV]] to <2 x i1> +// CHECK-NEXT: [[VX:%.*]] = extractelement <2 x i1> [[LV]], i32 0 +// CHECK-NEXT: ret i1 [[VX]] +bool fn3() { + S s = {{true,true}, 1.0}; + return s.bv[0]; +} + +// CHECK-LABEL: define noundef i1 {{.*}}fn4{{.*}} +// CHECK: [[Arr:%.*]] = alloca [2 x <2 x i32>], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 8 [[Arr]], ptr align 8 [[ConstArr]], i32 16, i1 false) +// CHECK-NEXT: [[Idx:%.*]] = getelementptr inbounds [2 x <2 x i32>], ptr [[Arr]], i32 0, i32 0 +// CHECK-NEXT: [[L:%.*]] = load <2 x i32>, ptr [[Idx]], align 8 +// CHECK-NEXT: [[LV:%.*]] = trunc <2 x i32> [[L]] to <2 x i1> +// CHECK-NEXT: [[VX:%.*]] = extractelement <2 x i1> [[LV]], i32 1 +// CHECK-NEXT: ret i1 [[VX]] +bool fn4() { + bool2 Arr[2] = {{true,true}, {false,false}}; + return Arr[0][1]; +} + +// CHECK-LABEL: define void {{.*}}fn5{{.*}} +// CHECK: [[Arr:%.*]] = alloca <2 x i32>, align 8 +// CHECK-NEXT: store <2 x i32> splat (i32 1), ptr [[Arr]], align 8 +// CHECK-NEXT: [[L:%.*]] = load <2 x i32>, ptr [[Arr]], align 8 +// CHECK-NEXT: [[V:%.*]] = insertelement <2 x i32> [[L]], i32 0, i32 1 +// CHECK-NEXT: store <2 x i32> [[V]], ptr [[Arr]], align 8 +// CHECK-NEXT: ret void +void fn5() { + bool2 Arr = {true,true}; + Arr[1] = false; +} + +// CHECK-LABEL: define void {{.*}}fn6{{.*}} +// CHECK: [[V:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[S:%.*]] = alloca %struct.S, align 8 +// CHECK-NEXT: store i32 0, ptr [[V]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 8 [[S]], ptr align 8 {{.*}}, i32 16, i1 false) +// CHECK-NEXT: [[Y:%.*]] = load i32, ptr [[V]], align 4 +// CHECK-NEXT: [[LV:%.*]] = trunc i32 [[Y]] to i1 +// CHECK-NEXT: [[BV:%.*]] = getelementptr inbounds nuw %struct.S, ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: [[X:%.*]] = load <2 x i32>, ptr [[BV]], align 8 +// CHECK-NEXT: [[Z:%.*]] = zext i1 [[LV]] to i32 +// CHECK-NEXT: [[VI:%.*]] = insertelement <2 x i32> [[X]], i32 [[Z]], i32 1 +// CHECK-NEXT: store <2 x i32> [[VI]], ptr [[BV]], align 8 +// CHECK-NEXT: ret void +void fn6() { + bool V = false; + S s = {{true,true}, 1.0}; + s.bv[1] = V; +} + +// CHECK-LABEL: define void {{.*}}fn7{{.*}} +// CHECK: [[Arr:%.*]] = alloca [2 x <2 x i32>], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 8 [[Arr]], ptr align 8 {{.*}}, i32 16, i1 false) +// CHECK-NEXT: [[Idx:%.*]] = getelementptr inbounds [2 x <2 x i32>], ptr [[Arr]], i32 0, i32 0 +// CHECK-NEXT: [[X:%.*]] = load <2 x i32>, ptr [[Idx]], align 8 +// CHECK-NEXT: [[VI:%.*]] = insertelement <2 x i32> [[X]], i32 0, i32 1 +// CHECK-NEXT: store <2 x i32> [[VI]], ptr [[Idx]], align 8 +// CHECK-NEXT: ret void +void fn7() { + bool2 Arr[2] = {{true,true}, {false,false}}; + Arr[0][1] = false; +} diff --git a/clang/test/CodeGenHLSL/builtins/ScalarSwizzles.hlsl b/clang/test/CodeGenHLSL/builtins/ScalarSwizzles.hlsl index 97711c9ee25a1..96e17046ee934 100644 --- a/clang/test/CodeGenHLSL/builtins/ScalarSwizzles.hlsl +++ b/clang/test/CodeGenHLSL/builtins/ScalarSwizzles.hlsl @@ -11,13 +11,23 @@ int2 ToTwoInts(int V){ } // CHECK-LABEL: ToFourFloats -// [[splat:%.*]] = insertelement <1 x float> poison, float {{.*}}, i64 0 -// [[vec4:%.*]] = shufflevector <1 x float> [[splat]], <1 x float> poison, <4 x i32> zeroinitializer +// CHECK: [[splat:%.*]] = insertelement <1 x float> poison, float {{.*}}, i64 0 +// CHECK: [[vec4:%.*]] = shufflevector <1 x float> [[splat]], <1 x float> poison, <4 x i32> zeroinitializer // ret <4 x float> [[vec4]] float4 ToFourFloats(float V){ return V.rrrr; } +// CHECK-LABEL: ToFourBools +// CHECK: {{%.*}} = zext i1 {{.*}} to i32 +// CHECK: [[splat:%.*]] = insertelement <1 x i32> poison, i32 {{.*}}, i64 0 +// CHECK-NEXT: [[vec4:%.*]] = shufflevector <1 x i32> [[splat]], <1 x i32> poison, <4 x i32> zeroinitializer +// CHECK-NEXT: [[vec2Ret:%.*]] = trunc <4 x i32> [[vec4]] to <4 x i1> +// CHECK-NEXT: ret <4 x i1> [[vec2Ret]] +bool4 ToFourBools(bool V) { + return V.rrrr; +} + // CHECK-LABEL: FillOne // CHECK: [[vec1Ptr:%.*]] = alloca <1 x i32>, align 4 // CHECK: store <1 x i32> splat (i32 1), ptr [[vec1Ptr]], align 4 @@ -93,6 +103,17 @@ vector FillOneHalfFloat(){ return .5f.r; } +// CHECK-LABEL: FillTrue +// CHECK: [[Tmp:%.*]] = alloca <1 x i32>, align 4 +// CHECK-NEXT: store <1 x i32> splat (i32 1), ptr [[Tmp]], align 4 +// CHECK-NEXT: [[Vec1:%.*]] = load <1 x i32>, ptr [[Tmp]], align 4 +// CHECK-NEXT: [[Vec2:%.*]] = shufflevector <1 x i32> [[Vec1]], <1 x i32> poison, <2 x i32> zeroinitializer +// CHECK-NEXT: [[Vec2Ret:%.*]] = trunc <2 x i32> [[Vec2]] to <2 x i1> +// CHECK-NEXT: ret <2 x i1> [[Vec2Ret]] +bool2 FillTrue() { + return true.xx; +} + // The initial codegen for this case is correct but a bit odd. The IR optimizer // cleans this up very nicely. @@ -110,6 +131,25 @@ float2 HowManyFloats(float V) { return V.rr.rr; } +// CHECK-LABEL: HowManyBools +// CHECK: [[VAddr:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[Vec2Ptr:%.*]] = alloca <2 x i32>, align 8 +// CHECK-NEXT: [[Tmp:%.*]] = zext i1 {{.*}} to i32 +// CHECK-NEXT: store i32 [[Tmp]], ptr [[VAddr]], align 4 +// CHECK-NEXT: [[VVal:%.*]] = load i32, ptr [[VAddr]], align 4 +// CHECK-NEXT: [[Splat:%.*]] = insertelement <1 x i32> poison, i32 [[VVal]], i64 0 +// CHECK-NEXT: [[Vec2:%.*]] = shufflevector <1 x i32> [[Splat]], <1 x i32> poison, <2 x i32> zeroinitializer +// CHECK-NEXT: [[Trunc:%.*]] = trunc <2 x i32> [[Vec2]] to <2 x i1> +// CHECK-NEXT: [[Ext:%.*]] = zext <2 x i1> [[Trunc]] to <2 x i32> +// CHECK-NEXT: store <2 x i32> [[Ext]], ptr [[Vec2Ptr]], align 8 +// CHECK-NEXT: [[V2:%.*]] = load <2 x i32>, ptr [[Vec2Ptr]], align 8 +// CHECK-NEXT: [[V3:%.*]] = shufflevector <2 x i32> [[V2]], <2 x i32> poison, <2 x i32> zeroinitializer +// CHECK-NEXT: [[LV1:%.*]] = trunc <2 x i32> [[V3]] to <2 x i1> +// CHECK-NEXT: ret <2 x i1> [[LV1]] +bool2 HowManyBools(bool V) { + return V.rr.rr; +} + // This codegen is gnarly because `1.l` is a double, so this creates double // vectors that need to be truncated down to floats. The optimizer cleans this // up nicely too. @@ -166,3 +206,99 @@ int AssignInt(int V){ X.x = V.x + V.x; return X; } + +// CHECK-LABEL: AssignBool +// CHECK: [[VAddr:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[XAddr:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[Zext:%.*]] = zext i1 %V to i32 +// CHECK-NEXT: store i32 [[Zext]], ptr [[VAddr]], align 4 +// CHECK-NEXT: [[X:%.*]] = load i32, ptr [[VAddr]], align 4 +// CHECK-NEXT: [[Splat:%.*]] = insertelement <1 x i32> poison, i32 [[X]], i64 0 +// CHECK-NEXT: [[Y:%.*]] = extractelement <1 x i32> [[Splat]], i32 0 +// CHECK-NEXT: [[Z:%.*]] = trunc i32 [[Y]] to i1 +// CHECK-NEXT: [[A:%.*]] = zext i1 [[Z]] to i32 +// CHECK-NEXT: store i32 [[A]], ptr [[XAddr]], align 4 +// CHECK-NEXT: [[B:%.*]] = load i32, ptr [[VAddr]], align 4 +// CHECK-NEXT: [[Splat2:%.*]] = insertelement <1 x i32> poison, i32 [[B]], i64 0 +// CHECK-NEXT: [[C:%.*]] = extractelement <1 x i32> [[Splat2]], i32 0 +// CHECK-NEXT: [[D:%.*]] = trunc i32 [[C]] to i1 +// CHECK-NEXT: br i1 [[D]], label %lor.end, label %lor.rhs + +// CHECK: lor.rhs: +// CHECK-NEXT: [[E:%.*]] = load i32, ptr [[VAddr]], align 4 +// CHECK-NEXT: [[Splat3:%.*]] = insertelement <1 x i32> poison, i32 [[E]], i64 0 +// CHECK-NEXT: [[F:%.*]] = extractelement <1 x i32> [[Splat3]], i32 0 +// CHECK-NEXT: [[G:%.*]] = trunc i32 [[F]] to i1 +// CHECK-NEXT: br label %lor.end + +// CHECK: lor.end: +// CHECK-NEXT: [[H:%.*]] = phi i1 [ true, %entry ], [ [[G]], %lor.rhs ] +// CHECK-NEXT: store i1 [[H]], ptr [[XAddr]], align 4 +// CHECK-NEXT: [[I:%.*]] = load i32, ptr [[XAddr]], align 4 +// CHECK-NEXT: [[LoadV:%.*]] = trunc i32 [[I]] to i1 +// CHECK-NEXT: ret i1 [[LoadV]] +bool AssignBool(bool V) { + bool X = V.x; + X.x = V.x || V.x; + return X; +} + +// CHECK-LABEL: AssignBool2 +// CHECK: [[VAdddr:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[X:%.*]] = alloca <2 x i32>, align 8 +// CHECK-NEXT: [[Tmp:%.*]] = alloca <1 x i32>, align 4 +// CHECK-NEXT: [[SV:%.*]] = zext i1 %V to i32 +// CHECK-NEXT: store i32 [[SV]], ptr [[VAddr]], align 4 +// CHECK-NEXT: store <1 x i32> splat (i32 1), ptr [[Tmp]], align 4 +// CHECK-NEXT: [[Y:%.*]] = load <1 x i32>, ptr [[Tmp]], align 4 +// CHECK-NEXT: [[Z:%.*]] = shufflevector <1 x i32> [[Y]], <1 x i32> poison, <2 x i32> zeroinitializer +// CHECK-NEXT: [[LV:%.*]] = trunc <2 x i32> [[Z]] to <2 x i1> +// CHECK-NEXT: [[A:%.*]] = zext <2 x i1> [[LV]] to <2 x i32> +// CHECK-NEXT: store <2 x i32> [[A]], ptr [[X]], align 8 +// CHECK-NEXT: [[B:%.*]] = load i32, ptr [[VAddr]], align 4 +// CHECK-NEXT: [[LV1:%.*]] = trunc i32 [[B]] to i1 +// CHECK-NEXT: [[C:%.*]] = load <2 x i32>, ptr [[X]], align 8 +// CHECK-NEXT: [[D:%.*]] = zext i1 [[LV1]] to i32 +// CHECK-NEXT: [[E:%.*]] = insertelement <2 x i32> [[C]], i32 [[D]], i32 1 +// CHECK-NEXT: store <2 x i32> [[E]], ptr [[X]], align 8 +// CHECK-NEXT: ret void +void AssignBool2(bool V) { + bool2 X = true.xx; + X.y = V; +} + +// CHECK-LABEL: AssignBool3 +// CHECK: [[VAddr:%.*]] = alloca <2 x i32>, align 8 +// CHECK-NEXT: [[X:%.*]] = alloca <2 x i32>, align 8 +// CHECK-NEXT: [[Y:%.*]] = zext <2 x i1> %V to <2 x i32> +// CHECK-NEXT: store <2 x i32> [[Y]], ptr [[VAddr]], align 8 +// CHECK-NEXT: store <2 x i32> splat (i32 1), ptr [[X]], align 8 +// CHECK-NEXT: [[Z:%.*]] = load <2 x i32>, ptr [[VAddr]], align 8 +// CHECK-NEXT: [[LV:%.*]] = trunc <2 x i32> [[Z]] to <2 x i1> +// CHECK-NEXT: [[A:%.*]] = load <2 x i32>, ptr [[X]], align 8 +// CHECK-NEXT: [[B:%.*]] = zext <2 x i1> [[LV]] to <2 x i32> +// CHECK-NEXT: [[C:%.*]] = shufflevector <2 x i32> [[B]], <2 x i32> poison, <2 x i32> +// CHECK-NEXT: store <2 x i32> [[C]], ptr [[X]], align 8 +// CHECK-NEXT: ret void +void AssignBool3(bool2 V) { + bool2 X = {true,true}; + X.xy = V; +} + +// CHECK-LABEL: AccessBools +// CHECK: [[X:%.*]] = alloca <4 x i32>, align 16 +// CHECK-NEXT: [[Tmp:%.*]] = alloca <1 x i32>, align 4 +// CHECK-NEXT: store <1 x i32> splat (i32 1), ptr [[Tmp]], align 4 +// CHECK-NEXT: [[Y:%.*]] = load <1 x i32>, ptr [[Tmp]], align 4 +// CHECK-NEXT: [[Z:%.*]] = shufflevector <1 x i32> [[Y]], <1 x i32> poison, <4 x i32> zeroinitializer +// CHECK-NEXT: [[LV:%.*]] = trunc <4 x i32> [[Z]] to <4 x i1> +// CHECK-NEXT: [[A:%.*]] = zext <4 x i1> [[LV]] to <4 x i32> +// CHECK-NEXT: store <4 x i32> [[A]], ptr [[X]], align 16 +// CHECK-NEXT: [[B:%.*]] = load <4 x i32>, ptr [[X]], align 16 +// CHECK-NEXT: [[C:%.*]] = shufflevector <4 x i32> [[B]], <4 x i32> poison, <2 x i32> +// CHECK-NEXT: [[LV1:%.*]] = trunc <2 x i32> [[C]] to <2 x i1> +// CHECK-NEXT: ret <2 x i1> [[LV1]] +bool2 AccessBools() { + bool4 X = true.xxxx; + return X.zw; +} diff --git a/clang/test/CodeGenHLSL/builtins/select.hlsl b/clang/test/CodeGenHLSL/builtins/select.hlsl index cade938b71a2b..196b8a90cd877 100644 --- a/clang/test/CodeGenHLSL/builtins/select.hlsl +++ b/clang/test/CodeGenHLSL/builtins/select.hlsl @@ -52,3 +52,32 @@ int3 test_select_vector_3(bool3 cond0, int3 tVals, int3 fVals) { int4 test_select_vector_4(bool4 cond0, int4 tVals, int4 fVals) { return select(cond0, tVals, fVals); } + +// CHECK-LABEL: test_select_vector_scalar_vector +// CHECK: [[SPLAT_SRC1:%.*]] = insertelement <4 x i32> poison, i32 {{%.*}}, i64 0 +// CHECK: [[SPLAT1:%.*]] = shufflevector <4 x i32> [[SPLAT_SRC1]], <4 x i32> poison, <4 x i32> zeroinitializer +// CHECK: [[SELECT:%.*]] = select <4 x i1> {{%.*}}, <4 x i32> [[SPLAT1]], <4 x i32> {{%.*}} +// CHECK: ret <4 x i32> [[SELECT]] +int4 test_select_vector_scalar_vector(bool4 cond0, int tVal, int4 fVals) { + return select(cond0, tVal, fVals); +} + +// CHECK-LABEL: test_select_vector_vector_scalar +// CHECK: [[SPLAT_SRC1:%.*]] = insertelement <4 x i32> poison, i32 {{%.*}}, i64 0 +// CHECK: [[SPLAT1:%.*]] = shufflevector <4 x i32> [[SPLAT_SRC1]], <4 x i32> poison, <4 x i32> zeroinitializer +// CHECK: [[SELECT:%.*]] = select <4 x i1> {{%.*}}, <4 x i32> {{%.*}}, <4 x i32> [[SPLAT1]] +// CHECK: ret <4 x i32> [[SELECT]] +int4 test_select_vector_vector_scalar(bool4 cond0, int4 tVals, int fVal) { + return select(cond0, tVals, fVal); +} + +// CHECK-LABEL: test_select_vector_scalar_scalar +// CHECK: [[SPLAT_SRC1:%.*]] = insertelement <4 x i32> poison, i32 {{%.*}}, i64 0 +// CHECK: [[SPLAT1:%.*]] = shufflevector <4 x i32> [[SPLAT_SRC1]], <4 x i32> poison, <4 x i32> zeroinitializer +// CHECK: [[SPLAT_SRC2:%.*]] = insertelement <4 x i32> poison, i32 %3, i64 0 +// CHECK: [[SPLAT2:%.*]] = shufflevector <4 x i32> [[SPLAT_SRC2]], <4 x i32> poison, <4 x i32> zeroinitializer +// CHECK: [[SELECT:%.*]] = select <4 x i1> {{%.*}}, <4 x i32> [[SPLAT1]], <4 x i32> [[SPLAT2]] +// CHECK: ret <4 x i32> [[SELECT]] +int4 test_select_vector_scalar_scalar(bool4 cond0, int tVal, int fVal) { + return select(cond0, tVal, fVal); +} diff --git a/clang/test/CodeGenHLSL/cbuffer.hlsl b/clang/test/CodeGenHLSL/cbuffer.hlsl index 38093c6dfacd7..b5e435619438f 100644 --- a/clang/test/CodeGenHLSL/cbuffer.hlsl +++ b/clang/test/CodeGenHLSL/cbuffer.hlsl @@ -20,6 +20,14 @@ // CHECK: %anon = type <{ float }> // CHECK: %anon.0 = type <{ <2 x i32> }> +// CHECK: %__cblayout_CB_A = type <{ [2 x double], [3 x <3 x float>], float, [3 x double], half, [1 x <2 x double>], float, [2 x <3 x half>], <3 x half> }> +// CHECK: %__cblayout_CB_B = type <{ [3 x <3 x double>], <3 x half> }> +// CHECK: %__cblayout_CB_C = type <{ i32, target("dx.Layout", %F, 96, 0, 16, 28, 32, 56, 64, 80, 84, 90), half, target("dx.Layout", %G, 258, 0, 48, 64, 256), double }> + +// CHECK: %F = type <{ double, <3 x float>, float, <3 x double>, half, <2 x double>, float, <3 x half>, <3 x half> }> +// CHECK: %G = type <{ target("dx.Layout", %E, 36, 0, 8, 16, 20, 22, 24, 32), [1 x float], [2 x target("dx.Layout", %F, 96, 0, 16, 28, 32, 56, 64, 80, 84, 90)], half }> +// CHECK: %E = type <{ float, double, float, half, i16, i64, i32 }> + cbuffer CBScalars : register(b1, space5) { float a1; double a2; @@ -155,6 +163,64 @@ cbuffer CBMix { uint16_t f9; }; +// CHECK: @CB_A.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB_A, 188, 0, 32, 76, 80, 120, 128, 144, 160, 182)) + +cbuffer CB_A { + double B0[2]; + float3 B1[3]; + float B2; + double B3[3]; + half B4; + double2 B5[1]; + float B6; + half3 B7[2]; + half3 B8; +} + +// CHECK: @CB_B.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB_B, 94, 0, 88)) +cbuffer CB_B { + double3 B9[3]; + half3 B10; +} + +struct E { + float A0; + double A1; + float A2; + half A3; + int16_t A4; + int64_t A5; + int A6; +}; + +struct F { + double B0; + float3 B1; + float B2; + double3 B3; + half B4; + double2 B5; + float B6; + half3 B7; + half3 B8; +}; + +struct G { + E C0; + float C1[1]; + F C2[2]; + half C3; +}; + +// CHECK: @CB_C.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB_C, 400, 0, 16, 112, 128, 392)) +cbuffer CB_C { + int D0; + F D1; + half D2; + G D3; + double D4; +} + // CHECK: define internal void @_init_resource_CBScalars.cb() // CHECK-NEXT: entry: // CHECK-NEXT: %[[HANDLE1:.*]] = call target("dx.CBuffer", target("dx.Layout", %__cblayout_CBScalars, 56, 0, 8, 16, 24, 32, 36, 40, 48)) @@ -171,7 +237,7 @@ RWBuffer Buf; [numthreads(4,1,1)] void main() { - Buf[0] = a1 + b1.z + c1[2] + a.f1.y + f1; + Buf[0] = a1 + b1.z + c1[2] + a.f1.y + f1 + B1[0].x + B10.z + D1.B2; } // CHECK: define internal void @_GLOBAL__sub_I_cbuffer.hlsl() @@ -179,7 +245,8 @@ void main() { // CHECK-NEXT: call void @_init_resource_CBScalars.cb() // CHECK-NEXT: call void @_init_resource_CBArrays.cb() -// CHECK: !hlsl.cbs = !{![[CBSCALARS:[0-9]+]], ![[CBVECTORS:[0-9]+]], ![[CBARRAYS:[0-9]+]], ![[CBSTRUCTS:[0-9]+]], ![[CBMIX:[0-9]+]]} +// CHECK: !hlsl.cbs = !{![[CBSCALARS:[0-9]+]], ![[CBVECTORS:[0-9]+]], ![[CBARRAYS:[0-9]+]], ![[CBSTRUCTS:[0-9]+]], ![[CBMIX:[0-9]+]], +// CHECK-SAME: ![[CB_A:[0-9]+]], ![[CB_B:[0-9]+]], ![[CB_C:[0-9]+]]} // CHECK: ![[CBSCALARS]] = !{ptr @CBScalars.cb, ptr addrspace(2) @a1, ptr addrspace(2) @a2, ptr addrspace(2) @a3, ptr addrspace(2) @a4, // CHECK-SAME: ptr addrspace(2) @a5, ptr addrspace(2) @a6, ptr addrspace(2) @a7, ptr addrspace(2) @a8} @@ -195,3 +262,10 @@ void main() { // CHECK: ![[CBMIX]] = !{ptr @CBMix.cb, ptr addrspace(2) @test, ptr addrspace(2) @f1, ptr addrspace(2) @f2, ptr addrspace(2) @f3, // CHECK-SAME: ptr addrspace(2) @f4, ptr addrspace(2) @f5, ptr addrspace(2) @f6, ptr addrspace(2) @f7, ptr addrspace(2) @f8, ptr addrspace(2) @f9} + +// CHECK: ![[CB_A]] = !{ptr @CB_A.cb, ptr addrspace(2) @B0, ptr addrspace(2) @B1, ptr addrspace(2) @B2, ptr addrspace(2) @B3, ptr addrspace(2) @B4, +// CHECK-SAME: ptr addrspace(2) @B5, ptr addrspace(2) @B6, ptr addrspace(2) @B7, ptr addrspace(2) @B8} + +// CHECK: ![[CB_B]] = !{ptr @CB_B.cb, ptr addrspace(2) @B9, ptr addrspace(2) @B10} + +// CHECK: ![[CB_C]] = !{ptr @CB_C.cb, ptr addrspace(2) @D0, ptr addrspace(2) @D1, ptr addrspace(2) @D2, ptr addrspace(2) @D3, ptr addrspace(2) @D4} diff --git a/clang/test/CodeGenOpenCL/amdgpu-enqueue-kernel-linking.cl b/clang/test/CodeGenOpenCL/amdgpu-enqueue-kernel-linking.cl new file mode 100644 index 0000000000000..a61c5530c8a73 --- /dev/null +++ b/clang/test/CodeGenOpenCL/amdgpu-enqueue-kernel-linking.cl @@ -0,0 +1,80 @@ +// Make sure that invoking blocks in static functions with the same name in +// different modules are linked together. + +// RUN: %clang_cc1 -O0 -cl-std=CL2.0 -triple amdgcn-amd-amdhsa -fno-ident -DKERNEL_NAME=test_kernel_first -DTYPE=float -DCONST=256.0f -emit-llvm-bc -o %t.0.bc %s +// RUN: %clang_cc1 -O0 -cl-std=CL2.0 -triple amdgcn-amd-amdhsa -fno-ident -DKERNEL_NAME=test_kernel_second -DTYPE=int -DCONST=128.0f -emit-llvm-bc -o %t.1.bc %s + +// Make sure nothing strange happens with the linkage choices. +// RUN: opt -passes=globalopt -o %t.opt.0.bc %t.0.bc +// RUN: opt -passes=globalopt -o %t.opt.1.bc %t.1.bc + +// Check the result of linking +// RUN: llvm-link -S %t.opt.0.bc %t.opt.1.bc -o - | FileCheck %s + +// Make sure that a block invoke used with the same name works in multiple +// translation units + +// CHECK: @llvm.used = appending addrspace(1) global [4 x ptr] [ptr @__static_invoker_block_invoke_kernel, ptr addrspacecast (ptr addrspace(1) @__static_invoker_block_invoke_kernel.runtime.handle to ptr), ptr @__static_invoker_block_invoke_kernel.2, ptr addrspacecast (ptr addrspace(1) @__static_invoker_block_invoke_kernel.runtime.handle.3 to ptr)], section "llvm.metadata" + + +// CHECK: @__static_invoker_block_invoke_kernel.runtime.handle = internal addrspace(1) externally_initialized constant %block.runtime.handle.t zeroinitializer, section ".amdgpu.kernel.runtime.handle" +// CHECK: @__static_invoker_block_invoke_kernel.runtime.handle.3 = internal addrspace(1) externally_initialized constant %block.runtime.handle.t zeroinitializer, section ".amdgpu.kernel.runtime.handle" + +// CHECK: define internal amdgpu_kernel void @__static_invoker_block_invoke_kernel(<{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1) }> %0) #{{[0-9]+}} !associated ![[ASSOC_FIRST_MD:[0-9]+]] + + +// CHECK-LABEL: define internal void @__static_invoker_block_invoke(ptr noundef %.block_descriptor) +// CHECK: call float @llvm.fmuladd.f32 + + +// CHECK-LABEL: define dso_local amdgpu_kernel void @test_kernel_first( + + +// CHECK-LABEL: define internal fastcc void @static_invoker(ptr addrspace(1) noundef %outptr, ptr addrspace(1) noundef %argptr) +// CHECK: call i32 @__enqueue_kernel_basic(ptr addrspace(1) %{{[0-9]+}}, i32 %{{[0-9]+}}, ptr addrspace(5) %tmp, ptr addrspacecast (ptr addrspace(1) @__static_invoker_block_invoke_kernel.runtime.handle to ptr), ptr %{{.+}}) + +// CHECK: declare i32 @__enqueue_kernel_basic(ptr addrspace(1), i32, ptr addrspace(5), ptr, ptr) local_unnamed_addr + + +// CHECK: define internal amdgpu_kernel void @__static_invoker_block_invoke_kernel.2(<{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1) }> %0) #{{[0-9]+}} !associated ![[ASSOC_SECOND_MD:[0-9]+]] +// CHECK: call void @__static_invoker_block_invoke.4(ptr % + + +// CHECK-LABEL: define internal void @__static_invoker_block_invoke.4(ptr noundef %.block_descriptor) +// CHECK: mul nsw i32 +// CHECK: sitofp +// CHECK: fadd +// CHECK: fptosi + +// CHECK-LABEL: define dso_local amdgpu_kernel void @test_kernel_second(ptr addrspace(1) noundef align 4 %outptr, ptr addrspace(1) noundef align 4 %argptr, ptr addrspace(1) noundef align 4 %difference) + +// CHECK-LABEL: define internal fastcc void @static_invoker.5(ptr addrspace(1) noundef %outptr, ptr addrspace(1) noundef %argptr) unnamed_addr #{{[0-9]+}} { +// CHECK: call i32 @__enqueue_kernel_basic(ptr addrspace(1) %{{[0-9]+}}, i32 %{{[0-9]+}}, ptr addrspace(5) %tmp, ptr addrspacecast (ptr addrspace(1) @__static_invoker_block_invoke_kernel.runtime.handle.3 to ptr), ptr %{{.+}}) + + +typedef struct {int a;} ndrange_t; + +static void static_invoker(global TYPE* outptr, global TYPE* argptr) { + queue_t default_queue; + unsigned flags = 0; + ndrange_t ndrange; + + enqueue_kernel(default_queue, flags, ndrange, + ^(void) { + global TYPE* f = argptr; + outptr[0] = f[1] * f[2] + CONST; + }); +} + +kernel void KERNEL_NAME(global TYPE *outptr, global TYPE *argptr, global TYPE *difference) { + queue_t default_queue; + unsigned flags = 0; + ndrange_t ndrange; + + static_invoker(outptr, argptr); + + *difference = CONST; +} + +// CHECK: ![[ASSOC_FIRST_MD]] = !{ptr addrspace(1) @__static_invoker_block_invoke_kernel.runtime.handle} +// CHECK: ![[ASSOC_SECOND_MD]] = !{ptr addrspace(1) @__static_invoker_block_invoke_kernel.runtime.handle.3} diff --git a/clang/test/CodeGenOpenCL/amdgpu-enqueue-kernel.cl b/clang/test/CodeGenOpenCL/amdgpu-enqueue-kernel.cl index 9ba36643c4b9e..367217579e765 100644 --- a/clang/test/CodeGenOpenCL/amdgpu-enqueue-kernel.cl +++ b/clang/test/CodeGenOpenCL/amdgpu-enqueue-kernel.cl @@ -61,7 +61,13 @@ kernel void test_target_features_kernel(global int *i) { } //. +// CHECK: @__test_block_invoke_kernel.runtime.handle = internal addrspace(1) externally_initialized constant %block.runtime.handle.t zeroinitializer, section ".amdgpu.kernel.runtime.handle" +// CHECK: @__test_block_invoke_2_kernel.runtime.handle = internal addrspace(1) externally_initialized constant %block.runtime.handle.t.0 zeroinitializer, section ".amdgpu.kernel.runtime.handle" +// CHECK: @__test_block_invoke_3_kernel.runtime.handle = internal addrspace(1) externally_initialized constant %block.runtime.handle.t.1 zeroinitializer, section ".amdgpu.kernel.runtime.handle" +// CHECK: @__test_block_invoke_4_kernel.runtime.handle = internal addrspace(1) externally_initialized constant %block.runtime.handle.t.2 zeroinitializer, section ".amdgpu.kernel.runtime.handle" // CHECK: @__block_literal_global = internal addrspace(1) constant { i32, i32, ptr } { i32 16, i32 8, ptr @__test_target_features_kernel_block_invoke }, align 8 #0 +// CHECK: @__test_target_features_kernel_block_invoke_kernel.runtime.handle = internal addrspace(1) externally_initialized constant %block.runtime.handle.t.3 zeroinitializer, section ".amdgpu.kernel.runtime.handle" +// CHECK: @llvm.used = appending addrspace(1) global [10 x ptr] [ptr @__test_block_invoke_kernel, ptr addrspacecast (ptr addrspace(1) @__test_block_invoke_kernel.runtime.handle to ptr), ptr @__test_block_invoke_2_kernel, ptr addrspacecast (ptr addrspace(1) @__test_block_invoke_2_kernel.runtime.handle to ptr), ptr @__test_block_invoke_3_kernel, ptr addrspacecast (ptr addrspace(1) @__test_block_invoke_3_kernel.runtime.handle to ptr), ptr @__test_block_invoke_4_kernel, ptr addrspacecast (ptr addrspace(1) @__test_block_invoke_4_kernel.runtime.handle to ptr), ptr @__test_target_features_kernel_block_invoke_kernel, ptr addrspacecast (ptr addrspace(1) @__test_target_features_kernel_block_invoke_kernel.runtime.handle to ptr)], section "llvm.metadata" // CHECK: @__oclc_ABI_version = weak_odr hidden local_unnamed_addr addrspace(4) constant i32 500 //. // NOCPU: Function Attrs: convergent noinline norecurse nounwind optnone @@ -140,7 +146,7 @@ kernel void test_target_features_kernel(global int *i) { // NOCPU-NEXT: [[BLOCK_CAPTURED1:%.*]] = getelementptr inbounds nuw <{ i32, i32, ptr, ptr addrspace(1), i8 }>, ptr [[BLOCK_ASCAST]], i32 0, i32 4 // NOCPU-NEXT: [[TMP3:%.*]] = load i8, ptr [[B_ADDR_ASCAST]], align 1 // NOCPU-NEXT: store i8 [[TMP3]], ptr [[BLOCK_CAPTURED1]], align 8 -// NOCPU-NEXT: [[TMP4:%.*]] = call i32 @__enqueue_kernel_basic(ptr addrspace(1) [[TMP0]], i32 [[TMP1]], ptr addrspace(5) [[TMP]], ptr @__test_block_invoke_kernel, ptr [[BLOCK_ASCAST]]) +// NOCPU-NEXT: [[TMP4:%.*]] = call i32 @__enqueue_kernel_basic(ptr addrspace(1) [[TMP0]], i32 [[TMP1]], ptr addrspace(5) [[TMP]], ptr addrspacecast (ptr addrspace(1) @__test_block_invoke_kernel.runtime.handle to ptr), ptr [[BLOCK_ASCAST]]) // NOCPU-NEXT: [[TMP5:%.*]] = load ptr addrspace(1), ptr [[DEFAULT_QUEUE_ASCAST]], align 8 // NOCPU-NEXT: [[TMP6:%.*]] = load i32, ptr [[FLAGS_ASCAST]], align 4 // NOCPU-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP2_ASCAST]], ptr align 4 [[NDRANGE_ASCAST]], i64 4, i1 false) @@ -162,7 +168,7 @@ kernel void test_target_features_kernel(global int *i) { // NOCPU-NEXT: [[BLOCK_CAPTURED10:%.*]] = getelementptr inbounds nuw <{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1), i64, i8 }>, ptr [[BLOCK3_ASCAST]], i32 0, i32 5 // NOCPU-NEXT: [[TMP10:%.*]] = load i64, ptr [[D_ADDR_ASCAST]], align 8 // NOCPU-NEXT: store i64 [[TMP10]], ptr [[BLOCK_CAPTURED10]], align 8 -// NOCPU-NEXT: [[TMP11:%.*]] = call i32 @__enqueue_kernel_basic(ptr addrspace(1) [[TMP5]], i32 [[TMP6]], ptr addrspace(5) [[VARTMP2]], ptr @__test_block_invoke_2_kernel, ptr [[BLOCK3_ASCAST]]) +// NOCPU-NEXT: [[TMP11:%.*]] = call i32 @__enqueue_kernel_basic(ptr addrspace(1) [[TMP5]], i32 [[TMP6]], ptr addrspace(5) [[VARTMP2]], ptr addrspacecast (ptr addrspace(1) @__test_block_invoke_2_kernel.runtime.handle to ptr), ptr [[BLOCK3_ASCAST]]) // NOCPU-NEXT: [[TMP12:%.*]] = load ptr addrspace(1), ptr [[DEFAULT_QUEUE_ASCAST]], align 8 // NOCPU-NEXT: [[TMP13:%.*]] = load i32, ptr [[FLAGS_ASCAST]], align 4 // NOCPU-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP11_ASCAST]], ptr align 4 [[NDRANGE_ASCAST]], i64 4, i1 false) @@ -186,7 +192,7 @@ kernel void test_target_features_kernel(global int *i) { // NOCPU-NEXT: store i64 [[TMP17]], ptr [[BLOCK_CAPTURED19]], align 8 // NOCPU-NEXT: [[TMP18:%.*]] = getelementptr [1 x i64], ptr [[BLOCK_SIZES_ASCAST]], i32 0, i32 0 // NOCPU-NEXT: store i64 100, ptr [[TMP18]], align 8 -// NOCPU-NEXT: [[TMP19:%.*]] = call i32 @__enqueue_kernel_varargs(ptr addrspace(1) [[TMP12]], i32 [[TMP13]], ptr addrspace(5) [[VARTMP11]], ptr @__test_block_invoke_3_kernel, ptr [[BLOCK12_ASCAST]], i32 1, ptr [[TMP18]]) +// NOCPU-NEXT: [[TMP19:%.*]] = call i32 @__enqueue_kernel_varargs(ptr addrspace(1) [[TMP12]], i32 [[TMP13]], ptr addrspace(5) [[VARTMP11]], ptr addrspacecast (ptr addrspace(1) @__test_block_invoke_3_kernel.runtime.handle to ptr), ptr [[BLOCK12_ASCAST]], i32 1, ptr [[TMP18]]) // NOCPU-NEXT: [[BLOCK_SIZE22:%.*]] = getelementptr inbounds nuw <{ i32, i32, ptr, i64, ptr addrspace(1) }>, ptr [[BLOCK21_ASCAST]], i32 0, i32 0 // NOCPU-NEXT: store i32 32, ptr [[BLOCK_SIZE22]], align 8 // NOCPU-NEXT: [[BLOCK_ALIGN23:%.*]] = getelementptr inbounds nuw <{ i32, i32, ptr, i64, ptr addrspace(1) }>, ptr [[BLOCK21_ASCAST]], i32 0, i32 1 @@ -204,7 +210,7 @@ kernel void test_target_features_kernel(global int *i) { // NOCPU-NEXT: [[TMP23:%.*]] = load i32, ptr [[FLAGS_ASCAST]], align 4 // NOCPU-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP27_ASCAST]], ptr align 4 [[NDRANGE_ASCAST]], i64 4, i1 false) // NOCPU-NEXT: [[TMP24:%.*]] = load ptr, ptr [[BLOCK20_ASCAST]], align 8 -// NOCPU-NEXT: [[TMP25:%.*]] = call i32 @__enqueue_kernel_basic(ptr addrspace(1) [[TMP22]], i32 [[TMP23]], ptr addrspace(5) [[VARTMP27]], ptr @__test_block_invoke_4_kernel, ptr [[BLOCK21_ASCAST]]) +// NOCPU-NEXT: [[TMP25:%.*]] = call i32 @__enqueue_kernel_basic(ptr addrspace(1) [[TMP22]], i32 [[TMP23]], ptr addrspace(5) [[VARTMP27]], ptr addrspacecast (ptr addrspace(1) @__test_block_invoke_4_kernel.runtime.handle to ptr), ptr [[BLOCK21_ASCAST]]) // NOCPU-NEXT: ret void // // @@ -229,7 +235,7 @@ kernel void test_target_features_kernel(global int *i) { // // NOCPU: Function Attrs: convergent nounwind // NOCPU-LABEL: define {{[^@]+}}@__test_block_invoke_kernel -// NOCPU-SAME: (<{ i32, i32, ptr, ptr addrspace(1), i8 }> [[TMP0:%.*]]) #[[ATTR5:[0-9]+]] !kernel_arg_addr_space [[META7:![0-9]+]] !kernel_arg_access_qual [[META8:![0-9]+]] !kernel_arg_type [[META9:![0-9]+]] !kernel_arg_base_type [[META9]] !kernel_arg_type_qual [[META10:![0-9]+]] { +// NOCPU-SAME: (<{ i32, i32, ptr, ptr addrspace(1), i8 }> [[TMP0:%.*]]) #[[ATTR5:[0-9]+]] !associated [[META7:![0-9]+]] !kernel_arg_addr_space [[META8:![0-9]+]] !kernel_arg_access_qual [[META9:![0-9]+]] !kernel_arg_type [[META10:![0-9]+]] !kernel_arg_base_type [[META10]] !kernel_arg_type_qual [[META11:![0-9]+]] { // NOCPU-NEXT: entry: // NOCPU-NEXT: [[TMP1:%.*]] = alloca <{ i32, i32, ptr, ptr addrspace(1), i8 }>, align 8, addrspace(5) // NOCPU-NEXT: store <{ i32, i32, ptr, ptr addrspace(1), i8 }> [[TMP0]], ptr addrspace(5) [[TMP1]], align 8 @@ -265,7 +271,7 @@ kernel void test_target_features_kernel(global int *i) { // // NOCPU: Function Attrs: convergent nounwind // NOCPU-LABEL: define {{[^@]+}}@__test_block_invoke_2_kernel -// NOCPU-SAME: (<{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1), i64, i8 }> [[TMP0:%.*]]) #[[ATTR5]] !kernel_arg_addr_space [[META7]] !kernel_arg_access_qual [[META8]] !kernel_arg_type [[META9]] !kernel_arg_base_type [[META9]] !kernel_arg_type_qual [[META10]] { +// NOCPU-SAME: (<{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1), i64, i8 }> [[TMP0:%.*]]) #[[ATTR5]] !associated [[META12:![0-9]+]] !kernel_arg_addr_space [[META8]] !kernel_arg_access_qual [[META9]] !kernel_arg_type [[META10]] !kernel_arg_base_type [[META10]] !kernel_arg_type_qual [[META11]] { // NOCPU-NEXT: entry: // NOCPU-NEXT: [[TMP1:%.*]] = alloca <{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1), i64, i8 }>, align 8, addrspace(5) // NOCPU-NEXT: store <{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1), i64, i8 }> [[TMP0]], ptr addrspace(5) [[TMP1]], align 8 @@ -307,7 +313,7 @@ kernel void test_target_features_kernel(global int *i) { // // NOCPU: Function Attrs: convergent nounwind // NOCPU-LABEL: define {{[^@]+}}@__test_block_invoke_3_kernel -// NOCPU-SAME: (<{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1), i64, i8 }> [[TMP0:%.*]], ptr addrspace(3) [[TMP1:%.*]]) #[[ATTR5]] !kernel_arg_addr_space [[META11:![0-9]+]] !kernel_arg_access_qual [[META12:![0-9]+]] !kernel_arg_type [[META13:![0-9]+]] !kernel_arg_base_type [[META13]] !kernel_arg_type_qual [[META14:![0-9]+]] { +// NOCPU-SAME: (<{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1), i64, i8 }> [[TMP0:%.*]], ptr addrspace(3) [[TMP1:%.*]]) #[[ATTR5]] !associated [[META13:![0-9]+]] !kernel_arg_addr_space [[META14:![0-9]+]] !kernel_arg_access_qual [[META15:![0-9]+]] !kernel_arg_type [[META16:![0-9]+]] !kernel_arg_base_type [[META16]] !kernel_arg_type_qual [[META17:![0-9]+]] { // NOCPU-NEXT: entry: // NOCPU-NEXT: [[TMP2:%.*]] = alloca <{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1), i64, i8 }>, align 8, addrspace(5) // NOCPU-NEXT: store <{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1), i64, i8 }> [[TMP0]], ptr addrspace(5) [[TMP2]], align 8 @@ -336,7 +342,7 @@ kernel void test_target_features_kernel(global int *i) { // // NOCPU: Function Attrs: convergent nounwind // NOCPU-LABEL: define {{[^@]+}}@__test_block_invoke_4_kernel -// NOCPU-SAME: (<{ i32, i32, ptr, i64, ptr addrspace(1) }> [[TMP0:%.*]]) #[[ATTR5]] !kernel_arg_addr_space [[META7]] !kernel_arg_access_qual [[META8]] !kernel_arg_type [[META9]] !kernel_arg_base_type [[META9]] !kernel_arg_type_qual [[META10]] { +// NOCPU-SAME: (<{ i32, i32, ptr, i64, ptr addrspace(1) }> [[TMP0:%.*]]) #[[ATTR5]] !associated [[META18:![0-9]+]] !kernel_arg_addr_space [[META8]] !kernel_arg_access_qual [[META9]] !kernel_arg_type [[META10]] !kernel_arg_base_type [[META10]] !kernel_arg_type_qual [[META11]] { // NOCPU-NEXT: entry: // NOCPU-NEXT: [[TMP1:%.*]] = alloca <{ i32, i32, ptr, i64, ptr addrspace(1) }>, align 8, addrspace(5) // NOCPU-NEXT: store <{ i32, i32, ptr, i64, ptr addrspace(1) }> [[TMP0]], ptr addrspace(5) [[TMP1]], align 8 @@ -347,7 +353,7 @@ kernel void test_target_features_kernel(global int *i) { // // NOCPU: Function Attrs: convergent noinline norecurse nounwind optnone // NOCPU-LABEL: define {{[^@]+}}@test_target_features_kernel -// NOCPU-SAME: (ptr addrspace(1) noundef align 4 [[I:%.*]]) #[[ATTR6:[0-9]+]] !kernel_arg_addr_space [[META15:![0-9]+]] !kernel_arg_access_qual [[META8]] !kernel_arg_type [[META16:![0-9]+]] !kernel_arg_base_type [[META16]] !kernel_arg_type_qual [[META10]] { +// NOCPU-SAME: (ptr addrspace(1) noundef align 4 [[I:%.*]]) #[[ATTR6:[0-9]+]] !kernel_arg_addr_space [[META19:![0-9]+]] !kernel_arg_access_qual [[META9]] !kernel_arg_type [[META20:![0-9]+]] !kernel_arg_base_type [[META20]] !kernel_arg_type_qual [[META11]] { // NOCPU-NEXT: entry: // NOCPU-NEXT: [[I_ADDR:%.*]] = alloca ptr addrspace(1), align 8, addrspace(5) // NOCPU-NEXT: [[DEFAULT_QUEUE:%.*]] = alloca ptr addrspace(1), align 8, addrspace(5) @@ -365,7 +371,7 @@ kernel void test_target_features_kernel(global int *i) { // NOCPU-NEXT: [[TMP1:%.*]] = load ptr addrspace(1), ptr [[DEFAULT_QUEUE_ASCAST]], align 8 // NOCPU-NEXT: [[TMP2:%.*]] = load i32, ptr [[FLAGS_ASCAST]], align 4 // NOCPU-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP_ASCAST]], ptr align 4 [[NDRANGE_ASCAST]], i64 4, i1 false) -// NOCPU-NEXT: [[TMP3:%.*]] = call i32 @__enqueue_kernel_basic(ptr addrspace(1) [[TMP1]], i32 [[TMP2]], ptr addrspace(5) [[TMP]], ptr @__test_target_features_kernel_block_invoke_kernel, ptr addrspacecast (ptr addrspace(1) @__block_literal_global to ptr)) +// NOCPU-NEXT: [[TMP3:%.*]] = call i32 @__enqueue_kernel_basic(ptr addrspace(1) [[TMP1]], i32 [[TMP2]], ptr addrspace(5) [[TMP]], ptr addrspacecast (ptr addrspace(1) @__test_target_features_kernel_block_invoke_kernel.runtime.handle to ptr), ptr addrspacecast (ptr addrspace(1) @__block_literal_global to ptr)) // NOCPU-NEXT: ret void // // @@ -385,7 +391,7 @@ kernel void test_target_features_kernel(global int *i) { // // NOCPU: Function Attrs: convergent nounwind // NOCPU-LABEL: define {{[^@]+}}@__test_target_features_kernel_block_invoke_kernel -// NOCPU-SAME: ({ i32, i32, ptr } [[TMP0:%.*]]) #[[ATTR5]] !kernel_arg_addr_space [[META7]] !kernel_arg_access_qual [[META8]] !kernel_arg_type [[META9]] !kernel_arg_base_type [[META9]] !kernel_arg_type_qual [[META10]] { +// NOCPU-SAME: ({ i32, i32, ptr } [[TMP0:%.*]]) #[[ATTR5]] !associated [[META21:![0-9]+]] !kernel_arg_addr_space [[META8]] !kernel_arg_access_qual [[META9]] !kernel_arg_type [[META10]] !kernel_arg_base_type [[META10]] !kernel_arg_type_qual [[META11]] { // NOCPU-NEXT: entry: // NOCPU-NEXT: [[TMP1:%.*]] = alloca { i32, i32, ptr }, align 8, addrspace(5) // NOCPU-NEXT: store { i32, i32, ptr } [[TMP0]], ptr addrspace(5) [[TMP1]], align 8 @@ -467,10 +473,10 @@ kernel void test_target_features_kernel(global int *i) { // GFX900-NEXT: store i8 [[B]], ptr [[B_ADDR_ASCAST]], align 1, !tbaa [[TBAA16:![0-9]+]] // GFX900-NEXT: store ptr addrspace(1) [[C]], ptr [[C_ADDR_ASCAST]], align 8, !tbaa [[TBAA7]] // GFX900-NEXT: store i64 [[D]], ptr [[D_ADDR_ASCAST]], align 8, !tbaa [[TBAA3]] -// GFX900-NEXT: call void @llvm.lifetime.start.p5(i64 8, ptr addrspace(5) [[DEFAULT_QUEUE]]) #[[ATTR8:[0-9]+]] -// GFX900-NEXT: call void @llvm.lifetime.start.p5(i64 4, ptr addrspace(5) [[FLAGS]]) #[[ATTR8]] +// GFX900-NEXT: call void @llvm.lifetime.start.p5(i64 8, ptr addrspace(5) [[DEFAULT_QUEUE]]) #[[ATTR7:[0-9]+]] +// GFX900-NEXT: call void @llvm.lifetime.start.p5(i64 4, ptr addrspace(5) [[FLAGS]]) #[[ATTR7]] // GFX900-NEXT: store i32 0, ptr [[FLAGS_ASCAST]], align 4, !tbaa [[TBAA17:![0-9]+]] -// GFX900-NEXT: call void @llvm.lifetime.start.p5(i64 4, ptr addrspace(5) [[NDRANGE]]) #[[ATTR8]] +// GFX900-NEXT: call void @llvm.lifetime.start.p5(i64 4, ptr addrspace(5) [[NDRANGE]]) #[[ATTR7]] // GFX900-NEXT: [[TMP0:%.*]] = load ptr addrspace(1), ptr [[DEFAULT_QUEUE_ASCAST]], align 8, !tbaa [[TBAA19:![0-9]+]] // GFX900-NEXT: [[TMP1:%.*]] = load i32, ptr [[FLAGS_ASCAST]], align 4, !tbaa [[TBAA17]] // GFX900-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP_ASCAST]], ptr align 4 [[NDRANGE_ASCAST]], i64 4, i1 false), !tbaa.struct [[TBAA_STRUCT21:![0-9]+]] @@ -486,7 +492,7 @@ kernel void test_target_features_kernel(global int *i) { // GFX900-NEXT: [[BLOCK_CAPTURED1:%.*]] = getelementptr inbounds nuw <{ i32, i32, ptr, ptr addrspace(1), i8 }>, ptr [[BLOCK_ASCAST]], i32 0, i32 4 // GFX900-NEXT: [[TMP3:%.*]] = load i8, ptr [[B_ADDR_ASCAST]], align 1, !tbaa [[TBAA16]] // GFX900-NEXT: store i8 [[TMP3]], ptr [[BLOCK_CAPTURED1]], align 8, !tbaa [[TBAA16]] -// GFX900-NEXT: [[TMP4:%.*]] = call i32 @__enqueue_kernel_basic(ptr addrspace(1) [[TMP0]], i32 [[TMP1]], ptr addrspace(5) [[TMP]], ptr @__test_block_invoke_kernel, ptr [[BLOCK_ASCAST]]) +// GFX900-NEXT: [[TMP4:%.*]] = call i32 @__enqueue_kernel_basic(ptr addrspace(1) [[TMP0]], i32 [[TMP1]], ptr addrspace(5) [[TMP]], ptr addrspacecast (ptr addrspace(1) @__test_block_invoke_kernel.runtime.handle to ptr), ptr [[BLOCK_ASCAST]]) // GFX900-NEXT: [[TMP5:%.*]] = load ptr addrspace(1), ptr [[DEFAULT_QUEUE_ASCAST]], align 8, !tbaa [[TBAA19]] // GFX900-NEXT: [[TMP6:%.*]] = load i32, ptr [[FLAGS_ASCAST]], align 4, !tbaa [[TBAA17]] // GFX900-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP2_ASCAST]], ptr align 4 [[NDRANGE_ASCAST]], i64 4, i1 false), !tbaa.struct [[TBAA_STRUCT21]] @@ -508,7 +514,7 @@ kernel void test_target_features_kernel(global int *i) { // GFX900-NEXT: [[BLOCK_CAPTURED10:%.*]] = getelementptr inbounds nuw <{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1), i64, i8 }>, ptr [[BLOCK3_ASCAST]], i32 0, i32 5 // GFX900-NEXT: [[TMP10:%.*]] = load i64, ptr [[D_ADDR_ASCAST]], align 8, !tbaa [[TBAA3]] // GFX900-NEXT: store i64 [[TMP10]], ptr [[BLOCK_CAPTURED10]], align 8, !tbaa [[TBAA3]] -// GFX900-NEXT: [[TMP11:%.*]] = call i32 @__enqueue_kernel_basic(ptr addrspace(1) [[TMP5]], i32 [[TMP6]], ptr addrspace(5) [[VARTMP2]], ptr @__test_block_invoke_2_kernel, ptr [[BLOCK3_ASCAST]]) +// GFX900-NEXT: [[TMP11:%.*]] = call i32 @__enqueue_kernel_basic(ptr addrspace(1) [[TMP5]], i32 [[TMP6]], ptr addrspace(5) [[VARTMP2]], ptr addrspacecast (ptr addrspace(1) @__test_block_invoke_2_kernel.runtime.handle to ptr), ptr [[BLOCK3_ASCAST]]) // GFX900-NEXT: [[TMP12:%.*]] = load ptr addrspace(1), ptr [[DEFAULT_QUEUE_ASCAST]], align 8, !tbaa [[TBAA19]] // GFX900-NEXT: [[TMP13:%.*]] = load i32, ptr [[FLAGS_ASCAST]], align 4, !tbaa [[TBAA17]] // GFX900-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP11_ASCAST]], ptr align 4 [[NDRANGE_ASCAST]], i64 4, i1 false), !tbaa.struct [[TBAA_STRUCT21]] @@ -530,12 +536,12 @@ kernel void test_target_features_kernel(global int *i) { // GFX900-NEXT: [[BLOCK_CAPTURED19:%.*]] = getelementptr inbounds nuw <{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1), i64, i8 }>, ptr [[BLOCK12_ASCAST]], i32 0, i32 5 // GFX900-NEXT: [[TMP17:%.*]] = load i64, ptr [[D_ADDR_ASCAST]], align 8, !tbaa [[TBAA3]] // GFX900-NEXT: store i64 [[TMP17]], ptr [[BLOCK_CAPTURED19]], align 8, !tbaa [[TBAA3]] -// GFX900-NEXT: call void @llvm.lifetime.start.p5(i64 8, ptr addrspace(5) [[BLOCK_SIZES]]) #[[ATTR8]] +// GFX900-NEXT: call void @llvm.lifetime.start.p5(i64 8, ptr addrspace(5) [[BLOCK_SIZES]]) #[[ATTR7]] // GFX900-NEXT: [[TMP18:%.*]] = getelementptr [1 x i64], ptr [[BLOCK_SIZES_ASCAST]], i32 0, i32 0 // GFX900-NEXT: store i64 100, ptr [[TMP18]], align 8 -// GFX900-NEXT: [[TMP19:%.*]] = call i32 @__enqueue_kernel_varargs(ptr addrspace(1) [[TMP12]], i32 [[TMP13]], ptr addrspace(5) [[VARTMP11]], ptr @__test_block_invoke_3_kernel, ptr [[BLOCK12_ASCAST]], i32 1, ptr [[TMP18]]) -// GFX900-NEXT: call void @llvm.lifetime.end.p5(i64 8, ptr addrspace(5) [[BLOCK_SIZES]]) #[[ATTR8]] -// GFX900-NEXT: call void @llvm.lifetime.start.p5(i64 8, ptr addrspace(5) [[BLOCK20]]) #[[ATTR8]] +// GFX900-NEXT: [[TMP19:%.*]] = call i32 @__enqueue_kernel_varargs(ptr addrspace(1) [[TMP12]], i32 [[TMP13]], ptr addrspace(5) [[VARTMP11]], ptr addrspacecast (ptr addrspace(1) @__test_block_invoke_3_kernel.runtime.handle to ptr), ptr [[BLOCK12_ASCAST]], i32 1, ptr [[TMP18]]) +// GFX900-NEXT: call void @llvm.lifetime.end.p5(i64 8, ptr addrspace(5) [[BLOCK_SIZES]]) #[[ATTR7]] +// GFX900-NEXT: call void @llvm.lifetime.start.p5(i64 8, ptr addrspace(5) [[BLOCK20]]) #[[ATTR7]] // GFX900-NEXT: [[BLOCK_SIZE22:%.*]] = getelementptr inbounds nuw <{ i32, i32, ptr, i64, ptr addrspace(1) }>, ptr [[BLOCK21_ASCAST]], i32 0, i32 0 // GFX900-NEXT: store i32 32, ptr [[BLOCK_SIZE22]], align 8 // GFX900-NEXT: [[BLOCK_ALIGN23:%.*]] = getelementptr inbounds nuw <{ i32, i32, ptr, i64, ptr addrspace(1) }>, ptr [[BLOCK21_ASCAST]], i32 0, i32 1 @@ -553,11 +559,11 @@ kernel void test_target_features_kernel(global int *i) { // GFX900-NEXT: [[TMP23:%.*]] = load i32, ptr [[FLAGS_ASCAST]], align 4, !tbaa [[TBAA17]] // GFX900-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP27_ASCAST]], ptr align 4 [[NDRANGE_ASCAST]], i64 4, i1 false), !tbaa.struct [[TBAA_STRUCT21]] // GFX900-NEXT: [[TMP24:%.*]] = load ptr, ptr [[BLOCK20_ASCAST]], align 8, !tbaa [[TBAA16]] -// GFX900-NEXT: [[TMP25:%.*]] = call i32 @__enqueue_kernel_basic(ptr addrspace(1) [[TMP22]], i32 [[TMP23]], ptr addrspace(5) [[VARTMP27]], ptr @__test_block_invoke_4_kernel, ptr [[BLOCK21_ASCAST]]) -// GFX900-NEXT: call void @llvm.lifetime.end.p5(i64 8, ptr addrspace(5) [[BLOCK20]]) #[[ATTR8]] -// GFX900-NEXT: call void @llvm.lifetime.end.p5(i64 4, ptr addrspace(5) [[NDRANGE]]) #[[ATTR8]] -// GFX900-NEXT: call void @llvm.lifetime.end.p5(i64 4, ptr addrspace(5) [[FLAGS]]) #[[ATTR8]] -// GFX900-NEXT: call void @llvm.lifetime.end.p5(i64 8, ptr addrspace(5) [[DEFAULT_QUEUE]]) #[[ATTR8]] +// GFX900-NEXT: [[TMP25:%.*]] = call i32 @__enqueue_kernel_basic(ptr addrspace(1) [[TMP22]], i32 [[TMP23]], ptr addrspace(5) [[VARTMP27]], ptr addrspacecast (ptr addrspace(1) @__test_block_invoke_4_kernel.runtime.handle to ptr), ptr [[BLOCK21_ASCAST]]) +// GFX900-NEXT: call void @llvm.lifetime.end.p5(i64 8, ptr addrspace(5) [[BLOCK20]]) #[[ATTR7]] +// GFX900-NEXT: call void @llvm.lifetime.end.p5(i64 4, ptr addrspace(5) [[NDRANGE]]) #[[ATTR7]] +// GFX900-NEXT: call void @llvm.lifetime.end.p5(i64 4, ptr addrspace(5) [[FLAGS]]) #[[ATTR7]] +// GFX900-NEXT: call void @llvm.lifetime.end.p5(i64 8, ptr addrspace(5) [[DEFAULT_QUEUE]]) #[[ATTR7]] // GFX900-NEXT: ret void // // @@ -579,7 +585,7 @@ kernel void test_target_features_kernel(global int *i) { // // GFX900: Function Attrs: convergent nounwind // GFX900-LABEL: define {{[^@]+}}@__test_block_invoke_kernel -// GFX900-SAME: (<{ i32, i32, ptr, ptr addrspace(1), i8 }> [[TMP0:%.*]]) #[[ATTR6:[0-9]+]] !kernel_arg_addr_space [[META22:![0-9]+]] !kernel_arg_access_qual [[META23:![0-9]+]] !kernel_arg_type [[META24:![0-9]+]] !kernel_arg_base_type [[META24]] !kernel_arg_type_qual [[META25:![0-9]+]] { +// GFX900-SAME: (<{ i32, i32, ptr, ptr addrspace(1), i8 }> [[TMP0:%.*]]) #[[ATTR5]] !associated [[META22:![0-9]+]] !kernel_arg_addr_space [[META23:![0-9]+]] !kernel_arg_access_qual [[META24:![0-9]+]] !kernel_arg_type [[META25:![0-9]+]] !kernel_arg_base_type [[META25]] !kernel_arg_type_qual [[META26:![0-9]+]] { // GFX900-NEXT: entry: // GFX900-NEXT: [[TMP1:%.*]] = alloca <{ i32, i32, ptr, ptr addrspace(1), i8 }>, align 8, addrspace(5) // GFX900-NEXT: store <{ i32, i32, ptr, ptr addrspace(1), i8 }> [[TMP0]], ptr addrspace(5) [[TMP1]], align 8 @@ -612,7 +618,7 @@ kernel void test_target_features_kernel(global int *i) { // // GFX900: Function Attrs: convergent nounwind // GFX900-LABEL: define {{[^@]+}}@__test_block_invoke_2_kernel -// GFX900-SAME: (<{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1), i64, i8 }> [[TMP0:%.*]]) #[[ATTR6]] !kernel_arg_addr_space [[META22]] !kernel_arg_access_qual [[META23]] !kernel_arg_type [[META24]] !kernel_arg_base_type [[META24]] !kernel_arg_type_qual [[META25]] { +// GFX900-SAME: (<{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1), i64, i8 }> [[TMP0:%.*]]) #[[ATTR5]] !associated [[META27:![0-9]+]] !kernel_arg_addr_space [[META23]] !kernel_arg_access_qual [[META24]] !kernel_arg_type [[META25]] !kernel_arg_base_type [[META25]] !kernel_arg_type_qual [[META26]] { // GFX900-NEXT: entry: // GFX900-NEXT: [[TMP1:%.*]] = alloca <{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1), i64, i8 }>, align 8, addrspace(5) // GFX900-NEXT: store <{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1), i64, i8 }> [[TMP0]], ptr addrspace(5) [[TMP1]], align 8 @@ -630,7 +636,7 @@ kernel void test_target_features_kernel(global int *i) { // GFX900-NEXT: [[DOTBLOCK_DESCRIPTOR_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[DOTBLOCK_DESCRIPTOR_ADDR]] to ptr // GFX900-NEXT: [[LP_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[LP_ADDR]] to ptr // GFX900-NEXT: store ptr [[DOTBLOCK_DESCRIPTOR]], ptr [[DOTBLOCK_DESCRIPTOR_ADDR_ASCAST]], align 8 -// GFX900-NEXT: store ptr addrspace(3) [[LP]], ptr [[LP_ADDR_ASCAST]], align 4, !tbaa [[TBAA26:![0-9]+]] +// GFX900-NEXT: store ptr addrspace(3) [[LP]], ptr [[LP_ADDR_ASCAST]], align 4, !tbaa [[TBAA28:![0-9]+]] // GFX900-NEXT: [[BLOCK_CAPTURE_ADDR:%.*]] = getelementptr inbounds nuw <{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1), i64, i8 }>, ptr [[DOTBLOCK_DESCRIPTOR]], i32 0, i32 6 // GFX900-NEXT: [[TMP0:%.*]] = load i8, ptr [[BLOCK_CAPTURE_ADDR]], align 8, !tbaa [[TBAA16]] // GFX900-NEXT: [[BLOCK_CAPTURE_ADDR1:%.*]] = getelementptr inbounds nuw <{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1), i64, i8 }>, ptr [[DOTBLOCK_DESCRIPTOR]], i32 0, i32 3 @@ -643,7 +649,7 @@ kernel void test_target_features_kernel(global int *i) { // GFX900-NEXT: [[TMP3:%.*]] = load ptr addrspace(1), ptr [[BLOCK_CAPTURE_ADDR3]], align 8, !tbaa [[TBAA7]] // GFX900-NEXT: [[ARRAYIDX4:%.*]] = getelementptr inbounds i64, ptr addrspace(1) [[TMP3]], i64 0 // GFX900-NEXT: store i64 [[TMP2]], ptr addrspace(1) [[ARRAYIDX4]], align 8, !tbaa [[TBAA3]] -// GFX900-NEXT: [[TMP4:%.*]] = load ptr addrspace(3), ptr [[LP_ADDR_ASCAST]], align 4, !tbaa [[TBAA26]] +// GFX900-NEXT: [[TMP4:%.*]] = load ptr addrspace(3), ptr [[LP_ADDR_ASCAST]], align 4, !tbaa [[TBAA28]] // GFX900-NEXT: [[ARRAYIDX5:%.*]] = getelementptr inbounds i32, ptr addrspace(3) [[TMP4]], i64 0 // GFX900-NEXT: store i32 1, ptr addrspace(3) [[ARRAYIDX5]], align 4, !tbaa [[TBAA17]] // GFX900-NEXT: ret void @@ -651,7 +657,7 @@ kernel void test_target_features_kernel(global int *i) { // // GFX900: Function Attrs: convergent nounwind // GFX900-LABEL: define {{[^@]+}}@__test_block_invoke_3_kernel -// GFX900-SAME: (<{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1), i64, i8 }> [[TMP0:%.*]], ptr addrspace(3) [[TMP1:%.*]]) #[[ATTR6]] !kernel_arg_addr_space [[META27:![0-9]+]] !kernel_arg_access_qual [[META28:![0-9]+]] !kernel_arg_type [[META29:![0-9]+]] !kernel_arg_base_type [[META29]] !kernel_arg_type_qual [[META30:![0-9]+]] { +// GFX900-SAME: (<{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1), i64, i8 }> [[TMP0:%.*]], ptr addrspace(3) [[TMP1:%.*]]) #[[ATTR5]] !associated [[META29:![0-9]+]] !kernel_arg_addr_space [[META30:![0-9]+]] !kernel_arg_access_qual [[META31:![0-9]+]] !kernel_arg_type [[META32:![0-9]+]] !kernel_arg_base_type [[META32]] !kernel_arg_type_qual [[META33:![0-9]+]] { // GFX900-NEXT: entry: // GFX900-NEXT: [[TMP2:%.*]] = alloca <{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1), i64, i8 }>, align 8, addrspace(5) // GFX900-NEXT: store <{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1), i64, i8 }> [[TMP0]], ptr addrspace(5) [[TMP2]], align 8 @@ -671,13 +677,13 @@ kernel void test_target_features_kernel(global int *i) { // GFX900-NEXT: [[TMP0:%.*]] = load i64, ptr [[BLOCK_CAPTURE_ADDR]], align 8, !tbaa [[TBAA3]] // GFX900-NEXT: [[BLOCK_CAPTURE_ADDR1:%.*]] = getelementptr inbounds nuw <{ i32, i32, ptr, i64, ptr addrspace(1) }>, ptr [[DOTBLOCK_DESCRIPTOR]], i32 0, i32 4 // GFX900-NEXT: [[TMP1:%.*]] = load ptr addrspace(1), ptr [[BLOCK_CAPTURE_ADDR1]], align 8, !tbaa [[TBAA7]] -// GFX900-NEXT: call void @callee(i64 noundef [[TMP0]], ptr addrspace(1) noundef [[TMP1]]) #[[ATTR9:[0-9]+]] +// GFX900-NEXT: call void @callee(i64 noundef [[TMP0]], ptr addrspace(1) noundef [[TMP1]]) #[[ATTR8:[0-9]+]] // GFX900-NEXT: ret void // // // GFX900: Function Attrs: convergent nounwind // GFX900-LABEL: define {{[^@]+}}@__test_block_invoke_4_kernel -// GFX900-SAME: (<{ i32, i32, ptr, i64, ptr addrspace(1) }> [[TMP0:%.*]]) #[[ATTR6]] !kernel_arg_addr_space [[META22]] !kernel_arg_access_qual [[META23]] !kernel_arg_type [[META24]] !kernel_arg_base_type [[META24]] !kernel_arg_type_qual [[META25]] { +// GFX900-SAME: (<{ i32, i32, ptr, i64, ptr addrspace(1) }> [[TMP0:%.*]]) #[[ATTR5]] !associated [[META34:![0-9]+]] !kernel_arg_addr_space [[META23]] !kernel_arg_access_qual [[META24]] !kernel_arg_type [[META25]] !kernel_arg_base_type [[META25]] !kernel_arg_type_qual [[META26]] { // GFX900-NEXT: entry: // GFX900-NEXT: [[TMP1:%.*]] = alloca <{ i32, i32, ptr, i64, ptr addrspace(1) }>, align 8, addrspace(5) // GFX900-NEXT: store <{ i32, i32, ptr, i64, ptr addrspace(1) }> [[TMP0]], ptr addrspace(5) [[TMP1]], align 8 @@ -688,7 +694,7 @@ kernel void test_target_features_kernel(global int *i) { // // GFX900: Function Attrs: convergent norecurse nounwind // GFX900-LABEL: define {{[^@]+}}@test_target_features_kernel -// GFX900-SAME: (ptr addrspace(1) noundef align 4 [[I:%.*]]) #[[ATTR2]] !kernel_arg_addr_space [[META31:![0-9]+]] !kernel_arg_access_qual [[META23]] !kernel_arg_type [[META32:![0-9]+]] !kernel_arg_base_type [[META32]] !kernel_arg_type_qual [[META25]] { +// GFX900-SAME: (ptr addrspace(1) noundef align 4 [[I:%.*]]) #[[ATTR2]] !kernel_arg_addr_space [[META35:![0-9]+]] !kernel_arg_access_qual [[META24]] !kernel_arg_type [[META36:![0-9]+]] !kernel_arg_base_type [[META36]] !kernel_arg_type_qual [[META26]] { // GFX900-NEXT: entry: // GFX900-NEXT: [[I_ADDR:%.*]] = alloca ptr addrspace(1), align 8, addrspace(5) // GFX900-NEXT: [[DEFAULT_QUEUE:%.*]] = alloca ptr addrspace(1), align 8, addrspace(5) @@ -700,19 +706,19 @@ kernel void test_target_features_kernel(global int *i) { // GFX900-NEXT: [[FLAGS_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[FLAGS]] to ptr // GFX900-NEXT: [[NDRANGE_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[NDRANGE]] to ptr // GFX900-NEXT: [[TMP_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[TMP]] to ptr -// GFX900-NEXT: store ptr addrspace(1) [[I]], ptr [[I_ADDR_ASCAST]], align 8, !tbaa [[TBAA33:![0-9]+]] -// GFX900-NEXT: call void @llvm.lifetime.start.p5(i64 8, ptr addrspace(5) [[DEFAULT_QUEUE]]) #[[ATTR8]] -// GFX900-NEXT: call void @llvm.lifetime.start.p5(i64 4, ptr addrspace(5) [[FLAGS]]) #[[ATTR8]] +// GFX900-NEXT: store ptr addrspace(1) [[I]], ptr [[I_ADDR_ASCAST]], align 8, !tbaa [[TBAA37:![0-9]+]] +// GFX900-NEXT: call void @llvm.lifetime.start.p5(i64 8, ptr addrspace(5) [[DEFAULT_QUEUE]]) #[[ATTR7]] +// GFX900-NEXT: call void @llvm.lifetime.start.p5(i64 4, ptr addrspace(5) [[FLAGS]]) #[[ATTR7]] // GFX900-NEXT: store i32 0, ptr [[FLAGS_ASCAST]], align 4, !tbaa [[TBAA17]] -// GFX900-NEXT: call void @llvm.lifetime.start.p5(i64 4, ptr addrspace(5) [[NDRANGE]]) #[[ATTR8]] +// GFX900-NEXT: call void @llvm.lifetime.start.p5(i64 4, ptr addrspace(5) [[NDRANGE]]) #[[ATTR7]] // GFX900-NEXT: [[TMP0:%.*]] = call i64 @llvm.amdgcn.s.memtime() // GFX900-NEXT: [[TMP1:%.*]] = load ptr addrspace(1), ptr [[DEFAULT_QUEUE_ASCAST]], align 8, !tbaa [[TBAA19]] // GFX900-NEXT: [[TMP2:%.*]] = load i32, ptr [[FLAGS_ASCAST]], align 4, !tbaa [[TBAA17]] // GFX900-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP_ASCAST]], ptr align 4 [[NDRANGE_ASCAST]], i64 4, i1 false), !tbaa.struct [[TBAA_STRUCT21]] -// GFX900-NEXT: [[TMP3:%.*]] = call i32 @__enqueue_kernel_basic(ptr addrspace(1) [[TMP1]], i32 [[TMP2]], ptr addrspace(5) [[TMP]], ptr @__test_target_features_kernel_block_invoke_kernel, ptr addrspacecast (ptr addrspace(1) @__block_literal_global to ptr)) -// GFX900-NEXT: call void @llvm.lifetime.end.p5(i64 4, ptr addrspace(5) [[NDRANGE]]) #[[ATTR8]] -// GFX900-NEXT: call void @llvm.lifetime.end.p5(i64 4, ptr addrspace(5) [[FLAGS]]) #[[ATTR8]] -// GFX900-NEXT: call void @llvm.lifetime.end.p5(i64 8, ptr addrspace(5) [[DEFAULT_QUEUE]]) #[[ATTR8]] +// GFX900-NEXT: [[TMP3:%.*]] = call i32 @__enqueue_kernel_basic(ptr addrspace(1) [[TMP1]], i32 [[TMP2]], ptr addrspace(5) [[TMP]], ptr addrspacecast (ptr addrspace(1) @__test_target_features_kernel_block_invoke_kernel.runtime.handle to ptr), ptr addrspacecast (ptr addrspace(1) @__block_literal_global to ptr)) +// GFX900-NEXT: call void @llvm.lifetime.end.p5(i64 4, ptr addrspace(5) [[NDRANGE]]) #[[ATTR7]] +// GFX900-NEXT: call void @llvm.lifetime.end.p5(i64 4, ptr addrspace(5) [[FLAGS]]) #[[ATTR7]] +// GFX900-NEXT: call void @llvm.lifetime.end.p5(i64 8, ptr addrspace(5) [[DEFAULT_QUEUE]]) #[[ATTR7]] // GFX900-NEXT: ret void // // @@ -729,7 +735,7 @@ kernel void test_target_features_kernel(global int *i) { // // GFX900: Function Attrs: convergent nounwind // GFX900-LABEL: define {{[^@]+}}@__test_target_features_kernel_block_invoke_kernel -// GFX900-SAME: ({ i32, i32, ptr } [[TMP0:%.*]]) #[[ATTR6]] !kernel_arg_addr_space [[META22]] !kernel_arg_access_qual [[META23]] !kernel_arg_type [[META24]] !kernel_arg_base_type [[META24]] !kernel_arg_type_qual [[META25]] { +// GFX900-SAME: ({ i32, i32, ptr } [[TMP0:%.*]]) #[[ATTR5]] !associated [[META39:![0-9]+]] !kernel_arg_addr_space [[META23]] !kernel_arg_access_qual [[META24]] !kernel_arg_type [[META25]] !kernel_arg_base_type [[META25]] !kernel_arg_type_qual [[META26]] { // GFX900-NEXT: entry: // GFX900-NEXT: [[TMP1:%.*]] = alloca { i32, i32, ptr }, align 8, addrspace(5) // GFX900-NEXT: store { i32, i32, ptr } [[TMP0]], ptr addrspace(5) [[TMP1]], align 8 @@ -743,7 +749,7 @@ kernel void test_target_features_kernel(global int *i) { // NOCPU: attributes #[[ATTR2]] = { convergent noinline norecurse nounwind optnone "amdgpu-flat-work-group-size"="1,256" "denormal-fp-math-f32"="preserve-sign,preserve-sign" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "uniform-work-group-size"="false" } // NOCPU: attributes #[[ATTR3:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } // NOCPU: attributes #[[ATTR4]] = { convergent noinline nounwind optnone "denormal-fp-math-f32"="preserve-sign,preserve-sign" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } -// NOCPU: attributes #[[ATTR5]] = { convergent nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" "enqueued-block" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } +// NOCPU: attributes #[[ATTR5]] = { convergent nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } // NOCPU: attributes #[[ATTR6]] = { convergent noinline norecurse nounwind optnone "amdgpu-flat-work-group-size"="1,256" "denormal-fp-math-f32"="preserve-sign,preserve-sign" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+s-memtime-inst" "uniform-work-group-size"="false" } // NOCPU: attributes #[[ATTR7:[0-9]+]] = { nocallback nofree nosync nounwind willreturn } // NOCPU: attributes #[[ATTR8]] = { convergent nounwind } @@ -754,10 +760,9 @@ kernel void test_target_features_kernel(global int *i) { // GFX900: attributes #[[ATTR3:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } // GFX900: attributes #[[ATTR4:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } // GFX900: attributes #[[ATTR5]] = { convergent nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="gfx900" "target-features"="+16-bit-insts,+ci-insts,+dpp,+gfx8-insts,+gfx9-insts,+s-memrealtime,+s-memtime-inst,+wavefrontsize64,-sram-ecc" } -// GFX900: attributes #[[ATTR6]] = { convergent nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" "enqueued-block" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="gfx900" "target-features"="+16-bit-insts,+ci-insts,+dpp,+gfx8-insts,+gfx9-insts,+s-memrealtime,+s-memtime-inst,+wavefrontsize64,-sram-ecc" } -// GFX900: attributes #[[ATTR7:[0-9]+]] = { nocallback nofree nosync nounwind willreturn } -// GFX900: attributes #[[ATTR8]] = { nounwind } -// GFX900: attributes #[[ATTR9]] = { convergent nounwind } +// GFX900: attributes #[[ATTR6:[0-9]+]] = { nocallback nofree nosync nounwind willreturn } +// GFX900: attributes #[[ATTR7]] = { nounwind } +// GFX900: attributes #[[ATTR8]] = { convergent nounwind } //. // NOCPU: [[META0:![0-9]+]] = !{i32 1, !"amdhsa_code_object_version", i32 500} // NOCPU: [[META1:![0-9]+]] = !{i32 1, !"wchar_size", i32 4} @@ -766,16 +771,21 @@ kernel void test_target_features_kernel(global int *i) { // NOCPU: [[META4]] = !{!"none", !"none", !"none", !"none"} // NOCPU: [[META5]] = !{!"char*", !"char", !"long*", !"long"} // NOCPU: [[META6]] = !{!"", !"", !"", !""} -// NOCPU: [[META7]] = !{i32 0} -// NOCPU: [[META8]] = !{!"none"} -// NOCPU: [[META9]] = !{!"__block_literal"} -// NOCPU: [[META10]] = !{!""} -// NOCPU: [[META11]] = !{i32 0, i32 3} -// NOCPU: [[META12]] = !{!"none", !"none"} -// NOCPU: [[META13]] = !{!"__block_literal", !"void*"} -// NOCPU: [[META14]] = !{!"", !""} -// NOCPU: [[META15]] = !{i32 1} -// NOCPU: [[META16]] = !{!"int*"} +// NOCPU: [[META7]] = !{ptr addrspace(1) @__test_block_invoke_kernel.runtime.handle} +// NOCPU: [[META8]] = !{i32 0} +// NOCPU: [[META9]] = !{!"none"} +// NOCPU: [[META10]] = !{!"__block_literal"} +// NOCPU: [[META11]] = !{!""} +// NOCPU: [[META12]] = !{ptr addrspace(1) @__test_block_invoke_2_kernel.runtime.handle} +// NOCPU: [[META13]] = !{ptr addrspace(1) @__test_block_invoke_3_kernel.runtime.handle} +// NOCPU: [[META14]] = !{i32 0, i32 3} +// NOCPU: [[META15]] = !{!"none", !"none"} +// NOCPU: [[META16]] = !{!"__block_literal", !"void*"} +// NOCPU: [[META17]] = !{!"", !""} +// NOCPU: [[META18]] = !{ptr addrspace(1) @__test_block_invoke_4_kernel.runtime.handle} +// NOCPU: [[META19]] = !{i32 1} +// NOCPU: [[META20]] = !{!"int*"} +// NOCPU: [[META21]] = !{ptr addrspace(1) @__test_target_features_kernel_block_invoke_kernel.runtime.handle} //. // GFX900: [[META0:![0-9]+]] = !{i32 1, !"amdhsa_code_object_version", i32 500} // GFX900: [[META1:![0-9]+]] = !{i32 1, !"wchar_size", i32 4} @@ -799,19 +809,24 @@ kernel void test_target_features_kernel(global int *i) { // GFX900: [[TBAA19]] = !{[[META20:![0-9]+]], [[META20]], i64 0} // GFX900: [[META20]] = !{!"queue_t", [[META5]], i64 0} // GFX900: [[TBAA_STRUCT21]] = !{i64 0, i64 4, [[TBAA17]]} -// GFX900: [[META22]] = !{i32 0} -// GFX900: [[META23]] = !{!"none"} -// GFX900: [[META24]] = !{!"__block_literal"} -// GFX900: [[META25]] = !{!""} -// GFX900: [[TBAA26]] = !{[[META9]], [[META9]], i64 0} -// GFX900: [[META27]] = !{i32 0, i32 3} -// GFX900: [[META28]] = !{!"none", !"none"} -// GFX900: [[META29]] = !{!"__block_literal", !"void*"} -// GFX900: [[META30]] = !{!"", !""} -// GFX900: [[META31]] = !{i32 1} -// GFX900: [[META32]] = !{!"int*"} -// GFX900: [[TBAA33]] = !{[[META34:![0-9]+]], [[META34]], i64 0} -// GFX900: [[META34]] = !{!"p1 int", [[META9]], i64 0} +// GFX900: [[META22]] = !{ptr addrspace(1) @__test_block_invoke_kernel.runtime.handle} +// GFX900: [[META23]] = !{i32 0} +// GFX900: [[META24]] = !{!"none"} +// GFX900: [[META25]] = !{!"__block_literal"} +// GFX900: [[META26]] = !{!""} +// GFX900: [[META27]] = !{ptr addrspace(1) @__test_block_invoke_2_kernel.runtime.handle} +// GFX900: [[TBAA28]] = !{[[META9]], [[META9]], i64 0} +// GFX900: [[META29]] = !{ptr addrspace(1) @__test_block_invoke_3_kernel.runtime.handle} +// GFX900: [[META30]] = !{i32 0, i32 3} +// GFX900: [[META31]] = !{!"none", !"none"} +// GFX900: [[META32]] = !{!"__block_literal", !"void*"} +// GFX900: [[META33]] = !{!"", !""} +// GFX900: [[META34]] = !{ptr addrspace(1) @__test_block_invoke_4_kernel.runtime.handle} +// GFX900: [[META35]] = !{i32 1} +// GFX900: [[META36]] = !{!"int*"} +// GFX900: [[TBAA37]] = !{[[META38:![0-9]+]], [[META38]], i64 0} +// GFX900: [[META38]] = !{!"p1 int", [[META9]], i64 0} +// GFX900: [[META39]] = !{ptr addrspace(1) @__test_target_features_kernel_block_invoke_kernel.runtime.handle} //. //// NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: // CHECK: {{.*}} diff --git a/clang/test/Driver/HLSL/metal-converter.hlsl b/clang/test/Driver/HLSL/metal-converter.hlsl new file mode 100644 index 0000000000000..536f24be6e73b --- /dev/null +++ b/clang/test/Driver/HLSL/metal-converter.hlsl @@ -0,0 +1,19 @@ +// RUN: %clang_dxc -T cs_6_0 %s -metal -Fo %t.mtl -### 2>&1 | FileCheck %s +// RUN: %clang_dxc -T cs_6_0 %s -metal -Vd -Fo %t.mtl -### 2>&1 | FileCheck %s +// CHECK: "{{.*}}metal-shaderconverter{{(.exe)?}}" "{{.*}}.obj" "-o" "{{.*}}.mtl" + +// RUN: %clang_dxc -T cs_6_0 %s -metal -### 2>&1 | FileCheck --check-prefix=NO_MTL %s +// NO_MTL-NOT: metal-shaderconverter + +// RUN: echo "dxv" > %T/dxv && chmod 754 %T/dxv +// RUN: %clang_dxc -T cs_6_0 %s --dxv-path=%T -metal -Fo %t.mtl -### 2>&1 | FileCheck --check-prefix=DXV %s +// DXV: "{{.*}}dxv{{(.exe)?}}" "{{.*}}.obj" "-o" "{{.*}}.dxo" +// DXV: "{{.*}}metal-shaderconverter{{(.exe)?}}" "{{.*}}.dxo" "-o" "{{.*}}.mtl" + +RWBuffer In : register(u0, space0); +RWBuffer Out : register(u1, space4); + +[numthreads(1,1,1)] +void main(uint GI : SV_GroupIndex) { + Out[GI] = In[GI] * In[GI]; +} diff --git a/clang/test/Driver/aarch64-execute-only.c b/clang/test/Driver/aarch64-execute-only.c new file mode 100644 index 0000000000000..04370064a61f3 --- /dev/null +++ b/clang/test/Driver/aarch64-execute-only.c @@ -0,0 +1,9 @@ +// RUN: %clang --sysroot=%S/Inputs -c -fdriver-only -Werror --target=aarch64-unknown-linux-gnu \ +// RUN: -mexecute-only %s 2>&1 | count 0 + +// RUN: %clang -### --target=aarch64-unknown-linux-gnu -x assembler -mexecute-only %s -c -### 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-NO-EXECUTE-ONLY-ASM +// RUN: %clang -### --multi-lib-config=%S/Inputs/multilib/empty.yaml --sysroot= \ +// RUN: --target=aarch64-none-elf -x assembler -mexecute-only %s -c -### 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-NO-EXECUTE-ONLY-ASM +// CHECK-NO-EXECUTE-ONLY-ASM: warning: argument unused during compilation: '-mexecute-only' diff --git a/clang/test/Driver/arm-thread-pointer.c b/clang/test/Driver/arm-thread-pointer.c index 985c5046f6d26..37d1a3e4d7e89 100644 --- a/clang/test/Driver/arm-thread-pointer.c +++ b/clang/test/Driver/arm-thread-pointer.c @@ -14,18 +14,26 @@ // RUN: FileCheck -check-prefix=ARMv7_THREAD_POINTER-TPIDRPRW %s // ARMv7_THREAD_POINTER-TPIDRPRW: "-target-feature" "+read-tp-tpidrprw" -// RUN: %clang --target=armv6t2-linux -mtp=cp15 -### -S %s 2>&1 | \ -// RUN: FileCheck -check-prefix=ARM_THREAD_POINTER-HARD %s -// RUN: %clang --target=thumbv6t2-linux -mtp=cp15 -### -S %s 2>&1 | \ -// RUN: FileCheck -check-prefix=ARM_THREAD_POINTER-HARD %s // RUN: %clang --target=armv6k-linux -mtp=cp15 -### -S %s 2>&1 | \ // RUN: FileCheck -check-prefix=ARM_THREAD_POINTER-HARD %s -// RUN: %clang --target=armv6-linux -mtp=cp15 -### -S %s 2>&1 | \ -// RUN: FileCheck -check-prefix=ARM_THREAD_POINTER-HARD %s -// RUN: %clang --target=armv5t-linux -mtp=cp15 -### -S %s 2>&1 | \ -// RUN: FileCheck -check-prefix=ARM_THREAD_POINTER-HARD %s // ARM_THREAD_POINTER-HARD: "-target-feature" "+read-tp-tpidruro" +// RUN: %clang --target=armv6k-linux -mtp=auto -### -S %s 2>&1 | \ +// RUN: FileCheck -check-prefix=ARM_THREAD_POINTER_AUTO %s +// ARM_THREAD_POINTER_AUTO-NOT: "-target-feature" "+read-tp-tpidruro" + +// RUN: %clang --target=thumbv6k-apple-darwin -### -S %s 2>&1 | \ +// RUN: FileCheck -check-prefix=THUMBv6_THREAD_POINTER_NO_AUTO %s +// THUMBv6_THREAD_POINTER_NO_AUTO-NOT: "-target-feature" "+read-tp-tpidruro" + +// RUN: not %clang --target=thumbv6k-apple-darwin -mtp=cp15 -### -S %s 2>&1 | \ +// RUN: FileCheck -check-prefix=THUMBv6_THREAD_POINTER_NO_HARD %s +// THUMBv6_THREAD_POINTER_NO_HARD: unsupported option '-mtp=' for target 'thumbv6k-apple-darwin' + +// RUN: not %clang --target=thumbv6t2-linux -mtp=cp15 -### -S %s 2>&1 | \ +// RUN: FileCheck -check-prefix=ARM_THREAD_POINTER_NO_HARD %s +// ARM_THREAD_POINTER_NO_HARD: hardware TLS register is not supported for the armv6t2 sub-architecture + // RUN: %clang --target=armv5t-linux -mtp=cp15 -x assembler -### %s 2>&1 | \ // RUN: FileCheck -check-prefix=ARMv5_THREAD_POINTER_ASSEMBLER %s // ARMv5_THREAD_POINTER_ASSEMBLER-NOT: hardware TLS register is not supported for the armv5 sub-architecture @@ -47,3 +55,39 @@ // RUN: %clang --target=armv7-linux -mtp=auto -### -S %s 2>&1 | \ // RUN: FileCheck -check-prefix=ARMv7_THREAD_POINTER_Auto %s // ARMv7_THREAD_POINTER_Auto: "-target-feature" "+read-tp-tpidruro" + +// RUN: %clang --target=armv7-linux -mtp=cp15 -### -S %s 2>&1 | \ +// RUN: FileCheck -check-prefix=ARMv7_THREAD_POINTER_HARD %s +// ARMv7_THREAD_POINTER_HARD: "-target-feature" "+read-tp-tpidruro" + +// RUN: %clang --target=armv7m-linux -mtp=auto -### -S %s 2>&1 | \ +// RUN: FileCheck -check-prefix=ARMv7m_THREAD_POINTER_Auto %s +// ARMv7m_THREAD_POINTER_Auto-NOT: "-target-feature" "+read-tp-tpidruro" + +// RUN: not %clang --target=armv7m-linux -mtp=cp15 -### -S %s 2>&1 | \ +// RUN: FileCheck -check-prefix=ARMv7m_THREAD_POINTER_HARD %s +// ARMv7m_THREAD_POINTER_HARD: hardware TLS register is not supported for the thumbv7m sub-architecture + +// RUN: %clang --target=armv5t-linux -mtp=auto -### -S %s 2>&1 | \ +// RUN: FileCheck -check-prefix=ARMv5t_THREAD_POINTER_Auto %s +// ARMv5t_THREAD_POINTER_Auto-NOT: "-target-feature" "+read-tp-tpidruro" + +// RUN: %clang --target=armv6k-linux -mtp=cp15 -### -S %s 2>&1 | \ +// RUN: FileCheck -check-prefix=ARMv6k_THREAD_POINTER_Auto %s +// ARMv6k_THREAD_POINTER_Auto: "-target-feature" "+read-tp-tpidruro" + +// RUN: not %clang --target=armv6t2-linux -mtp=cp15 -### -S %s 2>&1 | \ +// RUN: FileCheck -check-prefix=ARMv6t2_THREAD_POINTER_HARD %s +// ARMv6t2_THREAD_POINTER_HARD: hardware TLS register is not supported for the armv6t2 sub-architecture + +// RUN: %clang --target=armv6t2-linux -mtp=auto -### -S %s 2>&1 | \ +// RUN: FileCheck -check-prefix=ARMV6t2_THREAD_POINTER_AUTO %s +// ARMV6t2_THREAD_POINTER_AUTO-NOT: "-target-feature" "+read-tp-tpidruro" + +// RUN: %clang --target=armv6kz-linux -mtp=cp15 -### -S %s 2>&1 | \ +// RUN: FileCheck -check-prefix=ARMv6kz_THREAD_POINTER_HARD %s +// ARMv6kz_THREAD_POINTER_HARD: "-target-feature" "+read-tp-tpidruro" + +// RUN: %clang --target=armv6kz-linux -mtp=auto -### -S %s 2>&1 | \ +// RUN: FileCheck -check-prefix=ARMV6KZ_THREAD_POINTER_AUTO %s +// ARMV6KZ_THREAD_POINTER_AUTO-NOT: "-target-feature" "+read-tp-tpidruro" \ No newline at end of file diff --git a/clang/test/Driver/dxc_dxv_path.hlsl b/clang/test/Driver/dxc_dxv_path.hlsl index db2c87063ac31..55a07f34a648e 100644 --- a/clang/test/Driver/dxc_dxv_path.hlsl +++ b/clang/test/Driver/dxc_dxv_path.hlsl @@ -4,15 +4,15 @@ // CHECK:dxv not found // RUN: echo "dxv" > %T/dxv && chmod 754 %T/dxv && %clang_dxc --dxv-path=%T %s -Tlib_6_3 -### 2>&1 | FileCheck %s --check-prefix=DXV_PATH -// DXV_PATH:dxv{{(.exe)?}}" "-" "-o" "-" +// DXV_PATH:dxv{{(.exe)?}}" "-" "-o" "{{.*}}.dxo" // RUN: %clang_dxc -I test -Vd -Tlib_6_3 -### %s 2>&1 | FileCheck %s --check-prefix=VD // VD:"-cc1"{{.*}}"-triple" "dxilv1.3-unknown-shadermodel6.3-library" // VD-NOT:dxv not found // RUN: %clang_dxc -Tlib_6_3 -ccc-print-bindings --dxv-path=%T -Fo %t.dxo %s 2>&1 | FileCheck %s --check-prefix=BINDINGS -// BINDINGS: "dxilv1.3-unknown-shadermodel6.3-library" - "clang", inputs: ["[[INPUT:.+]]"], output: "[[DXC:.+]].dxo" -// BINDINGS-NEXT: "dxilv1.3-unknown-shadermodel6.3-library" - "hlsl::Validator", inputs: ["[[DXC]].dxo"] +// BINDINGS: "dxilv1.3-unknown-shadermodel6.3-library" - "clang", inputs: ["[[INPUT:.+]]"], output: "[[obj:.+]].obj" +// BINDINGS-NEXT: "dxilv1.3-unknown-shadermodel6.3-library" - "hlsl::Validator", inputs: ["[[obj]].obj"], output: "{{.+}}.dxo" // RUN: %clang_dxc -Tlib_6_3 -ccc-print-phases --dxv-path=%T -Fo %t.dxc %s 2>&1 | FileCheck %s --check-prefix=PHASES diff --git a/clang/test/Driver/fprofile-continuous.c b/clang/test/Driver/fprofile-continuous.c index cc8e56c8f9fe0..b76f2cd2fe3b8 100644 --- a/clang/test/Driver/fprofile-continuous.c +++ b/clang/test/Driver/fprofile-continuous.c @@ -7,6 +7,7 @@ // RUN: %clang --target=powerpc64-ibm-aix -fprofile-generate -fprofile-continuous -### -c %s 2>&1 | FileCheck %s --check-prefix=RELOC // RUN: %clang --target=x86_64-unknown-fuchsia -fprofile-generate -fprofile-continuous -### -c %s 2>&1 | FileCheck %s --check-prefix=RELOC // RUN: %clang --target=x86_64-windows-msvc -fprofile-generate -fprofile-continuous -### -c %s 2>&1 | FileCheck %s --check-prefix=RELOC +// RUN: %clang --target=x86_64-windows-gnu -fprofile-generate -fprofile-continuous -### -c %s 2>&1 | FileCheck %s --check-prefix=RELOC // RELOC: "-cc1" {{.*}} "-fprofile-continuous" "-mllvm" "-runtime-counter-relocation" // 2) test -fprofile-continuous with cs-profile-generate and -fprofile-instr-generate diff --git a/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c index 79c936a79ffd2..8652f1d875e68 100644 --- a/clang/test/Driver/fsanitize.c +++ b/clang/test/Driver/fsanitize.c @@ -1053,13 +1053,18 @@ // RUN: not %clang --target=x86_64-sie-ps5 -fsanitize=kcfi %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-KCFI-TARGET // RUN: not %clang --target=x86_64-sie-ps5 -fsanitize=function -fsanitize=kcfi %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-KCFI-TARGET --check-prefix=CHECK-UBSAN-FUNCTION-TARGET // RUN: %clang --target=x86_64-sie-ps5 -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-UNDEFINED -// CHECK-UBSAN-UNDEFINED: "-fsanitize={{((alignment|array-bounds|bool|builtin|enum|float-cast-overflow|integer-divide-by-zero|nonnull-attribute|null|pointer-overflow|return|returns-nonnull-attribute|shift-base|shift-exponent|signed-integer-overflow|unreachable|vla-bound),?){17}"}} // RUN: not %clang --target=armv6t2-eabi -mexecute-only -fsanitize=function %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-FUNCTION-MEXECUTE-ONLY // RUN: not %clang --target=armv6t2-eabi -mpure-code -fsanitize=function %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-FUNCTION-MPURE-CODE // RUN: not %clang --target=armv6t2-eabi -mexecute-only -fsanitize=kcfi %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-KCFI-MEXECUTE-ONLY // RUN: not %clang --target=armv6t2-eabi -mpure-code -fsanitize=kcfi %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-KCFI-MPURE-CODE -// RUN: %clang --target=armv6t2-eabi -mexecute-only -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-UNDEFINED-VPTR +// RUN: %clang --target=armv6t2-eabi -mexecute-only -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-UNDEFINED + +// RUN: not %clang --target=aarch64 -mexecute-only -fsanitize=function %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-FUNCTION-MEXECUTE-ONLY +// RUN: not %clang --target=aarch64 -mpure-code -fsanitize=function %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-FUNCTION-MPURE-CODE +// RUN: not %clang --target=aarch64 -mexecute-only -fsanitize=kcfi %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-KCFI-MEXECUTE-ONLY +// RUN: not %clang --target=aarch64 -mpure-code -fsanitize=kcfi %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-KCFI-MPURE-CODE +// RUN: %clang --target=aarch64 -mexecute-only -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-UNDEFINED // CHECK-UBSAN-KCFI-TARGET-DAG: error: unsupported option '-fsanitize=kcfi' for target 'x86_64-sie-ps5' // CHECK-UBSAN-KCFI-MEXECUTE-ONLY-DAG: error: invalid argument '-fsanitize=kcfi' not allowed with '-mexecute-only' @@ -1067,7 +1072,7 @@ // CHECK-UBSAN-FUNCTION-TARGET-DAG: error: unsupported option '-fsanitize=function' for target 'x86_64-sie-ps5' // CHECK-UBSAN-FUNCTION-MEXECUTE-ONLY-DAG: error: invalid argument '-fsanitize=function' not allowed with '-mexecute-only' // CHECK-UBSAN-FUNCTION-MPURE-CODE-DAG: error: invalid argument '-fsanitize=function' not allowed with '-mpure-code' -// CHECK-UBSAN-UNDEFINED-VPTR: "-fsanitize={{((alignment|array-bounds|bool|builtin|enum|float-cast-overflow|integer-divide-by-zero|nonnull-attribute|null|pointer-overflow|return|returns-nonnull-attribute|shift-base|shift-exponent|signed-integer-overflow|unreachable|vla-bound),?){17}"}} +// CHECK-UBSAN-UNDEFINED: "-fsanitize={{((alignment|array-bounds|bool|builtin|enum|float-cast-overflow|integer-divide-by-zero|nonnull-attribute|null|pointer-overflow|return|returns-nonnull-attribute|shift-base|shift-exponent|signed-integer-overflow|unreachable|vla-bound),?){17}"}} // * Test BareMetal toolchain sanitizer support * diff --git a/clang/test/Driver/hexagon-toolchain-elf.c b/clang/test/Driver/hexagon-toolchain-elf.c index be812dda40d57..de2ebfeeda26c 100644 --- a/clang/test/Driver/hexagon-toolchain-elf.c +++ b/clang/test/Driver/hexagon-toolchain-elf.c @@ -555,6 +555,7 @@ // RUN: -ccc-install-dir %S/Inputs/hexagon_tree/Tools/bin \ // RUN: -mcpu=hexagonv60 \ // RUN: -fuse-ld=lld %s 2>&1 | FileCheck -check-prefix=CHECK382 %s +// CHECK382: "--eh-frame-hdr // CHECK382-NOT: "-march= // CHECK382-NOT: "-mcpu= // ----------------------------------------------------------------------------- diff --git a/clang/test/Driver/hexagon-toolchain-linux.c b/clang/test/Driver/hexagon-toolchain-linux.c index 6f7f3b20f9141..e791353cca07f 100644 --- a/clang/test/Driver/hexagon-toolchain-linux.c +++ b/clang/test/Driver/hexagon-toolchain-linux.c @@ -127,6 +127,7 @@ // RUN: --target=hexagon-unknown-linux-musl %s -### 2>&1 \ // RUN: | FileCheck -check-prefix=CHECK011 %s // CHECK011: InstalledDir: [[INSTALLED_DIR:.+]] +// CHECK011: "--eh-frame-hdr" // CHECK011: crt1.o // CHECK011-NOT: "-lunwind" // CHECK011-NOT: "-lgcc_eh" diff --git a/clang/test/Driver/hip-cuid-hash.hip b/clang/test/Driver/hip-cuid-hash.hip index 103a1cbf26d50..a4167d664537e 100644 --- a/clang/test/Driver/hip-cuid-hash.hip +++ b/clang/test/Driver/hip-cuid-hash.hip @@ -1,13 +1,20 @@ // Check CUID generated by hash. // The same CUID is generated for the same file with the same options. +// This test requires relative paths for input files. Since the test may be +// done out of source tree, create the local directory structure and copy the +// input file from the source tree into that directory. +// RUN: mkdir -p %t/Inputs/hip_multiple_inputs +// RUN: cp %S/Inputs/hip_multiple_inputs/a.cu %t/Inputs/hip_multiple_inputs/a.cu +// RUN: cd %t + // RUN: %clang -### -x hip --target=x86_64-unknown-linux-gnu --no-offload-new-driver \ // RUN: --offload-arch=gfx906 -c -nogpuinc -nogpulib -fuse-cuid=hash \ -// RUN: %S/Inputs/hip_multiple_inputs/a.cu >%t.out 2>&1 +// RUN: Inputs/hip_multiple_inputs/a.cu >%t.out 2>&1 // RUN: %clang -### -x hip --target=x86_64-unknown-linux-gnu --no-offload-new-driver \ // RUN: --offload-arch=gfx906 -c -nogpuinc -nogpulib -fuse-cuid=hash \ -// RUN: %S/Inputs/hip_multiple_inputs/a.cu >>%t.out 2>&1 +// RUN: Inputs/hip_multiple_inputs/a.cu >>%t.out 2>&1 // RUN: FileCheck %s -check-prefixes=SAME -input-file %t.out @@ -16,11 +23,11 @@ // RUN: %clang -### -x hip --target=x86_64-unknown-linux-gnu -DX=1 --no-offload-new-driver \ // RUN: --offload-arch=gfx906 -c -nogpuinc -nogpulib -fuse-cuid=hash \ -// RUN: %S/Inputs/hip_multiple_inputs/a.cu >%t.out 2>&1 +// RUN: Inputs/hip_multiple_inputs/a.cu >%t.out 2>&1 // RUN: %clang -### -x hip --target=x86_64-unknown-linux-gnu -DX=2 --no-offload-new-driver \ // RUN: --offload-arch=gfx906 -c -nogpuinc -nogpulib -fuse-cuid=hash \ -// RUN: %S/Inputs/../Inputs/hip_multiple_inputs/a.cu >>%t.out 2>&1 +// RUN: Inputs/../Inputs/hip_multiple_inputs/a.cu >>%t.out 2>&1 // RUN: FileCheck %s -check-prefixes=DIFF -input-file %t.out diff --git a/clang/test/Driver/hip-partial-link.hip b/clang/test/Driver/hip-partial-link.hip index 8b27f78f3bdd1..d1a57bbe1d75c 100644 --- a/clang/test/Driver/hip-partial-link.hip +++ b/clang/test/Driver/hip-partial-link.hip @@ -1,4 +1,4 @@ -// REQUIRES: x86-registered-target, amdgpu-registered-target, lld, system-linux +// REQUIRES: x86-registered-target, amdgpu-registered-target, lld // RUN: %clang -x hip --target=x86_64-unknown-linux-gnu --no-offload-new-driver \ // RUN: --offload-arch=gfx906 -c -nostdinc -nogpuinc -nohipwrapperinc \ @@ -32,11 +32,11 @@ // LD-R: Found undefined HIP fatbin symbol: __hip_fatbin_[[ID2:[0-9a-f]+]] // LD-R: Found undefined HIP gpubin handle symbol: __hip_gpubin_handle_[[ID1]] // LD-R: Found undefined HIP gpubin handle symbol: __hip_gpubin_handle_[[ID2]] -// LD-R: "{{.*}}/clang-offload-bundler" {{.*}}-unbundle -// LD-R: "{{.*}}/lld" -flavor gnu -m elf64_amdgpu -// LD-R: "{{.*}}/clang-offload-bundler" -// LD-R: "{{.*}}/clang{{.*}}" -target x86_64-unknown-linux-gnu -// LD-R: "{{.*}}/ld.lld" {{.*}} -r +// LD-R: "{{.*[/\\]}}clang-offload-bundler" {{.*}}-unbundle +// LD-R: "{{.*[/\\]}}lld" -flavor gnu -m elf64_amdgpu +// LD-R: "{{.*[/\\]}}clang-offload-bundler" +// LD-R: "{{.*[/\\]}}clang{{.*}}" -target x86_64-unknown-linux-gnu +// LD-R: "{{.*[/\\]}}ld.lld" {{.*}} -r // RUN: llvm-nm %t.lib.o | FileCheck -check-prefix=OBJ %s // OBJ: B __hip_cuid_[[ID1:[0-9a-f]+]] @@ -62,11 +62,11 @@ // STATIC: Found undefined HIP fatbin symbol: __hip_fatbin_[[ID2:[0-9a-f]+]] // STATIC: Found undefined HIP gpubin handle symbol: __hip_gpubin_handle_[[ID1]] // STATIC: Found undefined HIP gpubin handle symbol: __hip_gpubin_handle_[[ID2]] -// STATIC: "{{.*}}/clang-offload-bundler" {{.*}}-unbundle -// STATIC: "{{.*}}/lld" -flavor gnu -m elf64_amdgpu -// STATIC: "{{.*}}/clang-offload-bundler" -// STATIC: "{{.*}}/clang{{.*}}" -target x86_64-unknown-linux-gnu -// STATIC: "{{.*}}/llvm-ar" +// STATIC: "{{.*[/\\]}}clang-offload-bundler" {{.*}}-unbundle +// STATIC: "{{.*[/\\]}}lld" -flavor gnu -m elf64_amdgpu +// STATIC: "{{.*[/\\]}}clang-offload-bundler" +// STATIC: "{{.*[/\\]}}clang{{.*}}" -target x86_64-unknown-linux-gnu +// STATIC: "{{.*[/\\]}}llvm-ar" // RUN: %clang -v --target=x86_64-unknown-linux-gnu --no-offload-new-driver \ // RUN: --hip-link -no-hip-rt -fgpu-rdc --offload-arch=gfx906 \ diff --git a/clang/test/Driver/linker-wrapper.c b/clang/test/Driver/linker-wrapper.c index 7586b87743bf5..0c77c2b34216a 100644 --- a/clang/test/Driver/linker-wrapper.c +++ b/clang/test/Driver/linker-wrapper.c @@ -1,9 +1,8 @@ +// UNSUPPORTED: system-windows // REQUIRES: x86-registered-target // REQUIRES: nvptx-registered-target // REQUIRES: amdgpu-registered-target -// REQUIRES: system-linux - // An externally visible variable so static libraries extract. __attribute__((visibility("protected"), used)) int x; @@ -39,7 +38,7 @@ __attribute__((visibility("protected"), used)) int x; // RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run \ // RUN: --linker-path=/usr/bin/ld %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=AMDGPU-LINK -// AMDGPU-LINK: clang{{.*}} -o {{.*}}.img --target=amdgcn-amd-amdhsa -mcpu=gfx908 -Wl,--no-undefined {{.*}}.o {{.*}}.o +// AMDGPU-LINK: clang{{.*}} -o {{.*}}.img --target=amdgcn-amd-amdhsa -mcpu=gfx908 -flto -Wl,--no-undefined {{.*}}.o {{.*}}.o // RUN: clang-offload-packager -o %t.out \ // RUN: --image=file=%t.amdgpu.bc,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx1030 \ @@ -48,7 +47,7 @@ __attribute__((visibility("protected"), used)) int x; // RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run --device-compiler=--save-temps \ // RUN: --linker-path=/usr/bin/ld %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=AMDGPU-LTO-TEMPS -// AMDGPU-LTO-TEMPS: clang{{.*}} --target=amdgcn-amd-amdhsa -mcpu=gfx1030 {{.*}}-save-temps +// AMDGPU-LTO-TEMPS: clang{{.*}} --target=amdgcn-amd-amdhsa -mcpu=gfx1030 -flto {{.*}}-save-temps // RUN: clang-offload-packager -o %t.out \ // RUN: --image=file=%t.elf.o,kind=openmp,triple=x86_64-unknown-linux-gnu \ @@ -120,7 +119,7 @@ __attribute__((visibility("protected"), used)) int x; // HIP: clang{{.*}} -o [[IMG_GFX90A:.+]] --target=amdgcn-amd-amdhsa -mcpu=gfx90a // HIP: clang{{.*}} -o [[IMG_GFX908:.+]] --target=amdgcn-amd-amdhsa -mcpu=gfx908 -// HIP: clang-offload-bundler{{.*}}-type=o -bundle-align=4096 -compress -compression-level=6 -targets=host-x86_64-unknown-linux-gnu,hip-amdgcn-amd-amdhsa--gfx90a,hip-amdgcn-amd-amdhsa--gfx908 -input=/dev/null -input=[[IMG_GFX90A]] -input=[[IMG_GFX908]] -output={{.*}}.hipfb +// HIP: clang-offload-bundler{{.*}}-type=o -bundle-align=4096 -compress -compression-level=6 -targets=host-x86_64-unknown-linux-gnu,hip-amdgcn-amd-amdhsa--gfx90a,hip-amdgcn-amd-amdhsa--gfx908 -input={{/dev/null|NUL}} -input=[[IMG_GFX90A]] -input=[[IMG_GFX908]] -output={{.*}}.hipfb // RUN: clang-offload-packager -o %t.out \ // RUN: --image=file=%t.elf.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx908 \ @@ -148,7 +147,7 @@ __attribute__((visibility("protected"), used)) int x; // RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run --clang-backend \ // RUN: --linker-path=/usr/bin/ld %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=CLANG-BACKEND -// CLANG-BACKEND: clang{{.*}} -o {{.*}}.img --target=amdgcn-amd-amdhsa -mcpu=gfx908 -Wl,--no-undefined {{.*}}.o +// CLANG-BACKEND: clang{{.*}} -o {{.*}}.img --target=amdgcn-amd-amdhsa -mcpu=gfx908 -flto -Wl,--no-undefined {{.*}}.o // RUN: clang-offload-packager -o %t.out \ // RUN: --image=file=%t.elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70 @@ -171,8 +170,8 @@ __attribute__((visibility("protected"), used)) int x; // RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run \ // RUN: --linker-path=/usr/bin/ld %t-on.o %t-off.o %t.a -o a.out 2>&1 | FileCheck %s --check-prefix=AMD-TARGET-ID -// AMD-TARGET-ID: clang{{.*}} -o {{.*}}.img --target=amdgcn-amd-amdhsa -mcpu=gfx90a:xnack+ -Wl,--no-undefined {{.*}}.o {{.*}}.o -// AMD-TARGET-ID: clang{{.*}} -o {{.*}}.img --target=amdgcn-amd-amdhsa -mcpu=gfx90a:xnack- -Wl,--no-undefined {{.*}}.o {{.*}}.o +// AMD-TARGET-ID: clang{{.*}} -o {{.*}}.img --target=amdgcn-amd-amdhsa -mcpu=gfx90a:xnack+ -flto -Wl,--no-undefined {{.*}}.o {{.*}}.o +// AMD-TARGET-ID: clang{{.*}} -o {{.*}}.img --target=amdgcn-amd-amdhsa -mcpu=gfx90a:xnack- -flto -Wl,--no-undefined {{.*}}.o {{.*}}.o // RUN: clang-offload-packager -o %t-lib.out \ // RUN: --image=file=%t.elf.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=generic @@ -187,8 +186,8 @@ __attribute__((visibility("protected"), used)) int x; // RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run \ // RUN: --linker-path=/usr/bin/ld %t1.o %t2.o %t.a -o a.out 2>&1 | FileCheck %s --check-prefix=ARCH-ALL -// ARCH-ALL: clang{{.*}} -o {{.*}}.img --target=amdgcn-amd-amdhsa -mcpu=gfx90a -Wl,--no-undefined {{.*}}.o {{.*}}.o -// ARCH-ALL: clang{{.*}} -o {{.*}}.img --target=amdgcn-amd-amdhsa -mcpu=gfx908 -Wl,--no-undefined {{.*}}.o {{.*}}.o +// ARCH-ALL: clang{{.*}} -o {{.*}}.img --target=amdgcn-amd-amdhsa -mcpu=gfx90a -flto -Wl,--no-undefined {{.*}}.o {{.*}}.o +// ARCH-ALL: clang{{.*}} -o {{.*}}.img --target=amdgcn-amd-amdhsa -mcpu=gfx908 -flto -Wl,--no-undefined {{.*}}.o {{.*}}.o // RUN: clang-offload-packager -o %t.out \ // RUN: --image=file=%t.elf.o,kind=openmp,triple=x86_64-unknown-linux-gnu \ @@ -211,7 +210,7 @@ __attribute__((visibility("protected"), used)) int x; // RUN: %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=RELOCATABLE-LINK-HIP // RELOCATABLE-LINK-HIP: clang{{.*}} -o {{.*}}.img --target=amdgcn-amd-amdhsa -// RELOCATABLE-LINK-HIP: clang-offload-bundler{{.*}} -type=o -bundle-align=4096 -targets=host-x86_64-unknown-linux-gnu,hip-amdgcn-amd-amdhsa--gfx90a -input=/dev/null -input={{.*}} -output={{.*}} +// RELOCATABLE-LINK-HIP: clang-offload-bundler{{.*}} -type=o -bundle-align=4096 -targets=host-x86_64-unknown-linux-gnu,hip-amdgcn-amd-amdhsa--gfx90a -input={{/dev/null|NUL}} -input={{.*}} -output={{.*}} // RELOCATABLE-LINK-HIP: /usr/bin/ld.lld{{.*}}-r // RELOCATABLE-LINK-HIP: llvm-objcopy{{.*}}a.out --remove-section .llvm.offloading diff --git a/clang/test/Driver/print-supported-extensions-riscv.c b/clang/test/Driver/print-supported-extensions-riscv.c index 69b76f0c4c4cd..d9335fe502bb6 100644 --- a/clang/test/Driver/print-supported-extensions-riscv.c +++ b/clang/test/Driver/print-supported-extensions-riscv.c @@ -201,7 +201,7 @@ // CHECK-NEXT: xqcicm 0.2 'Xqcicm' (Qualcomm uC Conditional Move Extension) // CHECK-NEXT: xqcics 0.2 'Xqcics' (Qualcomm uC Conditional Select Extension) // CHECK-NEXT: xqcicsr 0.2 'Xqcicsr' (Qualcomm uC CSR Extension) -// CHECK-NEXT: xqciint 0.2 'Xqciint' (Qualcomm uC Interrupts Extension) +// CHECK-NEXT: xqciint 0.4 'Xqciint' (Qualcomm uC Interrupts Extension) // CHECK-NEXT: xqcilia 0.2 'Xqcilia' (Qualcomm uC Large Immediate Arithmetic Extension) // CHECK-NEXT: xqcilo 0.2 'Xqcilo' (Qualcomm uC Large Offset Load Store Extension) // CHECK-NEXT: xqcilsm 0.2 'Xqcilsm' (Qualcomm uC Load Store Multiple Extension) diff --git a/clang/test/Driver/spirv-openmp-toolchain.c b/clang/test/Driver/spirv-openmp-toolchain.c index 5a76bf70e7ed3..3fd6d94a1222b 100644 --- a/clang/test/Driver/spirv-openmp-toolchain.c +++ b/clang/test/Driver/spirv-openmp-toolchain.c @@ -4,8 +4,7 @@ // verify the tools invocations // CHECK: "-cc1" "-triple" "x86_64-unknown-linux-gnu"{{.*}}"-emit-llvm-bc"{{.*}}"-x" "c" -// CHECK: "-cc1" "-triple" "spirv64-intel" "-aux-triple" "x86_64-unknown-linux-gnu" -// CHECK: llvm-spirv{{.*}} +// CHECK: "-cc1" "-triple" "spirv64-intel" "-aux-triple" "x86_64-unknown-linux-gnu"{{.*}} "-o" "{{.*}}.o" // CHECK: "-cc1" "-triple" "x86_64-unknown-linux-gnu"{{.*}}"-emit-obj" // CHECK: clang-linker-wrapper{{.*}} "-o" "a.out" @@ -32,8 +31,7 @@ // RUN: %clang -### --target=x86_64-unknown-linux-gnu -ccc-print-bindings -fopenmp=libomp -fopenmp-targets=spirv64-intel -nogpulib %s 2>&1 | FileCheck %s --check-prefix=CHECK-BINDINGS // CHECK-BINDINGS: "x86_64-unknown-linux-gnu" - "clang", inputs: ["[[INPUT:.+]]"], output: "[[HOST_BC:.+]]" -// CHECK-BINDINGS: "spirv64-intel" - "clang", inputs: ["[[INPUT]]", "[[HOST_BC]]"], output: "[[DEVICE_TEMP_BC:.+]]" -// CHECK-BINDINGS: "spirv64-intel" - "SPIR-V::Translator", inputs: ["[[DEVICE_TEMP_BC]]"], output: "[[DEVICE_SPV:.+]]" +// CHECK-BINDINGS: "spirv64-intel" - "clang", inputs: ["[[INPUT]]", "[[HOST_BC]]"], output: "[[DEVICE_SPV:.+]]" // CHECK-BINDINGS: "x86_64-unknown-linux-gnu" - "Offload::Packager", inputs: ["[[DEVICE_SPV]]"], output: "[[DEVICE_IMAGE:.+]]" // CHECK-BINDINGS: "x86_64-unknown-linux-gnu" - "clang", inputs: ["[[HOST_BC]]", "[[DEVICE_IMAGE]]"], output: "[[HOST_OBJ:.+]]" // CHECK-BINDINGS: "x86_64-unknown-linux-gnu" - "Offload::Linker", inputs: ["[[HOST_OBJ]]"], output: "a.out" @@ -43,8 +41,8 @@ // CHECK-BINDINGS-TEMPS: "x86_64-unknown-linux-gnu" - "clang", inputs: ["[[INPUT:.+]]"], output: "[[HOST_PP:.+]]" // CHECK-BINDINGS-TEMPS: "x86_64-unknown-linux-gnu" - "clang", inputs: ["[[HOST_PP]]"], output: "[[HOST_BC:.+]]" // CHECK-BINDINGS-TEMPS: "spirv64-intel" - "clang", inputs: ["[[INPUT]]"], output: "[[DEVICE_PP:.+]]" -// CHECK-BINDINGS-TEMPS: "spirv64-intel" - "clang", inputs: ["[[DEVICE_PP]]", "[[HOST_BC]]"], output: "[[DEVICE_TEMP_BC:.+]]" -// CHECK-BINDINGS-TEMPS: "spirv64-intel" - "SPIR-V::Translator", inputs: ["[[DEVICE_TEMP_BC]]"], output: "[[DEVICE_ASM:.+]]" +// CHECK-BINDINGS-TEMPS: "spirv64-intel" - "clang", inputs: ["[[DEVICE_PP]]", "[[HOST_BC]]"], output: "[[DEVICE_BC:.+]]" +// CHECK-BINDINGS-TEMPS: "spirv64-intel" - "clang", inputs: ["[[DEVICE_BC]]"], output: "[[DEVICE_ASM:.+]]" // CHECK-BINDINGS-TEMPS: "spirv64-intel" - "SPIR-V::Assembler", inputs: ["[[DEVICE_ASM]]"], output: "[[DEVICE_SPV:.+]]" // CHECK-BINDINGS-TEMPS: "x86_64-unknown-linux-gnu" - "Offload::Packager", inputs: ["[[DEVICE_SPV]]"], output: "[[DEVICE_IMAGE:.+]]" // CHECK-BINDINGS-TEMPS: "x86_64-unknown-linux-gnu" - "clang", inputs: ["[[HOST_BC]]", "[[DEVICE_IMAGE]]"], output: "[[HOST_ASM:.+]]" diff --git a/clang/test/Driver/spirv-toolchain.cl b/clang/test/Driver/spirv-toolchain.cl index fd29dbe71e910..53e8455e3d3dd 100644 --- a/clang/test/Driver/spirv-toolchain.cl +++ b/clang/test/Driver/spirv-toolchain.cl @@ -6,8 +6,7 @@ // RUN: %clang -### --target=spirv64 -x c -c %s 2>&1 | FileCheck --check-prefix=SPV64 %s // SPV64: "-cc1" "-triple" "spirv64" -// SPV64-SAME: "-o" [[BC:".*bc"]] -// SPV64: {{llvm-spirv.*"}} [[BC]] "-o" {{".*o"}} +// SPV64-SAME: "-o" {{".*o"}} // RUN: %clang -### --target=spirv32 -x cl -c %s 2>&1 | FileCheck --check-prefix=SPV32 %s // RUN: %clang -### --target=spirv32 %s 2>&1 | FileCheck --check-prefix=SPV32 %s @@ -16,8 +15,7 @@ // RUN: %clang -### --target=spirv32 -x c -c %s 2>&1 | FileCheck --check-prefix=SPV32 %s // SPV32: "-cc1" "-triple" "spirv32" -// SPV32-SAME: "-o" [[BC:".*bc"]] -// SPV32: {{llvm-spirv.*"}} [[BC]] "-o" {{".*o"}} +// SPV32-SAME: "-o" {{".*o"}} //----------------------------------------------------------------------------- // Check Assembly emission. @@ -27,8 +25,7 @@ // RUN: %clang -### --target=spirv64 -x c -S %s 2>&1 | FileCheck --check-prefix=SPT64 %s // SPT64: "-cc1" "-triple" "spirv64" -// SPT64-SAME: "-o" [[BC:".*bc"]] -// SPT64: {{llvm-spirv.*"}} [[BC]] "--spirv-tools-dis" "-o" {{".*s"}} +// SPT64-SAME: "-o" {{".*s"}} // RUN: %clang -### --target=spirv32 -x cl -S %s 2>&1 | FileCheck --check-prefix=SPT32 %s // RUN: %clang -### --target=spirv32 -x ir -S %s 2>&1 | FileCheck --check-prefix=SPT32 %s @@ -36,8 +33,7 @@ // RUN: %clang -### --target=spirv32 -x c -S %s 2>&1 | FileCheck --check-prefix=SPT32 %s // SPT32: "-cc1" "-triple" "spirv32" -// SPT32-SAME: "-o" [[BC:".*bc"]] -// SPT32: {{llvm-spirv.*"}} [[BC]] "--spirv-tools-dis" "-o" {{".*s"}} +// SPT32-SAME: "-o" {{".*s"}} //----------------------------------------------------------------------------- // Check assembly input -> object output @@ -55,7 +51,9 @@ // TMP: "-cc1" "-triple" "spirv64" // TMP-SAME: "-o" [[BC:".*bc"]] // TMP-SAME: [[I]] -// TMP: {{llvm-spirv.*"}} [[BC]] "--spirv-tools-dis" "-o" [[S:".*s"]] +// TMP: "-cc1" +// TMP-SAME: "-o" [[S:".*s"]] +// TMP-SAME: [[BC]] // TMP: {{spirv-as.*"}} [[S]] "-o" {{".*o"}} //----------------------------------------------------------------------------- @@ -63,21 +61,17 @@ // RUN: %clang -### -target spirv64 %s %s 2>&1 | FileCheck --check-prefix=SPLINK %s // SPLINK: "-cc1" "-triple" "spirv64" -// SPLINK-SAME: "-o" [[BC:".*bc"]] -// SPLINK: {{llvm-spirv.*"}} [[BC]] "-o" [[SPV1:".*o"]] +// SPLINK-SAME: "-o" [[SPV1:".*o"]] // SPLINK: "-cc1" "-triple" "spirv64" -// SPLINK-SAME: "-o" [[BC:".*bc"]] -// SPLINK: {{llvm-spirv.*"}} [[BC]] "-o" [[SPV2:".*o"]] +// SPLINK-SAME: "-o" [[SPV2:".*o"]] // SPLINK: {{spirv-link.*"}} [[SPV1]] [[SPV2]] "-o" "a.out" //----------------------------------------------------------------------------- // Check bindings when linking when multiple input files are passed. // RUN: %clang -### -target spirv64 -ccc-print-bindings %s %s 2>&1 | FileCheck --check-prefix=SPLINK-BINDINGS %s -// SPLINK-BINDINGS: "clang", inputs: [[[CL:".*cl"]]], output: [[BC1:".*bc"]] -// SPLINK-BINDINGS: "SPIR-V::Translator", inputs: [[[BC1]]], output: [[OBJ1:".*o"]] -// SPLINK-BINDINGS: "clang", inputs: [[[CL]]], output: [[BC2:".*bc"]] -// SPLINK-BINDINGS: "SPIR-V::Translator", inputs: [[[BC2]]], output: [[OBJ2:".*o"]] +// SPLINK-BINDINGS: "clang", inputs: [[[CL:".*cl"]]], output: [[OBJ1:".*o"]] +// SPLINK-BINDINGS: "clang", inputs: [[[CL]]], output: [[OBJ2:".*o"]] // SPLINK-BINDINGS: "SPIR-V::Linker", inputs: [[[OBJ1]], [[OBJ2]]], output: "a.out" //----------------------------------------------------------------------------- @@ -85,20 +79,20 @@ // RUN: %clang -### --target=spirv64 -fno-integrated-objemitter %s 2>&1 | FileCheck --check-prefix=XTOR %s // RUN: %clang -### --target=spirv64 -fintegrated-objemitter %s 2>&1 | FileCheck --check-prefix=BACKEND %s -// XTOR: {{llvm-spirv.*"}} -// BACKEND-NOT: {{llvm-spirv.*"}} +// XTOR-NOT: "llvm-spirv.*" +// BACKEND-NOT: "llvm-spirv.*" //----------------------------------------------------------------------------- -// Check llvm-spirv- is used if it is found in PATH. +// Check spirv-as- is used if it is found in PATH. // // This test uses the PATH environment variable; on Windows, we may need to retain // the original path for the built Clang binary to be able to execute (as it is // used for locating dependent DLLs). Therefore, skip this test on system-windows. // // RUN: mkdir -p %t/versioned -// RUN: touch %t/versioned/llvm-spirv-%llvm-version-major \ -// RUN: && chmod +x %t/versioned/llvm-spirv-%llvm-version-major -// RUN: %if !system-windows %{ env "PATH=%t/versioned" %clang -### --target=spirv64 -x cl -c %s 2>&1 \ +// RUN: touch %t/versioned/spirv-as-%llvm-version-major \ +// RUN: && chmod +x %t/versioned/spirv-as-%llvm-version-major +// RUN: %if !system-windows %{ env "PATH=%t/versioned" %clang -### --target=spirv64 -x cl -c --save-temps %s 2>&1 \ // RUN: | FileCheck -DVERSION=%llvm-version-major --check-prefix=VERSIONED %s %} -// VERSIONED: {{.*}}llvm-spirv-[[VERSION]] +// VERSIONED: {{.*}}spirv-as-[[VERSION]] diff --git a/clang/test/Headers/gpuintrin.c b/clang/test/Headers/gpuintrin.c index 89efe12ee8def..30aa6f147ba03 100644 --- a/clang/test/Headers/gpuintrin.c +++ b/clang/test/Headers/gpuintrin.c @@ -1,10 +1,10 @@ -// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 -// RUN: %clang_cc1 -internal-isystem %S/Inputs/include \ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --include-generated-funcs --version 5 +// RUN: %clang_cc1 -internal-isystem %S/Inputs/include \ // RUN: -internal-isystem %S/../../lib/Headers/ \ // RUN: -triple amdgcn-amd-amdhsa -emit-llvm %s -o - \ // RUN: | FileCheck %s --check-prefix=AMDGPU // -// RUN: %clang_cc1 -internal-isystem %S/Inputs/include \ +// RUN: %clang_cc1 -internal-isystem %S/Inputs/include \ // RUN: -internal-isystem %S/../../lib/Headers/ \ // RUN: -target-feature +ptx62 \ // RUN: -triple nvptx64-nvidia-cuda -emit-llvm %s -o - \ @@ -12,6 +12,35 @@ #include +__gpu_kernel void foo() { + __gpu_num_blocks_x(); + __gpu_num_blocks_y(); + __gpu_num_blocks_z(); + __gpu_num_blocks(0); + __gpu_block_id_x(); + __gpu_block_id_y(); + __gpu_block_id_z(); + __gpu_block_id(0); + __gpu_num_threads_x(); + __gpu_num_threads_y(); + __gpu_num_threads_z(); + __gpu_num_threads(0); + __gpu_thread_id_x(); + __gpu_thread_id_y(); + __gpu_thread_id_z(); + __gpu_thread_id(0); + __gpu_num_lanes(); + __gpu_lane_id(); + __gpu_lane_mask(); + __gpu_read_first_lane_u32(-1, -1); + __gpu_ballot(-1, 1); + __gpu_sync_threads(); + __gpu_sync_lane(-1); + __gpu_shuffle_idx_u32(-1, -1, -1, 0); + __gpu_first_lane_id(-1); + __gpu_is_first_in_lane(-1); + __gpu_exit(); +} // AMDGPU-LABEL: define protected amdgpu_kernel void @foo( // AMDGPU-SAME: ) #[[ATTR0:[0-9]+]] { // AMDGPU-NEXT: [[ENTRY:.*:]] @@ -44,52 +73,244 @@ // AMDGPU-NEXT: call void @__gpu_exit() #[[ATTR8:[0-9]+]] // AMDGPU-NEXT: unreachable // -// NVPTX-LABEL: define protected ptx_kernel void @foo( -// NVPTX-SAME: ) #[[ATTR0:[0-9]+]] { -// NVPTX-NEXT: [[ENTRY:.*:]] -// NVPTX-NEXT: [[CALL:%.*]] = call i32 @__gpu_num_blocks_x() #[[ATTR6:[0-9]+]] -// NVPTX-NEXT: [[CALL1:%.*]] = call i32 @__gpu_num_blocks_y() #[[ATTR6]] -// NVPTX-NEXT: [[CALL2:%.*]] = call i32 @__gpu_num_blocks_z() #[[ATTR6]] -// NVPTX-NEXT: [[CALL3:%.*]] = call i32 @__gpu_num_blocks(i32 noundef 0) #[[ATTR6]] -// NVPTX-NEXT: [[CALL4:%.*]] = call i32 @__gpu_block_id_x() #[[ATTR6]] -// NVPTX-NEXT: [[CALL5:%.*]] = call i32 @__gpu_block_id_y() #[[ATTR6]] -// NVPTX-NEXT: [[CALL6:%.*]] = call i32 @__gpu_block_id_z() #[[ATTR6]] -// NVPTX-NEXT: [[CALL7:%.*]] = call i32 @__gpu_block_id(i32 noundef 0) #[[ATTR6]] -// NVPTX-NEXT: [[CALL8:%.*]] = call i32 @__gpu_num_threads_x() #[[ATTR6]] -// NVPTX-NEXT: [[CALL9:%.*]] = call i32 @__gpu_num_threads_y() #[[ATTR6]] -// NVPTX-NEXT: [[CALL10:%.*]] = call i32 @__gpu_num_threads_z() #[[ATTR6]] -// NVPTX-NEXT: [[CALL11:%.*]] = call i32 @__gpu_num_threads(i32 noundef 0) #[[ATTR6]] -// NVPTX-NEXT: [[CALL12:%.*]] = call i32 @__gpu_thread_id_x() #[[ATTR6]] -// NVPTX-NEXT: [[CALL13:%.*]] = call i32 @__gpu_thread_id_y() #[[ATTR6]] -// NVPTX-NEXT: [[CALL14:%.*]] = call i32 @__gpu_thread_id_z() #[[ATTR6]] -// NVPTX-NEXT: [[CALL15:%.*]] = call i32 @__gpu_thread_id(i32 noundef 0) #[[ATTR6]] -// NVPTX-NEXT: [[CALL16:%.*]] = call i32 @__gpu_num_lanes() #[[ATTR6]] -// NVPTX-NEXT: [[CALL17:%.*]] = call i32 @__gpu_lane_id() #[[ATTR6]] -// NVPTX-NEXT: [[CALL18:%.*]] = call i64 @__gpu_lane_mask() #[[ATTR6]] -// NVPTX-NEXT: [[CALL19:%.*]] = call i32 @__gpu_read_first_lane_u32(i64 noundef -1, i32 noundef -1) #[[ATTR6]] -// NVPTX-NEXT: [[CALL20:%.*]] = call i64 @__gpu_ballot(i64 noundef -1, i1 noundef zeroext true) #[[ATTR6]] -// NVPTX-NEXT: call void @__gpu_sync_threads() #[[ATTR6]] -// NVPTX-NEXT: call void @__gpu_sync_lane(i64 noundef -1) #[[ATTR6]] -// NVPTX-NEXT: [[CALL21:%.*]] = call i32 @__gpu_shuffle_idx_u32(i64 noundef -1, i32 noundef -1, i32 noundef -1, i32 noundef 0) #[[ATTR6]] -// NVPTX-NEXT: [[CALL22:%.*]] = call i64 @__gpu_first_lane_id(i64 noundef -1) #[[ATTR6]] -// NVPTX-NEXT: [[CALL23:%.*]] = call zeroext i1 @__gpu_is_first_in_lane(i64 noundef -1) #[[ATTR6]] -// NVPTX-NEXT: call void @__gpu_exit() #[[ATTR7:[0-9]+]] -// NVPTX-NEXT: unreachable // -__gpu_kernel void foo() { - __gpu_num_blocks_x(); - __gpu_num_blocks_y(); - __gpu_num_blocks_z(); - __gpu_num_blocks(0); - __gpu_block_id_x(); - __gpu_block_id_y(); - __gpu_block_id_z(); - __gpu_block_id(0); - __gpu_num_threads_x(); - __gpu_num_threads_y(); - __gpu_num_threads_z(); - __gpu_num_threads(0); - __gpu_thread_id_x(); +// AMDGPU-LABEL: define internal i32 @__gpu_num_blocks_x( +// AMDGPU-SAME: ) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[RETVAL:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr +// AMDGPU-NEXT: [[TMP0:%.*]] = call align 4 dereferenceable(64) ptr addrspace(4) @llvm.amdgcn.dispatch.ptr() +// AMDGPU-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr addrspace(4) [[TMP0]], i32 12 +// AMDGPU-NEXT: [[TMP2:%.*]] = load i32, ptr addrspace(4) [[TMP1]], align 4, !range [[RNG3:![0-9]+]], !invariant.load [[META4:![0-9]+]] +// AMDGPU-NEXT: [[TMP3:%.*]] = call align 8 dereferenceable(256) ptr addrspace(4) @llvm.amdgcn.implicitarg.ptr() +// AMDGPU-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr addrspace(4) [[TMP3]], i32 12 +// AMDGPU-NEXT: [[TMP5:%.*]] = load i16, ptr addrspace(4) [[TMP4]], align 2, !range [[RNG5:![0-9]+]], !invariant.load [[META4]], !noundef [[META4]] +// AMDGPU-NEXT: [[CONV:%.*]] = zext i16 [[TMP5]] to i32 +// AMDGPU-NEXT: [[DIV:%.*]] = udiv i32 [[TMP2]], [[CONV]] +// AMDGPU-NEXT: ret i32 [[DIV]] +// +// +// AMDGPU-LABEL: define internal i32 @__gpu_num_blocks_y( +// AMDGPU-SAME: ) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[RETVAL:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr +// AMDGPU-NEXT: [[TMP0:%.*]] = call align 4 dereferenceable(64) ptr addrspace(4) @llvm.amdgcn.dispatch.ptr() +// AMDGPU-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr addrspace(4) [[TMP0]], i32 16 +// AMDGPU-NEXT: [[TMP2:%.*]] = load i32, ptr addrspace(4) [[TMP1]], align 4, !range [[RNG3]], !invariant.load [[META4]] +// AMDGPU-NEXT: [[TMP3:%.*]] = call align 8 dereferenceable(256) ptr addrspace(4) @llvm.amdgcn.implicitarg.ptr() +// AMDGPU-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr addrspace(4) [[TMP3]], i32 14 +// AMDGPU-NEXT: [[TMP5:%.*]] = load i16, ptr addrspace(4) [[TMP4]], align 2, !range [[RNG5]], !invariant.load [[META4]], !noundef [[META4]] +// AMDGPU-NEXT: [[CONV:%.*]] = zext i16 [[TMP5]] to i32 +// AMDGPU-NEXT: [[DIV:%.*]] = udiv i32 [[TMP2]], [[CONV]] +// AMDGPU-NEXT: ret i32 [[DIV]] +// +// +// AMDGPU-LABEL: define internal i32 @__gpu_num_blocks_z( +// AMDGPU-SAME: ) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[RETVAL:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr +// AMDGPU-NEXT: [[TMP0:%.*]] = call align 4 dereferenceable(64) ptr addrspace(4) @llvm.amdgcn.dispatch.ptr() +// AMDGPU-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr addrspace(4) [[TMP0]], i32 20 +// AMDGPU-NEXT: [[TMP2:%.*]] = load i32, ptr addrspace(4) [[TMP1]], align 4, !range [[RNG3]], !invariant.load [[META4]] +// AMDGPU-NEXT: [[TMP3:%.*]] = call align 8 dereferenceable(256) ptr addrspace(4) @llvm.amdgcn.implicitarg.ptr() +// AMDGPU-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr addrspace(4) [[TMP3]], i32 16 +// AMDGPU-NEXT: [[TMP5:%.*]] = load i16, ptr addrspace(4) [[TMP4]], align 2, !range [[RNG5]], !invariant.load [[META4]], !noundef [[META4]] +// AMDGPU-NEXT: [[CONV:%.*]] = zext i16 [[TMP5]] to i32 +// AMDGPU-NEXT: [[DIV:%.*]] = udiv i32 [[TMP2]], [[CONV]] +// AMDGPU-NEXT: ret i32 [[DIV]] +// +// +// AMDGPU-LABEL: define internal i32 @__gpu_num_blocks( +// AMDGPU-SAME: i32 noundef [[__DIM:%.*]]) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[RETVAL:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[__DIM_ADDR:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr +// AMDGPU-NEXT: [[__DIM_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[__DIM_ADDR]] to ptr +// AMDGPU-NEXT: store i32 [[__DIM]], ptr [[__DIM_ADDR_ASCAST]], align 4 +// AMDGPU-NEXT: [[TMP0:%.*]] = load i32, ptr [[__DIM_ADDR_ASCAST]], align 4 +// AMDGPU-NEXT: switch i32 [[TMP0]], label %[[SW_DEFAULT:.*]] [ +// AMDGPU-NEXT: i32 0, label %[[SW_BB:.*]] +// AMDGPU-NEXT: i32 1, label %[[SW_BB1:.*]] +// AMDGPU-NEXT: i32 2, label %[[SW_BB3:.*]] +// AMDGPU-NEXT: ] +// AMDGPU: [[SW_BB]]: +// AMDGPU-NEXT: [[CALL:%.*]] = call i32 @__gpu_num_blocks_x() #[[ATTR7]] +// AMDGPU-NEXT: store i32 [[CALL]], ptr [[RETVAL_ASCAST]], align 4 +// AMDGPU-NEXT: br label %[[RETURN:.*]] +// AMDGPU: [[SW_BB1]]: +// AMDGPU-NEXT: [[CALL2:%.*]] = call i32 @__gpu_num_blocks_y() #[[ATTR7]] +// AMDGPU-NEXT: store i32 [[CALL2]], ptr [[RETVAL_ASCAST]], align 4 +// AMDGPU-NEXT: br label %[[RETURN]] +// AMDGPU: [[SW_BB3]]: +// AMDGPU-NEXT: [[CALL4:%.*]] = call i32 @__gpu_num_blocks_z() #[[ATTR7]] +// AMDGPU-NEXT: store i32 [[CALL4]], ptr [[RETVAL_ASCAST]], align 4 +// AMDGPU-NEXT: br label %[[RETURN]] +// AMDGPU: [[SW_DEFAULT]]: +// AMDGPU-NEXT: unreachable +// AMDGPU: [[RETURN]]: +// AMDGPU-NEXT: [[TMP1:%.*]] = load i32, ptr [[RETVAL_ASCAST]], align 4 +// AMDGPU-NEXT: ret i32 [[TMP1]] +// +// +// AMDGPU-LABEL: define internal i32 @__gpu_block_id_x( +// AMDGPU-SAME: ) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[RETVAL:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr +// AMDGPU-NEXT: [[TMP0:%.*]] = call i32 @llvm.amdgcn.workgroup.id.x() +// AMDGPU-NEXT: ret i32 [[TMP0]] +// +// +// AMDGPU-LABEL: define internal i32 @__gpu_block_id_y( +// AMDGPU-SAME: ) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[RETVAL:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr +// AMDGPU-NEXT: [[TMP0:%.*]] = call i32 @llvm.amdgcn.workgroup.id.y() +// AMDGPU-NEXT: ret i32 [[TMP0]] +// +// +// AMDGPU-LABEL: define internal i32 @__gpu_block_id_z( +// AMDGPU-SAME: ) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[RETVAL:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr +// AMDGPU-NEXT: [[TMP0:%.*]] = call i32 @llvm.amdgcn.workgroup.id.z() +// AMDGPU-NEXT: ret i32 [[TMP0]] +// +// +// AMDGPU-LABEL: define internal i32 @__gpu_block_id( +// AMDGPU-SAME: i32 noundef [[__DIM:%.*]]) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[RETVAL:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[__DIM_ADDR:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr +// AMDGPU-NEXT: [[__DIM_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[__DIM_ADDR]] to ptr +// AMDGPU-NEXT: store i32 [[__DIM]], ptr [[__DIM_ADDR_ASCAST]], align 4 +// AMDGPU-NEXT: [[TMP0:%.*]] = load i32, ptr [[__DIM_ADDR_ASCAST]], align 4 +// AMDGPU-NEXT: switch i32 [[TMP0]], label %[[SW_DEFAULT:.*]] [ +// AMDGPU-NEXT: i32 0, label %[[SW_BB:.*]] +// AMDGPU-NEXT: i32 1, label %[[SW_BB1:.*]] +// AMDGPU-NEXT: i32 2, label %[[SW_BB3:.*]] +// AMDGPU-NEXT: ] +// AMDGPU: [[SW_BB]]: +// AMDGPU-NEXT: [[CALL:%.*]] = call i32 @__gpu_block_id_x() #[[ATTR7]] +// AMDGPU-NEXT: store i32 [[CALL]], ptr [[RETVAL_ASCAST]], align 4 +// AMDGPU-NEXT: br label %[[RETURN:.*]] +// AMDGPU: [[SW_BB1]]: +// AMDGPU-NEXT: [[CALL2:%.*]] = call i32 @__gpu_block_id_y() #[[ATTR7]] +// AMDGPU-NEXT: store i32 [[CALL2]], ptr [[RETVAL_ASCAST]], align 4 +// AMDGPU-NEXT: br label %[[RETURN]] +// AMDGPU: [[SW_BB3]]: +// AMDGPU-NEXT: [[CALL4:%.*]] = call i32 @__gpu_block_id_z() #[[ATTR7]] +// AMDGPU-NEXT: store i32 [[CALL4]], ptr [[RETVAL_ASCAST]], align 4 +// AMDGPU-NEXT: br label %[[RETURN]] +// AMDGPU: [[SW_DEFAULT]]: +// AMDGPU-NEXT: unreachable +// AMDGPU: [[RETURN]]: +// AMDGPU-NEXT: [[TMP1:%.*]] = load i32, ptr [[RETVAL_ASCAST]], align 4 +// AMDGPU-NEXT: ret i32 [[TMP1]] +// +// +// AMDGPU-LABEL: define internal i32 @__gpu_num_threads_x( +// AMDGPU-SAME: ) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[RETVAL:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr +// AMDGPU-NEXT: [[TMP0:%.*]] = call align 8 dereferenceable(256) ptr addrspace(4) @llvm.amdgcn.implicitarg.ptr() +// AMDGPU-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr addrspace(4) [[TMP0]], i32 12 +// AMDGPU-NEXT: [[TMP2:%.*]] = load i16, ptr addrspace(4) [[TMP1]], align 2, !range [[RNG5]], !invariant.load [[META4]], !noundef [[META4]] +// AMDGPU-NEXT: [[CONV:%.*]] = zext i16 [[TMP2]] to i32 +// AMDGPU-NEXT: ret i32 [[CONV]] +// +// +// AMDGPU-LABEL: define internal i32 @__gpu_num_threads_y( +// AMDGPU-SAME: ) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[RETVAL:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr +// AMDGPU-NEXT: [[TMP0:%.*]] = call align 8 dereferenceable(256) ptr addrspace(4) @llvm.amdgcn.implicitarg.ptr() +// AMDGPU-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr addrspace(4) [[TMP0]], i32 14 +// AMDGPU-NEXT: [[TMP2:%.*]] = load i16, ptr addrspace(4) [[TMP1]], align 2, !range [[RNG5]], !invariant.load [[META4]], !noundef [[META4]] +// AMDGPU-NEXT: [[CONV:%.*]] = zext i16 [[TMP2]] to i32 +// AMDGPU-NEXT: ret i32 [[CONV]] +// +// +// AMDGPU-LABEL: define internal i32 @__gpu_num_threads_z( +// AMDGPU-SAME: ) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[RETVAL:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr +// AMDGPU-NEXT: [[TMP0:%.*]] = call align 8 dereferenceable(256) ptr addrspace(4) @llvm.amdgcn.implicitarg.ptr() +// AMDGPU-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr addrspace(4) [[TMP0]], i32 16 +// AMDGPU-NEXT: [[TMP2:%.*]] = load i16, ptr addrspace(4) [[TMP1]], align 2, !range [[RNG5]], !invariant.load [[META4]], !noundef [[META4]] +// AMDGPU-NEXT: [[CONV:%.*]] = zext i16 [[TMP2]] to i32 +// AMDGPU-NEXT: ret i32 [[CONV]] +// +// +// AMDGPU-LABEL: define internal i32 @__gpu_num_threads( +// AMDGPU-SAME: i32 noundef [[__DIM:%.*]]) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[RETVAL:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[__DIM_ADDR:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr +// AMDGPU-NEXT: [[__DIM_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[__DIM_ADDR]] to ptr +// AMDGPU-NEXT: store i32 [[__DIM]], ptr [[__DIM_ADDR_ASCAST]], align 4 +// AMDGPU-NEXT: [[TMP0:%.*]] = load i32, ptr [[__DIM_ADDR_ASCAST]], align 4 +// AMDGPU-NEXT: switch i32 [[TMP0]], label %[[SW_DEFAULT:.*]] [ +// AMDGPU-NEXT: i32 0, label %[[SW_BB:.*]] +// AMDGPU-NEXT: i32 1, label %[[SW_BB1:.*]] +// AMDGPU-NEXT: i32 2, label %[[SW_BB3:.*]] +// AMDGPU-NEXT: ] +// AMDGPU: [[SW_BB]]: +// AMDGPU-NEXT: [[CALL:%.*]] = call i32 @__gpu_num_threads_x() #[[ATTR7]] +// AMDGPU-NEXT: store i32 [[CALL]], ptr [[RETVAL_ASCAST]], align 4 +// AMDGPU-NEXT: br label %[[RETURN:.*]] +// AMDGPU: [[SW_BB1]]: +// AMDGPU-NEXT: [[CALL2:%.*]] = call i32 @__gpu_num_threads_y() #[[ATTR7]] +// AMDGPU-NEXT: store i32 [[CALL2]], ptr [[RETVAL_ASCAST]], align 4 +// AMDGPU-NEXT: br label %[[RETURN]] +// AMDGPU: [[SW_BB3]]: +// AMDGPU-NEXT: [[CALL4:%.*]] = call i32 @__gpu_num_threads_z() #[[ATTR7]] +// AMDGPU-NEXT: store i32 [[CALL4]], ptr [[RETVAL_ASCAST]], align 4 +// AMDGPU-NEXT: br label %[[RETURN]] +// AMDGPU: [[SW_DEFAULT]]: +// AMDGPU-NEXT: unreachable +// AMDGPU: [[RETURN]]: +// AMDGPU-NEXT: [[TMP1:%.*]] = load i32, ptr [[RETVAL_ASCAST]], align 4 +// AMDGPU-NEXT: ret i32 [[TMP1]] +// +// +// AMDGPU-LABEL: define internal i32 @__gpu_thread_id_x( +// AMDGPU-SAME: ) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[RETVAL:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr +// AMDGPU-NEXT: [[TMP0:%.*]] = call noundef range(i32 0, 1024) i32 @llvm.amdgcn.workitem.id.x() +// AMDGPU-NEXT: ret i32 [[TMP0]] +// +// +// AMDGPU-LABEL: define internal i32 @__gpu_thread_id_y( +// AMDGPU-SAME: ) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[RETVAL:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr +// AMDGPU-NEXT: [[TMP0:%.*]] = call noundef range(i32 0, 1024) i32 @llvm.amdgcn.workitem.id.y() +// AMDGPU-NEXT: ret i32 [[TMP0]] +// +// +// AMDGPU-LABEL: define internal i32 @__gpu_thread_id_z( +// AMDGPU-SAME: ) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[RETVAL:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr +// AMDGPU-NEXT: [[TMP0:%.*]] = call noundef range(i32 0, 1024) i32 @llvm.amdgcn.workitem.id.z() +// AMDGPU-NEXT: ret i32 [[TMP0]] +// +// // AMDGPU-LABEL: define internal i32 @__gpu_thread_id( // AMDGPU-SAME: i32 noundef [[__DIM:%.*]]) #[[ATTR0]] { // AMDGPU-NEXT: [[ENTRY:.*:]] @@ -122,6 +343,375 @@ __gpu_kernel void foo() { // AMDGPU-NEXT: [[TMP1:%.*]] = load i32, ptr [[RETVAL_ASCAST]], align 4 // AMDGPU-NEXT: ret i32 [[TMP1]] // +// +// AMDGPU-LABEL: define internal i32 @__gpu_num_lanes( +// AMDGPU-SAME: ) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[RETVAL:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr +// AMDGPU-NEXT: [[TMP0:%.*]] = call i32 @llvm.amdgcn.wavefrontsize() +// AMDGPU-NEXT: ret i32 [[TMP0]] +// +// +// AMDGPU-LABEL: define internal i32 @__gpu_lane_id( +// AMDGPU-SAME: ) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[RETVAL:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr +// AMDGPU-NEXT: [[TMP0:%.*]] = call i32 @llvm.amdgcn.mbcnt.lo(i32 -1, i32 0) +// AMDGPU-NEXT: [[TMP1:%.*]] = call i32 @llvm.amdgcn.mbcnt.hi(i32 -1, i32 [[TMP0]]) +// AMDGPU-NEXT: ret i32 [[TMP1]] +// +// +// AMDGPU-LABEL: define internal i64 @__gpu_lane_mask( +// AMDGPU-SAME: ) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[RETVAL:%.*]] = alloca i64, align 8, addrspace(5) +// AMDGPU-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr +// AMDGPU-NEXT: [[TMP0:%.*]] = call i64 @llvm.amdgcn.ballot.i64(i1 true) +// AMDGPU-NEXT: ret i64 [[TMP0]] +// +// +// AMDGPU-LABEL: define internal i32 @__gpu_read_first_lane_u32( +// AMDGPU-SAME: i64 noundef [[__LANE_MASK:%.*]], i32 noundef [[__X:%.*]]) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[RETVAL:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[__LANE_MASK_ADDR:%.*]] = alloca i64, align 8, addrspace(5) +// AMDGPU-NEXT: [[__X_ADDR:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr +// AMDGPU-NEXT: [[__LANE_MASK_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[__LANE_MASK_ADDR]] to ptr +// AMDGPU-NEXT: [[__X_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[__X_ADDR]] to ptr +// AMDGPU-NEXT: store i64 [[__LANE_MASK]], ptr [[__LANE_MASK_ADDR_ASCAST]], align 8 +// AMDGPU-NEXT: store i32 [[__X]], ptr [[__X_ADDR_ASCAST]], align 4 +// AMDGPU-NEXT: [[TMP0:%.*]] = load i32, ptr [[__X_ADDR_ASCAST]], align 4 +// AMDGPU-NEXT: [[TMP1:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[TMP0]]) +// AMDGPU-NEXT: ret i32 [[TMP1]] +// +// +// AMDGPU-LABEL: define internal i64 @__gpu_ballot( +// AMDGPU-SAME: i64 noundef [[__LANE_MASK:%.*]], i1 noundef zeroext [[__X:%.*]]) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[RETVAL:%.*]] = alloca i64, align 8, addrspace(5) +// AMDGPU-NEXT: [[__LANE_MASK_ADDR:%.*]] = alloca i64, align 8, addrspace(5) +// AMDGPU-NEXT: [[__X_ADDR:%.*]] = alloca i8, align 1, addrspace(5) +// AMDGPU-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr +// AMDGPU-NEXT: [[__LANE_MASK_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[__LANE_MASK_ADDR]] to ptr +// AMDGPU-NEXT: [[__X_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[__X_ADDR]] to ptr +// AMDGPU-NEXT: store i64 [[__LANE_MASK]], ptr [[__LANE_MASK_ADDR_ASCAST]], align 8 +// AMDGPU-NEXT: [[STOREDV:%.*]] = zext i1 [[__X]] to i8 +// AMDGPU-NEXT: store i8 [[STOREDV]], ptr [[__X_ADDR_ASCAST]], align 1 +// AMDGPU-NEXT: [[TMP0:%.*]] = load i64, ptr [[__LANE_MASK_ADDR_ASCAST]], align 8 +// AMDGPU-NEXT: [[TMP1:%.*]] = load i8, ptr [[__X_ADDR_ASCAST]], align 1 +// AMDGPU-NEXT: [[LOADEDV:%.*]] = trunc i8 [[TMP1]] to i1 +// AMDGPU-NEXT: [[TMP2:%.*]] = call i64 @llvm.amdgcn.ballot.i64(i1 [[LOADEDV]]) +// AMDGPU-NEXT: [[AND:%.*]] = and i64 [[TMP0]], [[TMP2]] +// AMDGPU-NEXT: ret i64 [[AND]] +// +// +// AMDGPU-LABEL: define internal void @__gpu_sync_threads( +// AMDGPU-SAME: ) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: call void @llvm.amdgcn.s.barrier() +// AMDGPU-NEXT: fence syncscope("workgroup") seq_cst +// AMDGPU-NEXT: ret void +// +// +// AMDGPU-LABEL: define internal void @__gpu_sync_lane( +// AMDGPU-SAME: i64 noundef [[__LANE_MASK:%.*]]) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[__LANE_MASK_ADDR:%.*]] = alloca i64, align 8, addrspace(5) +// AMDGPU-NEXT: [[__LANE_MASK_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[__LANE_MASK_ADDR]] to ptr +// AMDGPU-NEXT: store i64 [[__LANE_MASK]], ptr [[__LANE_MASK_ADDR_ASCAST]], align 8 +// AMDGPU-NEXT: call void @llvm.amdgcn.wave.barrier() +// AMDGPU-NEXT: ret void +// +// +// AMDGPU-LABEL: define internal i32 @__gpu_shuffle_idx_u32( +// AMDGPU-SAME: i64 noundef [[__LANE_MASK:%.*]], i32 noundef [[__IDX:%.*]], i32 noundef [[__X:%.*]], i32 noundef [[__WIDTH:%.*]]) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[RETVAL:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[__LANE_MASK_ADDR:%.*]] = alloca i64, align 8, addrspace(5) +// AMDGPU-NEXT: [[__IDX_ADDR:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[__X_ADDR:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[__WIDTH_ADDR:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[__LANE:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGPU-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr +// AMDGPU-NEXT: [[__LANE_MASK_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[__LANE_MASK_ADDR]] to ptr +// AMDGPU-NEXT: [[__IDX_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[__IDX_ADDR]] to ptr +// AMDGPU-NEXT: [[__X_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[__X_ADDR]] to ptr +// AMDGPU-NEXT: [[__WIDTH_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[__WIDTH_ADDR]] to ptr +// AMDGPU-NEXT: [[__LANE_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[__LANE]] to ptr +// AMDGPU-NEXT: store i64 [[__LANE_MASK]], ptr [[__LANE_MASK_ADDR_ASCAST]], align 8 +// AMDGPU-NEXT: store i32 [[__IDX]], ptr [[__IDX_ADDR_ASCAST]], align 4 +// AMDGPU-NEXT: store i32 [[__X]], ptr [[__X_ADDR_ASCAST]], align 4 +// AMDGPU-NEXT: store i32 [[__WIDTH]], ptr [[__WIDTH_ADDR_ASCAST]], align 4 +// AMDGPU-NEXT: [[TMP0:%.*]] = load i32, ptr [[__IDX_ADDR_ASCAST]], align 4 +// AMDGPU-NEXT: [[CALL:%.*]] = call i32 @__gpu_lane_id() #[[ATTR7]] +// AMDGPU-NEXT: [[TMP1:%.*]] = load i32, ptr [[__WIDTH_ADDR_ASCAST]], align 4 +// AMDGPU-NEXT: [[SUB:%.*]] = sub i32 [[TMP1]], 1 +// AMDGPU-NEXT: [[NOT:%.*]] = xor i32 [[SUB]], -1 +// AMDGPU-NEXT: [[AND:%.*]] = and i32 [[CALL]], [[NOT]] +// AMDGPU-NEXT: [[ADD:%.*]] = add i32 [[TMP0]], [[AND]] +// AMDGPU-NEXT: store i32 [[ADD]], ptr [[__LANE_ASCAST]], align 4 +// AMDGPU-NEXT: [[TMP2:%.*]] = load i32, ptr [[__LANE_ASCAST]], align 4 +// AMDGPU-NEXT: [[SHL:%.*]] = shl i32 [[TMP2]], 2 +// AMDGPU-NEXT: [[TMP3:%.*]] = load i32, ptr [[__X_ADDR_ASCAST]], align 4 +// AMDGPU-NEXT: [[TMP4:%.*]] = call i32 @llvm.amdgcn.ds.bpermute(i32 [[SHL]], i32 [[TMP3]]) +// AMDGPU-NEXT: ret i32 [[TMP4]] +// +// +// AMDGPU-LABEL: define internal i64 @__gpu_first_lane_id( +// AMDGPU-SAME: i64 noundef [[__LANE_MASK:%.*]]) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[RETVAL:%.*]] = alloca i64, align 8, addrspace(5) +// AMDGPU-NEXT: [[__LANE_MASK_ADDR:%.*]] = alloca i64, align 8, addrspace(5) +// AMDGPU-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr +// AMDGPU-NEXT: [[__LANE_MASK_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[__LANE_MASK_ADDR]] to ptr +// AMDGPU-NEXT: store i64 [[__LANE_MASK]], ptr [[__LANE_MASK_ADDR_ASCAST]], align 8 +// AMDGPU-NEXT: [[TMP0:%.*]] = load i64, ptr [[__LANE_MASK_ADDR_ASCAST]], align 8 +// AMDGPU-NEXT: [[TMP1:%.*]] = call i64 @llvm.cttz.i64(i64 [[TMP0]], i1 true) +// AMDGPU-NEXT: [[TMP2:%.*]] = add i64 [[TMP1]], 1 +// AMDGPU-NEXT: [[ISZERO:%.*]] = icmp eq i64 [[TMP0]], 0 +// AMDGPU-NEXT: [[FFS:%.*]] = select i1 [[ISZERO]], i64 0, i64 [[TMP2]] +// AMDGPU-NEXT: [[CAST:%.*]] = trunc i64 [[FFS]] to i32 +// AMDGPU-NEXT: [[SUB:%.*]] = sub nsw i32 [[CAST]], 1 +// AMDGPU-NEXT: [[CONV:%.*]] = sext i32 [[SUB]] to i64 +// AMDGPU-NEXT: ret i64 [[CONV]] +// +// +// AMDGPU-LABEL: define internal zeroext i1 @__gpu_is_first_in_lane( +// AMDGPU-SAME: i64 noundef [[__LANE_MASK:%.*]]) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[RETVAL:%.*]] = alloca i1, align 1, addrspace(5) +// AMDGPU-NEXT: [[__LANE_MASK_ADDR:%.*]] = alloca i64, align 8, addrspace(5) +// AMDGPU-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr +// AMDGPU-NEXT: [[__LANE_MASK_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[__LANE_MASK_ADDR]] to ptr +// AMDGPU-NEXT: store i64 [[__LANE_MASK]], ptr [[__LANE_MASK_ADDR_ASCAST]], align 8 +// AMDGPU-NEXT: [[CALL:%.*]] = call i32 @__gpu_lane_id() #[[ATTR7]] +// AMDGPU-NEXT: [[CONV:%.*]] = zext i32 [[CALL]] to i64 +// AMDGPU-NEXT: [[TMP0:%.*]] = load i64, ptr [[__LANE_MASK_ADDR_ASCAST]], align 8 +// AMDGPU-NEXT: [[CALL1:%.*]] = call i64 @__gpu_first_lane_id(i64 noundef [[TMP0]]) #[[ATTR7]] +// AMDGPU-NEXT: [[CMP:%.*]] = icmp eq i64 [[CONV]], [[CALL1]] +// AMDGPU-NEXT: ret i1 [[CMP]] +// +// +// AMDGPU-LABEL: define internal void @__gpu_exit( +// AMDGPU-SAME: ) #[[ATTR1:[0-9]+]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: call void @llvm.amdgcn.endpgm() +// AMDGPU-NEXT: ret void +// +// +// NVPTX-LABEL: define protected ptx_kernel void @foo( +// NVPTX-SAME: ) #[[ATTR0:[0-9]+]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[CALL:%.*]] = call i32 @__gpu_num_blocks_x() #[[ATTR6:[0-9]+]] +// NVPTX-NEXT: [[CALL1:%.*]] = call i32 @__gpu_num_blocks_y() #[[ATTR6]] +// NVPTX-NEXT: [[CALL2:%.*]] = call i32 @__gpu_num_blocks_z() #[[ATTR6]] +// NVPTX-NEXT: [[CALL3:%.*]] = call i32 @__gpu_num_blocks(i32 noundef 0) #[[ATTR6]] +// NVPTX-NEXT: [[CALL4:%.*]] = call i32 @__gpu_block_id_x() #[[ATTR6]] +// NVPTX-NEXT: [[CALL5:%.*]] = call i32 @__gpu_block_id_y() #[[ATTR6]] +// NVPTX-NEXT: [[CALL6:%.*]] = call i32 @__gpu_block_id_z() #[[ATTR6]] +// NVPTX-NEXT: [[CALL7:%.*]] = call i32 @__gpu_block_id(i32 noundef 0) #[[ATTR6]] +// NVPTX-NEXT: [[CALL8:%.*]] = call i32 @__gpu_num_threads_x() #[[ATTR6]] +// NVPTX-NEXT: [[CALL9:%.*]] = call i32 @__gpu_num_threads_y() #[[ATTR6]] +// NVPTX-NEXT: [[CALL10:%.*]] = call i32 @__gpu_num_threads_z() #[[ATTR6]] +// NVPTX-NEXT: [[CALL11:%.*]] = call i32 @__gpu_num_threads(i32 noundef 0) #[[ATTR6]] +// NVPTX-NEXT: [[CALL12:%.*]] = call i32 @__gpu_thread_id_x() #[[ATTR6]] +// NVPTX-NEXT: [[CALL13:%.*]] = call i32 @__gpu_thread_id_y() #[[ATTR6]] +// NVPTX-NEXT: [[CALL14:%.*]] = call i32 @__gpu_thread_id_z() #[[ATTR6]] +// NVPTX-NEXT: [[CALL15:%.*]] = call i32 @__gpu_thread_id(i32 noundef 0) #[[ATTR6]] +// NVPTX-NEXT: [[CALL16:%.*]] = call i32 @__gpu_num_lanes() #[[ATTR6]] +// NVPTX-NEXT: [[CALL17:%.*]] = call i32 @__gpu_lane_id() #[[ATTR6]] +// NVPTX-NEXT: [[CALL18:%.*]] = call i64 @__gpu_lane_mask() #[[ATTR6]] +// NVPTX-NEXT: [[CALL19:%.*]] = call i32 @__gpu_read_first_lane_u32(i64 noundef -1, i32 noundef -1) #[[ATTR6]] +// NVPTX-NEXT: [[CALL20:%.*]] = call i64 @__gpu_ballot(i64 noundef -1, i1 noundef zeroext true) #[[ATTR6]] +// NVPTX-NEXT: call void @__gpu_sync_threads() #[[ATTR6]] +// NVPTX-NEXT: call void @__gpu_sync_lane(i64 noundef -1) #[[ATTR6]] +// NVPTX-NEXT: [[CALL21:%.*]] = call i32 @__gpu_shuffle_idx_u32(i64 noundef -1, i32 noundef -1, i32 noundef -1, i32 noundef 0) #[[ATTR6]] +// NVPTX-NEXT: [[CALL22:%.*]] = call i64 @__gpu_first_lane_id(i64 noundef -1) #[[ATTR6]] +// NVPTX-NEXT: [[CALL23:%.*]] = call zeroext i1 @__gpu_is_first_in_lane(i64 noundef -1) #[[ATTR6]] +// NVPTX-NEXT: call void @__gpu_exit() #[[ATTR7:[0-9]+]] +// NVPTX-NEXT: unreachable +// +// +// NVPTX-LABEL: define internal i32 @__gpu_num_blocks_x( +// NVPTX-SAME: ) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[TMP0:%.*]] = call i32 @llvm.nvvm.read.ptx.sreg.nctaid.x() +// NVPTX-NEXT: ret i32 [[TMP0]] +// +// +// NVPTX-LABEL: define internal i32 @__gpu_num_blocks_y( +// NVPTX-SAME: ) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[TMP0:%.*]] = call i32 @llvm.nvvm.read.ptx.sreg.nctaid.y() +// NVPTX-NEXT: ret i32 [[TMP0]] +// +// +// NVPTX-LABEL: define internal i32 @__gpu_num_blocks_z( +// NVPTX-SAME: ) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[TMP0:%.*]] = call i32 @llvm.nvvm.read.ptx.sreg.nctaid.z() +// NVPTX-NEXT: ret i32 [[TMP0]] +// +// +// NVPTX-LABEL: define internal i32 @__gpu_num_blocks( +// NVPTX-SAME: i32 noundef [[__DIM:%.*]]) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// NVPTX-NEXT: [[__DIM_ADDR:%.*]] = alloca i32, align 4 +// NVPTX-NEXT: store i32 [[__DIM]], ptr [[__DIM_ADDR]], align 4 +// NVPTX-NEXT: [[TMP0:%.*]] = load i32, ptr [[__DIM_ADDR]], align 4 +// NVPTX-NEXT: switch i32 [[TMP0]], label %[[SW_DEFAULT:.*]] [ +// NVPTX-NEXT: i32 0, label %[[SW_BB:.*]] +// NVPTX-NEXT: i32 1, label %[[SW_BB1:.*]] +// NVPTX-NEXT: i32 2, label %[[SW_BB3:.*]] +// NVPTX-NEXT: ] +// NVPTX: [[SW_BB]]: +// NVPTX-NEXT: [[CALL:%.*]] = call i32 @__gpu_num_blocks_x() #[[ATTR6]] +// NVPTX-NEXT: store i32 [[CALL]], ptr [[RETVAL]], align 4 +// NVPTX-NEXT: br label %[[RETURN:.*]] +// NVPTX: [[SW_BB1]]: +// NVPTX-NEXT: [[CALL2:%.*]] = call i32 @__gpu_num_blocks_y() #[[ATTR6]] +// NVPTX-NEXT: store i32 [[CALL2]], ptr [[RETVAL]], align 4 +// NVPTX-NEXT: br label %[[RETURN]] +// NVPTX: [[SW_BB3]]: +// NVPTX-NEXT: [[CALL4:%.*]] = call i32 @__gpu_num_blocks_z() #[[ATTR6]] +// NVPTX-NEXT: store i32 [[CALL4]], ptr [[RETVAL]], align 4 +// NVPTX-NEXT: br label %[[RETURN]] +// NVPTX: [[SW_DEFAULT]]: +// NVPTX-NEXT: unreachable +// NVPTX: [[RETURN]]: +// NVPTX-NEXT: [[TMP1:%.*]] = load i32, ptr [[RETVAL]], align 4 +// NVPTX-NEXT: ret i32 [[TMP1]] +// +// +// NVPTX-LABEL: define internal i32 @__gpu_block_id_x( +// NVPTX-SAME: ) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[TMP0:%.*]] = call i32 @llvm.nvvm.read.ptx.sreg.ctaid.x() +// NVPTX-NEXT: ret i32 [[TMP0]] +// +// +// NVPTX-LABEL: define internal i32 @__gpu_block_id_y( +// NVPTX-SAME: ) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[TMP0:%.*]] = call i32 @llvm.nvvm.read.ptx.sreg.ctaid.y() +// NVPTX-NEXT: ret i32 [[TMP0]] +// +// +// NVPTX-LABEL: define internal i32 @__gpu_block_id_z( +// NVPTX-SAME: ) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[TMP0:%.*]] = call i32 @llvm.nvvm.read.ptx.sreg.ctaid.z() +// NVPTX-NEXT: ret i32 [[TMP0]] +// +// +// NVPTX-LABEL: define internal i32 @__gpu_block_id( +// NVPTX-SAME: i32 noundef [[__DIM:%.*]]) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// NVPTX-NEXT: [[__DIM_ADDR:%.*]] = alloca i32, align 4 +// NVPTX-NEXT: store i32 [[__DIM]], ptr [[__DIM_ADDR]], align 4 +// NVPTX-NEXT: [[TMP0:%.*]] = load i32, ptr [[__DIM_ADDR]], align 4 +// NVPTX-NEXT: switch i32 [[TMP0]], label %[[SW_DEFAULT:.*]] [ +// NVPTX-NEXT: i32 0, label %[[SW_BB:.*]] +// NVPTX-NEXT: i32 1, label %[[SW_BB1:.*]] +// NVPTX-NEXT: i32 2, label %[[SW_BB3:.*]] +// NVPTX-NEXT: ] +// NVPTX: [[SW_BB]]: +// NVPTX-NEXT: [[CALL:%.*]] = call i32 @__gpu_block_id_x() #[[ATTR6]] +// NVPTX-NEXT: store i32 [[CALL]], ptr [[RETVAL]], align 4 +// NVPTX-NEXT: br label %[[RETURN:.*]] +// NVPTX: [[SW_BB1]]: +// NVPTX-NEXT: [[CALL2:%.*]] = call i32 @__gpu_block_id_y() #[[ATTR6]] +// NVPTX-NEXT: store i32 [[CALL2]], ptr [[RETVAL]], align 4 +// NVPTX-NEXT: br label %[[RETURN]] +// NVPTX: [[SW_BB3]]: +// NVPTX-NEXT: [[CALL4:%.*]] = call i32 @__gpu_block_id_z() #[[ATTR6]] +// NVPTX-NEXT: store i32 [[CALL4]], ptr [[RETVAL]], align 4 +// NVPTX-NEXT: br label %[[RETURN]] +// NVPTX: [[SW_DEFAULT]]: +// NVPTX-NEXT: unreachable +// NVPTX: [[RETURN]]: +// NVPTX-NEXT: [[TMP1:%.*]] = load i32, ptr [[RETVAL]], align 4 +// NVPTX-NEXT: ret i32 [[TMP1]] +// +// +// NVPTX-LABEL: define internal i32 @__gpu_num_threads_x( +// NVPTX-SAME: ) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[TMP0:%.*]] = call i32 @llvm.nvvm.read.ptx.sreg.ntid.x() +// NVPTX-NEXT: ret i32 [[TMP0]] +// +// +// NVPTX-LABEL: define internal i32 @__gpu_num_threads_y( +// NVPTX-SAME: ) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[TMP0:%.*]] = call i32 @llvm.nvvm.read.ptx.sreg.ntid.y() +// NVPTX-NEXT: ret i32 [[TMP0]] +// +// +// NVPTX-LABEL: define internal i32 @__gpu_num_threads_z( +// NVPTX-SAME: ) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[TMP0:%.*]] = call i32 @llvm.nvvm.read.ptx.sreg.ntid.z() +// NVPTX-NEXT: ret i32 [[TMP0]] +// +// +// NVPTX-LABEL: define internal i32 @__gpu_num_threads( +// NVPTX-SAME: i32 noundef [[__DIM:%.*]]) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// NVPTX-NEXT: [[__DIM_ADDR:%.*]] = alloca i32, align 4 +// NVPTX-NEXT: store i32 [[__DIM]], ptr [[__DIM_ADDR]], align 4 +// NVPTX-NEXT: [[TMP0:%.*]] = load i32, ptr [[__DIM_ADDR]], align 4 +// NVPTX-NEXT: switch i32 [[TMP0]], label %[[SW_DEFAULT:.*]] [ +// NVPTX-NEXT: i32 0, label %[[SW_BB:.*]] +// NVPTX-NEXT: i32 1, label %[[SW_BB1:.*]] +// NVPTX-NEXT: i32 2, label %[[SW_BB3:.*]] +// NVPTX-NEXT: ] +// NVPTX: [[SW_BB]]: +// NVPTX-NEXT: [[CALL:%.*]] = call i32 @__gpu_num_threads_x() #[[ATTR6]] +// NVPTX-NEXT: store i32 [[CALL]], ptr [[RETVAL]], align 4 +// NVPTX-NEXT: br label %[[RETURN:.*]] +// NVPTX: [[SW_BB1]]: +// NVPTX-NEXT: [[CALL2:%.*]] = call i32 @__gpu_num_threads_y() #[[ATTR6]] +// NVPTX-NEXT: store i32 [[CALL2]], ptr [[RETVAL]], align 4 +// NVPTX-NEXT: br label %[[RETURN]] +// NVPTX: [[SW_BB3]]: +// NVPTX-NEXT: [[CALL4:%.*]] = call i32 @__gpu_num_threads_z() #[[ATTR6]] +// NVPTX-NEXT: store i32 [[CALL4]], ptr [[RETVAL]], align 4 +// NVPTX-NEXT: br label %[[RETURN]] +// NVPTX: [[SW_DEFAULT]]: +// NVPTX-NEXT: unreachable +// NVPTX: [[RETURN]]: +// NVPTX-NEXT: [[TMP1:%.*]] = load i32, ptr [[RETVAL]], align 4 +// NVPTX-NEXT: ret i32 [[TMP1]] +// +// +// NVPTX-LABEL: define internal i32 @__gpu_thread_id_x( +// NVPTX-SAME: ) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[TMP0:%.*]] = call i32 @llvm.nvvm.read.ptx.sreg.tid.x() +// NVPTX-NEXT: ret i32 [[TMP0]] +// +// +// NVPTX-LABEL: define internal i32 @__gpu_thread_id_y( +// NVPTX-SAME: ) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[TMP0:%.*]] = call i32 @llvm.nvvm.read.ptx.sreg.tid.y() +// NVPTX-NEXT: ret i32 [[TMP0]] +// +// +// NVPTX-LABEL: define internal i32 @__gpu_thread_id_z( +// NVPTX-SAME: ) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[TMP0:%.*]] = call i32 @llvm.nvvm.read.ptx.sreg.tid.z() +// NVPTX-NEXT: ret i32 [[TMP0]] +// +// // NVPTX-LABEL: define internal i32 @__gpu_thread_id( // NVPTX-SAME: i32 noundef [[__DIM:%.*]]) #[[ATTR0]] { // NVPTX-NEXT: [[ENTRY:.*:]] @@ -152,18 +742,173 @@ __gpu_kernel void foo() { // NVPTX-NEXT: [[TMP1:%.*]] = load i32, ptr [[RETVAL]], align 4 // NVPTX-NEXT: ret i32 [[TMP1]] // - __gpu_thread_id_y(); - __gpu_thread_id_z(); - __gpu_thread_id(0); - __gpu_num_lanes(); - __gpu_lane_id(); - __gpu_lane_mask(); - __gpu_read_first_lane_u32(-1, -1); - __gpu_ballot(-1, 1); - __gpu_sync_threads(); - __gpu_sync_lane(-1); - __gpu_shuffle_idx_u32(-1, -1, -1, 0); - __gpu_first_lane_id(-1); - __gpu_is_first_in_lane(-1); - __gpu_exit(); -} +// +// NVPTX-LABEL: define internal i32 @__gpu_num_lanes( +// NVPTX-SAME: ) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[TMP0:%.*]] = call i32 @llvm.nvvm.read.ptx.sreg.warpsize() +// NVPTX-NEXT: ret i32 [[TMP0]] +// +// +// NVPTX-LABEL: define internal i32 @__gpu_lane_id( +// NVPTX-SAME: ) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[TMP0:%.*]] = call i32 @llvm.nvvm.read.ptx.sreg.laneid() +// NVPTX-NEXT: ret i32 [[TMP0]] +// +// +// NVPTX-LABEL: define internal i64 @__gpu_lane_mask( +// NVPTX-SAME: ) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[TMP0:%.*]] = call i32 @llvm.nvvm.activemask() +// NVPTX-NEXT: [[CONV:%.*]] = zext i32 [[TMP0]] to i64 +// NVPTX-NEXT: ret i64 [[CONV]] +// +// +// NVPTX-LABEL: define internal i32 @__gpu_read_first_lane_u32( +// NVPTX-SAME: i64 noundef [[__LANE_MASK:%.*]], i32 noundef [[__X:%.*]]) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[__LANE_MASK_ADDR:%.*]] = alloca i64, align 8 +// NVPTX-NEXT: [[__X_ADDR:%.*]] = alloca i32, align 4 +// NVPTX-NEXT: [[__MASK:%.*]] = alloca i32, align 4 +// NVPTX-NEXT: [[__ID:%.*]] = alloca i32, align 4 +// NVPTX-NEXT: store i64 [[__LANE_MASK]], ptr [[__LANE_MASK_ADDR]], align 8 +// NVPTX-NEXT: store i32 [[__X]], ptr [[__X_ADDR]], align 4 +// NVPTX-NEXT: [[TMP0:%.*]] = load i64, ptr [[__LANE_MASK_ADDR]], align 8 +// NVPTX-NEXT: [[CONV:%.*]] = trunc i64 [[TMP0]] to i32 +// NVPTX-NEXT: store i32 [[CONV]], ptr [[__MASK]], align 4 +// NVPTX-NEXT: [[TMP1:%.*]] = load i32, ptr [[__MASK]], align 4 +// NVPTX-NEXT: [[TMP2:%.*]] = call i32 @llvm.cttz.i32(i32 [[TMP1]], i1 true) +// NVPTX-NEXT: [[TMP3:%.*]] = add i32 [[TMP2]], 1 +// NVPTX-NEXT: [[ISZERO:%.*]] = icmp eq i32 [[TMP1]], 0 +// NVPTX-NEXT: [[FFS:%.*]] = select i1 [[ISZERO]], i32 0, i32 [[TMP3]] +// NVPTX-NEXT: [[SUB:%.*]] = sub nsw i32 [[FFS]], 1 +// NVPTX-NEXT: store i32 [[SUB]], ptr [[__ID]], align 4 +// NVPTX-NEXT: [[TMP4:%.*]] = load i32, ptr [[__MASK]], align 4 +// NVPTX-NEXT: [[TMP5:%.*]] = load i32, ptr [[__X_ADDR]], align 4 +// NVPTX-NEXT: [[TMP6:%.*]] = load i32, ptr [[__ID]], align 4 +// NVPTX-NEXT: [[CALL:%.*]] = call i32 @__gpu_num_lanes() #[[ATTR6]] +// NVPTX-NEXT: [[SUB1:%.*]] = sub i32 [[CALL]], 1 +// NVPTX-NEXT: [[TMP7:%.*]] = call i32 @llvm.nvvm.shfl.sync.idx.i32(i32 [[TMP4]], i32 [[TMP5]], i32 [[TMP6]], i32 [[SUB1]]) +// NVPTX-NEXT: ret i32 [[TMP7]] +// +// +// NVPTX-LABEL: define internal i64 @__gpu_ballot( +// NVPTX-SAME: i64 noundef [[__LANE_MASK:%.*]], i1 noundef zeroext [[__X:%.*]]) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[__LANE_MASK_ADDR:%.*]] = alloca i64, align 8 +// NVPTX-NEXT: [[__X_ADDR:%.*]] = alloca i8, align 1 +// NVPTX-NEXT: [[__MASK:%.*]] = alloca i32, align 4 +// NVPTX-NEXT: store i64 [[__LANE_MASK]], ptr [[__LANE_MASK_ADDR]], align 8 +// NVPTX-NEXT: [[STOREDV:%.*]] = zext i1 [[__X]] to i8 +// NVPTX-NEXT: store i8 [[STOREDV]], ptr [[__X_ADDR]], align 1 +// NVPTX-NEXT: [[TMP0:%.*]] = load i64, ptr [[__LANE_MASK_ADDR]], align 8 +// NVPTX-NEXT: [[CONV:%.*]] = trunc i64 [[TMP0]] to i32 +// NVPTX-NEXT: store i32 [[CONV]], ptr [[__MASK]], align 4 +// NVPTX-NEXT: [[TMP1:%.*]] = load i32, ptr [[__MASK]], align 4 +// NVPTX-NEXT: [[TMP2:%.*]] = load i8, ptr [[__X_ADDR]], align 1 +// NVPTX-NEXT: [[LOADEDV:%.*]] = trunc i8 [[TMP2]] to i1 +// NVPTX-NEXT: [[TMP3:%.*]] = call i32 @llvm.nvvm.vote.ballot.sync(i32 [[TMP1]], i1 [[LOADEDV]]) +// NVPTX-NEXT: [[CONV1:%.*]] = zext i32 [[TMP3]] to i64 +// NVPTX-NEXT: ret i64 [[CONV1]] +// +// +// NVPTX-LABEL: define internal void @__gpu_sync_threads( +// NVPTX-SAME: ) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: call void @llvm.nvvm.barrier0() +// NVPTX-NEXT: ret void +// +// +// NVPTX-LABEL: define internal void @__gpu_sync_lane( +// NVPTX-SAME: i64 noundef [[__LANE_MASK:%.*]]) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[__LANE_MASK_ADDR:%.*]] = alloca i64, align 8 +// NVPTX-NEXT: store i64 [[__LANE_MASK]], ptr [[__LANE_MASK_ADDR]], align 8 +// NVPTX-NEXT: [[TMP0:%.*]] = load i64, ptr [[__LANE_MASK_ADDR]], align 8 +// NVPTX-NEXT: [[CONV:%.*]] = trunc i64 [[TMP0]] to i32 +// NVPTX-NEXT: call void @llvm.nvvm.bar.warp.sync(i32 [[CONV]]) +// NVPTX-NEXT: ret void +// +// +// NVPTX-LABEL: define internal i32 @__gpu_shuffle_idx_u32( +// NVPTX-SAME: i64 noundef [[__LANE_MASK:%.*]], i32 noundef [[__IDX:%.*]], i32 noundef [[__X:%.*]], i32 noundef [[__WIDTH:%.*]]) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[__LANE_MASK_ADDR:%.*]] = alloca i64, align 8 +// NVPTX-NEXT: [[__IDX_ADDR:%.*]] = alloca i32, align 4 +// NVPTX-NEXT: [[__X_ADDR:%.*]] = alloca i32, align 4 +// NVPTX-NEXT: [[__WIDTH_ADDR:%.*]] = alloca i32, align 4 +// NVPTX-NEXT: [[__MASK:%.*]] = alloca i32, align 4 +// NVPTX-NEXT: [[__BITMASK:%.*]] = alloca i8, align 1 +// NVPTX-NEXT: store i64 [[__LANE_MASK]], ptr [[__LANE_MASK_ADDR]], align 8 +// NVPTX-NEXT: store i32 [[__IDX]], ptr [[__IDX_ADDR]], align 4 +// NVPTX-NEXT: store i32 [[__X]], ptr [[__X_ADDR]], align 4 +// NVPTX-NEXT: store i32 [[__WIDTH]], ptr [[__WIDTH_ADDR]], align 4 +// NVPTX-NEXT: [[TMP0:%.*]] = load i64, ptr [[__LANE_MASK_ADDR]], align 8 +// NVPTX-NEXT: [[CONV:%.*]] = trunc i64 [[TMP0]] to i32 +// NVPTX-NEXT: store i32 [[CONV]], ptr [[__MASK]], align 4 +// NVPTX-NEXT: [[TMP1:%.*]] = load i32, ptr [[__IDX_ADDR]], align 4 +// NVPTX-NEXT: [[SH_PROM:%.*]] = zext i32 [[TMP1]] to i64 +// NVPTX-NEXT: [[SHL:%.*]] = shl i64 1, [[SH_PROM]] +// NVPTX-NEXT: [[TMP2:%.*]] = load i64, ptr [[__LANE_MASK_ADDR]], align 8 +// NVPTX-NEXT: [[AND:%.*]] = and i64 [[SHL]], [[TMP2]] +// NVPTX-NEXT: [[TOBOOL:%.*]] = icmp ne i64 [[AND]], 0 +// NVPTX-NEXT: [[STOREDV:%.*]] = zext i1 [[TOBOOL]] to i8 +// NVPTX-NEXT: store i8 [[STOREDV]], ptr [[__BITMASK]], align 1 +// NVPTX-NEXT: [[TMP3:%.*]] = load i8, ptr [[__BITMASK]], align 1 +// NVPTX-NEXT: [[LOADEDV:%.*]] = trunc i8 [[TMP3]] to i1 +// NVPTX-NEXT: [[CONV1:%.*]] = zext i1 [[LOADEDV]] to i32 +// NVPTX-NEXT: [[SUB:%.*]] = sub nsw i32 0, [[CONV1]] +// NVPTX-NEXT: [[TMP4:%.*]] = load i32, ptr [[__MASK]], align 4 +// NVPTX-NEXT: [[TMP5:%.*]] = load i32, ptr [[__X_ADDR]], align 4 +// NVPTX-NEXT: [[TMP6:%.*]] = load i32, ptr [[__IDX_ADDR]], align 4 +// NVPTX-NEXT: [[CALL:%.*]] = call i32 @__gpu_num_lanes() #[[ATTR6]] +// NVPTX-NEXT: [[TMP7:%.*]] = load i32, ptr [[__WIDTH_ADDR]], align 4 +// NVPTX-NEXT: [[SUB2:%.*]] = sub i32 [[CALL]], [[TMP7]] +// NVPTX-NEXT: [[SHL3:%.*]] = shl i32 [[SUB2]], 8 +// NVPTX-NEXT: [[OR:%.*]] = or i32 [[SHL3]], 31 +// NVPTX-NEXT: [[TMP8:%.*]] = call i32 @llvm.nvvm.shfl.sync.idx.i32(i32 [[TMP4]], i32 [[TMP5]], i32 [[TMP6]], i32 [[OR]]) +// NVPTX-NEXT: [[AND4:%.*]] = and i32 [[SUB]], [[TMP8]] +// NVPTX-NEXT: ret i32 [[AND4]] +// +// +// NVPTX-LABEL: define internal i64 @__gpu_first_lane_id( +// NVPTX-SAME: i64 noundef [[__LANE_MASK:%.*]]) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[__LANE_MASK_ADDR:%.*]] = alloca i64, align 8 +// NVPTX-NEXT: store i64 [[__LANE_MASK]], ptr [[__LANE_MASK_ADDR]], align 8 +// NVPTX-NEXT: [[TMP0:%.*]] = load i64, ptr [[__LANE_MASK_ADDR]], align 8 +// NVPTX-NEXT: [[TMP1:%.*]] = call i64 @llvm.cttz.i64(i64 [[TMP0]], i1 true) +// NVPTX-NEXT: [[TMP2:%.*]] = add i64 [[TMP1]], 1 +// NVPTX-NEXT: [[ISZERO:%.*]] = icmp eq i64 [[TMP0]], 0 +// NVPTX-NEXT: [[FFS:%.*]] = select i1 [[ISZERO]], i64 0, i64 [[TMP2]] +// NVPTX-NEXT: [[CAST:%.*]] = trunc i64 [[FFS]] to i32 +// NVPTX-NEXT: [[SUB:%.*]] = sub nsw i32 [[CAST]], 1 +// NVPTX-NEXT: [[CONV:%.*]] = sext i32 [[SUB]] to i64 +// NVPTX-NEXT: ret i64 [[CONV]] +// +// +// NVPTX-LABEL: define internal zeroext i1 @__gpu_is_first_in_lane( +// NVPTX-SAME: i64 noundef [[__LANE_MASK:%.*]]) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[__LANE_MASK_ADDR:%.*]] = alloca i64, align 8 +// NVPTX-NEXT: store i64 [[__LANE_MASK]], ptr [[__LANE_MASK_ADDR]], align 8 +// NVPTX-NEXT: [[CALL:%.*]] = call i32 @__gpu_lane_id() #[[ATTR6]] +// NVPTX-NEXT: [[CONV:%.*]] = zext i32 [[CALL]] to i64 +// NVPTX-NEXT: [[TMP0:%.*]] = load i64, ptr [[__LANE_MASK_ADDR]], align 8 +// NVPTX-NEXT: [[CALL1:%.*]] = call i64 @__gpu_first_lane_id(i64 noundef [[TMP0]]) #[[ATTR6]] +// NVPTX-NEXT: [[CMP:%.*]] = icmp eq i64 [[CONV]], [[CALL1]] +// NVPTX-NEXT: ret i1 [[CMP]] +// +// +// NVPTX-LABEL: define internal void @__gpu_exit( +// NVPTX-SAME: ) #[[ATTR1:[0-9]+]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: call void @llvm.nvvm.exit() +// NVPTX-NEXT: ret void +// +//. +// AMDGPU: [[RNG3]] = !{i32 1, i32 0} +// AMDGPU: [[META4]] = !{} +// AMDGPU: [[RNG5]] = !{i16 1, i16 1025} +//. diff --git a/clang/test/Misc/integer-literal-printing.cpp b/clang/test/Misc/integer-literal-printing.cpp index bc52b3f044679..bd231a368fb70 100644 --- a/clang/test/Misc/integer-literal-printing.cpp +++ b/clang/test/Misc/integer-literal-printing.cpp @@ -14,7 +14,6 @@ enum class boolTy : bool { template struct Type3Helper; template <> struct Type3Helper { typedef boolTy Ty; }; template ::Ty U> struct Type3 {}; -// expected-note@-1 {{template parameter is declared here}} // PR14386 enum class charTy : char { @@ -24,7 +23,6 @@ enum class charTy : char { template struct Type4Helper; template <> struct Type4Helper { typedef charTy Ty; }; template ::Ty U> struct Type4 {}; -// expected-note@-1 {{template parameter is declared here}} enum class scharTy : signed char { c = 0, @@ -33,7 +31,6 @@ enum class scharTy : signed char { template struct Type5Helper; template <> struct Type5Helper { typedef scharTy Ty; }; template ::Ty U> struct Type5 {}; -// expected-note@-1 {{template parameter is declared here}} enum class ucharTy : unsigned char { c = 0, @@ -42,7 +39,6 @@ enum class ucharTy : unsigned char { template struct Type6Helper; template <> struct Type6Helper { typedef ucharTy Ty; }; template ::Ty U> struct Type6 {}; -// expected-note@-1 {{template parameter is declared here}} enum class wcharTy : wchar_t { c = 0, @@ -51,7 +47,6 @@ enum class wcharTy : wchar_t { template struct Type7Helper; template <> struct Type7Helper { typedef wcharTy Ty; }; template ::Ty U> struct Type7 {}; -// expected-note@-1 {{template parameter is declared here}} enum class char16Ty : char16_t { c = 0, @@ -60,7 +55,6 @@ enum class char16Ty : char16_t { template struct Type8Helper; template <> struct Type8Helper { typedef char16Ty Ty; }; template ::Ty U> struct Type8 {}; -// expected-note@-1 {{template parameter is declared here}} enum class char32Ty : char16_t { c = 0, @@ -69,7 +63,6 @@ enum class char32Ty : char16_t { template struct Type9Helper; template <> struct Type9Helper { typedef char32Ty Ty; }; template ::Ty U> struct Type9 {}; -// expected-note@-1 {{template parameter is declared here}} void Function() { Function1(Type1<-42>()); // expected-error{{no matching function for call to 'Function1'}} diff --git a/clang/test/Modules/malformed-constraint-template-non-type-parm-decl.cpp b/clang/test/Modules/malformed-constraint-template-non-type-parm-decl.cpp index 250b54ade0ecb..73dff88e506b4 100644 --- a/clang/test/Modules/malformed-constraint-template-non-type-parm-decl.cpp +++ b/clang/test/Modules/malformed-constraint-template-non-type-parm-decl.cpp @@ -11,7 +11,7 @@ //--- mod.cppm export module mod; -template // expected-note 2{{template parameter is declared here}} +template concept ReferenceOf = Q; // expected-error@+2 {{unknown type name 'AngleIsInvalidNow'}} diff --git a/clang/test/Modules/missing-body-in-import.cpp b/clang/test/Modules/missing-body-in-import.cpp index e25f7b5921301..b52ebba15087a 100644 --- a/clang/test/Modules/missing-body-in-import.cpp +++ b/clang/test/Modules/missing-body-in-import.cpp @@ -29,7 +29,6 @@ export module mod2; import mod1; struct C: B { // expected-error {{non-type template argument is not a constant expression}} - // expected-note@mod1.cppm:11 {{template parameter is declared here}} constexpr C(int a) { } }; diff --git a/clang/test/Modules/modules-merge-enum.m b/clang/test/Modules/modules-merge-enum.m new file mode 100644 index 0000000000000..f1010c1decc24 --- /dev/null +++ b/clang/test/Modules/modules-merge-enum.m @@ -0,0 +1,111 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t + + +// Expect no crash +// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t/modcache -fmodule-map-file=%t/module.modulemap %t/source.m + +// Add -ast-dump-all to check that the AST nodes are merged correctly. +// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t/modcache -fmodule-map-file=%t/module.modulemap %t/source.m -ast-dump-all 2>&1 | FileCheck %s + + +//--- shared.h +// This header is shared between two modules, but it's not a module itself. +// The enums defined here are parsed in both modules, and merged while building ModB. + +typedef enum MyEnum1 { MyVal_A } MyEnum1; +// CHECK: |-EnumDecl 0x{{.*}} imported in ModA.ModAFile1 MyEnum1 +// CHECK-NEXT: | |-also in ModB +// CHECK-NEXT: | `-EnumConstantDecl 0x{{.*}} imported in ModA.ModAFile1 referenced MyVal_A 'int' +// CHECK-NEXT: |-TypedefDecl 0x{{.*}} imported in ModA.ModAFile1 hidden MyEnum1 'enum MyEnum1' +// CHECK-NEXT: | `-ElaboratedType 0x{{.*}} 'enum MyEnum1' sugar imported +// CHECK-NEXT: | `-EnumType 0x{{.*}} 'enum MyEnum1' imported +// CHECK-NEXT: | `-Enum 0x{{.*}} 'MyEnum1' + + +enum MyEnum2 { MyVal_B }; +// CHECK: |-EnumDecl 0x{{.*}} imported in ModA.ModAFile1 MyEnum2 +// CHECK-NEXT: | |-also in ModB +// CHECK-NEXT: | `-EnumConstantDecl 0x{{.*}} imported in ModA.ModAFile1 referenced MyVal_B 'int' + + +typedef enum { MyVal_C } MyEnum3; +// CHECK: |-EnumDecl 0x{{.*}} imported in ModA.ModAFile1 +// CHECK-NEXT: | |-also in ModB +// CHECK-NEXT: | `-EnumConstantDecl 0x{{.*}} imported in ModA.ModAFile1 referenced MyVal_C 'int' +// CHECK-NEXT: |-TypedefDecl 0x{{.*}} imported in ModA.ModAFile1 hidden MyEnum3 'enum MyEnum3':'MyEnum3' +// CHECK-NEXT: | `-ElaboratedType 0x{{.*}} 'enum MyEnum3' sugar imported +// CHECK-NEXT: | `-EnumType 0x{{.*}} 'MyEnum3' imported +// CHECK-NEXT: | `-Enum 0x{{.*}} + +struct MyStruct { + enum MyEnum5 { MyVal_D } Field; +}; + +// CHECK: |-RecordDecl 0x{{.*}} imported in ModA.ModAFile1 struct MyStruct definition +// CHECK-NEXT: | |-also in ModB +// CHECK-NEXT: | |-EnumDecl 0x{{.*}} imported in ModA.ModAFile1 MyEnum5 +// CHECK-NEXT: | | |-also in ModB +// CHECK-NEXT: | | `-EnumConstantDecl 0x{{.*}} imported in ModA.ModAFile1 referenced MyVal_D 'int' +// CHECK-NEXT: | `-FieldDecl 0x{{.*}} imported in ModA.ModAFile1 hidden Field 'enum MyEnum5' + +// In this case, no merging happens on the EnumDecl in Objective-C, and ASTWriter writes both EnumConstantDecls when building ModB. +enum { MyVal_E }; +// CHECK: |-EnumDecl 0x{{.*}} imported in ModA.ModAFile1 hidden +// CHECK-NEXT: | `-EnumConstantDecl 0x{{.*}} imported in ModA.ModAFile1 hidden MyVal_E 'int' + + +// Redeclarations coming from ModB. +// CHECK: |-TypedefDecl 0x{{.*}} prev 0x{{.*}} imported in ModB MyEnum1 'enum MyEnum1' +// CHECK-NEXT: | `-ElaboratedType 0x{{.*}} 'enum MyEnum1' sugar imported +// CHECK-NEXT: | `-EnumType 0x{{.*}} 'enum MyEnum1' imported +// CHECK-NEXT: | `-Enum 0x{{.*}} 'MyEnum1' + +// CHECK: |-EnumDecl 0x{{.*}} prev 0x{{.*}} imported in ModB +// CHECK-NEXT: | |-also in ModB +// CHECK-NEXT: | `-EnumConstantDecl 0x{{.*}} imported in ModB MyVal_C 'int' +// CHECK-NEXT: |-TypedefDecl 0x{{.*}} prev 0x{{.*}} imported in ModB MyEnum3 'enum MyEnum3':'MyEnum3' +// CHECK-NEXT: | `-ElaboratedType 0x{{.*}} 'enum MyEnum3' sugar imported +// CHECK-NEXT: | `-EnumType 0x{{.*}} 'MyEnum3' imported +// CHECK-NEXT: | `-Enum 0x{{.*}} + +// CHECK: |-EnumDecl 0x{{.*}} imported in ModB +// CHECK-NEXT: | `-EnumConstantDecl 0x{{.*}} first 0x{{.*}} imported in ModB referenced MyVal_E 'int' + + + +//--- module.modulemap +module ModA { + module ModAFile1 { + header "ModAFile1.h" + export * + } + module ModAFile2 { + header "ModAFile2.h" + export * + } +} +// The goal of writing ModB is to test that ASTWriter can handle the merged AST nodes correctly. +// For example, a stale declaration in IdResolver can cause an assertion failure while writing the identifier table. +module ModB { + header "ModBFile.h" + export * +} + +//--- ModAFile1.h +#include "shared.h" + +//--- ModAFile2.h +// Including this file, triggers loading of the module ModA with nodes coming ModAFile1.h being hidden. + +//--- ModBFile.h +// ModBFile depends on ModAFile2.h only. +#include "ModAFile2.h" +// Including shared.h here causes Sema to merge the AST nodes from shared.h with the hidden ones from ModA. +#include "shared.h" + + +//--- source.m +#include "ModBFile.h" + +int main() { return MyVal_A + MyVal_B + MyVal_C + MyVal_D + MyVal_E; } diff --git a/clang/test/Modules/pr125999.cppm b/clang/test/Modules/pr125999.cppm new file mode 100644 index 0000000000000..a859ce7592604 --- /dev/null +++ b/clang/test/Modules/pr125999.cppm @@ -0,0 +1,33 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/foo.cppm -verify -fsyntax-only + +//--- bar.h +template +struct Singleton { + static T* instance_; + static T* get() { + static bool init = false; + if (!init) { + init = true; + instance_ = ::new T(); + } + return instance_; + } +}; + +template +T* Singleton::instance_ = nullptr; + +struct s{}; +inline void* foo() { + return Singleton::get(); +} + +//--- foo.cppm +// expected-no-diagnostics +module; +#include "bar.h" +export module foo; diff --git a/clang/test/Modules/template-default-args.cpp b/clang/test/Modules/template-default-args.cpp index 1d8de709fd598..85b2a18d9e506 100644 --- a/clang/test/Modules/template-default-args.cpp +++ b/clang/test/Modules/template-default-args.cpp @@ -22,7 +22,7 @@ template struct B; template struct C; template struct D {}; template struct F {}; -template struct G {}; // #G +template struct G {}; template struct J {}; template struct J; struct K : J<> {}; @@ -39,10 +39,8 @@ E<> e; F<> f; G<> g; // expected-error {{missing '#include "a.h"'; default argument of 'G' must be defined before it is used}} // expected-note@a.h:7 {{default argument declared here is not reachable}} -// expected-note@#G {{template parameter is declared here}} H<> h; // expected-error {{missing '#include "a.h"'; default argument of 'H' must be defined before it is used}} // expected-note@a.h:8 {{default argument declared here is not reachable}} -// expected-note@a.h:8 {{template parameter is declared here}} I<> i; L<> *l; END diff --git a/clang/test/OpenMP/cancel_codegen.cpp b/clang/test/OpenMP/cancel_codegen.cpp index 7a95c0138b3a1..16e7542a8e826 100644 --- a/clang/test/OpenMP/cancel_codegen.cpp +++ b/clang/test/OpenMP/cancel_codegen.cpp @@ -728,9 +728,7 @@ for (int i = 0; i < argc; ++i) { // CHECK3-NEXT: store ptr [[ARGV_ADDR]], ptr [[GEP_ARGV_ADDR]], align 8 // CHECK3-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @main..omp_par, ptr [[STRUCTARG]]) // CHECK3-NEXT: br label [[OMP_PAR_OUTLINED_EXIT:%.*]] -// CHECK3: omp.par.outlined.exit: -// CHECK3-NEXT: br label [[OMP_PAR_EXIT_SPLIT:%.*]] -// CHECK3: omp.par.exit.split: +// CHECK3: omp.par.exit: // CHECK3-NEXT: br label [[OMP_SECTION_LOOP_PREHEADER:%.*]] // CHECK3: omp_section_loop.preheader: // CHECK3-NEXT: store i32 0, ptr [[P_LOWERBOUND]], align 4 @@ -998,7 +996,7 @@ for (int i = 0; i < argc; ++i) { // CHECK3-NEXT: br label [[OMP_PAR_OUTLINED_EXIT_EXITSTUB]] // CHECK3: .split: // CHECK3-NEXT: br label [[TMP4]] -// CHECK3: omp.par.outlined.exit.exitStub: +// CHECK3: omp.par.exit.exitStub: // CHECK3-NEXT: ret void // // diff --git a/clang/test/OpenMP/irbuilder_nested_openmp_parallel_empty.c b/clang/test/OpenMP/irbuilder_nested_openmp_parallel_empty.c index 7bdb9749757cc..96962f71c709d 100644 --- a/clang/test/OpenMP/irbuilder_nested_openmp_parallel_empty.c +++ b/clang/test/OpenMP/irbuilder_nested_openmp_parallel_empty.c @@ -16,10 +16,10 @@ // ALL-NEXT: br label [[OMP_PARALLEL:%.*]] // ALL: omp_parallel: // ALL-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 0, ptr @_Z17nested_parallel_0v..omp_par.1) -// ALL-NEXT: br label [[OMP_PAR_OUTLINED_EXIT12:%.*]] -// ALL: omp.par.outlined.exit12: +// ALL-NEXT: br label [[OMP_PAR_EXIT:%.*]] +// ALL: omp.par.exit7: // ALL-NEXT: br label [[OMP_PAR_EXIT_SPLIT:%.*]] -// ALL: omp.par.exit.split: +// ALL: omp.par.exit.exitStub: // ALL-NEXT: ret void // void nested_parallel_0(void) { @@ -50,10 +50,8 @@ void nested_parallel_0(void) { // ALL-NEXT: [[GEP_R_ADDR17:%.*]] = getelementptr { ptr, ptr, ptr }, ptr [[STRUCTARG14]], i32 0, i32 2 // ALL-NEXT: store ptr [[R_ADDR]], ptr [[GEP_R_ADDR17]], align 8 // ALL-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z17nested_parallel_1Pfid..omp_par.2, ptr [[STRUCTARG14]]) -// ALL-NEXT: br label [[OMP_PAR_OUTLINED_EXIT13:%.*]] -// ALL: omp.par.outlined.exit13: -// ALL-NEXT: br label [[OMP_PAR_EXIT_SPLIT:%.*]] -// ALL: omp.par.exit.split: +// ALL-NEXT: br label [[OMP_PAR_EXIT:%.*]] +// ALL: omp.par.exit: // ALL-NEXT: ret void // void nested_parallel_1(float *r, int a, double b) { @@ -85,10 +83,8 @@ void nested_parallel_1(float *r, int a, double b) { // ALL-NEXT: [[GEP_R_ADDR:%.*]] = getelementptr { ptr, ptr, ptr }, ptr [[STRUCTARG]], i32 0, i32 2 // ALL-NEXT: store ptr [[R_ADDR]], ptr [[GEP_R_ADDR]], align 8 // ALL-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z17nested_parallel_2Pfid..omp_par.5, ptr [[STRUCTARG]]) -// ALL-NEXT: br label [[OMP_PAR_OUTLINED_EXIT55:%.*]] -// ALL: omp.par.outlined.exit55: -// ALL-NEXT: br label [[OMP_PAR_EXIT_SPLIT:%.*]] -// ALL: omp.par.exit.split: +// ALL-NEXT: br label [[OMP_PAR_EXIT:%.*]] +// ALL: omp.par.exit: // ALL-NEXT: [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4 // ALL-NEXT: [[CONV56:%.*]] = sitofp i32 [[TMP0]] to double // ALL-NEXT: [[TMP1:%.*]] = load double, ptr [[B_ADDR]], align 8 diff --git a/clang/test/OpenMP/irbuilder_nested_parallel_for.c b/clang/test/OpenMP/irbuilder_nested_parallel_for.c index 68727e0503ede..ae3570fda412d 100644 --- a/clang/test/OpenMP/irbuilder_nested_parallel_for.c +++ b/clang/test/OpenMP/irbuilder_nested_parallel_for.c @@ -78,10 +78,8 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-NEXT: br label [[OMP_PARALLEL:%.*]] // CHECK: omp_parallel: // CHECK-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 0, ptr @_Z14parallel_for_0v..omp_par) -// CHECK-NEXT: br label [[OMP_PAR_OUTLINED_EXIT:%.*]] -// CHECK: omp.par.outlined.exit: -// CHECK-NEXT: br label [[OMP_PAR_EXIT_SPLIT:%.*]] -// CHECK: omp.par.exit.split: +// CHECK-NEXT: br label [[OMP_PAR_EXIT:%.*]] +// CHECK: omp.par.exit: // CHECK-NEXT: ret void // // @@ -147,7 +145,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK: omp_loop.inc: // CHECK-NEXT: [[OMP_LOOP_NEXT]] = add nuw i32 [[OMP_LOOP_IV]], 1 // CHECK-NEXT: br label [[OMP_LOOP_HEADER]] -// CHECK: omp.par.outlined.exit.exitStub: +// CHECK: omp.par.exit.exitStub: // CHECK-NEXT: ret void // // @@ -231,10 +229,8 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-NEXT: [[GEP_R_ADDR20:%.*]] = getelementptr { ptr, ptr, ptr }, ptr [[STRUCTARG17]], i32 0, i32 2 // CHECK-NEXT: store ptr [[R_ADDR]], ptr [[GEP_R_ADDR20]], align 8 // CHECK-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z14parallel_for_1Pfid..omp_par.4, ptr [[STRUCTARG17]]) -// CHECK-NEXT: br label [[OMP_PAR_OUTLINED_EXIT16:%.*]] -// CHECK: omp.par.outlined.exit16: -// CHECK-NEXT: br label [[OMP_PAR_EXIT_SPLIT:%.*]] -// CHECK: omp.par.exit.split: +// CHECK-NEXT: br label [[OMP_PAR_EXIT:%.*]] +// CHECK: omp.par.exit: // CHECK-NEXT: ret void // // @@ -264,16 +260,14 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-NEXT: [[GEP_R_ADDR3:%.*]] = getelementptr { ptr, ptr, ptr }, ptr [[STRUCTARG]], i32 0, i32 2 // CHECK-NEXT: store ptr [[LOADGEP_R_ADDR]], ptr [[GEP_R_ADDR3]], align 8 // CHECK-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z14parallel_for_1Pfid..omp_par, ptr [[STRUCTARG]]) -// CHECK-NEXT: br label [[OMP_PAR_OUTLINED_EXIT:%.*]] -// CHECK: omp.par.outlined.exit: -// CHECK-NEXT: br label [[OMP_PAR_EXIT7_SPLIT:%.*]] -// CHECK: omp.par.exit7.split: +// CHECK-NEXT: br label [[OMP_PAR_EXIT:%.*]] +// CHECK: omp.par.exit7: // CHECK-NEXT: br label [[OMP_PAR_REGION_PARALLEL_AFTER:%.*]] // CHECK: omp.par.region.parallel.after: // CHECK-NEXT: br label [[OMP_PAR_PRE_FINALIZE:%.*]] // CHECK: omp.par.pre_finalize: -// CHECK-NEXT: br label [[OMP_PAR_OUTLINED_EXIT16_EXITSTUB:%.*]] -// CHECK: omp.par.outlined.exit16.exitStub: +// CHECK-NEXT: br label [[OMP_PAR_EXIT16_EXITSTUB:%.*]] +// CHECK: omp.par.exit.exitStub: // CHECK-NEXT: ret void // // @@ -352,7 +346,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK: omp_loop.inc: // CHECK-NEXT: [[OMP_LOOP_NEXT]] = add nuw i32 [[OMP_LOOP_IV]], 1 // CHECK-NEXT: br label [[OMP_LOOP_HEADER]] -// CHECK: omp.par.outlined.exit.exitStub: +// CHECK: omp.par.exit7.exitStub: // CHECK-NEXT: ret void // // @@ -444,10 +438,8 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-NEXT: [[GEP_R_ADDR:%.*]] = getelementptr { ptr, ptr, ptr }, ptr [[STRUCTARG]], i32 0, i32 2 // CHECK-NEXT: store ptr [[R_ADDR]], ptr [[GEP_R_ADDR]], align 8 // CHECK-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z14parallel_for_2Pfid..omp_par.23, ptr [[STRUCTARG]]) -// CHECK-NEXT: br label [[OMP_PAR_OUTLINED_EXIT184:%.*]] -// CHECK: omp.par.outlined.exit184: -// CHECK-NEXT: br label [[OMP_PAR_EXIT_SPLIT:%.*]] -// CHECK: omp.par.exit.split: +// CHECK-NEXT: br label [[OMP_PAR_EXIT184:%.*]] +// CHECK: omp.par.exit: // CHECK-NEXT: store i32 0, ptr [[I185]], align 4 // CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[STRUCT_ANON_17]], ptr [[AGG_CAPTURED186]], i32 0, i32 0 // CHECK-NEXT: store ptr [[I185]], ptr [[TMP0]], align 8 @@ -457,7 +449,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-NEXT: call void @__captured_stmt.19(ptr [[DOTCOUNT_ADDR188]], ptr [[AGG_CAPTURED186]]) // CHECK-NEXT: [[DOTCOUNT189:%.*]] = load i32, ptr [[DOTCOUNT_ADDR188]], align 4 // CHECK-NEXT: br label [[OMP_LOOP_PREHEADER190:%.*]] -// CHECK: omp_loop.preheader190: +// CHECK: omp_loop.preheader187: // CHECK-NEXT: store i32 0, ptr [[P_LOWERBOUND204]], align 4 // CHECK-NEXT: [[TMP3:%.*]] = sub i32 [[DOTCOUNT189]], 1 // CHECK-NEXT: store i32 [[TMP3]], ptr [[P_UPPERBOUND205]], align 4 @@ -469,13 +461,13 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-NEXT: [[TMP6:%.*]] = sub i32 [[TMP5]], [[TMP4]] // CHECK-NEXT: [[TMP7:%.*]] = add i32 [[TMP6]], 1 // CHECK-NEXT: br label [[OMP_LOOP_HEADER191:%.*]] -// CHECK: omp_loop.header191: +// CHECK: omp_loop.header188: // CHECK-NEXT: [[OMP_LOOP_IV197:%.*]] = phi i32 [ 0, [[OMP_LOOP_PREHEADER190]] ], [ [[OMP_LOOP_NEXT199:%.*]], [[OMP_LOOP_INC194:%.*]] ] // CHECK-NEXT: br label [[OMP_LOOP_COND192:%.*]] -// CHECK: omp_loop.cond192: +// CHECK: omp_loop.cond189: // CHECK-NEXT: [[OMP_LOOP_CMP198:%.*]] = icmp ult i32 [[OMP_LOOP_IV197]], [[TMP7]] // CHECK-NEXT: br i1 [[OMP_LOOP_CMP198]], label [[OMP_LOOP_BODY193:%.*]], label [[OMP_LOOP_EXIT195:%.*]] -// CHECK: omp_loop.body193: +// CHECK: omp_loop.body190: // CHECK-NEXT: [[TMP8:%.*]] = add i32 [[OMP_LOOP_IV197]], [[TMP4]] // CHECK-NEXT: call void @__captured_stmt.20(ptr [[I185]], i32 [[TMP8]], ptr [[AGG_CAPTURED187]]) // CHECK-NEXT: [[TMP9:%.*]] = load i32, ptr [[A_ADDR]], align 4 @@ -486,15 +478,15 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-NEXT: [[TMP11:%.*]] = load ptr, ptr [[R_ADDR]], align 8 // CHECK-NEXT: store float [[CONV202]], ptr [[TMP11]], align 4 // CHECK-NEXT: br label [[OMP_LOOP_INC194]] -// CHECK: omp_loop.inc194: +// CHECK: omp_loop.inc191: // CHECK-NEXT: [[OMP_LOOP_NEXT199]] = add nuw i32 [[OMP_LOOP_IV197]], 1 // CHECK-NEXT: br label [[OMP_LOOP_HEADER191]] -// CHECK: omp_loop.exit195: +// CHECK: omp_loop.exit192: // CHECK-NEXT: call void @__kmpc_for_static_fini(ptr @[[GLOB1]], i32 [[OMP_GLOBAL_THREAD_NUM207]]) // CHECK-NEXT: [[OMP_GLOBAL_THREAD_NUM208:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB1]]) // CHECK-NEXT: call void @__kmpc_barrier(ptr @[[GLOB2]], i32 [[OMP_GLOBAL_THREAD_NUM208]]) // CHECK-NEXT: br label [[OMP_LOOP_AFTER196:%.*]] -// CHECK: omp_loop.after196: +// CHECK: omp_loop.after193: // CHECK-NEXT: ret void // // @@ -573,10 +565,8 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-NEXT: [[GEP_R_ADDR217:%.*]] = getelementptr { ptr, ptr, ptr }, ptr [[STRUCTARG214]], i32 0, i32 2 // CHECK-NEXT: store ptr [[LOADGEP_R_ADDR]], ptr [[GEP_R_ADDR217]], align 8 // CHECK-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z14parallel_for_2Pfid..omp_par.22, ptr [[STRUCTARG214]]) -// CHECK-NEXT: br label [[OMP_PAR_OUTLINED_EXIT159:%.*]] -// CHECK: omp.par.outlined.exit159: -// CHECK-NEXT: br label [[OMP_PAR_EXIT11_SPLIT:%.*]] -// CHECK: omp.par.exit11.split: +// CHECK-NEXT: br label [[OMP_PAR_EXIT159:%.*]] +// CHECK: omp.par.exit11: // CHECK-NEXT: store i32 0, ptr [[I160]], align 4 // CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw [[STRUCT_ANON_15]], ptr [[AGG_CAPTURED161]], i32 0, i32 0 // CHECK-NEXT: store ptr [[I160]], ptr [[TMP10]], align 8 @@ -586,7 +576,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-NEXT: call void @__captured_stmt.17(ptr [[DOTCOUNT_ADDR163]], ptr [[AGG_CAPTURED161]]) // CHECK-NEXT: [[DOTCOUNT164:%.*]] = load i32, ptr [[DOTCOUNT_ADDR163]], align 4 // CHECK-NEXT: br label [[OMP_LOOP_PREHEADER165:%.*]] -// CHECK: omp_loop.preheader165: +// CHECK: omp_loop.preheader163: // CHECK-NEXT: store i32 0, ptr [[P_LOWERBOUND179]], align 4 // CHECK-NEXT: [[TMP13:%.*]] = sub i32 [[DOTCOUNT164]], 1 // CHECK-NEXT: store i32 [[TMP13]], ptr [[P_UPPERBOUND180]], align 4 @@ -598,24 +588,24 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-NEXT: [[TMP16:%.*]] = sub i32 [[TMP15]], [[TMP14]] // CHECK-NEXT: [[TMP17:%.*]] = add i32 [[TMP16]], 1 // CHECK-NEXT: br label [[OMP_LOOP_HEADER166:%.*]] -// CHECK: omp_loop.header166: +// CHECK: omp_loop.header164: // CHECK-NEXT: [[OMP_LOOP_IV172:%.*]] = phi i32 [ 0, [[OMP_LOOP_PREHEADER165]] ], [ [[OMP_LOOP_NEXT174:%.*]], [[OMP_LOOP_INC169:%.*]] ] // CHECK-NEXT: br label [[OMP_LOOP_COND167:%.*]] -// CHECK: omp_loop.cond167: +// CHECK: omp_loop.cond165: // CHECK-NEXT: [[OMP_LOOP_CMP173:%.*]] = icmp ult i32 [[OMP_LOOP_IV172]], [[TMP17]] // CHECK-NEXT: br i1 [[OMP_LOOP_CMP173]], label [[OMP_LOOP_BODY168:%.*]], label [[OMP_LOOP_EXIT170:%.*]] -// CHECK: omp_loop.exit170: +// CHECK: omp_loop.exit168: // CHECK-NEXT: call void @__kmpc_for_static_fini(ptr @[[GLOB1]], i32 [[OMP_GLOBAL_THREAD_NUM182]]) // CHECK-NEXT: [[OMP_GLOBAL_THREAD_NUM183:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB1]]) // CHECK-NEXT: call void @__kmpc_barrier(ptr @[[GLOB2]], i32 [[OMP_GLOBAL_THREAD_NUM183]]) // CHECK-NEXT: br label [[OMP_LOOP_AFTER171:%.*]] -// CHECK: omp_loop.after171: +// CHECK: omp_loop.after169: // CHECK-NEXT: br label [[OMP_PAR_REGION_PARALLEL_AFTER:%.*]] // CHECK: omp.par.region.parallel.after: // CHECK-NEXT: br label [[OMP_PAR_PRE_FINALIZE:%.*]] // CHECK: omp.par.pre_finalize: // CHECK-NEXT: br label [[OMP_PAR_OUTLINED_EXIT184_EXITSTUB:%.*]] -// CHECK: omp_loop.body168: +// CHECK: omp_loop.body166: // CHECK-NEXT: [[TMP18:%.*]] = add i32 [[OMP_LOOP_IV172]], [[TMP14]] // CHECK-NEXT: call void @__captured_stmt.18(ptr [[I160]], i32 [[TMP18]], ptr [[AGG_CAPTURED162]]) // CHECK-NEXT: [[TMP19:%.*]] = load i32, ptr [[LOADGEP_A_ADDR]], align 4 @@ -626,7 +616,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-NEXT: [[TMP21:%.*]] = load ptr, ptr [[LOADGEP_R_ADDR]], align 8 // CHECK-NEXT: store float [[CONV177]], ptr [[TMP21]], align 4 // CHECK-NEXT: br label [[OMP_LOOP_INC169]] -// CHECK: omp_loop.inc169: +// CHECK: omp_loop.inc167: // CHECK-NEXT: [[OMP_LOOP_NEXT174]] = add nuw i32 [[OMP_LOOP_IV172]], 1 // CHECK-NEXT: br label [[OMP_LOOP_HEADER166]] // CHECK: omp_loop.body: @@ -643,7 +633,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK: omp_loop.inc: // CHECK-NEXT: [[OMP_LOOP_NEXT]] = add nuw i32 [[OMP_LOOP_IV]], 1 // CHECK-NEXT: br label [[OMP_LOOP_HEADER]] -// CHECK: omp.par.outlined.exit184.exitStub: +// CHECK: omp.par.exit.exitStub: // CHECK-NEXT: ret void // // @@ -731,10 +721,8 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-NEXT: [[GEP_R_ADDR3:%.*]] = getelementptr { ptr, ptr, ptr }, ptr [[STRUCTARG]], i32 0, i32 2 // CHECK-NEXT: store ptr [[LOADGEP_R_ADDR]], ptr [[GEP_R_ADDR3]], align 8 // CHECK-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z14parallel_for_2Pfid..omp_par, ptr [[STRUCTARG]]) -// CHECK-NEXT: br label [[OMP_PAR_OUTLINED_EXIT:%.*]] -// CHECK: omp.par.outlined.exit: -// CHECK-NEXT: br label [[OMP_PAR_EXIT46_SPLIT:%.*]] -// CHECK: omp.par.exit46.split: +// CHECK-NEXT: br label [[OMP_PAR_EXIT:%.*]] +// CHECK: omp.par.exit46: // CHECK-NEXT: store i32 0, ptr [[I75]], align 4 // CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw [[STRUCT_ANON_9]], ptr [[AGG_CAPTURED76]], i32 0, i32 0 // CHECK-NEXT: store ptr [[I75]], ptr [[TMP10]], align 8 @@ -770,7 +758,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK: omp_loop.after86: // CHECK-NEXT: [[OMP_GLOBAL_THREAD_NUM99:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB1]]) // CHECK-NEXT: br label [[OMP_PARALLEL213:%.*]] -// CHECK: omp_parallel213: +// CHECK: omp_parallel210: // CHECK-NEXT: [[GEP_A_ADDR210:%.*]] = getelementptr { ptr, ptr, ptr }, ptr [[STRUCTARG209]], i32 0, i32 0 // CHECK-NEXT: store ptr [[LOADGEP_A_ADDR]], ptr [[GEP_A_ADDR210]], align 8 // CHECK-NEXT: [[GEP_B_ADDR211:%.*]] = getelementptr { ptr, ptr, ptr }, ptr [[STRUCTARG209]], i32 0, i32 1 @@ -779,9 +767,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-NEXT: store ptr [[LOADGEP_R_ADDR]], ptr [[GEP_R_ADDR212]], align 8 // CHECK-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z14parallel_for_2Pfid..omp_par.21, ptr [[STRUCTARG209]]) // CHECK-NEXT: br label [[OMP_PAR_OUTLINED_EXIT134:%.*]] -// CHECK: omp.par.outlined.exit134: -// CHECK-NEXT: br label [[OMP_PAR_EXIT105_SPLIT:%.*]] -// CHECK: omp.par.exit105.split: +// CHECK: omp.par.exit105: // CHECK-NEXT: store i32 0, ptr [[I135]], align 4 // CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw [[STRUCT_ANON_13]], ptr [[AGG_CAPTURED136]], i32 0, i32 0 // CHECK-NEXT: store ptr [[I135]], ptr [[TMP18]], align 8 @@ -791,7 +777,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-NEXT: call void @__captured_stmt.15(ptr [[DOTCOUNT_ADDR138]], ptr [[AGG_CAPTURED136]]) // CHECK-NEXT: [[DOTCOUNT139:%.*]] = load i32, ptr [[DOTCOUNT_ADDR138]], align 4 // CHECK-NEXT: br label [[OMP_LOOP_PREHEADER140:%.*]] -// CHECK: omp_loop.preheader140: +// CHECK: omp_loop.preheader139: // CHECK-NEXT: store i32 0, ptr [[P_LOWERBOUND154]], align 4 // CHECK-NEXT: [[TMP21:%.*]] = sub i32 [[DOTCOUNT139]], 1 // CHECK-NEXT: store i32 [[TMP21]], ptr [[P_UPPERBOUND155]], align 4 @@ -803,24 +789,24 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-NEXT: [[TMP24:%.*]] = sub i32 [[TMP23]], [[TMP22]] // CHECK-NEXT: [[TMP25:%.*]] = add i32 [[TMP24]], 1 // CHECK-NEXT: br label [[OMP_LOOP_HEADER141:%.*]] -// CHECK: omp_loop.header141: +// CHECK: omp_loop.header140: // CHECK-NEXT: [[OMP_LOOP_IV147:%.*]] = phi i32 [ 0, [[OMP_LOOP_PREHEADER140]] ], [ [[OMP_LOOP_NEXT149:%.*]], [[OMP_LOOP_INC144:%.*]] ] // CHECK-NEXT: br label [[OMP_LOOP_COND142:%.*]] -// CHECK: omp_loop.cond142: +// CHECK: omp_loop.cond141: // CHECK-NEXT: [[OMP_LOOP_CMP148:%.*]] = icmp ult i32 [[OMP_LOOP_IV147]], [[TMP25]] // CHECK-NEXT: br i1 [[OMP_LOOP_CMP148]], label [[OMP_LOOP_BODY143:%.*]], label [[OMP_LOOP_EXIT145:%.*]] -// CHECK: omp_loop.exit145: +// CHECK: omp_loop.exit144: // CHECK-NEXT: call void @__kmpc_for_static_fini(ptr @[[GLOB1]], i32 [[OMP_GLOBAL_THREAD_NUM157]]) // CHECK-NEXT: [[OMP_GLOBAL_THREAD_NUM158:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB1]]) // CHECK-NEXT: call void @__kmpc_barrier(ptr @[[GLOB2]], i32 [[OMP_GLOBAL_THREAD_NUM158]]) // CHECK-NEXT: br label [[OMP_LOOP_AFTER146:%.*]] -// CHECK: omp_loop.after146: +// CHECK: omp_loop.after145: // CHECK-NEXT: br label [[OMP_PAR_REGION9_PARALLEL_AFTER:%.*]] // CHECK: omp.par.region9.parallel.after: // CHECK-NEXT: br label [[OMP_PAR_PRE_FINALIZE10:%.*]] // CHECK: omp.par.pre_finalize10: // CHECK-NEXT: br label [[OMP_PAR_OUTLINED_EXIT159_EXITSTUB:%.*]] -// CHECK: omp_loop.body143: +// CHECK: omp_loop.body142: // CHECK-NEXT: [[TMP26:%.*]] = add i32 [[OMP_LOOP_IV147]], [[TMP22]] // CHECK-NEXT: call void @__captured_stmt.16(ptr [[I135]], i32 [[TMP26]], ptr [[AGG_CAPTURED137]]) // CHECK-NEXT: [[TMP27:%.*]] = load i32, ptr [[LOADGEP_A_ADDR]], align 4 @@ -831,7 +817,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-NEXT: [[TMP29:%.*]] = load ptr, ptr [[LOADGEP_R_ADDR]], align 8 // CHECK-NEXT: store float [[CONV152]], ptr [[TMP29]], align 4 // CHECK-NEXT: br label [[OMP_LOOP_INC144]] -// CHECK: omp_loop.inc144: +// CHECK: omp_loop.inc143: // CHECK-NEXT: [[OMP_LOOP_NEXT149]] = add nuw i32 [[OMP_LOOP_IV147]], 1 // CHECK-NEXT: br label [[OMP_LOOP_HEADER141]] // CHECK: omp_loop.body83: @@ -862,7 +848,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK: omp_loop.inc25: // CHECK-NEXT: [[OMP_LOOP_NEXT30]] = add nuw i32 [[OMP_LOOP_IV28]], 1 // CHECK-NEXT: br label [[OMP_LOOP_HEADER22]] -// CHECK: omp.par.outlined.exit159.exitStub: +// CHECK: omp.par.exit11.exitStub: // CHECK-NEXT: ret void // // @@ -941,7 +927,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK: omp_loop.inc119: // CHECK-NEXT: [[OMP_LOOP_NEXT124]] = add nuw i32 [[OMP_LOOP_IV122]], 1 // CHECK-NEXT: br label [[OMP_LOOP_HEADER116]] -// CHECK: omp.par.outlined.exit134.exitStub: +// CHECK: omp.par.exit105.exitStub: // CHECK-NEXT: ret void // // @@ -1020,7 +1006,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK: omp_loop.inc60: // CHECK-NEXT: [[OMP_LOOP_NEXT65]] = add nuw i32 [[OMP_LOOP_IV63]], 1 // CHECK-NEXT: br label [[OMP_LOOP_HEADER57]] -// CHECK: omp.par.outlined.exit.exitStub: +// CHECK: omp.par.exit46.exitStub: // CHECK-NEXT: ret void // // @@ -1512,9 +1498,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-DEBUG: omp_parallel: // CHECK-DEBUG-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 0, ptr @_Z14parallel_for_0v..omp_par), !dbg [[DBG14:![0-9]+]] // CHECK-DEBUG-NEXT: br label [[OMP_PAR_OUTLINED_EXIT:%.*]] -// CHECK-DEBUG: omp.par.outlined.exit: -// CHECK-DEBUG-NEXT: br label [[OMP_PAR_EXIT_SPLIT:%.*]] -// CHECK-DEBUG: omp.par.exit.split: +// CHECK-DEBUG: omp.par.exit: // CHECK-DEBUG-NEXT: ret void, !dbg [[DBG18:![0-9]+]] // // @@ -1581,7 +1565,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-DEBUG: omp_loop.inc: // CHECK-DEBUG-NEXT: [[OMP_LOOP_NEXT]] = add nuw i32 [[OMP_LOOP_IV]], 1, !dbg [[DBG27]] // CHECK-DEBUG-NEXT: br label [[OMP_LOOP_HEADER]], !dbg [[DBG27]] -// CHECK-DEBUG: omp.par.outlined.exit.exitStub: +// CHECK-DEBUG: omp.par.exit.exitStub: // CHECK-DEBUG-NEXT: ret void // // @@ -1677,9 +1661,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-DEBUG-NEXT: store ptr [[R_ADDR]], ptr [[GEP_R_ADDR20]], align 8 // CHECK-DEBUG-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB6]], i32 1, ptr @_Z14parallel_for_1Pfid..omp_par.4, ptr [[STRUCTARG17]]), !dbg [[DBG82:![0-9]+]] // CHECK-DEBUG-NEXT: br label [[OMP_PAR_OUTLINED_EXIT16:%.*]] -// CHECK-DEBUG: omp.par.outlined.exit16: -// CHECK-DEBUG-NEXT: br label [[OMP_PAR_EXIT_SPLIT:%.*]] -// CHECK-DEBUG: omp.par.exit.split: +// CHECK-DEBUG: omp.par.exit: // CHECK-DEBUG-NEXT: ret void, !dbg [[DBG84:![0-9]+]] // // @@ -1710,15 +1692,13 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-DEBUG-NEXT: store ptr [[LOADGEP_R_ADDR]], ptr [[GEP_R_ADDR3]], align 8 // CHECK-DEBUG-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB8]], i32 1, ptr @_Z14parallel_for_1Pfid..omp_par, ptr [[STRUCTARG]]), !dbg [[DBG88:![0-9]+]] // CHECK-DEBUG-NEXT: br label [[OMP_PAR_OUTLINED_EXIT:%.*]] -// CHECK-DEBUG: omp.par.outlined.exit: -// CHECK-DEBUG-NEXT: br label [[OMP_PAR_EXIT7_SPLIT:%.*]] -// CHECK-DEBUG: omp.par.exit7.split: +// CHECK-DEBUG: omp.par.exit7: // CHECK-DEBUG-NEXT: br label [[OMP_PAR_REGION_PARALLEL_AFTER:%.*]], !dbg [[DBG92:![0-9]+]] // CHECK-DEBUG: omp.par.region.parallel.after: // CHECK-DEBUG-NEXT: br label [[OMP_PAR_PRE_FINALIZE:%.*]] // CHECK-DEBUG: omp.par.pre_finalize: // CHECK-DEBUG-NEXT: br label [[OMP_PAR_OUTLINED_EXIT16_EXITSTUB:%.*]], !dbg [[DBG92]] -// CHECK-DEBUG: omp.par.outlined.exit16.exitStub: +// CHECK-DEBUG: omp.par.exit.exitStub: // CHECK-DEBUG-NEXT: ret void // // @@ -1798,7 +1778,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-DEBUG: omp_loop.inc: // CHECK-DEBUG-NEXT: [[OMP_LOOP_NEXT]] = add nuw i32 [[OMP_LOOP_IV]], 1, !dbg [[DBG100]] // CHECK-DEBUG-NEXT: br label [[OMP_LOOP_HEADER]], !dbg [[DBG100]] -// CHECK-DEBUG: omp.par.outlined.exit.exitStub: +// CHECK-DEBUG: omp.par.exit7.exitStub: // CHECK-DEBUG-NEXT: ret void // // @@ -1901,10 +1881,8 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-DEBUG-NEXT: [[GEP_R_ADDR:%.*]] = getelementptr { ptr, ptr, ptr }, ptr [[STRUCTARG]], i32 0, i32 2 // CHECK-DEBUG-NEXT: store ptr [[R_ADDR]], ptr [[GEP_R_ADDR]], align 8 // CHECK-DEBUG-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB13]], i32 1, ptr @_Z14parallel_for_2Pfid..omp_par.23, ptr [[STRUCTARG]]), !dbg [[DBG140:![0-9]+]] -// CHECK-DEBUG-NEXT: br label [[OMP_PAR_OUTLINED_EXIT184:%.*]] -// CHECK-DEBUG: omp.par.outlined.exit184: -// CHECK-DEBUG-NEXT: br label [[OMP_PAR_EXIT_SPLIT:%.*]] -// CHECK-DEBUG: omp.par.exit.split: +// CHECK-DEBUG-NEXT: br label [[OMP_PAR_EXIT184:%.*]] +// CHECK-DEBUG: omp.par.exit: // CHECK-DEBUG-NEXT: #dbg_declare(ptr [[I185]], [[META144:![0-9]+]], !DIExpression(), [[META147:![0-9]+]]) // CHECK-DEBUG-NEXT: store i32 0, ptr [[I185]], align 4, !dbg [[META147]] // CHECK-DEBUG-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[STRUCT_ANON_17]], ptr [[AGG_CAPTURED186]], i32 0, i32 0, !dbg [[DBG148:![0-9]+]] @@ -1915,7 +1893,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-DEBUG-NEXT: call void @__captured_stmt.19(ptr [[DOTCOUNT_ADDR188]], ptr [[AGG_CAPTURED186]]), !dbg [[DBG148]] // CHECK-DEBUG-NEXT: [[DOTCOUNT189:%.*]] = load i32, ptr [[DOTCOUNT_ADDR188]], align 4, !dbg [[DBG148]] // CHECK-DEBUG-NEXT: br label [[OMP_LOOP_PREHEADER190:%.*]], !dbg [[DBG148]] -// CHECK-DEBUG: omp_loop.preheader190: +// CHECK-DEBUG: omp_loop.preheader187: // CHECK-DEBUG-NEXT: store i32 0, ptr [[P_LOWERBOUND204]], align 4, !dbg [[DBG148]] // CHECK-DEBUG-NEXT: [[TMP3:%.*]] = sub i32 [[DOTCOUNT189]], 1, !dbg [[DBG148]] // CHECK-DEBUG-NEXT: store i32 [[TMP3]], ptr [[P_UPPERBOUND205]], align 4, !dbg [[DBG148]] @@ -1927,13 +1905,13 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-DEBUG-NEXT: [[TMP6:%.*]] = sub i32 [[TMP5]], [[TMP4]], !dbg [[DBG148]] // CHECK-DEBUG-NEXT: [[TMP7:%.*]] = add i32 [[TMP6]], 1, !dbg [[DBG148]] // CHECK-DEBUG-NEXT: br label [[OMP_LOOP_HEADER191:%.*]], !dbg [[DBG148]] -// CHECK-DEBUG: omp_loop.header191: +// CHECK-DEBUG: omp_loop.header188: // CHECK-DEBUG-NEXT: [[OMP_LOOP_IV197:%.*]] = phi i32 [ 0, [[OMP_LOOP_PREHEADER190]] ], [ [[OMP_LOOP_NEXT199:%.*]], [[OMP_LOOP_INC194:%.*]] ], !dbg [[DBG148]] // CHECK-DEBUG-NEXT: br label [[OMP_LOOP_COND192:%.*]], !dbg [[DBG148]] -// CHECK-DEBUG: omp_loop.cond192: +// CHECK-DEBUG: omp_loop.cond189: // CHECK-DEBUG-NEXT: [[OMP_LOOP_CMP198:%.*]] = icmp ult i32 [[OMP_LOOP_IV197]], [[TMP7]], !dbg [[DBG148]] // CHECK-DEBUG-NEXT: br i1 [[OMP_LOOP_CMP198]], label [[OMP_LOOP_BODY193:%.*]], label [[OMP_LOOP_EXIT195:%.*]], !dbg [[DBG148]] -// CHECK-DEBUG: omp_loop.body193: +// CHECK-DEBUG: omp_loop.body190: // CHECK-DEBUG-NEXT: [[TMP8:%.*]] = add i32 [[OMP_LOOP_IV197]], [[TMP4]], !dbg [[DBG150:![0-9]+]] // CHECK-DEBUG-NEXT: call void @__captured_stmt.20(ptr [[I185]], i32 [[TMP8]], ptr [[AGG_CAPTURED187]]), !dbg [[DBG148]] // CHECK-DEBUG-NEXT: [[TMP9:%.*]] = load i32, ptr [[A_ADDR]], align 4, !dbg [[DBG151:![0-9]+]] @@ -1944,15 +1922,15 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-DEBUG-NEXT: [[TMP11:%.*]] = load ptr, ptr [[R_ADDR]], align 8, !dbg [[DBG153:![0-9]+]] // CHECK-DEBUG-NEXT: store float [[CONV202]], ptr [[TMP11]], align 4, !dbg [[DBG154:![0-9]+]] // CHECK-DEBUG-NEXT: br label [[OMP_LOOP_INC194]], !dbg [[DBG148]] -// CHECK-DEBUG: omp_loop.inc194: +// CHECK-DEBUG: omp_loop.inc191: // CHECK-DEBUG-NEXT: [[OMP_LOOP_NEXT199]] = add nuw i32 [[OMP_LOOP_IV197]], 1, !dbg [[DBG148]] // CHECK-DEBUG-NEXT: br label [[OMP_LOOP_HEADER191]], !dbg [[DBG148]] -// CHECK-DEBUG: omp_loop.exit195: +// CHECK-DEBUG: omp_loop.exit192: // CHECK-DEBUG-NEXT: call void @__kmpc_for_static_fini(ptr @[[GLOB42]], i32 [[OMP_GLOBAL_THREAD_NUM207]]), !dbg [[DBG148]] // CHECK-DEBUG-NEXT: [[OMP_GLOBAL_THREAD_NUM208:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB42]]), !dbg [[DBG150]] // CHECK-DEBUG-NEXT: call void @__kmpc_barrier(ptr @[[GLOB43:[0-9]+]], i32 [[OMP_GLOBAL_THREAD_NUM208]]), !dbg [[DBG150]] // CHECK-DEBUG-NEXT: br label [[OMP_LOOP_AFTER196:%.*]], !dbg [[DBG148]] -// CHECK-DEBUG: omp_loop.after196: +// CHECK-DEBUG: omp_loop.after193: // CHECK-DEBUG-NEXT: ret void, !dbg [[DBG155:![0-9]+]] // // @@ -2033,9 +2011,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-DEBUG-NEXT: store ptr [[LOADGEP_R_ADDR]], ptr [[GEP_R_ADDR217]], align 8 // CHECK-DEBUG-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB18]], i32 1, ptr @_Z14parallel_for_2Pfid..omp_par.22, ptr [[STRUCTARG214]]), !dbg [[DBG166:![0-9]+]] // CHECK-DEBUG-NEXT: br label [[OMP_PAR_OUTLINED_EXIT159:%.*]] -// CHECK-DEBUG: omp.par.outlined.exit159: -// CHECK-DEBUG-NEXT: br label [[OMP_PAR_EXIT11_SPLIT:%.*]] -// CHECK-DEBUG: omp.par.exit11.split: +// CHECK-DEBUG: omp.par.exit11: // CHECK-DEBUG-NEXT: #dbg_declare(ptr [[I160]], [[META170:![0-9]+]], !DIExpression(), [[META173:![0-9]+]]) // CHECK-DEBUG-NEXT: store i32 0, ptr [[I160]], align 4, !dbg [[META173]] // CHECK-DEBUG-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw [[STRUCT_ANON_15]], ptr [[AGG_CAPTURED161]], i32 0, i32 0, !dbg [[DBG174:![0-9]+]] @@ -2046,7 +2022,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-DEBUG-NEXT: call void @__captured_stmt.17(ptr [[DOTCOUNT_ADDR163]], ptr [[AGG_CAPTURED161]]), !dbg [[DBG174]] // CHECK-DEBUG-NEXT: [[DOTCOUNT164:%.*]] = load i32, ptr [[DOTCOUNT_ADDR163]], align 4, !dbg [[DBG174]] // CHECK-DEBUG-NEXT: br label [[OMP_LOOP_PREHEADER165:%.*]], !dbg [[DBG174]] -// CHECK-DEBUG: omp_loop.preheader165: +// CHECK-DEBUG: omp_loop.preheader163: // CHECK-DEBUG-NEXT: store i32 0, ptr [[P_LOWERBOUND179]], align 4, !dbg [[DBG174]] // CHECK-DEBUG-NEXT: [[TMP13:%.*]] = sub i32 [[DOTCOUNT164]], 1, !dbg [[DBG174]] // CHECK-DEBUG-NEXT: store i32 [[TMP13]], ptr [[P_UPPERBOUND180]], align 4, !dbg [[DBG174]] @@ -2058,24 +2034,24 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-DEBUG-NEXT: [[TMP16:%.*]] = sub i32 [[TMP15]], [[TMP14]], !dbg [[DBG174]] // CHECK-DEBUG-NEXT: [[TMP17:%.*]] = add i32 [[TMP16]], 1, !dbg [[DBG174]] // CHECK-DEBUG-NEXT: br label [[OMP_LOOP_HEADER166:%.*]], !dbg [[DBG174]] -// CHECK-DEBUG: omp_loop.header166: +// CHECK-DEBUG: omp_loop.header164: // CHECK-DEBUG-NEXT: [[OMP_LOOP_IV172:%.*]] = phi i32 [ 0, [[OMP_LOOP_PREHEADER165]] ], [ [[OMP_LOOP_NEXT174:%.*]], [[OMP_LOOP_INC169:%.*]] ], !dbg [[DBG174]] // CHECK-DEBUG-NEXT: br label [[OMP_LOOP_COND167:%.*]], !dbg [[DBG174]] -// CHECK-DEBUG: omp_loop.cond167: +// CHECK-DEBUG: omp_loop.cond165: // CHECK-DEBUG-NEXT: [[OMP_LOOP_CMP173:%.*]] = icmp ult i32 [[OMP_LOOP_IV172]], [[TMP17]], !dbg [[DBG174]] // CHECK-DEBUG-NEXT: br i1 [[OMP_LOOP_CMP173]], label [[OMP_LOOP_BODY168:%.*]], label [[OMP_LOOP_EXIT170:%.*]], !dbg [[DBG174]] -// CHECK-DEBUG: omp_loop.exit170: +// CHECK-DEBUG: omp_loop.exit168: // CHECK-DEBUG-NEXT: call void @__kmpc_for_static_fini(ptr @[[GLOB39]], i32 [[OMP_GLOBAL_THREAD_NUM182]]), !dbg [[DBG174]] // CHECK-DEBUG-NEXT: [[OMP_GLOBAL_THREAD_NUM183:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB39]]), !dbg [[DBG176:![0-9]+]] // CHECK-DEBUG-NEXT: call void @__kmpc_barrier(ptr @[[GLOB40:[0-9]+]], i32 [[OMP_GLOBAL_THREAD_NUM183]]), !dbg [[DBG176]] // CHECK-DEBUG-NEXT: br label [[OMP_LOOP_AFTER171:%.*]], !dbg [[DBG174]] -// CHECK-DEBUG: omp_loop.after171: +// CHECK-DEBUG: omp_loop.after169: // CHECK-DEBUG-NEXT: br label [[OMP_PAR_REGION_PARALLEL_AFTER:%.*]], !dbg [[DBG177:![0-9]+]] // CHECK-DEBUG: omp.par.region.parallel.after: // CHECK-DEBUG-NEXT: br label [[OMP_PAR_PRE_FINALIZE:%.*]] // CHECK-DEBUG: omp.par.pre_finalize: // CHECK-DEBUG-NEXT: br label [[OMP_PAR_OUTLINED_EXIT184_EXITSTUB:%.*]], !dbg [[DBG177]] -// CHECK-DEBUG: omp_loop.body168: +// CHECK-DEBUG: omp_loop.body166: // CHECK-DEBUG-NEXT: [[TMP18:%.*]] = add i32 [[OMP_LOOP_IV172]], [[TMP14]], !dbg [[DBG176]] // CHECK-DEBUG-NEXT: call void @__captured_stmt.18(ptr [[I160]], i32 [[TMP18]], ptr [[AGG_CAPTURED162]]), !dbg [[DBG174]] // CHECK-DEBUG-NEXT: [[TMP19:%.*]] = load i32, ptr [[LOADGEP_A_ADDR]], align 4, !dbg [[DBG178:![0-9]+]] @@ -2086,7 +2062,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-DEBUG-NEXT: [[TMP21:%.*]] = load ptr, ptr [[LOADGEP_R_ADDR]], align 8, !dbg [[DBG180:![0-9]+]] // CHECK-DEBUG-NEXT: store float [[CONV177]], ptr [[TMP21]], align 4, !dbg [[DBG181:![0-9]+]] // CHECK-DEBUG-NEXT: br label [[OMP_LOOP_INC169]], !dbg [[DBG174]] -// CHECK-DEBUG: omp_loop.inc169: +// CHECK-DEBUG: omp_loop.inc167: // CHECK-DEBUG-NEXT: [[OMP_LOOP_NEXT174]] = add nuw i32 [[OMP_LOOP_IV172]], 1, !dbg [[DBG174]] // CHECK-DEBUG-NEXT: br label [[OMP_LOOP_HEADER166]], !dbg [[DBG174]] // CHECK-DEBUG: omp_loop.body: @@ -2103,7 +2079,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-DEBUG: omp_loop.inc: // CHECK-DEBUG-NEXT: [[OMP_LOOP_NEXT]] = add nuw i32 [[OMP_LOOP_IV]], 1, !dbg [[DBG162]] // CHECK-DEBUG-NEXT: br label [[OMP_LOOP_HEADER]], !dbg [[DBG162]] -// CHECK-DEBUG: omp.par.outlined.exit184.exitStub: +// CHECK-DEBUG: omp.par.exit.exitStub: // CHECK-DEBUG-NEXT: ret void // // @@ -2193,9 +2169,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-DEBUG-NEXT: store ptr [[LOADGEP_R_ADDR]], ptr [[GEP_R_ADDR3]], align 8 // CHECK-DEBUG-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB23]], i32 1, ptr @_Z14parallel_for_2Pfid..omp_par, ptr [[STRUCTARG]]), !dbg [[DBG197:![0-9]+]] // CHECK-DEBUG-NEXT: br label [[OMP_PAR_OUTLINED_EXIT:%.*]] -// CHECK-DEBUG: omp.par.outlined.exit: -// CHECK-DEBUG-NEXT: br label [[OMP_PAR_EXIT46_SPLIT:%.*]] -// CHECK-DEBUG: omp.par.exit46.split: +// CHECK-DEBUG: omp.par.exit46: // CHECK-DEBUG-NEXT: #dbg_declare(ptr [[I75]], [[META201:![0-9]+]], !DIExpression(), [[META204:![0-9]+]]) // CHECK-DEBUG-NEXT: store i32 0, ptr [[I75]], align 4, !dbg [[META204]] // CHECK-DEBUG-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw [[STRUCT_ANON_9]], ptr [[AGG_CAPTURED76]], i32 0, i32 0, !dbg [[DBG205:![0-9]+]] @@ -2232,7 +2206,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-DEBUG: omp_loop.after86: // CHECK-DEBUG-NEXT: [[OMP_GLOBAL_THREAD_NUM99:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB31:[0-9]+]]), !dbg [[DBG208:![0-9]+]] // CHECK-DEBUG-NEXT: br label [[OMP_PARALLEL213:%.*]] -// CHECK-DEBUG: omp_parallel213: +// CHECK-DEBUG: omp_parallel210: // CHECK-DEBUG-NEXT: [[GEP_A_ADDR210:%.*]] = getelementptr { ptr, ptr, ptr }, ptr [[STRUCTARG209]], i32 0, i32 0 // CHECK-DEBUG-NEXT: store ptr [[LOADGEP_A_ADDR]], ptr [[GEP_A_ADDR210]], align 8 // CHECK-DEBUG-NEXT: [[GEP_B_ADDR211:%.*]] = getelementptr { ptr, ptr, ptr }, ptr [[STRUCTARG209]], i32 0, i32 1 @@ -2241,9 +2215,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-DEBUG-NEXT: store ptr [[LOADGEP_R_ADDR]], ptr [[GEP_R_ADDR212]], align 8 // CHECK-DEBUG-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB31]], i32 1, ptr @_Z14parallel_for_2Pfid..omp_par.21, ptr [[STRUCTARG209]]), !dbg [[DBG209:![0-9]+]] // CHECK-DEBUG-NEXT: br label [[OMP_PAR_OUTLINED_EXIT134:%.*]] -// CHECK-DEBUG: omp.par.outlined.exit134: -// CHECK-DEBUG-NEXT: br label [[OMP_PAR_EXIT105_SPLIT:%.*]] -// CHECK-DEBUG: omp.par.exit105.split: +// CHECK-DEBUG: omp.par.exit105: // CHECK-DEBUG-NEXT: #dbg_declare(ptr [[I135]], [[META213:![0-9]+]], !DIExpression(), [[META216:![0-9]+]]) // CHECK-DEBUG-NEXT: store i32 0, ptr [[I135]], align 4, !dbg [[META216]] // CHECK-DEBUG-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw [[STRUCT_ANON_13]], ptr [[AGG_CAPTURED136]], i32 0, i32 0, !dbg [[DBG217:![0-9]+]] @@ -2254,7 +2226,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-DEBUG-NEXT: call void @__captured_stmt.15(ptr [[DOTCOUNT_ADDR138]], ptr [[AGG_CAPTURED136]]), !dbg [[DBG217]] // CHECK-DEBUG-NEXT: [[DOTCOUNT139:%.*]] = load i32, ptr [[DOTCOUNT_ADDR138]], align 4, !dbg [[DBG217]] // CHECK-DEBUG-NEXT: br label [[OMP_LOOP_PREHEADER140:%.*]], !dbg [[DBG217]] -// CHECK-DEBUG: omp_loop.preheader140: +// CHECK-DEBUG: omp_loop.preheader139: // CHECK-DEBUG-NEXT: store i32 0, ptr [[P_LOWERBOUND154]], align 4, !dbg [[DBG217]] // CHECK-DEBUG-NEXT: [[TMP21:%.*]] = sub i32 [[DOTCOUNT139]], 1, !dbg [[DBG217]] // CHECK-DEBUG-NEXT: store i32 [[TMP21]], ptr [[P_UPPERBOUND155]], align 4, !dbg [[DBG217]] @@ -2266,24 +2238,24 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-DEBUG-NEXT: [[TMP24:%.*]] = sub i32 [[TMP23]], [[TMP22]], !dbg [[DBG217]] // CHECK-DEBUG-NEXT: [[TMP25:%.*]] = add i32 [[TMP24]], 1, !dbg [[DBG217]] // CHECK-DEBUG-NEXT: br label [[OMP_LOOP_HEADER141:%.*]], !dbg [[DBG217]] -// CHECK-DEBUG: omp_loop.header141: +// CHECK-DEBUG: omp_loop.header140: // CHECK-DEBUG-NEXT: [[OMP_LOOP_IV147:%.*]] = phi i32 [ 0, [[OMP_LOOP_PREHEADER140]] ], [ [[OMP_LOOP_NEXT149:%.*]], [[OMP_LOOP_INC144:%.*]] ], !dbg [[DBG217]] // CHECK-DEBUG-NEXT: br label [[OMP_LOOP_COND142:%.*]], !dbg [[DBG217]] -// CHECK-DEBUG: omp_loop.cond142: +// CHECK-DEBUG: omp_loop.cond141: // CHECK-DEBUG-NEXT: [[OMP_LOOP_CMP148:%.*]] = icmp ult i32 [[OMP_LOOP_IV147]], [[TMP25]], !dbg [[DBG217]] // CHECK-DEBUG-NEXT: br i1 [[OMP_LOOP_CMP148]], label [[OMP_LOOP_BODY143:%.*]], label [[OMP_LOOP_EXIT145:%.*]], !dbg [[DBG217]] -// CHECK-DEBUG: omp_loop.exit145: +// CHECK-DEBUG: omp_loop.exit144: // CHECK-DEBUG-NEXT: call void @__kmpc_for_static_fini(ptr @[[GLOB36]], i32 [[OMP_GLOBAL_THREAD_NUM157]]), !dbg [[DBG217]] // CHECK-DEBUG-NEXT: [[OMP_GLOBAL_THREAD_NUM158:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB36]]), !dbg [[DBG219:![0-9]+]] // CHECK-DEBUG-NEXT: call void @__kmpc_barrier(ptr @[[GLOB37:[0-9]+]], i32 [[OMP_GLOBAL_THREAD_NUM158]]), !dbg [[DBG219]] // CHECK-DEBUG-NEXT: br label [[OMP_LOOP_AFTER146:%.*]], !dbg [[DBG217]] -// CHECK-DEBUG: omp_loop.after146: +// CHECK-DEBUG: omp_loop.after145: // CHECK-DEBUG-NEXT: br label [[OMP_PAR_REGION9_PARALLEL_AFTER:%.*]], !dbg [[DBG220:![0-9]+]] // CHECK-DEBUG: omp.par.region9.parallel.after: // CHECK-DEBUG-NEXT: br label [[OMP_PAR_PRE_FINALIZE10:%.*]] // CHECK-DEBUG: omp.par.pre_finalize10: // CHECK-DEBUG-NEXT: br label [[OMP_PAR_OUTLINED_EXIT159_EXITSTUB:%.*]], !dbg [[DBG220]] -// CHECK-DEBUG: omp_loop.body143: +// CHECK-DEBUG: omp_loop.body142: // CHECK-DEBUG-NEXT: [[TMP26:%.*]] = add i32 [[OMP_LOOP_IV147]], [[TMP22]], !dbg [[DBG219]] // CHECK-DEBUG-NEXT: call void @__captured_stmt.16(ptr [[I135]], i32 [[TMP26]], ptr [[AGG_CAPTURED137]]), !dbg [[DBG217]] // CHECK-DEBUG-NEXT: [[TMP27:%.*]] = load i32, ptr [[LOADGEP_A_ADDR]], align 4, !dbg [[DBG221:![0-9]+]] @@ -2294,7 +2266,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-DEBUG-NEXT: [[TMP29:%.*]] = load ptr, ptr [[LOADGEP_R_ADDR]], align 8, !dbg [[DBG223:![0-9]+]] // CHECK-DEBUG-NEXT: store float [[CONV152]], ptr [[TMP29]], align 4, !dbg [[DBG224:![0-9]+]] // CHECK-DEBUG-NEXT: br label [[OMP_LOOP_INC144]], !dbg [[DBG217]] -// CHECK-DEBUG: omp_loop.inc144: +// CHECK-DEBUG: omp_loop.inc143: // CHECK-DEBUG-NEXT: [[OMP_LOOP_NEXT149]] = add nuw i32 [[OMP_LOOP_IV147]], 1, !dbg [[DBG217]] // CHECK-DEBUG-NEXT: br label [[OMP_LOOP_HEADER141]], !dbg [[DBG217]] // CHECK-DEBUG: omp_loop.body83: @@ -2325,7 +2297,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-DEBUG: omp_loop.inc25: // CHECK-DEBUG-NEXT: [[OMP_LOOP_NEXT30]] = add nuw i32 [[OMP_LOOP_IV28]], 1, !dbg [[DBG193]] // CHECK-DEBUG-NEXT: br label [[OMP_LOOP_HEADER22]], !dbg [[DBG193]] -// CHECK-DEBUG: omp.par.outlined.exit159.exitStub: +// CHECK-DEBUG: omp.par.exit11.exitStub: // CHECK-DEBUG-NEXT: ret void // // @@ -2405,7 +2377,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-DEBUG: omp_loop.inc119: // CHECK-DEBUG-NEXT: [[OMP_LOOP_NEXT124]] = add nuw i32 [[OMP_LOOP_IV122]], 1, !dbg [[DBG241]] // CHECK-DEBUG-NEXT: br label [[OMP_LOOP_HEADER116]], !dbg [[DBG241]] -// CHECK-DEBUG: omp.par.outlined.exit134.exitStub: +// CHECK-DEBUG: omp.par.exit105.exitStub: // CHECK-DEBUG-NEXT: ret void // // @@ -2485,7 +2457,7 @@ void parallel_for_2(float *r, int a, double b) { // CHECK-DEBUG: omp_loop.inc60: // CHECK-DEBUG-NEXT: [[OMP_LOOP_NEXT65]] = add nuw i32 [[OMP_LOOP_IV63]], 1, !dbg [[DBG257]] // CHECK-DEBUG-NEXT: br label [[OMP_LOOP_HEADER57]], !dbg [[DBG257]] -// CHECK-DEBUG: omp.par.outlined.exit.exitStub: +// CHECK-DEBUG: omp.par.exit46.exitStub: // CHECK-DEBUG-NEXT: ret void // // diff --git a/clang/test/OpenMP/nested_loop_codegen.cpp b/clang/test/OpenMP/nested_loop_codegen.cpp index a08950e2e55c7..d8fab26bf1e7f 100644 --- a/clang/test/OpenMP/nested_loop_codegen.cpp +++ b/clang/test/OpenMP/nested_loop_codegen.cpp @@ -530,10 +530,8 @@ int inline_decl() { // CHECK3-NEXT: [[GEP_K:%.*]] = getelementptr { ptr, ptr }, ptr [[STRUCTARG]], i32 0, i32 1 // CHECK3-NEXT: store ptr [[K]], ptr [[GEP_K]], align 8 // CHECK3-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z12outline_declv..omp_par, ptr [[STRUCTARG]]) -// CHECK3-NEXT: br label [[OMP_PAR_OUTLINED_EXIT:%.*]] -// CHECK3: omp.par.outlined.exit: -// CHECK3-NEXT: br label [[OMP_PAR_EXIT_SPLIT:%.*]] -// CHECK3: omp.par.exit.split: +// CHECK3-NEXT: br label [[OMP_PAR_EXIT:%.*]] +// CHECK3: omp.par.exit: // CHECK3-NEXT: [[TMP0:%.*]] = load i32, ptr [[K]], align 4 // CHECK3-NEXT: ret i32 [[TMP0]] // @@ -620,7 +618,7 @@ int inline_decl() { // CHECK3: omp_loop.inc: // CHECK3-NEXT: [[OMP_LOOP_NEXT]] = add nuw i32 [[OMP_LOOP_IV]], 1 // CHECK3-NEXT: br label [[OMP_LOOP_HEADER]] -// CHECK3: omp.par.outlined.exit.exitStub: +// CHECK3: omp.par.exit.exitStub: // CHECK3-NEXT: ret void // // @@ -699,9 +697,7 @@ int inline_decl() { // CHECK3-NEXT: store ptr [[RES]], ptr [[GEP_RES]], align 8 // CHECK3-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z11inline_declv..omp_par, ptr [[STRUCTARG]]) // CHECK3-NEXT: br label [[OMP_PAR_OUTLINED_EXIT:%.*]] -// CHECK3: omp.par.outlined.exit: -// CHECK3-NEXT: br label [[OMP_PAR_EXIT_SPLIT:%.*]] -// CHECK3: omp.par.exit.split: +// CHECK3: omp.par.exit: // CHECK3-NEXT: [[TMP0:%.*]] = load i32, ptr [[RES]], align 4 // CHECK3-NEXT: ret i32 [[TMP0]] // @@ -789,7 +785,7 @@ int inline_decl() { // CHECK3: omp_loop.inc: // CHECK3-NEXT: [[OMP_LOOP_NEXT]] = add nuw i32 [[OMP_LOOP_IV]], 1 // CHECK3-NEXT: br label [[OMP_LOOP_HEADER]] -// CHECK3: omp.par.outlined.exit.exitStub: +// CHECK3: omp.par.exit.exitStub: // CHECK3-NEXT: ret void // // @@ -870,9 +866,7 @@ int inline_decl() { // CHECK4-NEXT: store ptr [[K]], ptr [[GEP_K]], align 8 // CHECK4-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z12outline_declv..omp_par, ptr [[STRUCTARG]]), !dbg [[DBG18:![0-9]+]] // CHECK4-NEXT: br label [[OMP_PAR_OUTLINED_EXIT:%.*]] -// CHECK4: omp.par.outlined.exit: -// CHECK4-NEXT: br label [[OMP_PAR_EXIT_SPLIT:%.*]] -// CHECK4: omp.par.exit.split: +// CHECK4: omp.par.exit: // CHECK4-NEXT: [[TMP0:%.*]] = load i32, ptr [[K]], align 4, !dbg [[DBG20:![0-9]+]] // CHECK4-NEXT: ret i32 [[TMP0]], !dbg [[DBG20]] // @@ -959,7 +953,7 @@ int inline_decl() { // CHECK4: omp_loop.inc: // CHECK4-NEXT: [[OMP_LOOP_NEXT]] = add nuw i32 [[OMP_LOOP_IV]], 1, !dbg [[DBG28]] // CHECK4-NEXT: br label [[OMP_LOOP_HEADER]], !dbg [[DBG28]] -// CHECK4: omp.par.outlined.exit.exitStub: +// CHECK4: omp.par.exit.exitStub: // CHECK4-NEXT: ret void // // @@ -1048,9 +1042,7 @@ int inline_decl() { // CHECK4-NEXT: store ptr [[RES]], ptr [[GEP_RES]], align 8 // CHECK4-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB6]], i32 1, ptr @_Z11inline_declv..omp_par, ptr [[STRUCTARG]]), !dbg [[DBG82:![0-9]+]] // CHECK4-NEXT: br label [[OMP_PAR_OUTLINED_EXIT:%.*]] -// CHECK4: omp.par.outlined.exit: -// CHECK4-NEXT: br label [[OMP_PAR_EXIT_SPLIT:%.*]] -// CHECK4: omp.par.exit.split: +// CHECK4: omp.par.exit: // CHECK4-NEXT: [[TMP0:%.*]] = load i32, ptr [[RES]], align 4, !dbg [[DBG84:![0-9]+]] // CHECK4-NEXT: ret i32 [[TMP0]], !dbg [[DBG84]] // @@ -1139,7 +1131,7 @@ int inline_decl() { // CHECK4: omp_loop.inc: // CHECK4-NEXT: [[OMP_LOOP_NEXT]] = add nuw i32 [[OMP_LOOP_IV]], 1, !dbg [[META95]] // CHECK4-NEXT: br label [[OMP_LOOP_HEADER]], !dbg [[META95]] -// CHECK4: omp.par.outlined.exit.exitStub: +// CHECK4: omp.par.exit.exitStub: // CHECK4-NEXT: ret void // // diff --git a/clang/test/OpenMP/parallel_codegen.cpp b/clang/test/OpenMP/parallel_codegen.cpp index 2a0a881b109be..c63c6f554f4ae 100644 --- a/clang/test/OpenMP/parallel_codegen.cpp +++ b/clang/test/OpenMP/parallel_codegen.cpp @@ -736,9 +736,7 @@ int main (int argc, char **argv) { // CHECK3-NEXT: store ptr [[VLA]], ptr [[GEP_VLA]], align 8 // CHECK3-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @main..omp_par, ptr [[STRUCTARG]]) // CHECK3-NEXT: br label [[OMP_PAR_OUTLINED_EXIT:%.*]] -// CHECK3: omp.par.outlined.exit: -// CHECK3-NEXT: br label [[OMP_PAR_EXIT_SPLIT:%.*]] -// CHECK3: omp.par.exit.split: +// CHECK3: omp.par.exit: // CHECK3-NEXT: [[TMP3:%.*]] = load ptr, ptr [[ARGV_ADDR]], align 8 // CHECK3-NEXT: [[CALL:%.*]] = call noundef i32 @_Z5tmainIPPcEiT_(ptr noundef [[TMP3]]) // CHECK3-NEXT: store i32 [[CALL]], ptr [[RETVAL]], align 4 @@ -770,7 +768,7 @@ int main (int argc, char **argv) { // CHECK3-NEXT: br label [[OMP_PAR_PRE_FINALIZE:%.*]] // CHECK3: omp.par.pre_finalize: // CHECK3-NEXT: br label [[OMP_PAR_OUTLINED_EXIT_EXITSTUB:%.*]] -// CHECK3: omp.par.outlined.exit.exitStub: +// CHECK3: omp.par.exit.exitStub: // CHECK3-NEXT: ret void // // @@ -805,9 +803,7 @@ int main (int argc, char **argv) { // CHECK3-NEXT: store ptr [[ARGC_ADDR]], ptr [[GEP_ARGC_ADDR]], align 8 // CHECK3-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z5tmainIPPcEiT_..omp_par, ptr [[STRUCTARG]]) // CHECK3-NEXT: br label [[OMP_PAR_OUTLINED_EXIT:%.*]] -// CHECK3: omp.par.outlined.exit: -// CHECK3-NEXT: br label [[OMP_PAR_EXIT_SPLIT:%.*]] -// CHECK3: omp.par.exit.split: +// CHECK3: omp.par.exit: // CHECK3-NEXT: ret i32 0 // // @@ -837,7 +833,7 @@ int main (int argc, char **argv) { // CHECK3-NEXT: br label [[OMP_PAR_PRE_FINALIZE:%.*]] // CHECK3: omp.par.pre_finalize: // CHECK3-NEXT: br label [[OMP_PAR_OUTLINED_EXIT_EXITSTUB:%.*]] -// CHECK3: omp.par.outlined.exit.exitStub: +// CHECK3: omp.par.exit.exitStub: // CHECK3-NEXT: ret void // // @@ -878,9 +874,7 @@ int main (int argc, char **argv) { // CHECK4-NEXT: store ptr [[VLA]], ptr [[GEP_VLA]], align 8 // CHECK4-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @main..omp_par, ptr [[STRUCTARG]]), !dbg [[DBG30:![0-9]+]] // CHECK4-NEXT: br label [[OMP_PAR_OUTLINED_EXIT:%.*]] -// CHECK4: omp.par.outlined.exit: -// CHECK4-NEXT: br label [[OMP_PAR_EXIT_SPLIT:%.*]] -// CHECK4: omp.par.exit.split: +// CHECK4: omp.par.exit: // CHECK4-NEXT: [[TMP3:%.*]] = load ptr, ptr [[ARGV_ADDR]], align 8, !dbg [[DBG31:![0-9]+]] // CHECK4-NEXT: [[CALL:%.*]] = call noundef i32 @_Z5tmainIPPcEiT_(ptr noundef [[TMP3]]), !dbg [[DBG31]] // CHECK4-NEXT: store i32 [[CALL]], ptr [[RETVAL]], align 4, !dbg [[DBG31]] @@ -912,7 +906,7 @@ int main (int argc, char **argv) { // CHECK4-NEXT: br label [[OMP_PAR_PRE_FINALIZE:%.*]] // CHECK4: omp.par.pre_finalize: // CHECK4-NEXT: br label [[OMP_PAR_OUTLINED_EXIT_EXITSTUB:%.*]], !dbg [[DBG35]] -// CHECK4: omp.par.outlined.exit.exitStub: +// CHECK4: omp.par.exit.exitStub: // CHECK4-NEXT: ret void // // @@ -949,9 +943,7 @@ int main (int argc, char **argv) { // CHECK4-NEXT: store ptr [[ARGC_ADDR]], ptr [[GEP_ARGC_ADDR]], align 8 // CHECK4-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB3]], i32 1, ptr @_Z5tmainIPPcEiT_..omp_par, ptr [[STRUCTARG]]), !dbg [[DBG52:![0-9]+]] // CHECK4-NEXT: br label [[OMP_PAR_OUTLINED_EXIT:%.*]] -// CHECK4: omp.par.outlined.exit: -// CHECK4-NEXT: br label [[OMP_PAR_EXIT_SPLIT:%.*]] -// CHECK4: omp.par.exit.split: +// CHECK4: omp.par.exit: // CHECK4-NEXT: ret i32 0, !dbg [[DBG54:![0-9]+]] // // @@ -982,7 +974,7 @@ int main (int argc, char **argv) { // CHECK4-NEXT: br label [[OMP_PAR_PRE_FINALIZE:%.*]] // CHECK4: omp.par.pre_finalize: // CHECK4-NEXT: br label [[OMP_PAR_OUTLINED_EXIT_EXITSTUB:%.*]], !dbg [[DBG66]] -// CHECK4: omp.par.outlined.exit.exitStub: +// CHECK4: omp.par.exit.exitStub: // CHECK4-NEXT: ret void // // diff --git a/clang/test/OpenMP/requires_ast_print.cpp b/clang/test/OpenMP/requires_ast_print.cpp index efa1551e75898..a602a1c41e950 100644 --- a/clang/test/OpenMP/requires_ast_print.cpp +++ b/clang/test/OpenMP/requires_ast_print.cpp @@ -13,6 +13,10 @@ // RUN: %clang_cc1 -verify -fopenmp-simd -fopenmp-version=99 -DOMP99 -ast-print %s | FileCheck --check-prefixes=CHECK,REV %s // RUN: %clang_cc1 -fopenmp-simd -fopenmp-version=99 -DOMP99 -x c++ -std=c++11 -emit-pch -o %t %s // RUN: %clang_cc1 -fopenmp-simd -fopenmp-version=99 -DOMP99 -std=c++11 -include-pch %t -verify %s -ast-print | FileCheck --check-prefixes=CHECK,REV %s + +// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=60 -DOMP60 -ast-print %s | FileCheck --check-prefixes=CHECK,CHECK_SELF %s +// RUN: %clang_cc1 -fopenmp -fopenmp-version=60 -DOMP60 -x c++ -std=c++11 -emit-pch -o %t %s +// RUN: %clang_cc1 -fopenmp -fopenmp-version=60 -DOMP60 -std=c++11 -include-pch %t -verify %s -ast-print | FileCheck --check-prefixes=CHECK,CHECK_SELF %s // expected-no-diagnostics #ifndef HEADER @@ -24,6 +28,11 @@ #pragma omp requires unified_shared_memory // CHECK:#pragma omp requires unified_shared_memory +#ifdef OMP60 +#pragma omp requires self_maps +// CHECK_SELF:#pragma omp requires self_maps +#endif + #ifdef OMP99 #pragma omp requires reverse_offload // REV:#pragma omp requires reverse_offload diff --git a/clang/test/OpenMP/requires_messages.cpp b/clang/test/OpenMP/requires_messages.cpp index dbb2b317067b7..db1805443e458 100644 --- a/clang/test/OpenMP/requires_messages.cpp +++ b/clang/test/OpenMP/requires_messages.cpp @@ -1,5 +1,6 @@ // RUN: %clang_cc1 -verify -fopenmp -ferror-limit 100 %s -Wuninitialized // RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=99 -DOMP99 -verify=expected,rev -ferror-limit 100 %s -Wuninitialized +// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=60 -DOMP60 -verify=self -ferror-limit 100 %s -Wuninitialized int a; #pragma omp requires unified_address allocate(a) // rev-note {{unified_address clause previously used here}} expected-note {{unified_address clause previously used here}} expected-note {{unified_address clause previously used here}} expected-note {{unified_address clause previously used here}} expected-note {{unified_address clause previously used here}} expected-note{{unified_address clause previously used here}} expected-error {{unexpected OpenMP clause 'allocate' in directive '#pragma omp requires'}} @@ -22,6 +23,12 @@ int a; #pragma omp requires dynamic_allocators, dynamic_allocators // expected-error {{only one dynamic_allocators clause can appear on a requires directive in a single translation unit}} expected-error {{directive '#pragma omp requires' cannot contain more than one 'dynamic_allocators' clause}} +#ifdef OMP60 +#pragma omp requires self_maps // self-note {{self_maps clause previously used here}} + +#pragma omp requires self_maps, self_maps // self-error {{only one self_maps clause can appear on a requires directive in a single translation unit}} self-error {{directive '#pragma omp requires' cannot contain more than one 'self_maps' clause}} +#endif + #pragma omp requires atomic_default_mem_order(seq_cst) // rev-note {{atomic_default_mem_order clause previously used here}} expected-note {{atomic_default_mem_order clause previously used here}} expected-note {{atomic_default_mem_order clause previously used here}} expected-note {{atomic_default_mem_order clause previously used here}} expected-note {{atomic_default_mem_order clause previously used here}} #pragma omp requires atomic_default_mem_order(acq_rel) // expected-error {{only one atomic_default_mem_order clause can appear on a requires directive in a single translation unit}} diff --git a/clang/test/OpenMP/target_data_ast_print.cpp b/clang/test/OpenMP/target_data_ast_print.cpp index 9e883b25f11d4..a41c7f1a0da53 100644 --- a/clang/test/OpenMP/target_data_ast_print.cpp +++ b/clang/test/OpenMP/target_data_ast_print.cpp @@ -13,6 +13,14 @@ // RUN: %clang_cc1 -DOMP51 -DOMPX -verify -fopenmp-simd -fopenmp-extensions -ast-print %s | FileCheck -check-prefixes=CHECK,OMP51,OMPX %s // RUN: %clang_cc1 -DOMP51 -DOMPX -fopenmp-simd -fopenmp-extensions -x c++ -std=c++11 -emit-pch -o %t %s // RUN: %clang_cc1 -DOMP51 -DOMPX -fopenmp-simd -fopenmp-extensions -std=c++11 -include-pch %t -verify %s -ast-print | FileCheck -check-prefixes=CHECK,OMP51,OMPX %s + +// RUN: %clang_cc1 -DOMP60 -verify -fopenmp -fopenmp-version=60 -fopenmp-extensions -ast-print %s | FileCheck -check-prefixes=CHECK,OMP60 %s +// RUN: %clang_cc1 -DOMP60 -fopenmp -fopenmp-version=60 -fopenmp-extensions -x c++ -std=c++11 -emit-pch -o %t %s +// RUN: %clang_cc1 -DOMP60 -fopenmp -fopenmp-version=60 -fopenmp-extensions -std=c++11 -include-pch %t -verify %s -ast-print | FileCheck -check-prefixes=CHECK,OMP60 %s + +// RUN: %clang_cc1 -DOMP60 -verify -fopenmp-simd -fopenmp -fopenmp-version=60 -fopenmp-extensions -ast-print %s | FileCheck -check-prefixes=CHECK,OMP60 %s +// RUN: %clang_cc1 -DOMP60 -fopenmp-simd -fopenmp -fopenmp-version=60 -fopenmp-extensions -x c++ -std=c++11 -emit-pch -o %t %s +// RUN: %clang_cc1 -DOMP60 -fopenmp-simd -fopenmp -fopenmp-version=60 -fopenmp-extensions -std=c++11 -include-pch %t -verify %s -ast-print | FileCheck -check-prefixes=CHECK,OMP60 %s // expected-no-diagnostics #ifndef HEADER @@ -51,6 +59,11 @@ T tmain(T argc, T *argv) { #pragma omp target data map(close,alloc: e) foo(); +#ifdef OMP60 +#pragma omp target data map(self,alloc: e) + foo(); +#endif + #ifdef OMP51 #pragma omp target data map(present,alloc: e) foo(); @@ -68,6 +81,10 @@ T tmain(T argc, T *argv) { foo(); #pragma omp target map(close, alloc: e) foo(); +#ifdef OMP60 + #pragma omp target map(self,alloc: e) + foo(); +#endif #ifdef OMP51 #pragma omp target map(present, alloc: e) foo(); @@ -101,6 +118,8 @@ T tmain(T argc, T *argv) { // CHECK-NEXT: foo(); // CHECK-NEXT: #pragma omp target data map(close,alloc: e) // CHECK-NEXT: foo(); +// OMP60-NEXT: #pragma omp target data map(self,alloc: e) +// OMP60-NEXT: foo(); // OMP51-NEXT: #pragma omp target data map(present,alloc: e) // OMP51-NEXT: foo(); // OMPX-NEXT: #pragma omp target data map(ompx_hold,alloc: e) @@ -111,6 +130,8 @@ T tmain(T argc, T *argv) { // CHECK-NEXT: foo(); // CHECK-NEXT: #pragma omp target map(close,alloc: e) // CHECK-NEXT: foo(); +// OMP60-NEXT: #pragma omp target map(self,alloc: e) +// OMP60-NEXT: foo(); // OMP51-NEXT: #pragma omp target map(present,alloc: e) // OMP51-NEXT: foo(); // OMPX-NEXT: #pragma omp target map(ompx_hold,alloc: e) @@ -135,6 +156,8 @@ T tmain(T argc, T *argv) { // CHECK-NEXT: foo(); // CHECK-NEXT: #pragma omp target data map(close,alloc: e) // CHECK-NEXT: foo(); +// OMP60-NEXT: #pragma omp target data map(self,alloc: e) +// OMP60-NEXT: foo(); // OMP51-NEXT: #pragma omp target data map(present,alloc: e) // OMP51-NEXT: foo(); // OMPX-NEXT: #pragma omp target data map(ompx_hold,alloc: e) @@ -145,6 +168,8 @@ T tmain(T argc, T *argv) { // CHECK-NEXT: foo(); // CHECK-NEXT: #pragma omp target map(close,alloc: e) // CHECK-NEXT: foo(); +// OMP60-NEXT: #pragma omp target map(self,alloc: e) +// OMP60-NEXT: foo(); // OMP51-NEXT: #pragma omp target map(present,alloc: e) // OMP51-NEXT: foo(); // OMPX-NEXT: #pragma omp target map(ompx_hold,alloc: e) @@ -169,6 +194,8 @@ T tmain(T argc, T *argv) { // CHECK-NEXT: foo(); // CHECK-NEXT: #pragma omp target data map(close,alloc: e) // CHECK-NEXT: foo(); +// OMP60-NEXT: #pragma omp target data map(self,alloc: e) +// OMP60-NEXT: foo(); // OMP51-NEXT: #pragma omp target data map(present,alloc: e) // OMP51-NEXT: foo(); // OMPX-NEXT: #pragma omp target data map(ompx_hold,alloc: e) @@ -179,6 +206,8 @@ T tmain(T argc, T *argv) { // CHECK-NEXT: foo(); // CHECK-NEXT: #pragma omp target map(close,alloc: e) // CHECK-NEXT: foo(); +// OMP60-NEXT: #pragma omp target map(self,alloc: e) +// OMP60-NEXT: foo(); // OMP51-NEXT: #pragma omp target map(present,alloc: e) // OMP51-NEXT: foo(); // OMPX-NEXT: #pragma omp target map(ompx_hold,alloc: e) @@ -235,6 +264,13 @@ int main (int argc, char **argv) { foo(); // CHECK-NEXT: foo(); +// OMP60-NEXT: #pragma omp target data map(self,alloc: e) +// OMP60-NEXT: foo(); +#ifdef OMP60 +#pragma omp target data map(self,alloc: e) + foo(); +#endif + // OMP51-NEXT: #pragma omp target data map(present,alloc: e) // OMP51-NEXT: foo(); #ifdef OMP51 diff --git a/clang/test/OpenMP/target_map_messages.cpp b/clang/test/OpenMP/target_map_messages.cpp index 10f46687d6379..911031d5412a9 100644 --- a/clang/test/OpenMP/target_map_messages.cpp +++ b/clang/test/OpenMP/target_map_messages.cpp @@ -128,6 +128,16 @@ struct SA { {} #pragma omp target map(always) // expected-error {{use of undeclared identifier 'always'}} {} + #pragma omp target map(self, tofrom: c,f) // lt60-error {{incorrect map type modifier, expected one of: 'always', 'close', 'mapper'}} + {} + #pragma omp target map(self, tofrom: c[1:2],f) // lt60-error {{incorrect map type modifier, expected one of: 'always', 'close', 'mapper'}} + {} + #pragma omp target map(self, tofrom: c,f[1:2]) // lt60-error {{incorrect map type modifier, expected one of: 'always', 'close', 'mapper'}} + {} + #pragma omp target map(self, tofrom: c[:],f) // lt60-error {{incorrect map type modifier, expected one of: 'always', 'close', 'mapper'}} // expected-error {{section length is unspecified and cannot be inferred because subscripted value is not an array}} + {} + #pragma omp target map(self, tofrom: c,f[:]) // lt60-error {{incorrect map type modifier, expected one of: 'always', 'close', 'mapper'}} // expected-error {{section length is unspecified and cannot be inferred because subscripted value is not an array}} + {} #pragma omp target map(close, tofrom: c,f) {} #pragma omp target map(close, tofrom: c[1:2],f) @@ -194,6 +204,8 @@ struct SA { {} #pragma omp target map(always, close, always, close, tofrom: a) // expected-error 2 {{same map type modifier has been specified more than once}} {} + #pragma omp target map(self, self, tofrom: a) // lt60-error {{incorrect map type modifier, expected one of: 'always', 'close', 'mapper'}} // lt60-error {{incorrect map type modifier, expected one of: 'always', 'close', 'mapper'}} // lt60-error {{same map type modifier has been specified more than once}} // ge60-error {{same map type modifier has been specified more than once}} + {} // ge60-error@+3 {{same map type modifier has been specified more than once}} // ge51-error@+2 {{same map type modifier has been specified more than once}} // lt51-error@+1 2 {{incorrect map type modifier, expected one of: 'always', 'close', 'mapper'}} @@ -725,6 +737,10 @@ T tmain(T argc) { #pragma omp target data map(close, tofrom: close, tofrom, x) foo(); +#pragma omp target data map(self, tofrom: x) // lt60-error {{incorrect map type modifier, expected one of: 'always', 'close', 'mapper'}} +#pragma omp target data map(self: x) // lt60-error {{incorrect map type modifier, expected one of: 'always', 'close', 'mapper'}} // lt60-error {{missing map type}} + foo(); + // lt51-error@+1 {{incorrect map type modifier, expected one of: 'always', 'close', 'mapper'}} #pragma omp target data map(present, tofrom: x) // ge51-error@+2 {{missing map type}} diff --git a/clang/test/OpenMP/taskgroup_codegen.cpp b/clang/test/OpenMP/taskgroup_codegen.cpp index 61105a6472e37..72653144d08dd 100644 --- a/clang/test/OpenMP/taskgroup_codegen.cpp +++ b/clang/test/OpenMP/taskgroup_codegen.cpp @@ -224,9 +224,7 @@ void parallel_taskgroup() { // CHECK2: omp_parallel: // CHECK2-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 0, ptr @_Z18parallel_taskgroupv..omp_par) // CHECK2-NEXT: br label [[OMP_PAR_OUTLINED_EXIT:%.*]] -// CHECK2: omp.par.outlined.exit: -// CHECK2-NEXT: br label [[OMP_PAR_EXIT_SPLIT:%.*]] -// CHECK2: omp.par.exit.split: +// CHECK2: omp.par.exit: // CHECK2-NEXT: ret void // // @@ -250,6 +248,6 @@ void parallel_taskgroup() { // CHECK2-NEXT: br label [[OMP_PAR_PRE_FINALIZE:%.*]] // CHECK2: omp.par.pre_finalize: // CHECK2-NEXT: br label [[OMP_PAR_OUTLINED_EXIT_EXITSTUB:%.*]] -// CHECK2: omp.par.outlined.exit.exitStub: +// CHECK2: omp.par.exit.exitStub: // CHECK2-NEXT: ret void // diff --git a/clang/test/Parser/MicrosoftExtensions.cpp b/clang/test/Parser/MicrosoftExtensions.cpp index e47b4ef78afdf..9102bca8f6bb2 100644 --- a/clang/test/Parser/MicrosoftExtensions.cpp +++ b/clang/test/Parser/MicrosoftExtensions.cpp @@ -126,7 +126,7 @@ void template_uuid() } -template // expected-note 2{{template parameter is declared here}} +template // expected-note {{template parameter is declared here}} class COM_CLASS_TEMPLATE { }; typedef COM_CLASS_TEMPLATE COM_TYPE_1; // expected-warning {{non-type template argument containing a dereference operation is a Microsoft extension}} diff --git a/clang/test/Parser/cxx-attributes.cpp b/clang/test/Parser/cxx-attributes.cpp index 51d1f9c822812..cd09f8d11f1a6 100644 --- a/clang/test/Parser/cxx-attributes.cpp +++ b/clang/test/Parser/cxx-attributes.cpp @@ -14,7 +14,7 @@ class c { }; template class X { - template void X::f() __attribute__((locks_excluded())); // expected-error{{nested name specifier 'X::' for declaration does not refer into a class, class template or class template partial specialization}} \ + template void X::f() __attribute__((locks_excluded())); // expected-error{{nested name specifier 'X' for declaration does not refer into a class, class template or class template partial specialization}} \ // expected-warning{{attribute locks_excluded ignored, because it is not attached to a declaration}} }; diff --git a/clang/test/Parser/cxx-template-argument.cpp b/clang/test/Parser/cxx-template-argument.cpp index ffe53e7c6f77e..3c2169f86d6e7 100644 --- a/clang/test/Parser/cxx-template-argument.cpp +++ b/clang/test/Parser/cxx-template-argument.cpp @@ -57,9 +57,9 @@ namespace PR13210 { // Don't emit spurious messages namespace pr16225add { - template struct Known { }; // expected-note 3{{template parameter is declared here}} + template struct Known { }; // expected-note 3 {{template is declared here}} template struct X; - template struct ABC; // expected-note {{template parameter is declared here}} + template struct ABC; // expected-note {{template is declared here}} template struct ABC2 {}; template struct foo : @@ -68,7 +68,7 @@ namespace pr16225add { template struct foo2 : UnknownBase, // expected-error {{no template named 'UnknownBase'}} - Known // expected-error {{missing template argument for template parameter}} + Known // expected-error {{too few template arguments for class template 'Known'}} { }; template struct foo3 : @@ -76,8 +76,8 @@ namespace pr16225add { { }; template struct foo4 : - UnknownBase >, // expected-error {{missing template argument for template parameter}} - Known // expected-error {{missing template argument for template parameter}} + UnknownBase >, // expected-error {{too few template arguments for class template 'ABC'}} + Known // expected-error {{too few template arguments for class template 'Known'}} { }; template struct foo5 : @@ -92,7 +92,7 @@ namespace pr16225add { #if __cplusplus <= 199711L // expected-error@-2 {{use '> >'}} #endif - Known // expected-error {{missing template argument for template parameter}} + Known // expected-error {{too few template arguments for class template 'Known'}} { }; template struct foo7 : diff --git a/clang/test/Parser/cxx-template-template-recovery.cpp b/clang/test/Parser/cxx-template-template-recovery.cpp index 2ece5a8ccdbea..5700b160cd364 100644 --- a/clang/test/Parser/cxx-template-template-recovery.cpp +++ b/clang/test/Parser/cxx-template-template-recovery.cpp @@ -1,33 +1,33 @@ // RUN: %clang_cc1 -std=c++20 -verify -fsyntax-only %s namespace a { - template // #C1-T - concept C1 = true; + template + concept C1 = true; // #C1 template auto V1 = true; // #V1 namespace b { - template // #C2-T - concept C2 = true; + template + concept C2 = true; // #C2 template auto V2 = true; // #V2 } } -template // #C3-T -concept C3 = true; +template +concept C3 = true; // #C3 template auto V3 = true; // #V3 template