Merge branch 'main' into users/c8ef/generator

This commit is contained in:
Connector Switch 2026-08-01 22:29:07 +08:00 committed by GitHub
commit e456ba9196
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3517 changed files with 177165 additions and 102602 deletions

50
.ci/monolithic-macos.sh Executable file
View file

@ -0,0 +1,50 @@
#!/usr/bin/env bash
#===----------------------------------------------------------------------===##
#
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
#===----------------------------------------------------------------------===##
#
# This script performs a monolithic build of the monorepo and runs the tests of
# most projects on macOS.
#
source .ci/utils.sh
projects="${1}"
targets="${2}"
runtimes="${3}"
runtime_targets="${4}"
start-group "CMake"
xcrun cmake -G Ninja \
-B "${BUILD_DIR}" \
-S "${MONOREPO_ROOT}"/llvm \
-D LLVM_ENABLE_PROJECTS="${projects}" \
-D LLVM_ENABLE_RUNTIMES="${runtimes}" \
-D LLVM_DISABLE_ASSEMBLY_FILES=ON \
-D CMAKE_BUILD_TYPE=Release \
-D LLDB_INCLUDE_TESTS=OFF \
-D LLVM_ENABLE_ASSERTIONS=ON \
-D Python3_EXECUTABLE="${RUNNER_TEMP}/venv/bin/python3" \
-D LLVM_LIT_ARGS="-v --xunit-xml-output ${BUILD_DIR}/test-results.xml --use-unique-output-file-name --timeout=1200 --time-tests --succinct" \
-D CMAKE_C_COMPILER_LAUNCHER=sccache \
-D CMAKE_CXX_COMPILER_LAUNCHER=sccache
start-group "ninja"
if [[ -n "${targets}" ]]; then
ninja -C "${BUILD_DIR}" ${targets} 2>&1 | tee ninja.log
cp ${BUILD_DIR}/.ninja_log ninja.ninja_log
fi
if [[ -n "${runtime_targets}" ]]; then
start-group "ninja Runtimes"
ninja -C "${BUILD_DIR}" ${runtime_targets} 2>&1 | tee ninja_runtimes.log
cp ${BUILD_DIR}/.ninja_log ninja_runtimes.ninja_log
fi

5
.github/CODEOWNERS vendored
View file

@ -11,7 +11,6 @@
# See https://llvm.org/docs/DeveloperPolicy.html#maintainers as well as the
# Maintainers.md files in the the respective subproject directories.
/bolt/ @llvm/reviewers-bolt
/libc/ @llvm/reviewers-libc
/libcxx/ @llvm/reviewers-libcxx
/libcxxabi/ @llvm/reviewers-libcxxabi
@ -178,8 +177,12 @@
# MLIR IRDL-related
/mlir/**/*IRDL* @moxinilian
# BOLT
/bolt/ @aaupov @maksfb @rafaelauler @ayermolo @yota9 @paschalis-mpeis @yozhu @yavtuk
# Bazel build system.
/utils/bazel/ @rupprecht @keith @aaronmondal @googlewalt
/utils/bazel/llvm-project-overlay/libc/ @labath @kaladron
# InstallAPI and TextAPI
/llvm/**/TextAPI/ @cyndyishida

View file

@ -16,8 +16,8 @@ jobs:
runs-on: ${{ matrix.os }}
container:
image: ${{ (startsWith(matrix.os, 'ubuntu-24.04-arm') &&
'ghcr.io/llvm/arm64v8/libc-ubuntu-24.04:latest@sha256:138636a45bb70f7b51a858b282e66291cb3c5c85371afee09032a5e09b263395') ||
'ghcr.io/llvm/libc-ubuntu-24.04:latest@sha256:a902fb53bdad5e4a4bb6c11b6584e717a7b3d6e886f1ea58e11f95e46226249c'}}
'ghcr.io/llvm/arm64v8/libc-ubuntu-24.04:latest@sha256:9ca390ed3546754c1d7cb669033c532c28d95a0327551c5754cbf72375178e37') ||
'ghcr.io/llvm/libc-ubuntu-24.04:latest@sha256:8fee4c9ce5a1fd095686593cd36e032c48a31b6ae575378c82accd1d86a08d59'}}
# We need to enable privileged containers so that certain libc tests
# have the necessary permissions (like SYS_TIME). There are no security
# implications as we are already running in an isolated VM.

View file

@ -3,18 +3,25 @@
# it requires several inputs that allow customizing its behavior.
name: "[libc++] Run benchmark suite against commit"
run-name: "[libc++] Run benchmark suite against ${{ inputs.commit }} on ${{ inputs.lnt-machine }}"
permissions:
contents: read
on:
# TODO: Support dispatching only to selected configurations.
workflow_dispatch:
inputs:
commit:
description: 'The libc++ commit to benchmark'
required: true
type: string
lnt-machine:
description: 'The LNT machine to run the benchmarks on'
required: true
type: choice
options:
- macos-26.5-arm64
- linux-x86_64
benchmark-suite-version:
description: 'The version of the benchmark suite to use (a LLVM monorepo SHA)'
required: false
@ -36,19 +43,51 @@ on:
default: http://lnt.llvm.org
jobs:
# Determine which configuration to run based on the `lnt-machine` input.
select-machine:
runs-on: ubuntu-24.04
outputs:
matrix: ${{ steps.select.outputs.matrix }}
steps:
- name: Select the configuration to benchmark on
id: select
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
LNT_MACHINE: ${{ inputs.lnt-machine }}
with:
script: |
const MACHINES = [
{
'lnt-machine': 'macos-26.5-arm64',
runner: ['self-hosted', 'macOS', 'ARM64', '26', '26.5'],
cxx: 'clang++',
'running-on': 'macos',
'xcode-version': '26.5',
},
{
'lnt-machine': 'linux-x86_64',
runner: 'llvm-premerge-libcxx-runners',
cxx: 'clang++-22',
'running-on': 'linux',
},
];
const requested = process.env.LNT_MACHINE;
const selected = MACHINES.filter(m => m['lnt-machine'] === requested);
if (selected.length === 0) {
const known = MACHINES.map(m => m['lnt-machine']).join(', ');
core.setFailed(`Unknown LNT machine '${requested}' (known machines: ${known})`);
return;
}
core.setOutput('matrix', JSON.stringify(selected));
run-benchmarks:
needs:
- select-machine
strategy:
matrix:
include:
- lnt-machine: macos-26.5-arm64
runner: ["self-hosted", "macOS", "ARM64", "26", "26.5"]
cxx: clang++
running-on: macos
xcode-version: '26.5'
- lnt-machine: linux-x86_64
runner: llvm-premerge-libcxx-runners
cxx: clang++-22
running-on: linux
include: ${{ fromJSON(needs.select-machine.outputs.matrix) }}
fail-fast: false
runs-on: ${{ matrix.runner }}
env:
@ -91,6 +130,7 @@ jobs:
COMMIT: ${{ inputs.commit }}
BENCHMARK_SUITE_VERSION: ${{ inputs.benchmark-suite-version }}
FILTER: ${{ inputs.filter }}
LNT_MACHINE: ${{ matrix.lnt-machine }}
run: |
filter_arg=()
if [ -n "${FILTER}" ]; then
@ -98,7 +138,7 @@ jobs:
fi
libcxx/utils/ci/lnt/run-benchmarks \
--test-suite-commit "${BENCHMARK_SUITE_VERSION}" \
--machine ${{ matrix.lnt-machine }} \
--machine "${LNT_MACHINE}" \
--compiler "${COMPILER}" \
--benchmark-commit "${COMMIT}" \
"${filter_arg[@]}" \
@ -114,5 +154,5 @@ jobs:
run: |
libcxx/utils/ci/lnt/submit-benchmarks \
--lnt-url "${LNT_URL}" \
--test-suite libcxx2 \
--test-suite libcxx \
"${COMMIT}.json"

View file

@ -93,23 +93,37 @@ jobs:
ref: ${{ github.sha }}
repo: ${{ github.repository }}
steps:
- name: Check for cached baseline ABI
if: ${{ matrix.name == 'build-baseline' }}
id: cache
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 #v6.1.0
with:
key: ${{ matrix.ref}}.abi
path: |
${{ matrix.ref }}.abi
llvm.symbols
- name: Download source code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
if: steps.cache.outputs.cache-hit != 'true'
with:
persist-credentials: false
ref: ${{ matrix.ref }}
repository: ${{ matrix.repo }}
- name: Configure
if: steps.cache.outputs.cache-hit != 'true'
run: |
mkdir install
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DLLVM_TARGETS_TO_BUILD="" -DLLVM_BUILD_LLVM_DYLIB=ON -DCMAKE_INSTALL_PREFIX="$(pwd)"/install llvm
- name: Build
if: steps.cache.outputs.cache-hit != 'true'
# Need to run install-LLVM twice to ensure the symlink is installed (this is a bug).
run: |
ninja -C build install-LLVM
ninja -C build install-LLVM
ninja -C build install-llvm-headers
- name: Dump ABI
if: steps.cache.outputs.cache-hit != 'true'
env:
REF: ${{ matrix.ref }}
ABI_HEADERS: ${{ needs.abi-dump-setup.outputs.ABI_HEADERS }}
@ -138,6 +152,15 @@ jobs:
name: symbol-list
path: llvm.symbols
- name: Cache baseline ABI
if: ${{ matrix.name == 'build-baseline' }}
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 #v6.1.0
with:
key: ${{ matrix.ref }}.abi
path: |
${{ matrix.ref }}.abi
llvm.symbols
abi-compare:
if: github.repository_owner == 'llvm'
runs-on: ubuntu-24.04

View file

@ -218,6 +218,8 @@ jobs:
outputs:
macos-projects: ${{ steps.vars.outputs.macos-projects }}
macos-check-targets: ${{ steps.vars.outputs.macos-check-targets }}
macos-runtimes: ${{ steps.vars.outputs.macos-runtimes }}
macos-runtimes-check-targets: ${{ steps.vars.outputs.macos-runtimes-check-targets }}
steps:
# For pull requests we get the changed files from the GitHub API, so we
# only need a sparse checkout of .ci/ to run compute_projects.py. For
@ -261,6 +263,8 @@ jobs:
echo "macos-projects=${projects_to_build}" >> $GITHUB_OUTPUT
echo "macos-check-targets=${project_check_targets}" >> $GITHUB_OUTPUT
echo "macos-runtimes=${runtimes_to_build}" >> $GITHUB_OUTPUT
echo "macos-runtimes-check-targets=${runtimes_check_targets}" >> $GITHUB_OUTPUT
premerge-check-macos:
name: Build and Test macOS arm64
@ -282,6 +286,10 @@ jobs:
run: |
brew install ninja cmake
command -v sccache >/dev/null || brew install sccache
- name: Set up Python venv
run: |
"$(brew --prefix python@3.14)/bin/python3.14" -m venv "${RUNNER_TEMP}/venv"
echo "${RUNNER_TEMP}/venv/bin" >> "$GITHUB_PATH"
- name: Build and Test
timeout-minutes: 240
env:
@ -289,30 +297,17 @@ jobs:
SCCACHE_REGION: us-west-2
PROJECTS: ${{ needs.premerge-compute-macos.outputs.macos-projects }}
CHECK_TARGETS: ${{ needs.premerge-compute-macos.outputs.macos-check-targets }}
RUNTIMES: ${{ needs.premerge-compute-macos.outputs.macos-runtimes }}
RUNTIMES_CHECK_TARGETS: ${{ needs.premerge-compute-macos.outputs.macos-runtimes-check-targets }}
CLANG_TOOLCHAIN_PROGRAM_TIMEOUT: 120
GITHUB_TOKEN: ${{ github.token }}
GITHUB_PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
export SCCACHE_IDLE_TIMEOUT=0
mkdir -p artifacts
SCCACHE_LOG=info SCCACHE_ERROR_LOG=$(pwd)/artifacts/sccache.log sccache --start-server
xcrun cmake -G Ninja \
-B build \
-S llvm \
-DLLVM_ENABLE_PROJECTS="${PROJECTS}" \
-DLLVM_DISABLE_ASSEMBLY_FILES=ON \
-DCMAKE_BUILD_TYPE=Release \
-DLLDB_INCLUDE_TESTS=OFF \
-DLLVM_ENABLE_ASSERTIONS=ON \
-DCMAKE_C_COMPILER_LAUNCHER=sccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache
ninja -C build ${CHECK_TARGETS}
- name: Show sccache stats
if: always()
run: |
mkdir -p artifacts
sccache --show-stats
sccache --show-stats >> artifacts/sccache_stats.txt
./.ci/monolithic-macos.sh "${PROJECTS}" "${CHECK_TARGETS}" "${RUNTIMES}" "${RUNTIMES_CHECK_TARGETS}"
- name: Upload Artifacts
# In some cases, Github will fail to upload the artifact. We want to
# continue anyways as a failed artifact upload is an infra failure, not

View file

@ -53,9 +53,6 @@ permissions:
jobs:
prepare:
name: Prepare to build binaries
environment:
deployment: false
name: ${{ case( github.event_name == 'pull_request', null, 'release') }}
runs-on: ${{ inputs.runs-on }}
if: github.repository_owner == 'llvm'
outputs:
@ -83,14 +80,6 @@ jobs:
with:
release-version: ${{ inputs.release-version }}
- name: Check Permissions
if: github.event_name != 'pull_request'
uses: ./.github/workflows/require-team-membership
with:
team-slug: llvm-release-managers
LLVM_TOKEN_GENERATOR_CLIENT_ID: ${{ secrets.LLVM_TOKEN_GENERATOR_CLIENT_ID }}
LLVM_TOKEN_GENERATOR_PRIVATE_KEY: ${{ secrets.LLVM_TOKEN_GENERATOR_PRIVATE_KEY }}
# The name of the Windows binaries uses the version from source, so we need
# to fetch it here.
- id: version-from-source
@ -368,6 +357,9 @@ jobs:
upload-release-binaries:
name: "Upload Release Binaries"
environment:
deployment: false
name: release
needs:
- prepare
- build-release-package
@ -385,6 +377,7 @@ jobs:
with:
persist-credentials: false
sparse-checkout: |
.github/workflows/require-team-membership
.github/workflows/upload-release-artifact
.github/workflows/validate-release-version
llvm/utils/release/github-upload-release.py

View file

@ -135,7 +135,7 @@ jobs:
with:
persist-credentials: false
sparse-checkout: |
.github/workflows/upload-release-artifact
.github/
llvm/utils/release/github-upload-release.py
llvm/utils/git/requirements.txt
sparse-checkout-cone-mode: false
@ -143,6 +143,7 @@ jobs:
- name: Upload Artifacts
uses: ./.github/workflows/upload-release-artifact
with:
release-version: ${{ inputs.release-version }}
artifact-id: ${{ needs.release-sources.outputs.artifact-id }}
attestation-name: ${{ needs.inputs.outputs.ref }}-sources-attestation
digest: ${{ needs.release-sources.outputs.digest }}

View file

@ -65,6 +65,10 @@ jobs:
needs:
- validate-tag
- release-create
permissions:
contents: write
id-token: write
attestations: write
uses: ./.github/workflows/release-documentation.yml
with:
release-version: ${{ needs.validate-tag.outputs.release-version }}

View file

@ -6,7 +6,7 @@ inputs:
team-slug:
description: >-
The team name (in slug form) that the github actor must be a member of.
requried: true
required: true
LLVM_TOKEN_GENERATOR_CLIENT_ID:
description: >-
Client ID for our GitHub App we use for generating access tokens.

View file

@ -427,6 +427,11 @@ public:
PseudoProbeDecoder = Decoder;
}
/// Release the pseudo probe decoder once probes have been updated, freeing
/// its (potentially large) address-to-probe maps before later, memory-heavy
/// phases such as debug info rewriting.
void resetPseudoProbeDecoder() { PseudoProbeDecoder.reset(); }
/// Return BinaryFunction containing a given \p Address or nullptr if
/// no registered function contains the \p Address.
///

View file

@ -14,6 +14,7 @@
#ifndef BOLT_CORE_DEBUG_DATA_H
#define BOLT_CORE_DEBUG_DATA_H
#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/DIE.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
@ -63,6 +64,20 @@ findAttributeInfo(const DWARFDie DIE,
std::optional<AttrInfo> findAttributeInfo(const DWARFDie DIE,
dwarf::Attribute Attr);
/// Streams the DIEs of \p Unit one at a time via
/// DWARFDebugInfoEntry::extractFast, invoking \p Callback on each non-null DIE,
/// without ever materializing the unit's full DIE vector (as
/// DWARFUnit::dies() / extractDIEsToVector() would). Keeping those vectors out
/// of memory is a meaningful RSS win during debug-info processing -- BOLT's
/// memory-heaviest phase -- especially for split-DWARF binaries with tens of
/// thousands of .dwo units.
///
/// Only DIE attributes should be inspected in \p Callback: the tree structure
/// is not reconstructed, so DWARFDie::children() / getParent() / getSibling()
/// are unavailable on the visited DIEs.
void forEachDIEInUnit(DWARFUnit &Unit,
llvm::function_ref<void(const DWARFDie &)> Callback);
// DWARF5 Header in order of encoding.
// Types represent encoding sizes.
using UnitLengthType = uint64_t;

View file

@ -1791,8 +1791,15 @@ void BinaryContext::preprocessDWODebugInfo() {
}
// Prevent failures when DWOName is already an absolute path.
sys::path::make_absolute(DWOCompDir, AbsolutePath);
// Extract only the .dwo CU DIE here: we just need the DWO unit pointer
// (for DWOCUs) and the isDWOUnit()/DWOId checks. The full DIE vector is
// never read off this cached array -- every consumer streams the DIEs on
// demand with DWARFDebugInfoEntry::extractFast (DIEBuilder::
// constructFromUnit / collectReferencedTypeSignatures).
DWARFUnit *DWOCU =
DwarfUnit->getNonSkeletonUnitDIE(false, AbsolutePath).getDwarfUnit();
DwarfUnit
->getNonSkeletonUnitDIE(/*ExtractUnitDIEOnly=*/true, AbsolutePath)
.getDwarfUnit();
if (!DWOCU->isDWOUnit()) {
this->outs()
<< "BOLT-WARNING: Debug Fission: DWO debug information for "
@ -2022,38 +2029,10 @@ void BinaryContext::collectDebugScopeBoundaries() {
continue;
DWARFUnit *DIEUnit = CUDie.getDwarfUnit();
// For split DWARF, preprocessDWODebugInfo already fully extracts the .dwo's
// DIE array.
if (DIEUnit->isDWOUnit()) {
for (const DWARFDebugInfoEntry &Entry : DIEUnit->dies())
processScopeDie(DWARFDie(DIEUnit, &Entry));
continue;
}
// Walk the unit's DIEs by streaming them one at a time. Track nesting depth
// with a counter: a DIE with children descends a level (++), a null entry
// (sibling-chain terminator) ascends (--), the unit-end offset limits the
// walk. This is done to avoid recording all DIEs in a vector like
// DWARFUnit's extractDIEsToVector() does, since that is more work than
// needed if we just want to lookup specific tags.
DWARFDataExtractor DebugInfoData = DIEUnit->getDebugInfoExtractor();
uint64_t DIEOffset = DIEUnit->getOffset() + DIEUnit->getHeaderSize();
const uint64_t NextCUOffset = DIEUnit->getNextUnitOffset();
DWARFDebugInfoEntry DIEEntry;
int32_t CurrentDepth = 1;
while (CurrentDepth > 0 && DIEOffset < NextCUOffset &&
DIEEntry.extractFast(*DIEUnit, &DIEOffset, DebugInfoData,
NextCUOffset, 0)) {
const DWARFAbbreviationDeclaration *Abbrev =
DIEEntry.getAbbreviationDeclarationPtr();
if (!Abbrev) {
// End of the current sibling chain.
--CurrentDepth;
continue;
}
processScopeDie(DWARFDie(DIEUnit, &DIEEntry));
if (Abbrev->hasChildren())
++CurrentDepth;
}
// Stream the unit's DIEs one at a time rather than recording them all in a
// vector (as DWARFUnit's extractDIEsToVector() does): only DIE tags/ranges
// are inspected, so the tree structure is not needed.
forEachDIEInUnit(*DIEUnit, processScopeDie);
}
}

View file

@ -8,6 +8,7 @@
#include "bolt/Core/DIEBuilder.h"
#include "bolt/Core/BinaryContext.h"
#include "bolt/Core/DebugData.h"
#include "bolt/Core/ParallelUtilities.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/BinaryFormat/Dwarf.h"
@ -283,23 +284,17 @@ void DIEBuilder::buildTypeUnits(DebugStrOffsetsWriter *StrOffsetWriter,
}
}
/// Recursively collects type unit signatures from the given DIE and all of its
/// children.
/// Collects the signatures of all type units referenced (via DW_FORM_ref_sig8)
/// by any DIE of \p U. The DIEs are streamed with forEachDIEInUnit so the
/// unit's full DIE vector is never materialized.
///
/// Note: De-duplication of the collected signatures is handled at the outer
/// level by registerUnit.
static void collectReferencedTypeSignatures(DWARFDie Die,
static void collectReferencedTypeSignatures(DWARFUnit &U,
DenseSet<uint64_t> &ProcessedTU,
SmallVectorImpl<uint64_t> &TUlist) {
SmallVector<DWARFDie, 8> DIElist;
DIElist.push_back(Die);
while (!DIElist.empty()) {
DWARFDie Current = DIElist.pop_back_val();
if (!Current)
continue;
for (const DWARFAttribute &Attr : Current.attributes()) {
forEachDIEInUnit(U, [&](const DWARFDie &Die) {
for (const DWARFAttribute &Attr : Die.attributes()) {
if (Attr.Value.getForm() != dwarf::DW_FORM_ref_sig8)
continue;
if (const std::optional<uint64_t> Signature =
@ -307,10 +302,7 @@ static void collectReferencedTypeSignatures(DWARFDie Die,
if (ProcessedTU.insert(*Signature).second)
TUlist.push_back(*Signature);
}
for (DWARFDie Child : Current.children())
DIElist.push_back(Child);
}
});
}
void DIEBuilder::buildDWPTypeUnitsForUnit(DWARFUnit &U) {
@ -319,7 +311,7 @@ void DIEBuilder::buildDWPTypeUnitsForUnit(DWARFUnit &U) {
DenseSet<uint64_t> ProcessedTU;
SmallVector<uint64_t, 8> TUlist;
// Collecting signatures of type units referenced by this unit.
collectReferencedTypeSignatures(U.getUnitDIE(), ProcessedTU, TUlist);
collectReferencedTypeSignatures(U, ProcessedTU, TUlist);
getState().Type = U.getVersion() < 5 ? ProcessingType::DWARF4TUs
: ProcessingType::DWARF5TUs;
@ -336,7 +328,7 @@ void DIEBuilder::buildDWPTypeUnitsForUnit(DWARFUnit &U) {
if (!UnitId || getState().CloneUnitCtxMap[*UnitId].IsConstructed)
continue;
collectReferencedTypeSignatures(TU->getUnitDIE(), ProcessedTU, TUlist);
collectReferencedTypeSignatures(*TU, ProcessedTU, TUlist);
}
// Ensure original order of processing type units

View file

@ -101,6 +101,25 @@ std::optional<AttrInfo> findAttributeInfo(const DWARFDie DIE,
return findAttributeInfo(DIE, AbbrevDecl, *Index);
}
void forEachDIEInUnit(DWARFUnit &Unit,
function_ref<void(const DWARFDie &)> Callback) {
DWARFDataExtractor DebugInfoData = Unit.getDebugInfoExtractor();
uint64_t DIEOffset = Unit.getOffset() + Unit.getHeaderSize();
const uint64_t NextCUOffset = Unit.getNextUnitOffset();
DWARFDebugInfoEntry DIEEntry;
// ParentIdx is passed as 0 throughout: we visit every DIE but never
// reconstruct the tree, so the parent linkage extractFast would record is
// unused. The single reusable transient entry means no DIE vector is built.
while (DIEOffset < NextCUOffset &&
DIEEntry.extractFast(Unit, &DIEOffset, DebugInfoData, NextCUOffset,
/*ParentIdx=*/0)) {
if (!DIEEntry.getAbbreviationDeclarationPtr())
continue; // Null entry: terminator of a sibling chain.
DWARFDie Die(&Unit, &DIEEntry);
Callback(Die);
}
}
[[maybe_unused]]
static void printLE64(const std::string &S) {
for (uint32_t I = 0, Size = S.size(); I < Size; ++I) {

View file

@ -257,6 +257,7 @@ bool BinaryFunctionPass::shouldPrint(const BinaryFunction &BF) const {
}
void NormalizeCFG::runOnFunction(BinaryFunction &BF) {
const BinaryContext &BC = BF.getBinaryContext();
uint64_t NumRemoved = 0;
uint64_t NumDuplicateEdges = 0;
uint64_t NeedsFixBranches = 0;
@ -286,7 +287,14 @@ void NormalizeCFG::runOnFunction(BinaryFunction &BF) {
// Redirect all predecessors to the successor block.
while (!BB.pred_empty()) {
BinaryBasicBlock *Predecessor = *BB.pred_begin();
if (Predecessor->hasJumpTable())
// Do not redirect a predecessor that reaches this block via an indirect
// branch. This covers both a regular jump table and an unresolved
// "unknown control flow" indirect jump (in --strict mode), where the
// instruction has no jump table annotation but the jump table data still
// references this block. Removing this block would leave a dangling
// reference in .rodata, an undefined jump table entry at emission time.
const MCInst *LastInst = Predecessor->getLastNonPseudoInstr();
if (LastInst && BC.MIB->isIndirectBranch(*LastInst))
break;
if (Predecessor == Successor)

View file

@ -589,40 +589,28 @@ static SmallVector<SmallVector<DWARFUnit *>> partitionCUs(DWARFContext &DwCtx,
}
if (RefAddrAbbrevs.empty())
continue;
// Track CUs involved in cross-CU references via DW_FORM_ref_addr.
uint64_t DIEOffset = CU->getOffset() + CU->getHeaderSize();
const uint64_t NextCUOffset = CU->getNextUnitOffset();
DWARFDataExtractor DebugInfoData = CU->getDebugInfoExtractor();
DWARFDebugInfoEntry DIEEntry;
// extractFast() here only attributes are inspected here, so ParentIdx is
// passed as a dummy value.
while (DIEOffset < NextCUOffset) {
if (!DIEEntry.extractFast(*CU, &DIEOffset, DebugInfoData, NextCUOffset,
/*ParentIdx=*/0))
break;
const DWARFAbbreviationDeclaration *Abbrev =
DIEEntry.getAbbreviationDeclarationPtr();
if (Abbrev) {
if (RefAddrAbbrevs.count(Abbrev)) {
DWARFDie Die(CU, &DIEEntry);
for (const DWARFAttribute &Attr : Die.attributes()) {
if (Attr.Value.getForm() != dwarf::DW_FORM_ref_addr)
continue;
auto OptRef = Attr.Value.getAsDebugInfoReference();
if (!OptRef)
continue;
DWARFUnit *TargetCU = DwCtx.getUnitForOffset(*OptRef);
if (!TargetCU || TargetCU == CU)
continue;
if (CrossRefSet.insert(CU).second)
EC.insert(CU);
if (CrossRefSet.insert(TargetCU).second)
EC.insert(TargetCU);
EC.unionSets(CU, TargetCU);
}
}
// Track CUs involved in cross-CU references via DW_FORM_ref_addr. Only DIE
// attributes are inspected, so forEachDIEInUnit streams the DIEs without
// materializing the unit's full DIE vector.
forEachDIEInUnit(*CU, [&](const DWARFDie &Die) {
if (!RefAddrAbbrevs.count(Die.getAbbreviationDeclarationPtr()))
return;
for (const DWARFAttribute &Attr : Die.attributes()) {
if (Attr.Value.getForm() != dwarf::DW_FORM_ref_addr)
continue;
auto OptRef = Attr.Value.getAsDebugInfoReference();
if (!OptRef)
continue;
DWARFUnit *TargetCU = DwCtx.getUnitForOffset(*OptRef);
if (!TargetCU || TargetCU == CU)
continue;
if (CrossRefSet.insert(CU).second)
EC.insert(CU);
if (CrossRefSet.insert(TargetCU).second)
EC.insert(TargetCU);
EC.unionSets(CU, TargetCU);
}
}
});
}
DenseMap<DWARFUnit *, SmallVector<DWARFUnit *>> MembersByLeader;

View file

@ -104,6 +104,13 @@ Error PseudoProbeRewriter::postEmitFinalizer() {
parsePseudoProbe();
updatePseudoProbes();
// The decoder's address-to-probe maps can be very large and are no longer
// needed once probes have been updated. Release both our reference and the
// one held by BinaryContext so the memory is freed before the memory-heavy
// debug info rewriting phase runs.
BC.resetPseudoProbeDecoder();
ProbeDecoderPtr.reset();
return Error::success();
}

View file

@ -2633,8 +2633,8 @@ void RewriteInstance::adjustCommandLineOptions() {
BC->UseCompactAligner = opts::UseCompactAligner;
BC->X86AlignBranchBoundaryHotOnly = opts::X86AlignBranchBoundaryHotOnly;
if (BC->isX86() && opts::Lite.getNumOccurrences() == 0 && !opts::StrictMode &&
!opts::UseOldText)
if ((BC->isX86() || BC->isAArch64()) && opts::Lite.getNumOccurrences() == 0 &&
!opts::StrictMode && !opts::UseOldText)
opts::Lite = true;
if (opts::Lite && opts::UseOldText) {
@ -5990,7 +5990,7 @@ void RewriteInstance::patchELFAllocatableRelrSection(
RelOffset = RelOffset == 0 ? SectionAddress + Rel.Offset : RelOffset;
assert((RelOffset & 1) == 0 && "Wrong relocation offset");
RelOffsets.emplace(RelOffset);
FixAddend(Section, Rel, RelOffset);
FixAddend(Section, Rel, getFileOffsetForAddress(RelOffset));
}
}

View file

@ -156,6 +156,8 @@ Error SDTRewriter::postEmitFinalizer() {
SDTNotePatcher->addLE64Patch(SDTInfo.PCOffset, NewAddress);
}
SDTMarkers.clear();
return Error::success();
}

View file

@ -21,8 +21,23 @@
# RUN: llvm-readelf -rsW %t.relr.bolt | FileCheck --check-prefix=RELRELFCHECK %s
# RUN: llvm-readelf -SW %t.relr.bolt | FileCheck --check-prefix=RELRSZCHECK %s
// Check that RELR addends are patched at file offsets rather than virtual
// addresses when the two differ.
# RUN: %clang %cflags -fPIC -pie %t.o -o %t.relr.bias.exe -nostdlib \
# RUN: -Wl,-q -Wl,-z,notext -Wl,--pack-dyn-relocs=relr \
# RUN: -Wl,--image-base=0x20000
# RUN: llvm-objcopy --remove-section .rela.mytext %t.relr.bias.exe
# RUN: llvm-readelf -lW %t.relr.bias.exe | FileCheck --check-prefix=BIAS %s
# RUN: llvm-bolt %t.relr.bias.exe -o %t.relr.bias.bolt --use-old-text=0 \
# RUN: --lite=0
# RUN: llvm-objdump -j .text -d -z %t.relr.bias.bolt | \
# RUN: FileCheck %s --check-prefix=ADDENDCHECK
// Check that the CI value was updated
# CHECK: [[#%x,ADDR:]] <exitLocal>:
# BIAS: LOAD {{.*}} 0x0000000000020000
# CHECK: {{.*}} <$d>:
# CHECK-NEXT: {{.*}} .word 0x{{[0]+}}[[#ADDR]]
# CHECK-NEXT: {{.*}} .word 0x00000000

View file

@ -6,7 +6,7 @@
# RUN: %clang %cflags -O0 %t.o -o %t.exe -Wl,-q
# RUN: link_fdata %s %t.o %t.fdata
# RUN: llvm-bolt %t.exe -o %t.bolt \
# RUN: --data %t.fdata | FileCheck %s
# RUN: --data %t.fdata --lite=0 | FileCheck %s
# CHECK: BOLT-INFO: Inserted 1 stubs in the hot area and 0 stubs in the cold area.

View file

@ -0,0 +1,99 @@
## This reproduces a bug where a PIC jump table entry points to a basic block
## that becomes empty after the remove-nops pass.
##
## In relocation mode BOLT creates the jump table object from the PC-relative
## leaq JUMP_TABLE(%rip) reference (BinaryContext::handleAddressRef) whenever
## the referenced memory looks like a PIC jump table. This is independent of
## whether the indirect jump dispatch is recognized. The jump table annotation
## is attached to the jmp only when analyzeIndirectBranch matches the dispatch
## pattern -- and the pattern below defeats it by materializing the jump table
## base twice, into two separate registers. So the jump table object and its
## .rodata entries exist and reference basic blocks, but the jmp has no jump
## table annotation, i.e. BinaryBasicBlock::hasJumpTable() is false for it.
##
## In --strict mode the function is still fully processed: it stays simple with
## the block marked as unknown control flow. Jump table entry 0 targets a
## nop-only block that falls through to the next block. remove-nops empties that
## block, and NormalizeCFG used to redirect its predecessor and delete the empty
## block -- it did not recognize the block as a jump table target because the
## predecessor's hasJumpTable() is false. The jump table object still referenced
## the deleted block by label, leaving a dangling reference in .rodata and
## failing emission with "Undefined temporary symbol".
##
## The fix keeps the block because its predecessor ends in an indirect branch.
# REQUIRES: system-linux
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o
# RUN: llvm-strip --strip-unneeded %t.o
# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q -no-pie
# RUN: llvm-bolt %t.exe -o %t.out --lite=0 -v=1 --strict=1 \
# RUN: -print-cfg -print-only=main 2>&1 | FileCheck %s
## Confirm we exercise the unknown-control-flow / PIC jump table path.
# CHECK: Unknown CF : true
# CHECK: jmpq {{.*}}%rax # UNKNOWN CONTROL FLOW
# CHECK: PIC Jump table JUMP_TABLE for function main
## Emission must succeed: the (empty) jump table target block is preserved so
## its label stays defined.
# CHECK-NOT: Undefined temporary symbol
# CHECK-NOT: BOLT-ERROR: Emission failed
.text
.globl main
.type main, %function
.p2align 2
main:
LBB0:
subq $0x38, %rsp
cmpl $0x4, %edi
ja LBBdefault
## Jump table dispatch. Materializing the jump table base twice (into %rax and
## then %rdx) prevents analyzeIndirectBranch from recognizing the pattern, so no
## jump table annotation is attached to the jmp and it stays unknown control
## flow in strict mode.
LBB1:
movl %edi, %eax
leaq (,%rax,4), %rdx
leaq JUMP_TABLE(%rip), %rax
movl (%rdx,%rax), %eax
cltq
leaq JUMP_TABLE(%rip), %rdx
addq %rdx, %rax
jmpq *%rax
## Jump table entry 0 target: a single nop that falls through to LBBjoin. The
## remove-nops pass empties this block; NormalizeCFG would (without the fix)
## delete it while the jump table still references it.
LBBnop:
nop
LBBjoin:
addq $0x38, %rsp
ret
LBBc1:
movl $0x1, %eax
jmp LBBjoin2
LBBc2:
movl $0x2, %eax
LBBjoin2:
addq $0x38, %rsp
ret
LBBdefault:
xorl %eax, %eax
addq $0x38, %rsp
ret
.size main, .-main
.rodata
## jump table, entries must be R_X86_64_PC32 relocs
.globl JUMP_TABLE
JUMP_TABLE:
.long LBBnop-JUMP_TABLE
.long LBBc1-JUMP_TABLE
.long LBBc2-JUMP_TABLE
.long LBBjoin-JUMP_TABLE
.long LBBc1-JUMP_TABLE

View file

@ -167,7 +167,7 @@ void UseStdMinMaxCheck::check(const MatchFinder::MatchResult &Result) {
const auto *BinaryOp = Result.Nodes.getNodeAs<BinaryOperator>("binaryOp");
const BinaryOperatorKind BinaryOpcode = BinaryOp->getOpcode();
const SourceLocation IfLocation = If->getIfLoc();
const SourceLocation ThenLocation = If->getEndLoc();
SourceLocation ThenLocation = If->getEndLoc();
auto ReplaceAndDiagnose = [&](const StringRef FunctionName) {
const SourceManager &Source = *Result.SourceManager;
@ -218,6 +218,12 @@ void UseStdMinMaxCheck::check(const MatchFinder::MatchResult &Result) {
if (Semi != StringRef::npos && PostInner.take_front(Semi).trim().empty())
PostInner = PostInner.drop_front(Semi + 1);
AppendNormalized(PostInner);
} else if (const auto SemiTok =
Lexer::findNextToken(ThenLocation, Source, LO);
SemiTok && SemiTok->is(tok::semi)) {
AppendNormalized(
GetSourceText(ThenLocation, SemiTok->getLocation()).rtrim());
ThenLocation = SemiTok->getLocation();
}
diag(IfLocation, "use `%0` instead of `%1`")

View file

@ -171,13 +171,6 @@ TEST(PreamblePatchTest, ContainsNewIncludes) {
Field(&Inclusion::HashLine, 4))));
}
TEST(PreamblePatchTest, MainFileIsEscaped) {
auto Includes = collectPatchedIncludes("#include <a.h>", "", "file\"name.cpp")
.MainFileIncludes;
EXPECT_THAT(Includes, ElementsAre(AllOf(Field(&Inclusion::Written, "<a.h>"),
Field(&Inclusion::HashLine, 0))));
}
TEST(PreamblePatchTest, PatchesPreambleIncludes) {
MockFS FS;
IgnoreDiagnostics Diags;

View file

@ -121,6 +121,10 @@ Changes in existing checks
exclusively for overload resolution. Added the :option:`IgnoredTypes`
option to allow customizing the set of ignored types.
- Improved :doc:`readability-use-std-min-max
<clang-tidy/checks/readability/use-std-min-max>` check by fixing spurious
trailing semicolons and lost comments when the ``if`` body has no braces.
Removed checks
^^^^^^^^^^^^^^

View file

@ -7,11 +7,12 @@ Finds and removes redundant `void` argument lists.
Works in C++ and in C23 and up.
Examples:
: | Initial code | Code with applied fixes |
| --------------------------------- | ------------------------- |
| `int f(void);` | `int f();` |
| `int (*f(void))(void);` | `int (*f())();` |
| `typedef int (*f_t(void))(void);` | `typedef int (*f_t())();` |
| `void (C::*p)(void);` | `void (C::*p)();` |
| `C::C(void) {}` | `C::C() {}` |
| `C::~C(void) {}` | `C::~C() {}` |
| Initial code | Code with applied fixes |
| --------------------------------- | ------------------------- |
| `int f(void);` | `int f();` |
| `int (*f(void))(void);` | `int (*f())();` |
| `typedef int (*f_t(void))(void);` | `typedef int (*f_t())();` |
| `void (C::*p)(void);` | `void (C::*p)();` |
| `C::C(void) {}` | `C::C() {}` |
| `C::~C(void) {}` | `C::~C() {}` |

View file

@ -7,14 +7,16 @@
//===----------------------------------------------------------------------===//
#include "AnalysisInternal.h"
#include "TypesInternal.h"
#include "clang-include-cleaner/Types.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/Tooling/Inclusions/StandardLibrary.h"
#include "llvm/Support/Casting.h"
#include <utility>
#include "llvm/Support/ErrorHandling.h"
#include <vector>
namespace clang::include_cleaner {
@ -26,13 +28,19 @@ template <typename T> Hints completeIfDefinition(T *D) {
}
Hints declHints(const Decl *D) {
// Definition is only needed for classes and templates for completeness.
// For C++, definition is only needed for classes and templates for
// completeness. For Objective-C, definition is needed for classes and
// protocols. TagDecl covers enums and structs.
if (auto *TD = llvm::dyn_cast<TagDecl>(D))
return completeIfDefinition(TD);
else if (auto *CTD = llvm::dyn_cast<ClassTemplateDecl>(D))
return completeIfDefinition(CTD);
else if (auto *FTD = llvm::dyn_cast<FunctionTemplateDecl>(D))
return completeIfDefinition(FTD);
else if (auto *OID = llvm::dyn_cast<ObjCInterfaceDecl>(D))
return completeIfDefinition(OID);
else if (auto *OPD = llvm::dyn_cast<ObjCProtocolDecl>(D))
return completeIfDefinition(OPD);
// Any other declaration is assumed usable.
return Hints::CompleteSymbol;
}

View file

@ -15,7 +15,6 @@
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/TemplateName.h"
@ -25,7 +24,6 @@
#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/Specifiers.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Casting.h"
@ -395,6 +393,170 @@ public:
report(E->getExprLoc(), E->getOperatorDelete(), RefType::Ambiguous);
return true;
}
// Objective-C support
bool VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc TL) {
reportType(TL.getNameLoc(), TL.getIFaceDecl());
return true;
}
// Protocols are odd in that they are covered by Traverse instead of Visit.
bool TraverseObjCProtocolLoc(ObjCProtocolLoc ProtocolLoc) {
if (auto *Proto = ProtocolLoc.getProtocol()) {
report(ProtocolLoc.getLocation(), Proto);
}
return true;
}
bool VisitObjCImplementationDecl(ObjCImplementationDecl *D) {
if (auto *Interface = D->getClassInterface()) {
report(D->getLocation(), Interface);
}
return true;
}
bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
// Identify the selector and the method declaration
if (auto *Method = E->getMethodDecl()) {
// Report the method as a used symbol
report(E->getSelectorStartLoc(), Method);
}
// If it's a class message, report the interface/class as used
if (E->getReceiverKind() == ObjCMessageExpr::Class) {
if (auto *Interface = E->getReceiverInterface()) {
report(E->getReceiverRange().getBegin(), Interface);
}
}
return true;
}
bool VisitObjCPropertyDecl(clang::ObjCPropertyDecl *PD) {
reportType(PD->getLocation(), PD);
return true;
}
bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *E) {
// Unconditionally report property declarations and their backing accessor
// methods. Dot-notation or pseudo-object references (`foo.bar`) require
// the underlying property definition or getters/setters to compile.
// Bypassing transient compiler state flags (isMessagingGetter/
// isMessagingSetter) guarantees that the declaring
// header keeps the properties recorded as used.
if (E->isExplicitProperty()) {
if (auto *Prop = E->getExplicitProperty()) {
report(E->getLocation(), Prop);
if (auto *Getter = Prop->getGetterMethodDecl())
report(E->getLocation(), Getter);
if (auto *Setter = Prop->getSetterMethodDecl())
report(E->getLocation(), Setter);
}
} else {
if (auto *Getter = E->getImplicitPropertyGetter())
report(E->getLocation(), Getter);
if (auto *Setter = E->getImplicitPropertySetter())
report(E->getLocation(), Setter);
}
return true;
}
bool VisitObjCProtocolExpr(ObjCProtocolExpr *E) {
if (auto *Proto = E->getProtocol()) {
report(E->getProtocolIdLoc(), Proto);
}
return true;
}
bool VisitCastExpr(CastExpr *E) {
// Handle implicit or explicit casts between Objective-C object pointers
// aimed towards protocol-qualification (e.g., `ClassName *` to
// `id<Proto>`).
QualType SourceType = E->getSubExpr()->getType();
QualType DestType = E->getType();
const auto *SrcPtr = SourceType->getAs<ObjCObjectPointerType>();
const auto *DestPtr = DestType->getAs<ObjCObjectPointerType>();
// If we're casting from a known class pointer to protocol conformance.
if (SrcPtr && DestPtr && SrcPtr->getInterfaceDecl()) {
const ObjCInterfaceDecl *Class = SrcPtr->getInterfaceDecl();
ASTContext &Ctx = Class->getASTContext();
// For every protocol required by the destination type.
for (const ObjCProtocolDecl *Proto : DestPtr->quals()) {
const ObjCInterfaceDecl *Current = Class;
// Search the inheritance hierarchy for the provider of conformance.
while (Current) {
bool ConformsDirectly = false;
for (const auto *PI : Current->protocols()) {
if (Ctx.ProtocolCompatibleWithProtocol(
const_cast<ObjCProtocolDecl *>(Proto),
const_cast<ObjCProtocolDecl *>(PI))) {
ConformsDirectly = true;
break;
}
}
// If the class itself provides the conformance directly, we don't
// need to keep searching Categories.
if (ConformsDirectly)
break;
// If the class doesn't declare direct conformance but conformance is
// injected via a visible Category attached to this class, note that
// the category header is required by recording an Implicit reference
// to it.
for (const auto *Cat : Current->visible_categories()) {
for (auto *PI : Cat->protocols()) {
if (Ctx.ProtocolCompatibleWithProtocol(
const_cast<ObjCProtocolDecl *>(Proto),
const_cast<ObjCProtocolDecl *>(PI))) {
report(E->getExprLoc(), const_cast<ObjCCategoryDecl *>(Cat),
RefType::Implicit);
}
}
}
Current = Current->getSuperClass();
}
}
}
return true;
}
bool VisitObjCCategoryDecl(ObjCCategoryDecl *D) {
// A category declaration depends on its base interface.
if (auto *Interface = D->getClassInterface()) {
report(D->getLocation(), Interface);
}
return true;
}
bool VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *D) {
// Implementation requires the base interface.
if (auto *Interface = D->getClassInterface()) {
report(D->getLocation(), Interface);
}
// Implementation requires the category declaration.
if (auto *Category = D->getCategoryDecl()) {
report(D->getCategoryNameLoc(), Category);
}
return true;
}
bool VisitObjCCompatibleAliasDecl(ObjCCompatibleAliasDecl *D) {
// An alias declaration requires the underlying class.
if (auto *Aliased = D->getClassInterface()) {
report(D->getLocation(), Aliased);
}
return true;
}
bool VisitObjCIvarRefExpr(ObjCIvarRefExpr *E) {
if (auto *Ivar = E->getDecl()) {
report(E->getLocation(), Ivar);
}
return true;
}
};
} // namespace

View file

@ -21,6 +21,7 @@
#include "llvm/Testing/Annotations/Annotations.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <cassert>
#include <tuple>
#include <vector>
@ -46,7 +47,12 @@ public:
LocateExample(llvm::StringRef AnnotatedCode)
: Target(AnnotatedCode), AST([this] {
TestInputs Inputs(Target.code());
Inputs.ExtraArgs.push_back("-std=c++17");
if (Target.code().starts_with("@")) {
Inputs.ExtraArgs.push_back("-x");
Inputs.ExtraArgs.push_back("objective-c");
} else {
Inputs.ExtraArgs.push_back("-std=c++17");
}
return Inputs;
}()) {}
@ -161,9 +167,13 @@ TEST(LocateSymbol, CompleteSymbolHint) {
{
// Completeness is only absent in cases that matters.
const llvm::StringLiteral Cases[] = {
"enum ^foo : int; enum ^foo : int {};",
"enum class ^foo; enum class ^foo {};",
"struct ^foo; struct ^foo {};",
"template <typename> struct ^foo; template <typename> struct ^foo {};",
"template <typename> void ^foo(); template <typename> void ^foo() {};",
"@class ^foo; @interface ^foo @end",
"@protocol ^foo; @protocol ^foo @end",
};
for (auto &Case : Cases) {
SCOPED_TRACE(Case);

View file

@ -41,8 +41,9 @@ using testing::ElementsAre;
// Referencing: int x = ^foo();
// There must be exactly one referencing location marked.
// Returns target decls.
std::vector<Decl::Kind> testWalk(llvm::StringRef TargetCode,
llvm::StringRef ReferencingCode) {
std::vector<Decl::Kind>
testWalk(llvm::StringRef TargetCode, llvm::StringRef ReferencingCode,
std::vector<std::string> ExtraArgs = {"-std=c++20"}) {
llvm::Annotations Target(TargetCode);
llvm::Annotations Referencing(ReferencingCode);
@ -50,7 +51,8 @@ std::vector<Decl::Kind> testWalk(llvm::StringRef TargetCode,
Inputs.ExtraFiles["target.h"] = Target.code().str();
Inputs.ExtraArgs.push_back("-include");
Inputs.ExtraArgs.push_back("target.h");
Inputs.ExtraArgs.push_back("-std=c++20");
for (const auto &Arg : ExtraArgs)
Inputs.ExtraArgs.push_back(Arg);
TestAST AST(Inputs);
const auto &SM = AST.sourceManager();
@ -576,5 +578,293 @@ TEST(WalkAST, CleanupAttr) {
"void foo() { __attribute__((__cleanup__(^freep))) char* x = 0; }");
}
TEST(WalkAST, ObjCInterfaceTypeLoc) {
testWalk(R"objc(
@interface $explicit^MyClass
@end
)objc",
R"objc(
void test() {
^MyClass *obj;
}
)objc",
{"-x", "objective-c"});
}
TEST(WalkAST, ObjCImplementationDeclDependsOnInterface) {
testWalk(R"objc(
@interface $explicit^MyClass
@end
)objc",
R"objc(
@implementation ^MyClass
@end
)objc",
{"-x", "objective-c"});
}
TEST(WalkAST, ObjCMessageExprSelectorLoc) {
testWalk(R"objc(
@interface MyClass
$explicit^- (void)doSomething;
@end
)objc",
R"objc(
void test(MyClass *obj) {
[obj ^doSomething];
}
)objc",
{"-x", "objective-c"});
}
TEST(WalkAST, ObjCMessageExprClassReceiver) {
testWalk(R"objc(
@interface $explicit^MyClass
+ (void)classMethod;
@end
)objc",
R"objc(
void test() {
[^MyClass classMethod];
}
)objc",
{"-x", "objective-c"});
}
TEST(WalkAST, ObjCPropertyRefExprExplicit) {
testWalk(R"objc(
@interface MyClass
@property(nonatomic) int $explicit^foo;
@end
)objc",
R"objc(
void test(MyClass *obj) {
int x = obj.^foo;
}
)objc",
{"-x", "objective-c"});
}
TEST(WalkAST, ObjCPropertyRefExprImplicitGetter) {
testWalk(R"objc(
@interface MyClass
$explicit^- (int)foo;
@end
)objc",
R"objc(
void test(MyClass *obj) {
int x = obj.^foo;
}
)objc",
{"-x", "objective-c"});
}
TEST(WalkAST, ObjCPropertyRefExprImplicitSetter) {
testWalk(R"objc(
@interface MyClass
$explicit^- (void)setFoo:(int)val;
@end
)objc",
R"objc(
void test(MyClass *obj) {
obj.^foo = 42;
}
)objc",
{"-x", "objective-c"});
}
TEST(WalkAST, ObjCPropertyRefExprExplicitSetter) {
testWalk(R"objc(
@interface MyClass
@property(nonatomic) int $explicit^foo;
@end
)objc",
R"objc(
void test(MyClass *obj) {
obj.^foo = 42;
}
)objc",
{"-x", "objective-c"});
}
TEST(WalkAST, ObjCPropertyRefExprProtocol) {
testWalk(R"objc(
@protocol MyProtocol
@property(nonatomic) int $explicit^foo;
@end
)objc",
R"objc(
void test(id<MyProtocol> obj) {
int x = obj.^foo;
}
)objc",
{"-x", "objective-c"});
}
TEST(WalkAST, ObjCProtocolInType) {
testWalk(R"objc(
@protocol $explicit^MyProtocol
@end
)objc",
R"objc(
void test() {
id<^MyProtocol> obj;
}
)objc",
{"-x", "objective-c"});
}
TEST(WalkAST, ObjCProtocolInClassInterface) {
testWalk(R"objc(
@protocol $explicit^MyProtocol
@end
)objc",
R"objc(
@interface MyClass <^MyProtocol>
@end
)objc",
{"-x", "objective-c"});
}
TEST(WalkAST, ObjCProtocolInProtocolInheritance) {
testWalk(R"objc(
@protocol $explicit^ParentProtocol
@end
)objc",
R"objc(
@protocol MyProtocol <^ParentProtocol>
@end
)objc",
{"-x", "objective-c"});
}
TEST(WalkAST, ObjCProtocolExpr) {
testWalk(R"objc(
@protocol $explicit^MyProtocol
@end
)objc",
R"objc(
void test() {
Protocol* p = @protocol(^MyProtocol);
}
)objc",
{"-x", "objective-c"});
}
TEST(WalkAST, ObjCCategoryDeclDependsOnInterface) {
testWalk(R"objc(
@interface $explicit^MyClass
@end
)objc",
R"objc(
@interface ^MyClass (Category)
@end
)objc",
{"-x", "objective-c"});
}
TEST(WalkAST, ObjCCategoryImplDependsOnInterface) {
testWalk(R"objc(
@interface $explicit^MyClass
@end
)objc",
R"objc(
@interface MyClass (Category)
@end
@implementation ^MyClass (Category)
@end
)objc",
{"-x", "objective-c"});
}
TEST(WalkAST, ObjCCategoryImplDependsOnCategoryDecl) {
testWalk(R"objc(
@interface MyClass
@end
@interface $explicit^MyClass (Category)
@end
)objc",
R"objc(
@implementation MyClass (^Category)
@end
)objc",
{"-x", "objective-c"});
}
TEST(WalkAST, ObjCImplicitCastToProtocolConformingCategory) {
testWalk(R"objc(
@protocol MyProtocol
@end
@interface MyClass
@end
@interface $implicit^MyClass (MyCategory) <MyProtocol>
@end
)objc",
R"objc(
void test(MyClass *obj) {
id<MyProtocol> p = ^obj;
}
)objc",
{"-x", "objective-c"});
}
TEST(WalkAST, ObjCCompatibleAliasDecl) {
testWalk(R"objc(
@interface $explicit^MyClass
@end
)objc",
R"objc(
^@compatibility_alias AliasName MyClass;
)objc",
{"-x", "objective-c"});
}
TEST(WalkAST, ObjCCompatibleAliasUsage) {
testWalk(R"objc(
@interface $explicit^MyClass
@end
@compatibility_alias AliasName MyClass;
)objc",
R"objc(
void test() {
^AliasName *obj;
}
)objc",
{"-x", "objective-c"});
}
TEST(WalkAST, ObjCIvarRefExprExplicit) {
testWalk(R"objc(
@interface MyClass {
@public
int $explicit^foo;
}
@end
)objc",
R"objc(
void test(MyClass *obj) {
int x = obj->^foo;
}
)objc",
{"-x", "objective-c"});
}
TEST(WalkAST, ObjCIvarRefExprFree) {
testWalk(R"objc(
@interface MyClass {
int $explicit^foo;
}
@end
)objc",
R"objc(
@implementation MyClass
- (void)test {
int x = ^foo;
}
@end
)objc",
{"-x", "objective-c"});
}
} // namespace
} // namespace clang::include_cleaner

View file

@ -253,6 +253,18 @@ void testVectorSizeType() {
value = v.size();
}
namespace gh208708 {
void f(int &n) {
// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `<`
// CHECK-FIXES: n = std::max(n, -1);
if (n < -1) n = -1 ;
// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::min` instead of `>`
// CHECK-FIXES: n = std::min(n, 1); /*use clamp when c++17 or later is enabled*/
if (n > 1) n = 1/*use clamp when c++17 or later is enabled*/;
}
} // namespace gh208708
namespace gh121676 {
void useLeft() {

View file

@ -65,7 +65,7 @@ Sirraide \
aeternalmail@gmail.com (email), [Sirraide](https://github.com/Sirraide) (GitHub), Ætérnal (Discord), Sirraide (Discourse)
Mariya Podchishchaeva \
mariya.podchishchaeva@intel.com (email), [Fznamznon](https://github.com/Fznamznon) (GitHub), fznamznon (Discord), Fznamznon (Discourse)
fznamznon23@gmail.com (email), [Fznamznon](https://github.com/Fznamznon) (GitHub), fznamznon (Discord), Fznamznon (Discourse)
### Recovery AST
@ -167,7 +167,7 @@ i@maskray.me (email), MaskRay (Phabricator), [MaskRay](https://github.com/MaskRa
### Constant Expressions
Mariya Podchishchaeva \
mariya.podchishchaeva@intel.com (email), [Fznamznon](https://github.com/Fznamznon) (GitHub), fznamznon (Discord), Fznamznon (Discourse)
fznamznon23@gmail.com (email), [Fznamznon](https://github.com/Fznamznon) (GitHub), fznamznon (Discord), Fznamznon (Discourse)
### Thread Safety Analysis

View file

@ -199,13 +199,31 @@ foreach(target aarch64-unknown-linux-gnu;armv7-unknown-linux-gnueabihf;i386-unkn
endforeach()
if(FUCHSIA_SDK)
# Since there is only one build of the toolchain runtimes for users doing any
# supported -ffuchsia-api-level=... build, target the oldest unretired level.
file(READ "${FUCHSIA_SDK}/version_history.json" FUCHSIA_SDK_VERSION_HISTORY)
string(JSON FUCHSIA_SDK_API_LEVEL_COUNT LENGTH "${FUCHSIA_SDK_VERSION_HISTORY}" "data" "api_levels")
math(EXPR FUCHSIA_SDK_API_LEVEL_MAXIDX "${FUCHSIA_SDK_API_LEVEL_COUNT} - 1")
set(FUCHSIA_API_LEVEL 0)
foreach(idx RANGE ${FUCHSIA_SDK_API_LEVEL_MAXIDX})
string(JSON FUCHSIA_API_LEVEL_NAME MEMBER "${FUCHSIA_SDK_VERSION_HISTORY}" "data" "api_levels" ${idx})
string(JSON FUCHSIA_API_LEVEL_PHASE GET "${FUCHSIA_SDK_VERSION_HISTORY}" "data" "api_levels" "${FUCHSIA_API_LEVEL_NAME}" "phase")
if(NOT "${FUCHSIA_API_LEVEL_PHASE}" STREQUAL "retired")
set(FUCHSIA_API_LEVEL_INT ${FUCHSIA_API_LEVEL_NAME})
if(FUCHSIA_API_LEVEL EQUAL 0 OR FUCHSIA_API_LEVEL_INT LESS FUCHSIA_API_LEVEL)
set(FUCHSIA_API_LEVEL ${FUCHSIA_API_LEVEL_INT})
endif()
endif()
endforeach()
message(STATUS "Runtimes target Fuchsia API level ${FUCHSIA_API_LEVEL}")
set(FUCHSIA_aarch64-unknown-fuchsia_NAME arm64)
set(FUCHSIA_arm-unknown-fuchsia_NAME arm)
set(FUCHSIA_i386-unknown-fuchsia_NAME x64)
set(FUCHSIA_x86_64-unknown-fuchsia_NAME x64)
set(FUCHSIA_riscv64-unknown-fuchsia_NAME riscv64)
foreach(target i386-unknown-fuchsia;x86_64-unknown-fuchsia;aarch64-unknown-fuchsia;arm-unknown-fuchsia;riscv64-unknown-fuchsia)
set(FUCHSIA_${target}_COMPILER_FLAGS "--target=${target} -I${FUCHSIA_SDK}/pkg/sync/include -I${FUCHSIA_SDK}/pkg/fdio/include -fstack-size-section -fexperimental-call-graph-section")
set(FUCHSIA_${target}_COMPILER_FLAGS "--target=${target} -ffuchsia-api-level=${FUCHSIA_API_LEVEL} -I${FUCHSIA_SDK}/pkg/sync/include -I${FUCHSIA_SDK}/pkg/fdio/include -fstack-size-section -fexperimental-call-graph-section")
set(FUCHSIA_${target}_LINKER_FLAGS "-L${FUCHSIA_SDK}/arch/${FUCHSIA_${target}_NAME}/lib")
set(FUCHSIA_${target}_SYSROOT "${FUCHSIA_SDK}/arch/${FUCHSIA_${target}_NAME}/sysroot")
endforeach()

View file

@ -6821,6 +6821,16 @@ the configuration (without a prefix: ``Auto``).
# include "A.inc" # include "A.h"
# include "A-util.h" # include "A.inc"
* ``bool Natural`` Whether or not includes are sorted by natural ordering i.e., whether
embedded runs of digits are compared as numbers rather than sequences of
characters.
.. code-block:: c++
true: false:
#include "A2.h" vs. #include "A10.h"
#include "A10.h" #include "A2.h"
.. _SortJavaStaticImport:
@ -7393,6 +7403,35 @@ the configuration (without a prefix: ``Auto``).
.. _SpacesInBlockComments:
**SpacesInBlockComments** (``SpacesInBlockCommentsStyle``) :versionbadge:`clang-format 24` :ref:`¶ <SpacesInBlockComments>`
The SpacesInBlockCommentsStyle to use for ordinary block comments.
Documentation comments such as ``/** ... */`` and ``/*! ... */``
and parameter comments ending with ``=`` before the closing ``*/`` are
left unchanged.
Possible values:
* ``SIBCS_Never`` (in configuration: ``Never``)
Remove spaces after ``/*`` and before ``*/``.
.. code-block:: c++
/*comment*/
* ``SIBCS_Always`` (in configuration: ``Always``)
Add spaces after ``/*`` and before ``*/``.
.. code-block:: c++
/* comment */
* ``SIBCS_Leave`` (in configuration: ``Leave``)
Leave existing spaces unchanged.
.. _SpacesInCStyleCastParentheses:
**SpacesInCStyleCastParentheses** (``Boolean``) :versionbadge:`clang-format 3.7` :ref:`¶ <SpacesInCStyleCastParentheses>`

View file

@ -62,6 +62,18 @@ honored, and calls use the caller's features, matching GCC. Per-function
features cannot lower the translation-unit ABI level;
`-fclang-abi-compat=23` restores the previous behavior. (#GH193298)
- On MIPS, a `_Complex` value with an integer element type is now returned packed
into a single integer register when it fits in one, matching GCC. A `_Complex char` or
`_Complex short`, and on N32/N64 also a `_Complex int`, is no longer returned
with one part per register. `-fclang-abi-compat=23` restores the previous
behavior. (#GH212109)
- On MIPS N32/N64, a `_Complex float` or `_Complex double` argument is now packed
into integer registers, or onto the stack, once there is no longer room to give
each of its parts a floating-point register, matching GCC. Clang previously
always passed the parts separately. `-fclang-abi-compat=23` restores the previous
behavior. (#GH212109)
### AST Dumping Potentially Breaking Changes
### Clang Frontend Potentially Breaking Changes
@ -95,6 +107,10 @@ features cannot lower the translation-unit ABI level;
#### C++2d Feature Support
- Clang now supports [P3658R1](https://wg21.link/p3658r1) (Adjust identifier
following new Unicode recommendations), applied as a DR to all C++ language
modes.
#### C++2c Feature Support
- Clang now supports [P3533R2](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3533r2.html) (constexpr virtual inheritance).
@ -345,11 +361,17 @@ features cannot lower the translation-unit ABI level;
- Fixed an assertion failure when passing a wide string literal to `__builtin_nan`. (#GH212108)
- Fixed a constraint comparison bug in partial ordering. (#GH182671)
- Fixed a rejected-valid case that used an explicit object parameter in an out-of-line definition of a nested class member. (#GH136472)
- Fixed an assertion on omp taskloop transparent (#GH197162)
- Fixed a bug where `__func__`, `__PRETTY_FUNCTION__` and `__FUNCTION__` were not resolving to the proper function when inside a lambda return type (#GH211811)
- Fixed USR generation for declarations whose signature mentions a class-type
non-type template parameter. (#GH212351)
#### Bug Fixes to Compiler Builtins
- Fixed a crash when classifying a call to a builtin with dependent arguments,
such as when the call is used as an `auto` non-type template argument.
- Fixed a crash in ``__builtin_dump_struct`` when ``-Werror`` promotes
format warnings to errors. (#GH211943)
#### Bug Fixes to Attribute Support
@ -359,9 +381,14 @@ features cannot lower the translation-unit ABI level;
the `sized_by`/`sized_by_or_null` attributes. Because `sized_by` and
`sized_by_or_null` describe the size in bytes rather than a count of elements,
they are now correctly accepted on such pointers.
- Propagate attributes on redeclarations across modules.
#### Bug Fixes to C++ Support
- Fixed an issue where `__typeof__` incorrectly rejected cv-qualified function types.
- Fixed a bug where top-level CV qualifiers (such as ``const``) were dropped from pointers modified by Microsoft pointer attributes (like ``__ptr32`` and ``__ptr64``) and WebAssembly's ``__funcref``.
- Fixed an issue where we tried to compare invalid NTTPs for variable declarations, which ended up in hitting an assertion with a constrained non-plain-auto NTTP, which we don't quite implement yet. (#GH208658)
- Fixed a crash when a using-declaration naming an unresolvable member of a
@ -379,10 +406,24 @@ features cannot lower the translation-unit ABI level;
`[](Types... = args...) {}`). Clang now diagnoses the illegal default
argument instead of asserting. (#GH210714)
- Fixed a crash on invalid code where a ``decltype`` not followed by ``(`` was
parsed where a nested-name-specifier could appear (e.g. ``int decltype = 0;``).
Clang now diagnoses the error instead of asserting. (#GH211207)
- Fixed a crash when computing the implicit deletion of a defaulted comparison
operator required an access check that ran while an enclosing declaration
was still being parsed. (#GH210692)
- A workaround that was introduced to fix an issue with the `<format>` header present in some versions of
libstdc++15 has been extended to support preprocessed input. Previously, splitting the preprocessing and
compilation step would result in the fix not being applied. (#GH160314)
- A defaulted copy or move assignment operator for a union was left with an
empty body and copied nothing when the operator was actually called, for
example through a pointer to member. Clang now synthesizes a whole-object
copy so the union's object representation is copied, matching the defaulted
union copy constructor.
#### Bug Fixes to AST Handling
- Fixed a non-deterministic ordering of unused local typedefs that made
@ -393,6 +434,8 @@ features cannot lower the translation-unit ABI level;
#### Miscellaneous Clang Crashes Fixed
- Fixed a crash when instantiating an invalid dependent friend destructor declaration in a class template. (#GH210234)
- Fixed an assertion failure in `-extract-api` when a documentation comment
contains invalid UTF-8. (#GH212393)
### OpenACC Specific Changes
@ -426,6 +469,12 @@ features cannot lower the translation-unit ABI level;
#### Windows Support
- Fixed a bug where Clang did not match the MSVC ABI on Arm64 when an
over-aligned base class is followed by another base class. MSVC on Arm64 (but
not Arm64EC or x64) reuses the tail padding of the over-aligned base for the
subsequent base; Clang now does the same.
([#210174](https://github.com/llvm/llvm-project/issues/210174))
#### LoongArch Support
#### RISC-V Support
@ -454,8 +503,13 @@ features cannot lower the translation-unit ABI level;
### clang-format
- Add `SpacesInBlockComments` option to control spacing after `/*` and
before `*/` in ordinary block comments.
### libclang
- visit identifier initializers in lambda capture as VarDecl instead of VariableRef. Warning: this changes behaviour.
### Code Completion
### Static Analyzer

View file

@ -510,9 +510,10 @@ Use of these attributes has been deprecated.
### Function Pointers
Thread safety attributes may also be applied to function pointer variables and
fields. The attributes describe the locking behavior of calling through that
pointer, and the analysis will check calls through the pointer accordingly.
Thread safety attributes may also be applied to variables, fields, and
parameters of function pointer (or, in C++, function reference) type. The
attributes describe the locking behavior of calling through that pointer, and
the analysis will check calls through the pointer accordingly.
```c++
Mutex mu;
@ -531,16 +532,22 @@ void test(Ops *ops) {
ops->read();
unlock_fn();
}
void visit_all(void (*visit)(int) REQUIRES(mu), int n) {
lock_fn();
visit(n); // OK: 'mu' is held here
unlock_fn();
}
```
Note that the attributes are on the *variable* (or field), not on the function
pointer type. Assigning a function with different (or no) attributes to an
annotated function pointer variable is not diagnosed. The analysis trusts the
annotations on the variable at the call site.
Note that the attributes are on the *variable* (or field, or parameter), not on
the function pointer type. Assigning a function with different (or no)
attributes to an annotated function pointer variable is not diagnosed. The
analysis trusts the annotations on the variable at the call site.
This support is limited to plain function pointers. Pointers-to-member
functions, blocks, and wrapper types such as `std::function` are not
supported yet.
This support is limited to plain function pointers and function references.
Pointers-to-member functions, blocks, and wrapper types such as `std::function`
are not supported yet.
### Warning flags

View file

@ -17,6 +17,7 @@
#include "clang/APINotes/Types.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/VersionTuple.h"
@ -169,6 +170,16 @@ public:
lookupCXXMethod(ContextID CtxID, llvm::StringRef Name,
llvm::ArrayRef<std::string> Parameters);
/// Build the selector key for the given C++ method.
std::optional<APINotesFunctionSelectorKey>
getCXXMethodSelectorKey(ContextID CtxID, llvm::StringRef Name);
/// Build the selector key for the given C++ method with an exact parameter
/// selector.
std::optional<APINotesFunctionSelectorKey>
getCXXMethodSelectorKey(ContextID CtxID, llvm::StringRef Name,
llvm::ArrayRef<std::string> Parameters);
/// Look for information regarding the given global variable.
///
/// \param Name The name of the global variable.
@ -195,6 +206,27 @@ public:
llvm::ArrayRef<std::string> Parameters,
std::optional<Context> Ctx = std::nullopt);
/// Build the selector key for the given global function.
std::optional<APINotesFunctionSelectorKey>
getGlobalFunctionSelectorKey(llvm::StringRef Name,
std::optional<Context> Ctx = std::nullopt);
/// Build the selector key for the given global function with an exact
/// parameter selector.
std::optional<APINotesFunctionSelectorKey>
getGlobalFunctionSelectorKey(llvm::StringRef Name,
llvm::ArrayRef<std::string> Parameters,
std::optional<Context> Ctx = std::nullopt);
/// Collect exact parameter selector keys stored by this reader.
void collectExactFunctionParameterSelectors(
llvm::SmallVectorImpl<APINotesFunctionSelectorKey> &Selectors);
/// Reconstruct parameter selector strings for a stored exact selector key.
std::optional<llvm::SmallVector<std::string, 4>>
getParameterSelectorSpellingsForDiagnostics(
const APINotesFunctionSelectorKey &Key);
/// Look for information regarding the given enumerator.
///
/// \param Name The name of the enumerator.

View file

@ -11,9 +11,16 @@
#include "clang/Basic/Specifiers.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/PointerEmbeddedInt.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_ostream.h"
#include <climits>
#include <optional>
#include <string>
#include <vector>
namespace llvm {
@ -22,6 +29,17 @@ class raw_ostream;
namespace clang {
namespace api_notes {
template <typename RangeT>
std::string formatAPINotesParameterSelector(RangeT &&Parameters) {
std::string Result;
llvm::raw_string_ostream OS(Result);
OS << "[";
llvm::interleaveComma(Parameters, OS);
OS << "]";
return Result;
}
enum class RetainCountConventionKind {
None,
CFReturnsRetained,
@ -988,6 +1006,83 @@ struct Context {
Context(ContextID id, ContextKind kind) : id(id), kind(kind) {}
};
using IdentifierID = llvm::PointerEmbeddedInt<unsigned, 31>;
/// A key for a stored global-function or C++-method API notes entry.
///
/// The key is represented by the ID of its parent context, the declaration
/// name, and optional exact parameter types.
struct FunctionTableKey {
uint32_t parentContextID;
uint32_t nameID;
std::optional<llvm::SmallVector<IdentifierID, 2>> parameterTypeIDs;
FunctionTableKey() : parentContextID(-1), nameID(-1) {}
FunctionTableKey(uint32_t ParentContextID, uint32_t NameID)
: parentContextID(ParentContextID), nameID(NameID) {}
FunctionTableKey(uint32_t ParentContextID, uint32_t NameID,
const llvm::SmallVectorImpl<IdentifierID> &ParameterTypeIDs)
: parentContextID(ParentContextID), nameID(NameID) {
parameterTypeIDs.emplace(ParameterTypeIDs.begin(), ParameterTypeIDs.end());
}
FunctionTableKey(std::optional<Context> ParentCtx, IdentifierID NameID)
: parentContextID(ParentCtx ? ParentCtx->id.Value
: static_cast<uint32_t>(-1)),
nameID(NameID) {}
FunctionTableKey(std::optional<Context> ParentCtx, IdentifierID NameID,
const llvm::SmallVectorImpl<IdentifierID> &ParameterTypeIDs)
: parentContextID(ParentCtx ? ParentCtx->id.Value
: static_cast<uint32_t>(-1)),
nameID(NameID) {
parameterTypeIDs.emplace(ParameterTypeIDs.begin(), ParameterTypeIDs.end());
}
llvm::hash_code hashValue() const {
auto Hash = llvm::hash_combine(parentContextID, nameID,
static_cast<bool>(parameterTypeIDs));
if (parameterTypeIDs) {
Hash = llvm::hash_combine(Hash, parameterTypeIDs->size());
for (IdentifierID TypeID : *parameterTypeIDs)
Hash = llvm::hash_combine(Hash, static_cast<unsigned>(TypeID));
}
return Hash;
}
};
inline bool operator==(const FunctionTableKey &LHS,
const FunctionTableKey &RHS) {
return LHS.parentContextID == RHS.parentContextID &&
LHS.nameID == RHS.nameID &&
LHS.parameterTypeIDs == RHS.parameterTypeIDs;
}
/// Stable reader-facing identity for an API notes function selector entry.
///
/// The key keeps the serialized function table identity together with the table
/// kind. parentContextID names only the declaration context and does not say
/// whether the entry came from the global-function or C++-method table.
struct APINotesFunctionSelectorKey {
FunctionTableKey Key;
bool IsCXXMethod = false;
APINotesFunctionSelectorKey getWithoutParameterSelector() const {
return {FunctionTableKey(Key.parentContextID, Key.nameID), IsCXXMethod};
}
llvm::hash_code hashValue() const {
return llvm::hash_combine(Key.hashValue(), IsCXXMethod);
}
};
inline bool operator==(const APINotesFunctionSelectorKey &LHS,
const APINotesFunctionSelectorKey &RHS) {
return LHS.Key == RHS.Key && LHS.IsCXXMethod == RHS.IsCXXMethod;
}
/// A temporary reference to an Objective-C selector, suitable for
/// referencing selector data on the stack.
///
@ -1001,4 +1096,30 @@ struct ObjCSelectorRef {
} // namespace api_notes
} // namespace clang
namespace llvm {
template <> struct DenseMapInfo<clang::api_notes::FunctionTableKey> {
static unsigned getHashValue(const clang::api_notes::FunctionTableKey &Key) {
return Key.hashValue();
}
static bool isEqual(const clang::api_notes::FunctionTableKey &LHS,
const clang::api_notes::FunctionTableKey &RHS) {
return LHS == RHS;
}
};
template <> struct DenseMapInfo<clang::api_notes::APINotesFunctionSelectorKey> {
static unsigned
getHashValue(const clang::api_notes::APINotesFunctionSelectorKey &Key) {
return Key.hashValue();
}
static bool
isEqual(const clang::api_notes::APINotesFunctionSelectorKey &LHS,
const clang::api_notes::APINotesFunctionSelectorKey &RHS) {
return LHS == RHS;
}
};
} // namespace llvm
#endif

View file

@ -24,10 +24,10 @@
#include "clang/AST/Stmt.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/TypeBase.h"
#include "clang/Basic/BuiltinTraits.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SyncScope.h"
#include "clang/Basic/TypeTraits.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/SmallVector.h"

View file

@ -30,8 +30,8 @@
#include "clang/AST/TemplateBase.h"
#include "clang/AST/Type.h"
#include "clang/AST/UnresolvedSet.h"
#include "clang/Basic/BuiltinTraits.h"
#include "clang/Basic/ExceptionSpecificationType.h"
#include "clang/Basic/ExpressionTraits.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/Lambda.h"
#include "clang/Basic/LangOptions.h"
@ -39,7 +39,6 @@
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Basic/TemplateKinds.h"
#include "clang/Basic/TypeTraits.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/STLExtras.h"

View file

@ -485,13 +485,13 @@ public:
/// ObjCSelectorExpr used for \@selector in Objective-C.
class ObjCSelectorExpr : public Expr {
Selector SelName;
SourceLocation AtLoc, RParenLoc;
SourceLocation AtLoc, SelNameLoc, RParenLoc;
public:
ObjCSelectorExpr(QualType T, Selector selInfo, SourceLocation at,
SourceLocation rp)
SourceLocation selNameLoc, SourceLocation rp)
: Expr(ObjCSelectorExprClass, T, VK_PRValue, OK_Ordinary),
SelName(selInfo), AtLoc(at), RParenLoc(rp) {
SelName(selInfo), AtLoc(at), SelNameLoc(selNameLoc), RParenLoc(rp) {
setDependence(ExprDependence::None);
}
explicit ObjCSelectorExpr(EmptyShell Empty)
@ -501,8 +501,10 @@ public:
void setSelector(Selector S) { SelName = S; }
SourceLocation getAtLoc() const { return AtLoc; }
SourceLocation getSelectorNameLoc() const { return SelNameLoc; }
SourceLocation getRParenLoc() const { return RParenLoc; }
void setAtLoc(SourceLocation L) { AtLoc = L; }
void setSelectorNameLoc(SourceLocation L) { SelNameLoc = L; }
void setRParenLoc(SourceLocation L) { RParenLoc = L; }
SourceLocation getBeginLoc() const LLVM_READONLY { return AtLoc; }

View file

@ -2967,7 +2967,40 @@ public:
/// #pragma omp atomic update
/// \endcode
/// In this example directive '#pragma omp atomic' has 'update' clause.
/// Also, this class represents 'update' clause in '#pragma omp depobj'
class OMPUpdateClause : public OMPClause {
public:
/// Build 'update' clause.
///
/// \param StartLoc Starting location of the clause.
/// \param EndLoc Ending location of the clause.
OMPUpdateClause(SourceLocation StartLoc, SourceLocation EndLoc)
: OMPClause(llvm::omp::OMPC_update, StartLoc, EndLoc) {}
/// Build an empty clause.
OMPUpdateClause()
: OMPClause(llvm::omp::OMPC_update, 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_update;
}
};
/// This class represents 'update' clause in '#pragma omp depobj'
/// directive.
///
/// \code
@ -2975,38 +3008,32 @@ public:
/// \endcode
/// In this example directive '#pragma omp depobj' has 'update' clause with 'in'
/// dependence kind.
class OMPUpdateClause final
class OMPUpdateDependObjectsClause final
: public OMPClause,
private llvm::TrailingObjects<OMPUpdateClause, SourceLocation,
OpenMPDependClauseKind> {
private llvm::TrailingObjects<OMPUpdateDependObjectsClause,
SourceLocation, OpenMPDependClauseKind> {
friend class OMPClauseReader;
friend TrailingObjects;
/// true if extended version of the clause for 'depobj' directive.
bool IsExtended = false;
/// Define the sizes of each trailing object array except the last one. This
/// is required for TrailingObjects to work properly.
size_t numTrailingObjects(OverloadToken<SourceLocation>) const {
// 2 locations: for '(' and argument location.
return IsExtended ? 2 : 0;
return 2;
}
/// Sets the location of '(' in clause for 'depobj' directive.
void setLParenLoc(SourceLocation Loc) {
assert(IsExtended && "Expected extended clause.");
*getTrailingObjects<SourceLocation>() = Loc;
}
/// Sets the location of '(' in clause for 'depobj' directive.
void setArgumentLoc(SourceLocation Loc) {
assert(IsExtended && "Expected extended clause.");
*std::next(getTrailingObjects<SourceLocation>(), 1) = Loc;
}
/// Sets the dependence kind for the clause for 'depobj' directive.
void setDependencyKind(OpenMPDependClauseKind DK) {
assert(IsExtended && "Expected extended clause.");
*getTrailingObjects<OpenMPDependClauseKind>() = DK;
}
@ -3014,25 +3041,15 @@ class OMPUpdateClause final
///
/// \param StartLoc Starting location of the clause.
/// \param EndLoc Ending location of the clause.
OMPUpdateClause(SourceLocation StartLoc, SourceLocation EndLoc,
bool IsExtended)
: OMPClause(llvm::omp::OMPC_update, StartLoc, EndLoc),
IsExtended(IsExtended) {}
OMPUpdateDependObjectsClause(SourceLocation StartLoc, SourceLocation EndLoc)
: OMPClause(llvm::omp::OMPC_update_depend_objects, StartLoc, EndLoc) {}
/// Build an empty clause.
OMPUpdateClause(bool IsExtended)
: OMPClause(llvm::omp::OMPC_update, SourceLocation(), SourceLocation()),
IsExtended(IsExtended) {}
OMPUpdateDependObjectsClause()
: OMPClause(llvm::omp::OMPC_update_depend_objects, SourceLocation(),
SourceLocation()) {}
public:
/// Creates clause for 'atomic' directive.
///
/// \param C AST context.
/// \param StartLoc Starting location of the clause.
/// \param EndLoc Ending location of the clause.
static OMPUpdateClause *Create(const ASTContext &C, SourceLocation StartLoc,
SourceLocation EndLoc);
/// Creates clause for 'depobj' directive.
///
/// \param C AST context.
@ -3041,21 +3058,15 @@ public:
/// \param ArgumentLoc Location of the argument.
/// \param DK Dependence kind.
/// \param EndLoc Ending location of the clause.
static OMPUpdateClause *Create(const ASTContext &C, SourceLocation StartLoc,
SourceLocation LParenLoc,
SourceLocation ArgumentLoc,
OpenMPDependClauseKind DK,
SourceLocation EndLoc);
static OMPUpdateDependObjectsClause *
Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation LParenLoc,
SourceLocation ArgumentLoc, OpenMPDependClauseKind DK,
SourceLocation EndLoc);
/// Creates an empty clause with the place for \a N variables.
///
/// \param C AST context.
/// \param IsExtended true if extended clause for 'depobj' directive must be
/// created.
static OMPUpdateClause *CreateEmpty(const ASTContext &C, bool IsExtended);
/// Checks if the clause is the extended clauses for 'depobj' directive.
bool isExtended() const { return IsExtended; }
static OMPUpdateDependObjectsClause *CreateEmpty(const ASTContext &C);
child_range children() {
return child_range(child_iterator(), child_iterator());
@ -3074,24 +3085,21 @@ public:
/// Gets the location of '(' in clause for 'depobj' directive.
SourceLocation getLParenLoc() const {
assert(IsExtended && "Expected extended clause.");
return *getTrailingObjects<SourceLocation>();
}
/// Gets the location of argument in clause for 'depobj' directive.
SourceLocation getArgumentLoc() const {
assert(IsExtended && "Expected extended clause.");
return *std::next(getTrailingObjects<SourceLocation>(), 1);
}
/// Gets the dependence kind in clause for 'depobj' directive.
OpenMPDependClauseKind getDependencyKind() const {
assert(IsExtended && "Expected extended clause.");
return *getTrailingObjects<OpenMPDependClauseKind>();
}
static bool classof(const OMPClause *T) {
return T->getClauseKind() == llvm::omp::OMPC_update;
return T->getClauseKind() == llvm::omp::OMPC_update_depend_objects;
}
};

View file

@ -97,6 +97,12 @@ private:
/// which is the alignment of the object without virtual bases.
CharUnits NonVirtualAlignment;
/// NonRequiredNVAlignment - The non-virtual alignment (in chars) of an
/// object ignoring any over-alignment imposed via `alignas` /
/// `__declspec(align)` (i.e. the record's required alignment) on the record
/// or its bases.
CharUnits NonRequiredNVAlignment;
/// PreferredNVAlignment - The preferred non-virtual alignment (in chars) of
/// an object, which is the preferred alignment of the object without
/// virtual bases.
@ -164,6 +170,7 @@ private:
CharUnits datasize, ArrayRef<uint64_t> fieldoffsets,
CharUnits nonvirtualsize, CharUnits nonvirtualalignment,
CharUnits preferrednvalignment,
CharUnits nonrequirednvalignment,
CharUnits SizeOfLargestEmptySubobject,
const CXXRecordDecl *PrimaryBase, bool IsPrimaryBaseVirtual,
const CXXRecordDecl *BaseSharingVBPtr,
@ -222,6 +229,15 @@ public:
return CXXInfo->NonVirtualAlignment;
}
/// getNonRequiredNVAlignment - Get the non-virtual alignment (in chars) of an
/// object ignoring over-alignment imposed via `alignas` / `__declspec(align)`
/// on the object or its bases. Only meaningful for the Microsoft ABI.
CharUnits getNonRequiredNVAlignment() const {
assert(CXXInfo && "Record layout does not have C++ specific info!");
return CXXInfo->NonRequiredNVAlignment;
}
/// getPreferredNVAlignment - Get the preferred non-virtual alignment (in
/// chars) of an object, which is the preferred alignment of the object
/// without virtual bases.

View file

@ -3710,6 +3710,12 @@ bool RecursiveASTVisitor<Derived>::VisitOMPUpdateClause(OMPUpdateClause *) {
return true;
}
template <typename Derived>
bool RecursiveASTVisitor<Derived>::VisitOMPUpdateDependObjectsClause(
OMPUpdateDependObjectsClause *) {
return true;
}
template <typename Derived>
bool RecursiveASTVisitor<Derived>::VisitOMPCaptureClause(OMPCaptureClause *) {
return true;

View file

@ -18,8 +18,8 @@
#include "clang/AST/DependenceFlags.h"
#include "clang/AST/OperationKinds.h"
#include "clang/AST/StmtIterator.h"
#include "clang/Basic/BuiltinTraits.h"
#include "clang/Basic/CapturedStmt.h"
#include "clang/Basic/ExpressionTraits.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/Lambda.h"
@ -27,7 +27,6 @@
#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Basic/TypeTraits.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/BitmaskEnum.h"

View file

@ -74,13 +74,13 @@
#include "clang/ASTMatchers/ASTMatchersMacros.h"
#include "clang/ASTMatchers/LowLevelHelpers.h"
#include "clang/Basic/AttrKinds.h"
#include "clang/Basic/BuiltinTraits.h"
#include "clang/Basic/ExceptionSpecificationType.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Basic/TypeTraits.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"

View file

@ -162,6 +162,43 @@ public:
size_t param_size() const { return parameters().size(); }
bool param_empty() const { return parameters().empty(); }
/// \returns actual arguments for expression-backed calls, or an empty range
/// for declaration-backed calls and call kinds with implicit or synthesized
/// argument lists, such as allocators and destructors.
ArrayRef<const Expr *> arguments() const {
if (!E)
return {};
switch (K) {
case Function:
case Block: {
const auto *CE = cast<CallExpr>(E);
return {CE->getArgs(), CE->getNumArgs()};
}
case ObjCMethod: {
const auto *ME = cast<ObjCMessageExpr>(E);
return {ME->getArgs(), ME->getNumArgs()};
}
case Constructor: {
const auto *CE = cast<CXXConstructExpr>(E);
return {CE->getArgs(), CE->getNumArgs()};
}
case Destructor:
case InheritedConstructor:
case Allocator:
case Deallocator:
return {};
}
llvm_unreachable("Unknown AnyCall::Kind");
}
using arg_const_iterator = ArrayRef<const Expr *>::const_iterator;
arg_const_iterator arg_begin() const { return arguments().begin(); }
arg_const_iterator arg_end() const { return arguments().end(); }
size_t arg_size() const { return arguments().size(); }
bool arg_empty() const { return arguments().empty(); }
const Expr *getArg(unsigned I) const { return arguments()[I]; }
QualType getReturnType(ASTContext &Ctx) const {
switch (K) {
case Function:

View file

@ -149,6 +149,12 @@ ABI_VER_MAJOR(22)
/// This causes clang to:
/// - Ignore per-function target attributes when determining the x86 AVX ABI
/// level.
/// - On MIPS, return a `_Complex` value with an integer element type with one
/// part per integer register, instead of packing it into a single register
/// where it fits.
/// - On MIPS N32/N64, always pass a `_Complex float` or `_Complex double`
/// argument as its two parts, one floating-point register each, instead of
/// packing it into integer registers once there is no room for both.
ABI_VER_MAJOR(23)
/// Conform to the underlying platform's C and C++ ABIs as closely as we can.

View file

@ -1,4 +1,4 @@
//===--- TypeTraits.h - C++ Type Traits Support Enumerations ----*- C++ -*-===//
//===--- BuiltinTraits.h - C++ Traits Support Enumerations ----*- C++ -*-=====//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@ -7,7 +7,7 @@
//===----------------------------------------------------------------------===//
///
/// \file
/// Defines enumerations for the type traits support.
/// Defines enumerations for traits support.
///
//===----------------------------------------------------------------------===//
@ -38,18 +38,26 @@ namespace clang {
// UETT_ ...
// UETT_Last == last UETT_XX in the enum.
// };
/// Names for the expression traits.
// enum ExpressionTrait {
// ET_ ...
// ET_Last == last ET_XX in the enum.
// };
#define EMIT_ENUMS
#include "clang/Basic/Traits.inc"
/// Return the internal name of type trait \p T. Never null.
/// Return the internal name of the trait \p T. Never null.
const char *getTraitName(TypeTrait T) LLVM_READONLY;
const char *getTraitName(ArrayTypeTrait T) LLVM_READONLY;
const char *getTraitName(UnaryExprOrTypeTrait T) LLVM_READONLY;
const char *getTraitName(ExpressionTrait T) LLVM_READONLY;
/// Return the spelling of the type trait \p TT. Never null.
/// Return the spelling of the trait \p T. Never null.
const char *getTraitSpelling(TypeTrait T) LLVM_READONLY;
const char *getTraitSpelling(ArrayTypeTrait T) LLVM_READONLY;
const char *getTraitSpelling(UnaryExprOrTypeTrait T) LLVM_READONLY;
const char *getTraitSpelling(ExpressionTrait T) LLVM_READONLY;
/// Return the arity of the type trait \p T.
unsigned getTypeTraitArity(TypeTrait T) LLVM_READONLY;

View file

@ -5545,6 +5545,12 @@ def HLSLInterlockedOr : LangBuiltin<"HLSL_LANG"> {
let Prototype = "void (...)";
}
def HLSLInterlockedXor : LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_interlocked_xor"];
let Attributes = [NoThrow];
let Prototype = "void (...)";
}
def HLSLWaveActiveBallot : LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_wave_active_ballot"];
let Attributes = [NoThrow, Const];
@ -5647,18 +5653,6 @@ def HLSLClamp : LangBuiltin<"HLSL_LANG"> {
let Prototype = "void(...)";
}
def HLSLCrossFloat: LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_crossf32"];
let Attributes = [NoThrow, Const];
let Prototype = "_ExtVector<3, float>(_ExtVector<3, float>, _ExtVector<3, float>)";
}
def HLSLCrossHalf: LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_crossf16"];
let Attributes = [NoThrow, Const];
let Prototype = "_ExtVector<3, __fp16>(_ExtVector<3, __fp16>, _ExtVector<3, __fp16>)";
}
def HLSLDegrees : LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_elementwise_degrees"];
let Attributes = [NoThrow, Const, CustomTypeChecking];

View file

@ -761,8 +761,12 @@ def __builtin_amdgcn_s_barrier_join : AMDGPUBuiltin<"void(void *)", [], "gfx12-i
def __builtin_amdgcn_s_barrier_leave : AMDGPUBuiltin<"void(_Constant short)", [], "gfx12-insts">;
def __builtin_amdgcn_s_get_barrier_state : AMDGPUBuiltin<"unsigned int(int)", [], "gfx12-insts">;
def __builtin_amdgcn_s_get_named_barrier_state : AMDGPUBuiltin<"unsigned int(void *)", [], "gfx12-insts">;
def __builtin_amdgcn_s_prefetch_data : AMDGPUBuiltin<"void(void const *, unsigned int)", [Const], "gfx12-insts">;
def __builtin_amdgcn_s_buffer_prefetch_data : AMDGPUBuiltin<"void(__amdgpu_buffer_rsrc_t, _Constant int, unsigned int)", [Const], "gfx12-insts">;
def __builtin_amdgcn_s_prefetch_data : AMDGPUBuiltin<"void(void const *, unsigned int)", [Const], "smem-prefetch-insts">;
def __builtin_amdgcn_s_prefetch_inst : AMDGPUBuiltin<"void(void const *, unsigned int)", [Const], "smem-prefetch-insts"> {
let Documentation = [DocPrefetchInst_GFX12];
let ArgNames = ["ptr", "len"];
}
def __builtin_amdgcn_s_buffer_prefetch_data : AMDGPUBuiltin<"void(__amdgpu_buffer_rsrc_t, _Constant int, unsigned int)", [Const], "smem-prefetch-insts">;
def __builtin_amdgcn_global_load_tr_b64_v2i32 : AMDGPUBuiltin<"_ExtVector<2, int>(_ExtVector<2, int> address_space<1> *)", [Const], "gfx12-insts,wavefrontsize32">;
def __builtin_amdgcn_global_load_tr_b128_v8i16 : AMDGPUBuiltin<"_ExtVector<8, short>(_ExtVector<8, short> address_space<1> *)", [Const], "gfx12-insts,wavefrontsize32">;
@ -1047,7 +1051,7 @@ def __builtin_amdgcn_exp2_bf16 : AMDGPUBuiltin<"__bf16(__bf16)", [Const], "bf16-
def __builtin_amdgcn_sin_bf16 : AMDGPUBuiltin<"__bf16(__bf16)", [Const], "bf16-trans-insts">;
def __builtin_amdgcn_cos_bf16 : AMDGPUBuiltin<"__bf16(__bf16)", [Const], "bf16-trans-insts">;
def __builtin_amdgcn_cvt_sr_pk_bf16_f32 : AMDGPUBuiltin<"_ExtVector<2, __bf16>(float, float, int)", [Const], "bf16-cvt-insts">;
def __builtin_amdgcn_cvt_sr_pk_bf16_f32 : AMDGPUBuiltin<"_ExtVector<2, __bf16>(float, float, int)", [Const], "cvt-sr-pk-bf16-f32-inst">;
def __builtin_amdgcn_cvt_sr_pk_f16_f32 : AMDGPUBuiltin<"_ExtVector<2, _Float16>(float, float, int)", [Const], "gfx1250-insts">;
def __builtin_amdgcn_cvt_f16_fp8 : AMDGPUBuiltin<"_Float16(int, _Constant int)", [Const], "gfx1250-insts">;
def __builtin_amdgcn_cvt_f16_bf8 : AMDGPUBuiltin<"_Float16(int, _Constant int)", [Const], "gfx1250-insts">;

View file

@ -78,6 +78,13 @@ between standard floating-point types, integer types and packed low-precision fo
}];
}
def DocCatAMDGPUPrefetch : DocumentationCategory<"Prefetch Builtins"> {
let Content = [{
These builtins provide access to AMDGPU prefetch instructions, including instructions
for prefetching data and instructions.
}];
}
//===----------------------------------------------------------------------===//
// ABI / Special Register Builtins — Documentation records
//===----------------------------------------------------------------------===//
@ -725,7 +732,7 @@ value gives an undefined result.
def DocCatSBufferLoad : DocumentationCategory<"S-Buffer Load Builtins"> {
let Content = [{
These builtins lower to ``llvm.amdgcn.s.buffer.load`` and issue an
These builtins lower to ``llvm.amdgcn.ptr.s.buffer.load`` and issue an
``s_buffer_load`` when the byte offset is uniform across the wavefront.
When the offset is divergent, the backend may lower to a ``buffer_load``.
@ -742,8 +749,7 @@ When the offset is divergent, the backend may lower to a ``buffer_load``.
These builtins take the buffer resource as ``__amdgpu_buffer_rsrc_t``, which
can be created with ``__builtin_amdgcn_make_buffer_rsrc``. The backend
lowers the resource to the 4-dword SGPR descriptor required by
``llvm.amdgcn.s.buffer.load``.
lowers the resource to the 4-dword SGPR descriptor required by the instruction.
The return type selects the load width. Separate builtins are provided for
each supported scalar and vector element type.
@ -793,3 +799,16 @@ using the scale factor ``scale``, then converts the values to a packed
integers.
}];
}
//===----------------------------------------------------------------------===//
// Prefetch Builtins
//===----------------------------------------------------------------------===//
def DocPrefetchInst_GFX12 : Documentation {
let Category = DocCatAMDGPUPrefetch;
let Content = [{
Prefetch instructions into I-Cache from the address given by ``ptr`` pointing
to instruction memory, with the length specified by ``len``, which should be
0-31 (1-32 chunks, units of 128 bytes).
}];
}

View file

@ -584,6 +584,17 @@ private:
DiagSuppressionMapping;
public:
/// Returns a cache key representing the diagnostic state at \p Loc.
const void *getDiagStateKeyForLoc(SourceLocation Loc) const {
return GetDiagStateForLoc(Loc);
}
/// True if an active diagnostic suppression mapping makes severity dependent
/// on the file path.
bool hasDiagSuppressionMapping() const {
return static_cast<bool>(DiagSuppressionMapping);
}
explicit DiagnosticsEngine(IntrusiveRefCntPtr<DiagnosticIDs> Diags,
DiagnosticOptions &DiagOpts,
DiagnosticConsumer *client = nullptr,
@ -1113,6 +1124,22 @@ public:
}
};
/// RAII class that temporarily sets the "ignore all warnings" state on a
/// DiagnosticsEngine and restores the previous state on destruction. Use it to
/// silence warnings around a self-contained region of diagnostics, such as a
/// compiler-synthesized call whose arguments are known to be correct.
class IgnoreAllWarningDiagRAII {
DiagnosticsEngine &Diag;
bool OldValue;
public:
explicit IgnoreAllWarningDiagRAII(DiagnosticsEngine &Diag)
: Diag(Diag), OldValue(Diag.getIgnoreAllWarnings()) {
Diag.setIgnoreAllWarnings(true);
}
~IgnoreAllWarningDiagRAII() { Diag.setIgnoreAllWarnings(OldValue); }
};
/// The streaming interface shared between DiagnosticBuilder and
/// PartialDiagnostic. This class is not intended to be constructed directly
/// but only as base class of DiagnosticBuilder and PartialDiagnostic builder.

View file

@ -170,7 +170,7 @@ def note_constexpr_depth_limit_exceeded : Note<
def note_constexpr_call_limit_exceeded : Note<
"constexpr evaluation hit maximum call limit">;
def note_constexpr_step_limit_exceeded : Note<
"constexpr evaluation hit maximum step limit; possible infinite loop?">;
"constexpr evaluation hit maximum step limit of %0; possible infinite loop?">;
def note_constexpr_heap_alloc_limit_exceeded : Note<
"constexpr evaluation hit maximum heap allocation limit">;
def note_constexpr_this : Note<
@ -355,8 +355,9 @@ def note_constexpr_new_negative : Note<
def note_constexpr_new_too_large : Note<
"cannot allocate array; evaluated array bound %0 is too large">;
def note_constexpr_new_exceeds_limits : Note<
"cannot allocate array; evaluated array bound %0 exceeds the limit (%1); "
"use '-fconstexpr-steps' to increase this limit">;
"cannot allocate array; evaluated array bound %0 exceeds the limit (%1)">;
def note_constexpr_steps : Note<
"use -fconstexpr-steps= to increase this limit (=0 will remove the limit)">;
def note_constexpr_new_too_small : Note<
"cannot allocate array; evaluated array bound %0 is too small to hold "
"%1 explicitly initialized elements">;

View file

@ -455,6 +455,8 @@ def warn_omp_gpu_unsupported_clause: Warning<
def warn_omp_gpu_unsupported_modifier_for_clause: Warning<
"modifier '%0' is currently not supported on a GPU for the '%1' clause; modifier ignored">,
InGroup<OpenMPClauses>;
def err_omp_unified_shared_memory_unsupported : Error<
"target architecture '%0' does not support unified addressing">;
// Static Analyzer Core
def err_unknown_analyzer_checker_or_package : Error<

View file

@ -145,6 +145,8 @@ def warn_utf8_symbol_homoglyph : Warning<
def warn_utf8_symbol_zero_width : Warning<
"identifier contains character %0 that is invisible in "
"some environments">, InGroup<DiagGroup<"unicode-zero-width">>;
defm mathematical_notation : CXX29Compat<
"mathematical notation character %0 in an identifier is">;
def ext_mathematical_notation : ExtWarn<
"mathematical notation character %0 in an identifier is a Clang extension">,
InGroup<DiagGroup<"mathematical-notation-identifier-extension">>;
@ -864,6 +866,13 @@ def err_pp_include_in_arc_cf_code_audited : Error<
def err_pp_eof_in_arc_cf_code_audited : Error<
"'#pragma clang arc_cf_code_audited' was not ended within this file">;
def err_pp_pragma_set_pp_state_expected_name : Error<
"expected identifier after '#pragma clang __set_pp_state'">;
def err_pp_pragma_set_pp_state_expected_int_after : Error<
"expected integer after '#pragma clang __set_pp_state %0'">;
def err_pp_pragma_set_pp_state_invalid_arg : Error<
"invalid argument %0 in '#pragma clang __set_pp_state'">;
def warn_pp_date_time : Warning<
"expansion of date or time macro is not reproducible">,
ShowInSystemHeader, DefaultIgnore, InGroup<DiagGroup<"date-time">>;

View file

@ -4350,7 +4350,7 @@ def warn_thread_attribute_decl_not_pointer : Warning<
InGroup<ThreadSafetyAttributes>, DefaultIgnore;
def warn_thread_attribute_not_on_scoped_lockable_param : Warning<
"%0 attribute applies to function parameters only if their type is a "
"reference to a 'scoped_lockable'-annotated type">,
"function pointer or a reference to a 'scoped_lockable'-annotated type">,
InGroup<ThreadSafetyAttributes>, DefaultIgnore;
def warn_thread_attribute_requires_preceded : Warning<
"%0 attribute on %1 must be preceded by %2 attribute">,
@ -13852,6 +13852,11 @@ def warn_hlsl_assigning_local_resource_is_not_unique
def err_hlsl_push_constant_unique
: Error<"cannot have more than one push constant block">;
def err_hlsl_sample_double_element_type
: Error<"'%0' is not supported for resources containing %1">;
def err_hlsl_sample_integer_element_type
: Error<"'%0' on resources containing %1 requires shader model 6.7 or "
"newer; the target shader model is %2">;
def err_hlsl_samplecmp_requires_float
: Error<"'SampleCmp' and 'SampleCmpLevelZero' require resource to contain "
"a floating point type">;

View file

@ -1,37 +0,0 @@
//===- ExpressionTraits.h - C++ Expression Traits Support Enums -*- 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
//
//===----------------------------------------------------------------------===//
///
/// \file
/// Defines enumerations for expression traits intrinsics.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_BASIC_EXPRESSIONTRAITS_H
#define LLVM_CLANG_BASIC_EXPRESSIONTRAITS_H
#include "llvm/Support/Compiler.h"
namespace clang {
enum ExpressionTrait {
#define EXPRESSION_TRAIT(Spelling, Name, Key) ET_##Name,
#include "clang/Basic/Traits.inc"
ET_Last = -1 // ET_Last == last ET_XX in the enum.
#define EXPRESSION_TRAIT(Spelling, Name, Key) +1
#include "clang/Basic/Traits.inc"
};
/// Return the internal name of type trait \p T. Never null.
const char *getTraitName(ExpressionTrait T) LLVM_READONLY;
/// Return the spelling of the type trait \p TT. Never null.
const char *getTraitSpelling(ExpressionTrait T) LLVM_READONLY;
} // namespace clang
#endif

View file

@ -554,30 +554,6 @@ def hlsl_countbits : HLSLBuiltin<"countbits"> {
let VaryingVecSizes = [2, 3, 4];
}
// Returns the cross product of two floating-point, 3D vectors.
def hlsl_cross_float : HLSLBuiltin<"cross", "__builtin_hlsl_crossf32"> {
let Args = [VectorType<FloatTy, 3>, VectorType<FloatTy, 3>];
let ReturnType = VectorType<FloatTy, 3>;
}
def hlsl_cross_half : HLSLBuiltin<"cross", "__builtin_hlsl_crossf16"> {
let Doc = [{
\fn T cross(T x, T y)
\brief Returns the cross product of two floating-point, 3D vectors.
\param x [in] The first floating-point, 3D vector.
\param y [in] The second floating-point, 3D vector.
Result is the cross product of x and y, i.e., the resulting
components are, in order :
x[1] * y[2] - y[1] * x[2]
x[2] * y[0] - y[2] * x[0]
x[0] * y[1] - y[0] * x[1]
}];
let Args = [VectorType<HalfTy, 3>, VectorType<HalfTy, 3>];
let ReturnType = VectorType<HalfTy, 3>;
let Availability = SM6_2;
}
// Converts a floating-point, 4D vector set by a D3DCOLOR to a UBYTE4.
def hlsl_d3d_color_to_ubyte4 : HLSLBuiltin<"D3DCOLORtoUBYTE4"> {
let Doc = [{
@ -708,6 +684,27 @@ def hlsl_degrees : HLSLOneArgBuiltin<"degrees", "__builtin_hlsl_elementwise_degr
let VaryingTypes = [HalfTy, FloatTy];
let VaryingMatDims = [];
}
// Returns the cross product of two floating-point, 3D vectors.
def hlsl_cross : HLSLTwoArgDetail<"cross", "cross_impl"> {
let Doc = [{
\fn T cross(T x, T y)
\brief Returns the cross product of two floating-point, 3D vectors.
\param x [in] The first floating-point, 3D vector.
\param y [in] The second floating-point, 3D vector.
Result is the cross product of x and y, i.e., the resulting
components are, in order :
x[1] * y[2] - y[1] * x[2]
x[2] * y[0] - y[2] * x[0]
x[0] * y[1] - y[0] * x[1]
}];
let ParamNames = ["x", "y"];
let VaryingTypes = [HalfTy, FloatTy];
let VaryingScalar = 0;
let VaryingVecSizes = [3];
let VaryingMatDims = [];
let IsConstexpr = 1;
}
// Returns a distance scalar between X and Y.
// The distance between X and Y is length(X - Y).

View file

@ -10,161 +10,121 @@
#define LLVM_CLANG_BASIC_OFFLOADARCH_H
#include "llvm/ADT/StringRef.h"
#include <cstdint>
#include <tuple>
namespace llvm {
class Triple;
template <typename T> class SmallVectorImpl;
namespace NVPTX {
enum GPUKind : uint8_t;
}
namespace AMDGPU {
enum GPUKind : uint8_t;
}
} // namespace llvm
namespace clang {
enum class OffloadArch {
Unused,
Unknown,
// TODO: Deprecate and remove GPU architectures older than sm_52.
SM_20,
SM_21,
SM_30,
// This has a name conflict with sys/mac.h on AIX, rename it as a workaround.
SM_32_,
SM_35,
SM_37,
SM_50,
SM_52,
SM_53,
SM_60,
SM_61,
SM_62,
SM_70,
SM_72,
SM_75,
SM_80,
SM_86,
SM_87,
SM_88,
SM_89,
SM_90,
SM_90a,
SM_100,
SM_100a,
SM_100f,
SM_101,
SM_101a,
SM_101f,
SM_103,
SM_103a,
SM_103f,
SM_110,
SM_110a,
SM_110f,
SM_120,
SM_120a,
SM_120f,
SM_121,
SM_121a,
SM_121f,
GFX600,
GFX601,
GFX602,
GFX700,
GFX701,
GFX702,
GFX703,
GFX704,
GFX705,
GFX801,
GFX802,
GFX803,
GFX805,
GFX810,
GFX9_GENERIC,
GFX900,
GFX902,
GFX904,
GFX906,
GFX908,
GFX909,
GFX90a,
GFX90c,
GFX9_4_GENERIC,
GFX942,
GFX950,
GFX10_1_GENERIC,
GFX1010,
GFX1011,
GFX1012,
GFX1013,
GFX10_3_GENERIC,
GFX1030,
GFX1031,
GFX1032,
GFX1033,
GFX1034,
GFX1035,
GFX1036,
GFX11_GENERIC,
GFX1100,
GFX1101,
GFX1102,
GFX1103,
GFX1150,
GFX1151,
GFX1152,
GFX1153,
GFX1154,
GFX11_7_GENERIC,
GFX1170,
GFX1171,
GFX1172,
GFX12_GENERIC,
GFX1200,
GFX1201,
GFX12_5_GENERIC,
GFX1250,
GFX1251,
GFX13_GENERIC,
GFX1310,
AMDGCNSPIRV,
Generic, // A processor model named 'generic' if the target backend defines a
// public one.
// Intel CPUs
GRANITERAPIDS,
// Intel GPUs
BMG_G21,
LAST = BMG_G21,
/// A processor an offloading action can target. This is a tagged handle pairing
/// a TargetArch with the matching TargetParser GPU kind; it does not enumerate
/// individual GPUs, so new targets are added in the TargetParser data alone.
class OffloadArch {
public:
enum class TargetArch : uint8_t {
Unused, // Default-constructed; no architecture bound.
Unknown, // A name that matched no known architecture.
NVPTX, // Kind is an llvm::NVPTX::GPUKind.
AMDGPU, // Kind is an llvm::AMDGPU::GPUKind.
SPIRV, // The 'amdgcnspirv' pseudo target.
IntelCPU, // Kind is an IntelArch.
IntelGPU, // Kind is an IntelArch.
Generic, // The 'generic' processor model.
};
CudaDefault = OffloadArch::SM_52,
HIPDefault = OffloadArch::GFX906,
// Intel architectures, which have no TargetParser list yet.
enum class IntelArch : uint32_t {
GRANITERAPIDS,
BMG_G21,
};
private:
// Interpreted according to V; unused for the tagless TargetArch values.
uint32_t Kind = 0;
TargetArch V = TargetArch::Unused;
constexpr OffloadArch(TargetArch V, uint32_t Kind) : Kind(Kind), V(V) {}
public:
constexpr OffloadArch() = default;
static OffloadArch getNVPTX(llvm::NVPTX::GPUKind K) {
return {TargetArch::NVPTX, static_cast<uint32_t>(K)};
}
static OffloadArch getAMDGPU(llvm::AMDGPU::GPUKind K) {
return {TargetArch::AMDGPU, static_cast<uint32_t>(K)};
}
static constexpr OffloadArch getIntel(TargetArch V, IntelArch A) {
return {V, static_cast<uint32_t>(A)};
}
static constexpr OffloadArch getUnused() { return {TargetArch::Unused, 0}; }
static constexpr OffloadArch getUnknown() { return {TargetArch::Unknown, 0}; }
static constexpr OffloadArch getSPIRV() { return {TargetArch::SPIRV, 0}; }
static constexpr OffloadArch getGeneric() { return {TargetArch::Generic, 0}; }
/// Default architectures used when the user does not specify one.
static OffloadArch CudaDefault();
static OffloadArch HIPDefault();
TargetArch targetArch() const { return V; }
bool isNVPTX() const { return V == TargetArch::NVPTX; }
bool isAMDGPU() const { return V == TargetArch::AMDGPU; }
bool isSPIRV() const { return V == TargetArch::SPIRV; }
bool isIntelCPU() const { return V == TargetArch::IntelCPU; }
bool isIntelGPU() const { return V == TargetArch::IntelGPU; }
bool isIntel() const { return isIntelCPU() || isIntelGPU(); }
bool isGeneric() const { return V == TargetArch::Generic; }
bool isUnused() const { return V == TargetArch::Unused; }
bool isUnknown() const { return V == TargetArch::Unknown; }
// Only valid when isNVPTX() / isAMDGPU() respectively.
llvm::NVPTX::GPUKind nvptxKind() const {
return static_cast<llvm::NVPTX::GPUKind>(Kind);
}
llvm::AMDGPU::GPUKind amdgpuKind() const {
return static_cast<llvm::AMDGPU::GPUKind>(Kind);
}
bool operator==(const OffloadArch &Other) const {
return V == Other.V && Kind == Other.Kind;
}
bool operator!=(const OffloadArch &Other) const { return !(*this == Other); }
bool operator<(const OffloadArch &Other) const {
return std::tie(V, Kind) < std::tie(Other.V, Other.Kind);
}
};
static inline bool IsNVIDIAOffloadArch(OffloadArch A) {
return A >= OffloadArch::SM_20 && A < OffloadArch::GFX600;
}
static inline bool IsAMDOffloadArch(OffloadArch A) {
// Generic processor model is for testing only.
return A >= OffloadArch::GFX600 && A < OffloadArch::Generic;
}
static inline bool IsIntelCPUOffloadArch(OffloadArch Arch) {
return Arch >= OffloadArch::GRANITERAPIDS && Arch < OffloadArch::BMG_G21;
}
static inline bool IsIntelGPUOffloadArch(OffloadArch Arch) {
return Arch >= OffloadArch::BMG_G21 && Arch <= OffloadArch::LAST;
}
static inline bool IsIntelOffloadArch(OffloadArch Arch) {
return IsIntelCPUOffloadArch(Arch) || IsIntelGPUOffloadArch(Arch);
inline bool IsNVIDIAOffloadArch(OffloadArch A) { return A.isNVPTX(); }
inline bool IsAMDOffloadArch(OffloadArch A) {
// amdgcnspirv is compiled through the AMDGPU toolchain.
return A.isAMDGPU() || A.isSPIRV();
}
inline bool IsIntelCPUOffloadArch(OffloadArch A) { return A.isIntelCPU(); }
inline bool IsIntelGPUOffloadArch(OffloadArch A) { return A.isIntelGPU(); }
inline bool IsIntelOffloadArch(OffloadArch A) { return A.isIntel(); }
const char *OffloadArchToString(OffloadArch A);
const char *OffloadArchToVirtualArchString(OffloadArch A);
// Convert a string to an OffloadArch enum value. Returns
// OffloadArch::Unknown if the string is not recognized.
// Convert a string to an OffloadArch. Returns an Unknown OffloadArch if the
// string is not recognized.
OffloadArch StringToOffloadArch(llvm::StringRef S);
/// Append the canonical names of all NVIDIA and AMDGPU GPUs.
void fillValidOffloadArchList(llvm::SmallVectorImpl<llvm::StringRef> &Values);
llvm::Triple OffloadArchToTriple(const llvm::Triple &DefaultToolchainTriple,
OffloadArch ID);
@ -173,19 +133,19 @@ llvm::Triple OffloadArchToTriple(const llvm::Triple &DefaultToolchainTriple,
struct BoundArch {
llvm::StringRef ArchName;
/// The parsed offload architecture enum.
/// Will be OffloadArch::Unknown if ArchName not recognized.
OffloadArch Arch = OffloadArch::Unused;
/// The parsed offload architecture.
/// Will be an Unknown OffloadArch if ArchName is not recognized.
OffloadArch Arch = OffloadArch::getUnused();
BoundArch() = default;
explicit BoundArch(llvm::StringRef Name)
: ArchName(Name),
Arch(Name.empty() ? OffloadArch::Unknown : StringToOffloadArch(Name)) {}
: ArchName(Name), Arch(Name.empty() ? OffloadArch::getUnknown()
: StringToOffloadArch(Name)) {}
BoundArch(llvm::StringRef Name, OffloadArch A) : ArchName(Name), Arch(A) {}
bool empty() const { return ArchName.empty(); }
explicit operator bool() const { return Arch != OffloadArch::Unused; }
explicit operator bool() const { return !Arch.isUnused(); }
bool operator==(const BoundArch &Other) const {
return Arch == Other.Arch && ArchName == Other.ArchName;

View file

@ -30,7 +30,9 @@ template <class T> struct OptionalUnsigned {
OptionalUnsigned(T Val) : Rep(static_cast<underlying_type>(Val) + 1) {
assert(has_value());
}
OptionalUnsigned(int) = delete;
template <class U, std::enable_if_t<std::is_signed_v<U>, bool> = false>
OptionalUnsigned(U) = delete;
constexpr static OptionalUnsigned
fromInternalRepresentation(underlying_type Rep) {

View file

@ -23,40 +23,8 @@ namespace clang {
/// Constants for TokenKinds.def
enum TokenKey : unsigned {
KEYC99 = 0x1,
KEYCXX = 0x2,
KEYCXX11 = 0x4,
KEYGNU = 0x8,
KEYMS = 0x10,
BOOLSUPPORT = 0x20,
KEYALTIVEC = 0x40,
KEYNOCXX = 0x80,
KEYBORLAND = 0x100,
KEYOPENCLC = 0x200,
KEYC23 = 0x400,
KEYNOMS18 = 0x800,
KEYNOOPENCL = 0x1000,
WCHARSUPPORT = 0x2000,
HALFSUPPORT = 0x4000,
CHAR8SUPPORT = 0x8000,
KEYOBJC = 0x10000,
KEYZVECTOR = 0x20000,
KEYCOROUTINES = 0x40000,
KEYMODULES = 0x80000,
KEYCXX20 = 0x100000,
KEYOPENCLCXX = 0x200000,
KEYMSCOMPAT = 0x400000,
KEYSYCL = 0x800000,
KEYCUDA = 0x1000000,
KEYZOS = 0x2000000,
KEYHLSL = 0x4000000,
KEYFIXEDPOINT = 0x8000000,
KEYDEFERTS = 0x10000000,
KEYNOHLSL = 0x20000000,
KEYMAX = KEYNOHLSL, // The maximum key
KEYALLCXX = KEYCXX | KEYCXX11 | KEYCXX20,
KEYALL = (KEYMAX | (KEYMAX - 1)) & ~KEYNOMS18 & ~KEYNOOPENCL &
~KEYNOHLSL // KEYNOMS18, KEYNOOPENCL, KEYNOHLSL excluded.
#define EMIT_TOKENKEY
#include "clang/Basic/Traits.inc"
};
namespace tok {

View file

@ -0,0 +1,34 @@
//===-- CIRDataLayoutSpec.h - DLTI data layout for CIR modules --*- 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 file declares a helper that attaches the DLTI data-layout spec,
// including the CIR-native pointer entry, to a CIR module.
//
//===----------------------------------------------------------------------===//
#ifndef CLANG_CIR_CIRDATALAYOUTSPEC_H
#define CLANG_CIR_CIRDATALAYOUTSPEC_H
namespace llvm {
class DataLayout;
} // namespace llvm
namespace mlir {
class ModuleOp;
} // namespace mlir
namespace cir {
/// Translate \p dl into a DLTI data-layout spec and attach it to \p mod.
/// On top of the plain mlir::translateDataLayout entries this adds a
/// #cir.ptr_spec entry keyed on !cir.ptr, which CIR pointer types read for
/// their size and alignment; without it pointer widths default to 64 bits.
void setMLIRDataLayout(mlir::ModuleOp mod, const llvm::DataLayout &dl);
} // namespace cir
#endif // CLANG_CIR_CIRDATALAYOUTSPEC_H

View file

@ -797,6 +797,107 @@ def CIR_CmpThreeWayInfoAttr : CIR_Attr<"CmpThreeWayInfo", "cmp3way_info"> {
let canHaveIllegalCXXABIType = 0;
}
//===----------------------------------------------------------------------===//
// FenvAttr
//===----------------------------------------------------------------------===//
def CIR_FPDynamicRoundingMode : CIR_I32EnumAttr<
"FPDynamicRoundingMode", "floating-point dynamic rounding mode", [
I32EnumAttrCase<"ToNearest", 0, "tonearest">,
I32EnumAttrCase<"Downward", 1, "downward">,
I32EnumAttrCase<"Upward", 2, "upward">,
I32EnumAttrCase<"UpwardZero", 3, "upwardzero">,
I32EnumAttrCase<"ToNearestAway", 4, "tonearestaway">,
I32EnumAttrCase<"Unknown", 7, "unknown">
]> {
let description = [{
The known dynamic rounding mode at the point the instruction is executed.
If the actual dynamic rounding mode differs from this value, the behavior
is undefined.
}];
let genSpecializedAttr = 0;
}
def CIR_FPExceptionMode : CIR_I32EnumAttr<
"FPExceptionMode", "floating-point exception mode", [
I32EnumAttrCase<"Unknown", 0, "unknown">,
I32EnumAttrCase<"Masked", 1, "masked">,
I32EnumAttrCase<"Unmasked", 2, "unmasked">
]> {
let description = [{
The known floating-point exception mode at the point the instruction is
executed. If the actual exception mode differs from this value, the
behavior is undefined.
}];
let genSpecializedAttr = 0;
}
def CIR_FenvAttr : CIR_Attr<"Fenv", "fenv"> {
let summary = "Describes floating-point environment constraints";
let description = [{
The `#cir.fenv` attribute describes constraints on the floating-point
handling of a floating-point operation. It is attached to floating-point
operations to capture rounding and exception behavior. All of its
parameters are optional.
- `dynamic_rounding_mode`: the known dynamic rounding mode at the point the
instruction is executed. Otherwise the behavior is undefined.
- `except_mode`: the known exception mode at the point the instruction is
executed. Otherwise the behavior is undefined.
- `strict_except`: if `false`, any case that would produce an FP exception
produces it non-deterministically instead (i.e. it may or may not occur).
This means the FP status is written non-deterministically, and, if
exceptions are unmasked, the instruction traps non-deterministically.
An absent `dynamic_rounding_mode` defaults to `unknown`, an absent
`except_mode` defaults to `masked`, and an absent `strict_except` defaults
to `false`.
Example:
```
#cir.fenv<dynamic_rounding_mode = tonearest, except_mode = unknown,
strict_except = true>
```
}];
let parameters = (ins
OptionalParameter<
"std::optional<cir::FPDynamicRoundingMode>">:$dynamic_rounding_mode,
OptionalParameter<"std::optional<cir::FPExceptionMode>">:$except_mode,
OptionalParameter<"mlir::BoolAttr">:$strict_except);
let assemblyFormat = [{
`<` struct($dynamic_rounding_mode, $except_mode, $strict_except) `>`
}];
let extraClassDeclaration = [{
static constexpr cir::FPDynamicRoundingMode
getDefaultDynamicRoundingMode() {
return cir::FPDynamicRoundingMode::Unknown;
}
static constexpr cir::FPExceptionMode getDefaultExceptionMode() {
return cir::FPExceptionMode::Masked;
}
cir::FPDynamicRoundingMode getDynamicRoundingModeOrDefault() const {
return getDynamicRoundingMode().value_or(
getDefaultDynamicRoundingMode());
}
cir::FPExceptionMode getExceptionModeOrDefault() const {
return getExceptMode().value_or(getDefaultExceptionMode());
}
bool getStrictExceptOrDefault() const {
mlir::BoolAttr strictExcept = getStrictExcept();
return strictExcept && strictExcept.getValue();
}
}];
let canHaveIllegalCXXABIType = 0;
}
//===----------------------------------------------------------------------===//
// GlobalViewAttr
//===----------------------------------------------------------------------===//
@ -1087,6 +1188,52 @@ def CIR_TargetAddressSpaceAttr : CIR_Attr< "TargetAddressSpace",
let canHaveIllegalCXXABIType = 0;
}
//===----------------------------------------------------------------------===//
// PtrSpecAttr
//===----------------------------------------------------------------------===//
def CIR_PtrSpecAttr : CIR_Attr<"PtrSpec", "ptr_spec"> {
let summary = "!cir.ptr data layout spec";
let description = [{
Pointer data layout for `!cir.ptr`: `size`, `abi` and `preferred` are
required bitwidths; `index` is the optional bitwidth for index
computations, defaulting to `size`. All present values must be divisible
by 8, with `preferred` >= `abi`.
This is the CIR-native analogue of the ptr dialect's `#ptr.spec`; a
native attribute keeps CIR's data-layout queries free of any ptr-dialect
dependency.
Used as the value of the `!cir.ptr<!cir.void>`-keyed data-layout entry:
```mlir
#dlti.dl_spec<!cir.ptr<!cir.void> =
#cir.ptr_spec<size = 64, abi = 64, preferred = 64>>
```
}];
let parameters = (ins
"uint32_t":$size,
"uint32_t":$abi,
"uint32_t":$preferred,
DefaultValuedParameter<"uint32_t", "kOptionalSpecValue">:$index
);
let skipDefaultBuilders = 1;
let builders = [
AttrBuilder<(ins "uint32_t":$size, "uint32_t":$abi, "uint32_t":$preferred,
CArg<"uint32_t", "kOptionalSpecValue">:$index), [{
return $_get($_ctxt, size, abi, preferred, index);
}]>
];
let assemblyFormat = "`<` struct(params) `>`";
let extraClassDeclaration = [{
/// Sentinel value marking the optional `index` parameter as unset.
static constexpr uint32_t kOptionalSpecValue =
std::numeric_limits<uint32_t>::max();
}];
let genVerifyDecl = 1;
let canHaveIllegalCXXABIType = 0;
}
//===----------------------------------------------------------------------===//
// ConstComplexAttr
//===----------------------------------------------------------------------===//

View file

@ -42,6 +42,45 @@ using BuilderOpStateCallbackRef = llvm::function_ref<void(
namespace cir {
void buildTerminatedBody(mlir::OpBuilder &builder, mlir::Location loc);
/// The process floating-point environment, including its rounding mode and
/// exception state.
struct FloatingPointEnvironmentResource
: public mlir::SideEffects::Resource::Base<
FloatingPointEnvironmentResource> {
mlir::StringRef getName() const final { return "FloatingPointEnvironment"; }
bool isAddressable() const final { return false; }
};
template <typename ConcreteType>
class FenvOpTrait : public mlir::OpTrait::TraitBase<ConcreteType, FenvOpTrait> {
public:
mlir::Speculation::Speculatability getSpeculatability() {
// Masked exceptions cannot trap. When strict_except is false, exception
// side effects are non-deterministic, so speculation is still safe.
FPEnvConstrainedOpInterface fenvOp = getFenvOp();
if (fenvOp.getFenvExceptionMode() == cir::FPExceptionMode::Masked &&
!fenvOp.getFenvStrictExcept())
return mlir::Speculation::Speculatable;
return mlir::Speculation::NotSpeculatable;
}
void getEffects(
llvm::SmallVectorImpl<mlir::MemoryEffects::EffectInstance> &effects) {
if (!getFenvOp().getFenvAttr())
return;
effects.emplace_back(mlir::MemoryEffects::Read::get(),
FloatingPointEnvironmentResource::get());
effects.emplace_back(mlir::MemoryEffects::Write::get(),
FloatingPointEnvironmentResource::get());
}
private:
FPEnvConstrainedOpInterface getFenvOp() {
return mlir::cast<FPEnvConstrainedOpInterface>(this->getOperation());
}
};
/// Look up the RecordLayoutAttr for a named record in the module's
/// cir.record_layouts dictionary. Asserts if the entry is missing.
RecordLayoutAttr getRecordLayout(mlir::ModuleOp module, mlir::StringAttr name);

View file

@ -73,8 +73,20 @@ include "mlir/Interfaces/SideEffectInterfaces.td"
//
// If you want fully customized LLVM IR lowering logic, simply exclude the
// `llvmOp` field from your CIR operation definition.
//
// The `constrainedLLVMIntrinsic` field enables a specialized variant of this
// generated lowering for floating-point operations that carry an optional
// `fenv` attribute. When it is set and the operation has an `fenv` attribute
// the operation is lowering to a call to the corresponding constrained fp
// intrinsic. If `constrainedLLVMIntrinsic` is set `llvmOp` must be set as well.
//
// `constrainedLLVMIntrinsicHasRoundingMode` indicates whether the constrained
// intrinsic takes a rounding-mode metadata argument in addition to the
// exception-behavior metadata argument.
class LLVMLoweringInfo {
string llvmOp = "";
string constrainedLLVMIntrinsic = "";
bit constrainedLLVMIntrinsicHasRoundingMode = true;
}
class LoweringBuilders<dag p> {
@ -122,6 +134,37 @@ class HasAtMostOneOfAttrs<list<string> names> : PredOpTrait<
HasAtMostOneOfAttrsPred<!foreach(name, names, "$" # name)>
>;
// Like `SameTypeOperands`, but the operands are pointers that are only required
// to share the same pointee type; they may still differ in address space (e.g.
// a `cir.copy` that assigns through an address-space-qualified pointer).
def SameOperandsPointeeType : PredOpTrait<
"all operands have the same pointee type (address spaces may differ)",
CPred<[{
llvm::all_equal(llvm::map_range($_op.getOperands(), [](mlir::Value v) {
return mlir::cast<cir::PointerType>(v.getType()).getPointee();
}))
}]>
>;
//===----------------------------------------------------------------------===//
// Floating-Point Environment Traits
//===----------------------------------------------------------------------===//
def CIR_FenvOpTrait : NativeOpTrait<"FenvOpTrait"> {
let cppNamespace = "::cir";
}
defvar CIR_FenvOpTraits = [
CIR_FPEnvConstrainedOpInterface,
ConditionallySpeculatable,
MemoryEffectsOpInterface,
CIR_FenvOpTrait
];
// Passed as CIR_BinaryOp's hasNoMemoryEffect: fenv ops compute effects
// dynamically via FenvOpTrait, so they must not inherit NoMemoryEffect.
defvar CIR_DynamicMemoryEffects = 0;
//===----------------------------------------------------------------------===//
// CastOp
//===----------------------------------------------------------------------===//
@ -201,9 +244,9 @@ def CIR_CastKind : CIR_I32EnumAttr<"CastKind", "cast kind", [
I32EnumAttrCase<"bool_to_float", 1000>,
]>;
def CIR_CastOp : CIR_Op<"cast", [
Pure, DeclareOpInterfaceMethods<PromotableOpInterface>
]> {
def CIR_CastOp : CIR_Op<"cast", !listconcat([
DeclareOpInterfaceMethods<PromotableOpInterface>
], CIR_FenvOpTraits)> {
let summary = "Conversion between values of different types";
let description = [{
Apply the usual C/C++ conversion rules between values. This operation models
@ -239,6 +282,13 @@ def CIR_CastOp : CIR_Op<"cast", [
- `bool_to_float`
The optional `fenv` attribute describes constraints on the floating-point
handling of the operation. It is only valid for floating-point cast kinds:
`floating`, `int_to_float`, `float_to_int`, `float_to_bool`,
`bool_to_float`, `float_to_complex`, `float_complex_to_real`,
`float_complex_to_bool`, `float_complex`, `float_complex_to_int_complex`,
and `int_complex_to_float_complex`.
Example:
```
@ -246,16 +296,27 @@ def CIR_CastOp : CIR_Op<"cast", [
...
%x = cir.cast array_to_ptrdecay %0
: !cir.ptr<!cir.array<i32 x 10>> -> !cir.ptr<i32>
%y = cir.cast floating %f : !cir.double -> !cir.float {
fenv = #cir.fenv<>
}
```
}];
let arguments = (ins CIR_CastKind:$kind, CIR_AnyType:$src);
let arguments = (ins CIR_CastKind:$kind, CIR_AnyType:$src,
OptionalAttr<CIR_FenvAttr>:$fenv);
let results = (outs CIR_AnyType:$result);
let assemblyFormat = [{
$kind $src `:` type($src) `->` type($result) attr-dict
}];
let builders = [
OpBuilder<(ins "mlir::Type":$result, "cir::CastKind":$kind,
"mlir::Value":$src), [{
build($_builder, $_state, result, kind, src, cir::FenvAttr{});
}]>
];
// The input and output types should match the cast kind.
let hasVerifier = 1;
let hasFolder = 1;
@ -1852,6 +1913,35 @@ def CIR_GotoOp : CIR_Op<"goto", [Terminator]> {
let hasLLVMLowering = false;
}
//===----------------------------------------------------------------------===//
// IndirectGotoOp
//===----------------------------------------------------------------------===//
def CIR_IndirectGotoOp : CIR_Op<"indirect_goto", [Terminator]> {
let summary = "Symbolic indirect goto";
let description = [{
Transfers control to the block whose address is held in `$addr`, the
void-pointer value of a `goto *expr;` (the GNU computed-goto extension).
Like `cir.goto`, it is symbolic: it references no successor block, so it is
valid inside any region even before `FlattenCFG` merges nested scopes.
`GotoSolver` runs after `FlattenCFG` and rewrites each `cir.indirect_goto`
into a `cir.br` to a shared block holding a `cir.indirect_br` over every
address-taken label.
Example:
```mlir
%0 = cir.load %p : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
cir.indirect_goto %0 : !cir.ptr<!void>
```
}];
let arguments = (ins CIR_VoidPtrType:$addr);
let assemblyFormat = [{ $addr `:` qualified(type($addr)) attr-dict }];
let hasLLVMLowering = false;
}
//===----------------------------------------------------------------------===//
// LabelOp
//===----------------------------------------------------------------------===//
@ -1882,7 +1972,9 @@ class CIR_UnaryOp<string mnemonic, Type type, list<Trait> traits = []>
DeclareOpInterfaceMethods<CIR_UnaryOpInterface>
], traits)>
{
let arguments = (ins type:$input);
dag commonArgs = (ins type:$input);
let arguments = commonArgs;
let results = (outs type:$result);
@ -2420,12 +2512,14 @@ def CIR_CmpOpKind : CIR_I32EnumAttr<"CmpOpKind", "compare operation kind", [
I32EnumAttrCase<"uno", 7>
]>;
def CIR_CmpOp : CIR_Op<"cmp", [Pure, SameTypeOperands]> {
def CIR_CmpOp : CIR_Op<"cmp",
!listconcat([SameTypeOperands], CIR_FenvOpTraits)> {
let summary = "Compare two values and produce a boolean result";
let description = [{
The `cir.cmp` operation compares two operands of the same type and produces
a `!cir.bool` result. It supports integral, floating-point, and pointer
types.
a `!cir.bool` result. It supports integral, boolean, floating-point, and
pointer types. Booleans (including enums with a boolean underlying type)
are compared as unsigned integers.
The following comparison predicates are available:
@ -2443,16 +2537,22 @@ def CIR_CmpOp : CIR_Op<"cmp", [Pure, SameTypeOperands]> {
The `one` and `uno` predicates are floating-point specific: `one` is
ordered not-equal (false for NaN), `uno` tests if either operand is NaN.
The optional `fenv` attribute describes constraints on the floating-point
handling of the operation. It is only valid for floating-point
comparisons.
```
%0 = cir.cmp gt %1, %2 : !s32i
%1 = cir.cmp eq %a, %b : !cir.ptr<!u8i>
%2 = cir.cmp lt %x, %y : !cir.float {fenv = #cir.fenv<>}
```
}];
let arguments = (ins
CIR_CmpOpKind:$kind,
CIR_ComparableType:$lhs,
CIR_ComparableType:$rhs
CIR_ComparableType:$rhs,
OptionalAttr<CIR_FenvAttr>:$fenv
);
let results = (outs CIR_BoolType:$result);
@ -2461,6 +2561,18 @@ def CIR_CmpOp : CIR_Op<"cmp", [Pure, SameTypeOperands]> {
$kind $lhs `,` $rhs `:` type($lhs) attr-dict
}];
let builders = [
OpBuilder<(ins "cir::CmpOpKind":$kind, "mlir::Value":$lhs,
"mlir::Value":$rhs), [{
build($_builder, $_state, kind, lhs, rhs, cir::FenvAttr{});
}]>,
OpBuilder<(ins "mlir::Type":$result, "cir::CmpOpKind":$kind,
"mlir::Value":$lhs, "mlir::Value":$rhs), [{
build($_builder, $_state, result, kind, lhs, rhs, cir::FenvAttr{});
}]>
];
let hasVerifier = 1;
let isLLVMLoweringRecursive = true;
let hasCXXABILowering = true;
}
@ -2563,17 +2675,21 @@ def CIR_MulOverflowOp : CIR_BinOpOverflow<"mul.overflow", [Commutative]> {
// Base class for all CIR binary arithmetic/logic operations.
// `type` constrains the lhs/rhs/result type.
class CIR_BinaryOp<string mnemonic, Type type, list<Trait> traits = []>
: CIR_Op<mnemonic, !listconcat([
NoMemoryEffect, SameOperandsAndResultType,
class CIR_BinaryOp<string mnemonic, Type type, list<Trait> traits = [],
bit hasNoMemoryEffect = 1>
: CIR_Op<mnemonic, !listconcat(
!if(hasNoMemoryEffect, [NoMemoryEffect], []), [
SameOperandsAndResultType,
DeclareOpInterfaceMethods<CIR_BinaryOpInterface>
], traits)>
{
let arguments = (ins
dag commonArgs = (ins
type:$lhs,
type:$rhs
);
let arguments = commonArgs;
let results = (outs type:$result);
let assemblyFormat = [{
@ -2749,9 +2865,23 @@ def CIR_RemOp : CIR_BinaryOp<"rem", CIR_AnyIntOrVecOfIntType> {
// Base class for floating-point binary arithmetic operations. The lhs, rhs,
// and result must all be the same floating-point scalar or vector type.
//
// The optional `fenv` attribute describes constraints on the floating-point
// handling of the operation.
class CIR_FPBinaryOp<string mnemonic, list<Trait> traits = []>
: CIR_BinaryOp<mnemonic, CIR_AnyFloatOrVecOfFloatType,
!listconcat([Pure], traits)>;
!listconcat(CIR_FenvOpTraits, traits),
CIR_DynamicMemoryEffects> {
let arguments = !con(commonArgs, (ins OptionalAttr<CIR_FenvAttr>:$fenv));
let constrainedLLVMIntrinsic = mnemonic;
let builders = [
OpBuilder<(ins "mlir::Value":$lhs, "mlir::Value":$rhs), [{
build($_builder, $_state, lhs, rhs, cir::FenvAttr{});
}]>
];
}
//===----------------------------------------------------------------------===//
// FAddOp
@ -4708,7 +4838,7 @@ def CIR_CoReturnOp : CIR_Op<"co_return", [
//===----------------------------------------------------------------------===//
def CIR_CopyOp : CIR_Op<"copy",[
SameTypeOperands,
SameOperandsPointeeType,
DeclareOpInterfaceMethods<PromotableMemOpInterface>
]> {
let summary = "Copies contents from a CIR pointer to another";
@ -4718,7 +4848,9 @@ def CIR_CopyOp : CIR_Op<"copy",[
The number of bytes copied is inferred from the pointee type. The pointee
type of `src` and `dst` must match and both must implement the
`DataLayoutTypeInterface`.
`DataLayoutTypeInterface`. The pointers may differ in address space (e.g.
when assigning through an address-space-qualified pointer); when the two
pointer types differ, both are printed, `src` first.
The `volatile` keyword indicates that the operation is volatile.
@ -4754,7 +4886,7 @@ def CIR_CopyOp : CIR_Op<"copy",[
$dst (`align` `(` $dst_alignment^ `)`)?
(`volatile` $is_volatile^)?
(`skip_tail_padding` $skip_tail_padding^)?
attr-dict `:` qualified(type($dst))
attr-dict `:` custom<CopyTypes>(type($src), type($dst))
}];
let hasVerifier = 1;
@ -5617,7 +5749,8 @@ def CIR_VecExtractOp : CIR_Op<"vec.extract", [
// VecCmpOp
//===----------------------------------------------------------------------===//
def CIR_VecCmpOp : CIR_Op<"vec.cmp", [Pure, SameTypeOperands]> {
def CIR_VecCmpOp : CIR_Op<"vec.cmp",
!listconcat([SameTypeOperands], CIR_FenvOpTraits)> {
let summary = "Compare two vectors";
let description = [{
The `cir.vec.cmp` operation does an element-wise comparison of two vectors
@ -5625,16 +5758,24 @@ def CIR_VecCmpOp : CIR_Op<"vec.cmp", [Pure, SameTypeOperands]> {
whose element type is the signed integral type that is the same size as the
element type of the operands. The values in the result are 0 or -1.
The optional `fenv` attribute describes constraints on the floating-point
handling of the operation. It is only valid for floating-point vector
comparisons.
```
%eq = cir.vec.cmp(eq, %vec_a, %vec_b) : !cir.vector<4 x !s32i>, !cir.vector<4 x !s32i>
%lt = cir.vec.cmp(lt, %vec_a, %vec_b) : !cir.vector<4 x !s32i>, !cir.vector<4 x !s32i>
%gt = cir.vec.cmp(gt, %va, %vb) : !cir.vector<4 x !cir.float>, !cir.vector<4 x !s32i> {
fenv = #cir.fenv<>
}
```
}];
let arguments = (ins
CIR_CmpOpKind:$kind,
CIR_VectorType:$lhs,
CIR_VectorType:$rhs
CIR_VectorType:$rhs,
OptionalAttr<CIR_FenvAttr>:$fenv
);
let results = (outs CIR_VectorType:$result);
@ -5644,6 +5785,14 @@ def CIR_VecCmpOp : CIR_Op<"vec.cmp", [Pure, SameTypeOperands]> {
qualified(type($result)) attr-dict
}];
let builders = [
OpBuilder<(ins "mlir::Type":$result, "cir::CmpOpKind":$kind,
"mlir::Value":$lhs, "mlir::Value":$rhs), [{
build($_builder, $_state, result, kind, lhs, rhs, cir::FenvAttr{});
}]>
];
let hasVerifier = 1;
let hasFolder = 1;
}
@ -6905,15 +7054,26 @@ def CIR_PtrDiffOp : CIR_Op<"ptr_diff", [Pure, SameTypeOperands]> {
// Floating Point Ops
//===----------------------------------------------------------------------===//
// The optional `fenv` attribute describes constraints on the floating-point
// handling of the operation.
class CIR_UnaryFPToFPBuiltinOp<string mnemonic, string llvmOpName>
: CIR_Op<mnemonic, [Pure, SameOperandsAndResultType]>
: CIR_Op<mnemonic,
!listconcat([SameOperandsAndResultType], CIR_FenvOpTraits)>
{
let arguments = (ins CIR_AnyFloatOrVecOfFloatType:$src);
let arguments = (ins CIR_AnyFloatOrVecOfFloatType:$src,
OptionalAttr<CIR_FenvAttr>:$fenv);
let results = (outs CIR_AnyFloatOrVecOfFloatType:$result);
let assemblyFormat = "$src `:` type($src) attr-dict";
let builders = [
OpBuilder<(ins "mlir::Value":$src), [{
build($_builder, $_state, src, cir::FenvAttr{});
}]>
];
let llvmOp = llvmOpName;
let constrainedLLVMIntrinsic = mnemonic;
}
def CIR_SqrtOp : CIR_UnaryFPToFPBuiltinOp<"sqrt", "SqrtOp"> {
@ -6975,6 +7135,8 @@ def CIR_CeilOp : CIR_UnaryFPToFPBuiltinOp<"ceil", "FCeilOp"> {
Floating-point exceptions are ignored, and it does not set `errno`.
}];
let constrainedLLVMIntrinsicHasRoundingMode = 0;
}
def CIR_CosOp : CIR_UnaryFPToFPBuiltinOp<"cos", "CosOp"> {
@ -6987,6 +7149,16 @@ def CIR_CosOp : CIR_UnaryFPToFPBuiltinOp<"cos", "CosOp"> {
}];
}
def CIR_CoshOp : CIR_UnaryFPToFPBuiltinOp<"cosh", "CoshOp"> {
let summary = "Computes the floating-point hyperbolic cosine value";
let description = [{
`cir.cosh` computes the hyperbolic cosine of a floating-point operand and
returns a result of the same type.
Floating-point exceptions are ignored, and it does not set `errno`.
}];
}
def CIR_ExpOp : CIR_UnaryFPToFPBuiltinOp<"exp", "ExpOp"> {
let summary = "Computes the floating-point base-e exponential value";
let description = [{
@ -6997,6 +7169,16 @@ def CIR_ExpOp : CIR_UnaryFPToFPBuiltinOp<"exp", "ExpOp"> {
}];
}
def CIR_Exp10Op : CIR_UnaryFPToFPBuiltinOp<"exp10", "Exp10Op"> {
let summary = "Computes the floating-point base-10 exponential value";
let description = [{
`cir.exp10` computes the base-10 exponential of a floating-point operand and
returns a result of the same type.
Floating-point exceptions are ignored, and it does not set `errno`.
}];
}
def CIR_Exp2Op : CIR_UnaryFPToFPBuiltinOp<"exp2", "Exp2Op"> {
let summary = "Computes the floating-point base-2 exponential value";
let description = [{
@ -7067,6 +7249,8 @@ def CIR_RoundOp : CIR_UnaryFPToFPBuiltinOp<"round", "RoundOp"> {
Floating-point exceptions are ignored, and it does not set `errno`.
}];
let constrainedLLVMIntrinsicHasRoundingMode = 0;
}
def CIR_RoundEvenOp : CIR_UnaryFPToFPBuiltinOp<"roundeven", "RoundEvenOp"> {
@ -7077,6 +7261,8 @@ def CIR_RoundEvenOp : CIR_UnaryFPToFPBuiltinOp<"roundeven", "RoundEvenOp"> {
Floating-point exceptions are ignored, and it does not set `errno`.
}];
let constrainedLLVMIntrinsicHasRoundingMode = 0;
}
def CIR_SinOp : CIR_UnaryFPToFPBuiltinOp<"sin", "SinOp"> {
@ -7089,6 +7275,16 @@ def CIR_SinOp : CIR_UnaryFPToFPBuiltinOp<"sin", "SinOp"> {
}];
}
def CIR_SinhOp : CIR_UnaryFPToFPBuiltinOp<"sinh", "SinhOp"> {
let summary = "Computes the floating-point hyperbolic sine";
let description = [{
`cir.sinh` computes the hyperbolic sine of a floating-point operand and
returns a result of the same type.
Floating-point exceptions are ignored, and it does not set `errno`.
}];
}
def CIR_TanOp : CIR_UnaryFPToFPBuiltinOp<"tan", "TanOp"> {
let summary = "Computes the floating-point tangent";
let description = [{
@ -7099,6 +7295,16 @@ def CIR_TanOp : CIR_UnaryFPToFPBuiltinOp<"tan", "TanOp"> {
}];
}
def CIR_TanhOp : CIR_UnaryFPToFPBuiltinOp<"tanh", "TanhOp"> {
let summary = "Computes the floating-point hyperbolic tangent";
let description = [{
`cir.tanh` computes the hyperbolic tangent of a floating-point operand and
returns a result of the same type.
Floating-point exceptions are ignored, and it does not set `errno`.
}];
}
def CIR_TruncOp : CIR_UnaryFPToFPBuiltinOp<"trunc", "FTruncOp"> {
let summary = "Truncates floating-point value to integer";
let description = [{
@ -7107,6 +7313,8 @@ def CIR_TruncOp : CIR_UnaryFPToFPBuiltinOp<"trunc", "FTruncOp"> {
Floating-point exceptions are ignored, and it does not set `errno`.
}];
let constrainedLLVMIntrinsicHasRoundingMode = 0;
}
def CIR_FAbsOp : CIR_UnaryFPToFPBuiltinOp<"fabs", "FAbsOp"> {
@ -7117,6 +7325,10 @@ def CIR_FAbsOp : CIR_UnaryFPToFPBuiltinOp<"fabs", "FAbsOp"> {
Floating-point exceptions are ignored, and it does not set `errno`.
}];
// fabs is exact and does not raise exceptions, so it is always lowered to
// the plain llvm.fabs intrinsic.
let constrainedLLVMIntrinsic = "";
}
def CIR_AbsOp : CIR_Op<"abs", [Pure, SameOperandsAndResultType]> {
@ -7163,12 +7375,15 @@ def CIR_FloorOp : CIR_UnaryFPToFPBuiltinOp<"floor", "FFloorOp"> {
%y = cir.floor %x : !cir.double
```
}];
let constrainedLLVMIntrinsicHasRoundingMode = 0;
}
class CIR_UnaryFPToIntBuiltinOp<string mnemonic, string llvmOpName>
: CIR_Op<mnemonic, [Pure]>
: CIR_Op<mnemonic, CIR_FenvOpTraits>
{
let arguments = (ins CIR_AnyFloatType:$src);
let arguments = (ins CIR_AnyFloatType:$src,
OptionalAttr<CIR_FenvAttr>:$fenv);
let results = (outs CIR_IntType:$result);
let summary = [{
@ -7180,7 +7395,14 @@ class CIR_UnaryFPToIntBuiltinOp<string mnemonic, string llvmOpName>
$src `:` type($src) `->` type($result) attr-dict
}];
let builders = [
OpBuilder<(ins "mlir::Type":$result, "mlir::Value":$src), [{
build($_builder, $_state, result, src, cir::FenvAttr{});
}]>
];
let llvmOp = llvmOpName;
let constrainedLLVMIntrinsic = mnemonic;
}
def CIR_LroundOp : CIR_UnaryFPToIntBuiltinOp<"lround", "LroundOp"> {
@ -7189,6 +7411,8 @@ def CIR_LroundOp : CIR_UnaryFPToIntBuiltinOp<"lround", "LroundOp"> {
`cir.lround` rounds a floating-point value to the nearest integer value,
rounding halfway cases away from zero, and returns the result as a `long`.
}];
let constrainedLLVMIntrinsicHasRoundingMode = 0;
}
def CIR_LlroundOp : CIR_UnaryFPToIntBuiltinOp<"llround", "LlroundOp"> {
@ -7198,6 +7422,8 @@ def CIR_LlroundOp : CIR_UnaryFPToIntBuiltinOp<"llround", "LlroundOp"> {
rounding halfway cases away from zero, and returns the result as a
`long long`.
}];
let constrainedLLVMIntrinsicHasRoundingMode = 0;
}
def CIR_LrintOp : CIR_UnaryFPToIntBuiltinOp<"lrint", "LrintOp"> {
@ -7217,14 +7443,16 @@ def CIR_LlrintOp : CIR_UnaryFPToIntBuiltinOp<"llrint", "LlrintOp"> {
}
class CIR_BinaryFPToFPBuiltinOp<string mnemonic, string llvmOpName>
: CIR_Op<mnemonic, [Pure, SameOperandsAndResultType]> {
: CIR_Op<mnemonic,
!listconcat([SameOperandsAndResultType], CIR_FenvOpTraits)> {
let summary = [{
libc builtin equivalent ignoring floating-point exceptions and errno.
}];
let arguments = (ins
CIR_AnyFloatOrVecOfFloatType:$lhs,
CIR_AnyFloatOrVecOfFloatType:$rhs
CIR_AnyFloatOrVecOfFloatType:$rhs,
OptionalAttr<CIR_FenvAttr>:$fenv
);
let results = (outs CIR_AnyFloatOrVecOfFloatType:$result);
@ -7233,6 +7461,13 @@ class CIR_BinaryFPToFPBuiltinOp<string mnemonic, string llvmOpName>
$lhs `,` $rhs `:` qualified(type($lhs)) attr-dict
}];
let builders = [
OpBuilder<(ins "mlir::Type":$result, "mlir::Value":$lhs,
"mlir::Value":$rhs), [{
build($_builder, $_state, result, lhs, rhs, cir::FenvAttr{});
}]>
];
let llvmOp = llvmOpName;
}
@ -7242,6 +7477,9 @@ def CIR_CopysignOp : CIR_BinaryFPToFPBuiltinOp<"copysign", "CopySignOp"> {
`cir.copysign` returns a value with the magnitude of the first operand
and the sign of the second operand.
}];
// copysign is exact and does not raise exceptions, so it is always lowered
// to the plain llvm.copysign intrinsic.
}
def CIR_FMaxNumOp : CIR_BinaryFPToFPBuiltinOp<"fmaxnum", "MaxNumOp"> {
@ -7260,6 +7498,9 @@ def CIR_FMaximumOp : CIR_BinaryFPToFPBuiltinOp<"fmaximum", "MaximumOp"> {
`cir.fmaximum` returns the larger of its two operands according to
IEEE 754-2019 semantics. If either operand is NaN, NaN is returned.
}];
let constrainedLLVMIntrinsic = "maximum";
let constrainedLLVMIntrinsicHasRoundingMode = 0;
}
def CIR_FMinNumOp : CIR_BinaryFPToFPBuiltinOp<"fminnum", "MinNumOp"> {
@ -7278,6 +7519,9 @@ def CIR_FMinimumOp : CIR_BinaryFPToFPBuiltinOp<"fminimum", "MinimumOp"> {
`cir.fminimum` returns the smaller of its two operands according to
IEEE 754-2019 semantics. If either operand is NaN, NaN is returned.
}];
let constrainedLLVMIntrinsic = "minimum";
let constrainedLLVMIntrinsicHasRoundingMode = 0;
}
def CIR_FModOp : CIR_BinaryFPToFPBuiltinOp<"fmod", "FRemOp"> {
@ -7286,6 +7530,9 @@ def CIR_FModOp : CIR_BinaryFPToFPBuiltinOp<"fmod", "FRemOp"> {
`cir.fmod` computes the floating-point remainder of dividing the first
operand by the second operand.
}];
// fmod maps to the constrained frem intrinsic.
let constrainedLLVMIntrinsic = "frem";
}
def CIR_PowOp : CIR_BinaryFPToFPBuiltinOp<"pow", "PowOp"> {
@ -7294,6 +7541,8 @@ def CIR_PowOp : CIR_BinaryFPToFPBuiltinOp<"pow", "PowOp"> {
`cir.pow` computes the first operand raised to the power of the second
operand.
}];
let constrainedLLVMIntrinsic = "pow";
}
def CIR_ATan2Op : CIR_BinaryFPToFPBuiltinOp<"atan2", "ATan2Op"> {
@ -7302,6 +7551,8 @@ def CIR_ATan2Op : CIR_BinaryFPToFPBuiltinOp<"atan2", "ATan2Op"> {
`cir.atan2` computes the arc tangent of the first operand divided by the
second operand, using the signs of both to determine the quadrant.
}];
let constrainedLLVMIntrinsic = "atan2";
}
def CIR_FrexpOp : CIR_Op<"frexp", [Pure]> {
@ -7346,18 +7597,27 @@ def CIR_ModfOp : CIR_Op<"modf", [Pure]> {
}
class CIR_TernaryFPToFPBuiltinOp<string mnemonic, string llvmOpName>
: CIR_Op<mnemonic, [Pure, SameOperandsAndResultType]>
: CIR_Op<mnemonic,
!listconcat([SameOperandsAndResultType], CIR_FenvOpTraits)>
{
let arguments = (ins
let arguments = (ins
CIR_AnyFloatOrVecOfFloatType:$a,
CIR_AnyFloatOrVecOfFloatType:$b,
CIR_AnyFloatOrVecOfFloatType:$c
CIR_AnyFloatOrVecOfFloatType:$c,
OptionalAttr<CIR_FenvAttr>:$fenv
);
let results = (outs CIR_AnyFloatOrVecOfFloatType:$result);
let assemblyFormat = "$a `,` $b `,` $c `:` type($a) attr-dict";
let builders = [
OpBuilder<(ins "mlir::Type":$result, "mlir::Value":$a, "mlir::Value":$b,
"mlir::Value":$c), [{
build($_builder, $_state, result, a, b, c, cir::FenvAttr{});
}]>
];
let llvmOp = llvmOpName;
}

View file

@ -402,8 +402,8 @@ def CIR_AnyScalarType : AnyTypeOf<CIR_ScalarTypes, "cir scalar type"> {
def CIR_AnyMethodType : CIR_TypeBase<"::cir::MethodType", "method type">;
def CIR_ComparableType
: AnyTypeOf<[CIR_AnyIntType, CIR_AnyFloatType, CIR_AnyPtrType,
CIR_AnyComplexType, CIR_AnyVPtrType,
: AnyTypeOf<[CIR_AnyIntType, CIR_AnyBoolType, CIR_AnyFloatType,
CIR_AnyPtrType, CIR_AnyComplexType, CIR_AnyVPtrType,
CIR_AnyDataMemberType, CIR_AnyMethodType],
"comparable type"> {
let cppFunctionName = "isComparableType";

View file

@ -239,7 +239,9 @@ def CIR_ComplexType : CIR_Type<"Complex", "complex", [
//===----------------------------------------------------------------------===//
def CIR_PointerType : CIR_Type<"Pointer", "ptr", [
DeclareTypeInterfaceMethods<DataLayoutTypeInterface>,
DeclareTypeInterfaceMethods<DataLayoutTypeInterface,
["getPreferredAlignment", "getIndexBitwidth", "areCompatible",
"verifyEntries"]>,
DeclareTypeInterfaceMethods<CIR_SizedTypeInterface>
]> {
let summary = "CIR pointer type";

View file

@ -21,6 +21,7 @@
#include "clang/AST/Attr.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Mangle.h"
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
#include "clang/CIR/Dialect/IR/CIROpsEnums.h"
/// Include the generated interface declarations.

View file

@ -211,6 +211,47 @@ let cppNamespace = "::cir" in {
}];
}
def CIR_FPEnvConstrainedOpInterface
: OpInterface<"FPEnvConstrainedOpInterface"> {
let description = [{
Interface for floating-point operations that carry an optional
`#cir.fenv` attribute. It provides effective values for each field,
substituting the floating-point environment defaults for absent fields.
}];
let methods = [
InterfaceMethod<
"Return the `#cir.fenv` attribute, or a null attribute if absent.",
"cir::FenvAttr", "getFenvAttr", (ins), [{}],
/*defaultImplementation=*/[{
return $_op.getFenvAttr();
}]>,
InterfaceMethod<
"Return the effective dynamic rounding mode.",
"cir::FPDynamicRoundingMode", "getFenvDynamicRoundingMode", (ins),
[{}], /*defaultImplementation=*/[{
cir::FenvAttr fenv = $_op.getFenvAttr();
return fenv ? fenv.getDynamicRoundingModeOrDefault()
: cir::FenvAttr::getDefaultDynamicRoundingMode();
}]>,
InterfaceMethod<
"Return the effective floating-point exception mode.",
"cir::FPExceptionMode", "getFenvExceptionMode", (ins), [{}],
/*defaultImplementation=*/[{
cir::FenvAttr fenv = $_op.getFenvAttr();
return fenv ? fenv.getExceptionModeOrDefault()
: cir::FenvAttr::getDefaultExceptionMode();
}]>,
InterfaceMethod<
"Return the effective strict-exception flag.",
"bool", "getFenvStrictExcept", (ins), [{}],
/*defaultImplementation=*/[{
cir::FenvAttr fenv = $_op.getFenvAttr();
return fenv ? fenv.getStrictExceptOrDefault() : false;
}]>,
];
}
def CIR_BinaryOpInterface : OpInterface<"BinaryOpInterface"> {
let description = [{
Common interface for CIR binary arithmetic and logic operations.

View file

@ -91,7 +91,7 @@ struct MissingFeatures {
static bool opCallReturn() { return false; }
static bool opCallArgEvaluationOrder() { return false; }
static bool opCallCallConv() { return false; }
static bool opCallMustTail() { return false; }
static bool opCallThunkTailHint() { return false; }
static bool opCallInAlloca() { return false; }
static bool opCallAttrs() { return false; }
static bool opCallSurroundingTry() { return false; }

View file

@ -41,6 +41,8 @@ public:
virtual void handleVisibleModule(std::string ModuleName) = 0;
virtual void handleContextHash(std::string Hash) = 0;
virtual void finishQuery(StringRef ModuleName, bool Success) {}
};
} // namespace clang::dependencies

View file

@ -69,20 +69,6 @@ struct DiagnosticsEngineWithDiagOpts {
DiagnosticConsumer &DC);
};
struct TextDiagnosticsPrinterWithOutput {
// We need to bound the lifetime of the data that supports the DiagPrinter
// with it together so they have the same lifetime.
std::string DiagnosticOutput;
llvm::raw_string_ostream DiagnosticsOS;
std::unique_ptr<DiagnosticOptions> DiagOpts;
TextDiagnosticPrinter DiagPrinter;
TextDiagnosticsPrinterWithOutput(ArrayRef<std::string> CommandLine)
: DiagnosticsOS(DiagnosticOutput),
DiagOpts(createDiagOptions(CommandLine)),
DiagPrinter(DiagnosticsOS, *DiagOpts) {}
};
std::unique_ptr<CompilerInvocation>
createCompilerInvocation(ArrayRef<std::string> CommandLine,
DiagnosticsEngine &Diags);

View file

@ -28,10 +28,6 @@ namespace clang {
class DependencyOutputOptions;
namespace tooling {
class CompilerInstanceWithContext;
}
namespace dependencies {
class DependencyConsumer;
@ -64,6 +60,18 @@ public:
DiagnosticConsumer &DiagConsumer,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> OverlayFS = nullptr);
/// By-name scanning over a single cc1 command line. Builds a scanning session
/// local to this call, then pulls module names from \p getNextName until it
/// returns std::nullopt. Results flow to \p DepConsumer (per-name status via
/// finishQuery); diagnostics flow to \p DiagConsumer.
/// \returns false if session setup failed, true otherwise.
bool computeDependenciesByName(
StringRef CWD, ArrayRef<std::string> CC1CommandLine,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> OverlayFS,
DiagnosticConsumer &DiagConsumer, DependencyActionController &Controller,
llvm::function_ref<std::optional<std::string>()> getNextName,
DependencyConsumer &DepConsumer);
/// Creates the effective VFS that will be used for the scan.
///
/// If provided, OverlayFS will be overlaid on top of the Worker's dependency
@ -79,6 +87,15 @@ public:
return TracingFS.get();
}
// MaxNumOfByNameQueries is the upper limit of the number of names the by-name
// scanning API (computeDependenciesByName) can drain per call. At the time of
// this commit, the estimated number of total unique importable names is
// around 3000 from Apple's SDKs. We usually import them in parallel, so it is
// unlikely that all names are scanned by the same worker. Therefore the 64k
// (20x our estimate) size is sufficient to hold the unique source locations
// to report diagnostics per worker.
static const int32_t MaxNumOfByNameQueries = 1 << 16;
private:
/// The parent dependency scanning service.
DependencyScanningService &Service;
@ -89,7 +106,7 @@ private:
/// The tracing VFS overlaid on top of the base VFS.
IntrusiveRefCntPtr<llvm::vfs::TracingFileSystem> TracingFS;
friend tooling::CompilerInstanceWithContext;
friend class CompilerInstanceWithContext;
};
} // end namespace dependencies
} // end namespace clang

View file

@ -50,6 +50,10 @@ void addLinkerCompressDebugSectionsOption(const ToolChain &TC,
const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs);
void renderDebugInfoCompressionArgs(const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs,
const Driver &D, const ToolChain &TC);
void claimNoWarnArgs(const llvm::opt::ArgList &Args);
bool addSanitizerRuntimes(const ToolChain &TC, const llvm::opt::ArgList &Args,

View file

@ -11,6 +11,7 @@
#include "clang/Basic/Cuda.h"
#include "clang/Driver/Driver.h"
#include "llvm/TargetParser/NVPTXTargetParser.h"
#include <bitset>
namespace clang {
@ -28,9 +29,9 @@ private:
std::string IncludePath;
llvm::StringMap<std::string> LibDeviceMap;
// CUDA architectures for which we have raised an error in
// CheckCudaVersionSupportsArch.
mutable std::bitset<(int)OffloadArch::LAST> ArchsWithBadVersion;
// NVIDIA architectures (indexed by GPUKind) for which we have raised an error
// in CheckCudaVersionSupportsArch.
mutable std::bitset<llvm::NVPTX::GK_LAST + 1> ArchsWithBadVersion;
public:
CudaInstallationDetector(const Driver &D, const llvm::Triple &HostTriple,

View file

@ -5074,9 +5074,18 @@ struct FormatStyle {
/// # include "A-util.h" # include "A.inc"
/// \endcode
bool IgnoreExtension;
/// Whether or not includes are sorted by natural ordering i.e., whether
/// embedded runs of digits are compared as numbers rather than sequences of
/// characters.
/// \code
/// true: false:
/// #include "A2.h" vs. #include "A10.h"
/// #include "A10.h" #include "A2.h"
/// \endcode
bool Natural;
bool operator==(const SortIncludesOptions &R) const {
return Enabled == R.Enabled && IgnoreCase == R.IgnoreCase &&
IgnoreExtension == R.IgnoreExtension;
IgnoreExtension == R.IgnoreExtension && Natural == R.Natural;
}
bool operator!=(const SortIncludesOptions &R) const {
return !(*this == R);
@ -5590,6 +5599,30 @@ struct FormatStyle {
/// \version 3.4
SpacesInAnglesStyle SpacesInAngles;
/// Styles for controlling spacing after ``/*`` and before ``*/`` in block
/// comments.
enum SpacesInBlockCommentsStyle : int8_t {
/// Remove spaces after ``/*`` and before ``*/``.
/// \code
/// /*comment*/
/// \endcode
SIBCS_Never,
/// Add spaces after ``/*`` and before ``*/``.
/// \code
/// /* comment */
/// \endcode
SIBCS_Always,
/// Leave existing spaces unchanged.
SIBCS_Leave
};
/// The SpacesInBlockCommentsStyle to use for ordinary block comments.
/// Documentation comments such as ``/** ... */`` and ``/*! ... */``
/// and parameter comments ending with ``=`` before the closing ``*/`` are
/// left unchanged.
/// \version 24
SpacesInBlockCommentsStyle SpacesInBlockComments;
/// If ``true``, spaces will be inserted around if/for/switch/while
/// conditions.
/// This option is **deprecated**. See ``InConditionalStatements`` of
@ -6241,6 +6274,7 @@ struct FormatStyle {
SpaceInEmptyBraces == R.SpaceInEmptyBraces &&
SpacesBeforeTrailingComments == R.SpacesBeforeTrailingComments &&
SpacesInAngles == R.SpacesInAngles &&
SpacesInBlockComments == R.SpacesInBlockComments &&
SpacesInContainerLiterals == R.SpacesInContainerLiterals &&
SpacesInLineCommentPrefix.Minimum ==
R.SpacesInLineCommentPrefix.Minimum &&

View file

@ -342,6 +342,10 @@ public:
/// is read.
virtual void PragmaAssumeNonNullEnd(SourceLocation Loc) {}
/// Callback invoked when a \#pragma clang __set_pp_state directive is read.
virtual void PragmaSetPPState(SourceLocation Loc, IdentifierInfo *MacroName,
std::uint64_t Value) {}
/// Called by Preprocessor::HandleMacroExpandedIdentifier when a
/// macro invocation is found.
virtual void MacroExpands(const Token &MacroNameTok,
@ -700,6 +704,12 @@ public:
Second->PragmaAssumeNonNullEnd(Loc);
}
void PragmaSetPPState(SourceLocation Loc, IdentifierInfo *MacroName,
std::uint64_t Value) override {
First->PragmaSetPPState(Loc, MacroName, Value);
Second->PragmaSetPPState(Loc, MacroName, Value);
}
void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
SourceRange Range, const MacroArgs *Args) override {
First->MacroExpands(MacroNameTok, MD, Range, Args);

View file

@ -2549,6 +2549,15 @@ public:
/// in ""'s.
bool GetIncludeFilenameSpelling(SourceLocation Loc,StringRef &Buffer);
/// Turn the specified lexer token into a fully checked and spelled
/// filename, e.g. as an operand of \#line and \#.
///
/// The caller is expected to provide a buffer that is large enough to hold
/// the spelling of the filename, but is also expected to handle the case
/// when this method decides to use a different buffer.
///
void GetLineDirectiveFilenameSpelling(SourceLocation Loc, StringRef &Buffer);
/// Given a "foo" or \<foo> reference, look up the indicated file.
///
/// Returns std::nullopt on failure. \p isAngled indicates whether the file
@ -2818,6 +2827,7 @@ private:
public:
std::optional<std::uint64_t> getStdLibCxxVersion();
void setStdLibCxxVersion(std::uint64_t Version);
bool NeedsStdLibCxxWorkaroundBefore(std::uint64_t FixedVersion);
private:
@ -2978,6 +2988,9 @@ private:
// Pragmas.
void HandlePragmaDirective(PragmaIntroducer Introducer);
// Cached identifiers used to implement __set_pp_state.
IdentifierInfo *Ident__GLIBCXX__;
public:
void HandlePragmaOnce(Token &OnceTok);
void HandlePragmaMark(Token &MarkTok);
@ -2989,8 +3002,13 @@ public:
void HandlePragmaIncludeAlias(Token &Tok);
void HandlePragmaModuleBuild(Token &Tok);
void HandlePragmaHdrstop(Token &Tok);
void HandlePragmaSetPPState(PragmaIntroducer Introducer, Token &Tok);
IdentifierInfo *ParsePragmaPushOrPopMacro(Token &Tok);
/// Check whether this is a macro name that can be used as an argument to
/// '#pragma clang __set_pp_state'.
bool isPragmaSetPPStateMacro(IdentifierInfo *II);
// Return true and store the first token only if any CommentHandler
// has inserted some tokens and getCommentRetentionState() is false.
bool HandleComment(Token &result, SourceRange Comment);

View file

@ -4301,11 +4301,11 @@ def fno_openmp_target_new_runtime : Flag<["-"], "fno-openmp-target-new-runtime">
Group<f_Group>, Flags<[HelpHidden]>, Visibility<[ClangOption, CC1Option]>;
def fopenmp_target_fast : Flag<["-"], "fopenmp-target-fast">,
Group<f_Group>, Flags<[NoArgumentUnused, HelpHidden]>,
Visibility<[ClangOption]>,
Visibility<[ClangOption, FlangOption]>,
HelpText<"Assert common GPU usage patterns to enable OpenMP runtime optimizations">;
def fno_openmp_target_fast : Flag<["-"], "fno-openmp-target-fast">,
Group<f_Group>, Flags<[NoArgumentUnused, HelpHidden]>,
Visibility<[ClangOption]>;
Visibility<[ClangOption, FlangOption]>;
defm openmp_optimistic_collapse : BoolFOption<"openmp-optimistic-collapse",
LangOpts<"OpenMPOptimisticCollapse">, DefaultFalse,
PosFlag<SetTrue, [], [ClangOption, CC1Option]>,
@ -5307,8 +5307,10 @@ def gmodules : Flag <["-"], "gmodules">, Group<gN_Group>,
" or precompiled headers">;
def gno_modules : Flag <["-"], "gno-modules">, Group<g_flags_Group>;
def gz_EQ : Joined<["-"], "gz=">, Group<g_flags_Group>,
Visibility<[ClangOption, FlangOption]>,
HelpText<"DWARF debug sections compression type">;
def gz : Flag<["-"], "gz">, Alias<gz_EQ>, AliasArgs<["zlib"]>, Group<g_flags_Group>;
def gz : Flag<["-"], "gz">, Alias<gz_EQ>, AliasArgs<["zlib"]>,
Visibility<[ClangOption, FlangOption]>, Group<g_flags_Group>;
def gembed_source : Flag<["-"], "gembed-source">, Group<g_flags_Group>,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Embed source text in DWARF debug sections">,
@ -7950,6 +7952,12 @@ def dwarf_debug_flags : Separate<["-"], "dwarf-debug-flags">,
MarshallingInfoString<CodeGenOpts<"DwarfDebugFlags">>;
def dwarf_version_EQ : Joined<["-"], "dwarf-version=">,
MarshallingInfoInt<CodeGenOpts<"DwarfVersion">>;
def compress_debug_sections_EQ : Joined<["-", "--"], "compress-debug-sections=">,
HelpText<"DWARF debug sections compression type">, Values<"none,zlib,zstd">,
NormalizedValuesScope<"llvm::DebugCompressionType">, NormalizedValues<["None", "Zlib", "Zstd"]>,
MarshallingInfoEnum<CodeGenOpts<"CompressDebugSections">, "None">;
def compress_debug_sections : Flag<["-", "--"], "compress-debug-sections">,
Alias<compress_debug_sections_EQ>, AliasArgs<["zlib"]>;
} // let Visibility = [CC1Option, CC1AsOption, FC1Option]
@ -7969,12 +7977,6 @@ def debugger_tuning_EQ : Joined<["-"], "debugger-tuning=">,
Values<"gdb,lldb,sce,dbx">,
NormalizedValuesScope<"llvm::DebuggerKind">, NormalizedValues<["GDB", "LLDB", "SCE", "DBX"]>,
MarshallingInfoEnum<CodeGenOpts<"DebuggerTuning">, "Default">;
def compress_debug_sections_EQ : Joined<["-", "--"], "compress-debug-sections=">,
HelpText<"DWARF debug sections compression type">, Values<"none,zlib,zstd">,
NormalizedValuesScope<"llvm::DebugCompressionType">, NormalizedValues<["None", "Zlib", "Zstd"]>,
MarshallingInfoEnum<CodeGenOpts<"CompressDebugSections">, "None">;
def compress_debug_sections : Flag<["-", "--"], "compress-debug-sections">,
Alias<compress_debug_sections_EQ>, AliasArgs<["zlib"]>;
def mno_exec_stack : Flag<["-"], "mnoexecstack">,
HelpText<"Mark the file as not needing an executable stack">,
MarshallingInfoFlag<CodeGenOpts<"NoExecStack">>;

View file

@ -277,6 +277,12 @@ class Parser : public CodeCompletionHandler {
/// Implementations are in Parser.cpp
///@{
private:
/// Prepare the parser and its components.
///
/// The lack of initialization can lead to missing functionality.
void Initialize();
public:
friend class ColonProtectionRAIIObject;
friend class PoisonSEHIdentifiersRAIIObject;
@ -304,10 +310,6 @@ public:
typedef OpaquePtr<DeclGroupRef> DeclGroupPtrTy;
typedef OpaquePtr<TemplateName> TemplateTy;
/// Initialize - Warm up the parser.
///
void Initialize();
/// Parse the first top-level declaration in a translation unit.
///
/// \verbatim

View file

@ -66,6 +66,10 @@ private:
Policy PolicyOverrides;
void clearOverrides();
/// Caches results for getPolicyInEffectAt().
/// Flushed whenever a diagnostic pragma changes severities.
llvm::DenseMap<const void *, Policy> PolicyCache[4];
/// \name Statistics
/// @{
@ -134,6 +138,9 @@ public:
// will override the policy used to issue warnings.
Policy &getPolicyOverrides() { return PolicyOverrides; }
/// Drop cached getPolicyInEffectAt() results (diagnostic state changed).
void clearPolicyCache();
void PrintStats() const;
};

View file

@ -879,6 +879,8 @@ public:
/// is known.
bool AfterParameterList = true;
bool BeforeCompoundStatement = true;
ParmVarDecl *ExplicitObjectParameter = nullptr;
/// Source range covering the lambda introducer [...].

View file

@ -35,12 +35,12 @@
#include "clang/AST/TypeLoc.h"
#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
#include "clang/Basic/AttrSubjectMatchRules.h"
#include "clang/Basic/BuiltinTraits.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/CapturedStmt.h"
#include "clang/Basic/Cuda.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/ExceptionSpecificationType.h"
#include "clang/Basic/ExpressionTraits.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/Lambda.h"
#include "clang/Basic/LangOptions.h"
@ -54,7 +54,6 @@
#include "clang/Basic/StackExhaustionHandler.h"
#include "clang/Basic/TemplateKinds.h"
#include "clang/Basic/TokenKinds.h"
#include "clang/Basic/TypeTraits.h"
#include "clang/Sema/AnalysisBasedWarnings.h"
#include "clang/Sema/Attr.h"
#include "clang/Sema/CleanupInfo.h"
@ -157,6 +156,7 @@ enum class OverloadCandidateParamOrder : char;
enum OverloadCandidateRewriteKind : unsigned;
class OverloadCandidateSet;
class Preprocessor;
struct APINotesSelectorDiagnosticState;
class SemaAMDGPU;
class SemaARM;
class SemaAVR;
@ -1313,6 +1313,8 @@ public:
SourceManager &SourceMgr;
api_notes::APINotesManager APINotes;
std::unique_ptr<APINotesSelectorDiagnosticState> APINotesSelectorDiagnostics;
/// A RAII object to enter scope of a compound statement.
class CompoundScopeRAII {
public:
@ -1665,6 +1667,10 @@ public:
/// Apply the 'Type:' annotation to the specified declaration
void ApplyAPINotesType(Decl *D, StringRef TypeString);
/// Diagnose exact API notes selectors that were not matched by any
/// declaration processed in this translation unit.
void DiagnoseUnusedAPINotesSelectors();
/// Whether APINotes should be gathered for all applicable Swift language
/// versions, without being applied. Leaving clients of the current module
/// to select and apply the correct version.
@ -3580,7 +3586,7 @@ public:
/// A cache of the flags available in enumerations with the flag_enum
/// attribute.
mutable llvm::DenseMap<const EnumDecl *, llvm::APInt> FlagBitsCache;
llvm::DenseMap<const EnumDecl *, llvm::APInt> FlagBitsCache;
/// A cache of enumerator values for enums checked by -Wassign-enum.
llvm::DenseMap<const EnumDecl *, llvm::SmallVector<llvm::APSInt>>

View file

@ -691,7 +691,8 @@ public:
/// ParseObjCSelectorExpression - Build selector expression for \@selector
ExprResult ParseObjCSelectorExpression(Selector Sel, SourceLocation AtLoc,
SourceLocation SelLoc,
SourceLocation SelKWLoc,
SourceLocation SelNameLoc,
SourceLocation LParenLoc,
SourceLocation RParenLoc,
bool WarnMultipleSelectors);

View file

@ -1012,12 +1012,12 @@ public:
SourceLocation LParenLoc,
SourceLocation MLoc, SourceLocation KindLoc,
SourceLocation EndLoc);
/// Called on well-formed 'update' clause.
OMPClause *ActOnOpenMPUpdateClause(OpenMPDependClauseKind Kind,
SourceLocation KindLoc,
SourceLocation StartLoc,
SourceLocation LParenLoc,
SourceLocation EndLoc);
/// Called on well-formed 'update_depend_objects' clause.
OMPClause *ActOnOpenMPUpdateDependObjectsClause(OpenMPDependClauseKind Kind,
SourceLocation KindLoc,
SourceLocation StartLoc,
SourceLocation LParenLoc,
SourceLocation EndLoc);
/// Called on well-formed 'holds' clause.
OMPClause *ActOnOpenMPHoldsClause(Expr *E, SourceLocation StartLoc,
SourceLocation LParenLoc,

View file

@ -75,8 +75,8 @@ public:
/// Last function called on the visitor, no further calls to VisitNode
/// would follow.
virtual void finalizeVisitor(BugReporterContext &BRC,
const ExplodedNode *EndPathNode,
virtual void finalizeVisitor(const ExplodedNode *EndPathNode,
BugReporterContext &BRC,
PathSensitiveBugReport &BR);
/// Provide custom definition for the final diagnostic piece on the
@ -84,8 +84,8 @@ public:
///
/// NOTE that this function can be implemented on at most one used visitor,
/// and otherwise it crahes at runtime.
virtual PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC,
const ExplodedNode *N,
virtual PathDiagnosticPieceRef getEndPath(const ExplodedNode *N,
BugReporterContext &BRC,
PathSensitiveBugReport &BR);
virtual void Profile(llvm::FoldingSetNodeID &ID) const = 0;
@ -541,7 +541,7 @@ public:
return nullptr;
}
void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *N,
void finalizeVisitor(const ExplodedNode *N, BugReporterContext &BRC,
PathSensitiveBugReport &BR) override;
};

View file

@ -37,7 +37,7 @@ public:
BugReporterContext &BRC,
PathSensitiveBugReport &BR) override;
void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *EndPathNode,
void finalizeVisitor(const ExplodedNode *EndPathNode, BugReporterContext &BRC,
PathSensitiveBugReport &BR) override;
private:

View file

@ -54,7 +54,7 @@ class CoreEngine {
public:
using BlocksExhausted =
std::vector<std::pair<BlockEdge, const ExplodedNode *>>;
std::vector<std::pair<BlockEntrance, const ExplodedNode *>>;
using BlocksAborted =
std::vector<std::pair<const CFGBlock *, const ExplodedNode *>>;

View file

@ -388,8 +388,8 @@ public:
ExplodedNode *Pred, ExplodedNodeSet &Dst);
/// Called by CoreEngine when processing the entrance of a CFGBlock.
void processCFGBlockEntrance(const BlockEdge &L, const BlockEntrance &BE,
NodeBuilder &Builder, ExplodedNode *Pred);
void processCFGBlockEntrance(const BlockEntrance &BE, NodeBuilder &Builder,
ExplodedNode *Pred);
void runCheckersForBlockEntrance(const BlockEntrance &Entrance,
ExplodedNode *Pred, ExplodedNodeSet &Dst);

View file

@ -478,20 +478,8 @@ private:
static void computeAdjustment(SymbolRef &Sym, llvm::APSInt &Adjustment);
};
/// Try to simplify a given symbolic expression based on the constraints in
/// State. This is needed because the Environment bindings are not getting
/// updated when a new constraint is added to the State. If the symbol is
/// simplified to a non-symbol (e.g. to a constant) then the original symbol
/// is returned. We use this function in the family of assumeSymNE/EQ/LT/../GE
/// functions where we can work only with symbols. Use the other function
/// (simplifyToSVal) if you are interested in a simplification that may yield
/// a concrete constant value.
SymbolRef simplify(ProgramStateRef State, SymbolRef Sym);
/// Try to simplify a given symbolic expression's associated `SVal` based on the
/// constraints in State. This is very similar to `simplify`, but this function
/// always returns the simplified SVal. The simplified SVal might be a single
/// constant (i.e. `ConcreteInt`).
/// constraints in State.
SVal simplifyToSVal(ProgramStateRef State, SymbolRef Sym);
} // namespace ento

View file

@ -101,16 +101,21 @@ public:
dependencies::LookupModuleOutputCallback LookupModuleOutput,
std::optional<llvm::MemoryBufferRef> TUBuffer = std::nullopt);
/// Given a compilation context specified via the Clang driver command-line,
/// gather modular dependencies of module with the given name, and return the
/// information needed for explicit build.
/// TODO: this method should be removed as soon as Swift and our C-APIs adopt
/// CompilerInstanceWithContext. We are keeping it here so that it is easier
/// to coordinate with Swift and C-API changes.
llvm::Expected<dependencies::TranslationUnitDeps> getModuleDependencies(
StringRef ModuleName, ArrayRef<std::string> CommandLine, StringRef CWD,
const llvm::DenseSet<dependencies::ModuleID> &AlreadySeen,
dependencies::DependencyActionController &Controller);
/// By-name scanning given a Clang command-line. The scanning context is
/// initialized once and reused for each name pulled from
/// \p getNextName until it returns std::nullopt. Results flow to
/// \p DepConsumer (with per-name status via finishQuery); diagnostics flow to
/// \p DiagConsumer.
///
/// \returns true if the scanning context initialized successfully and all
/// scans succeeded. Query the DepConsumer for the state of each individual
/// scan.
bool getByNameDependencies(
StringRef CWD, ArrayRef<std::string> CommandLine,
DiagnosticConsumer &DiagConsumer,
dependencies::DependencyActionController &Controller,
llvm::function_ref<std::optional<std::string>()> getNextName,
dependencies::DependencyConsumer &DepConsumer);
/// Returns the worker tracing VFS, if it was requested via the service.
llvm::vfs::TracingFileSystem *getWorkerTracingVFS() const {
@ -119,8 +124,6 @@ public:
private:
dependencies::DependencyScanningWorker Worker;
friend class CompilerInstanceWithContext;
};
/// Run the dependency scanning worker for the given driver or frontend
@ -141,110 +144,6 @@ bool computeDependencies(
dependencies::DependencyActionController &Controller,
DiagnosticConsumer &DiagConsumer,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> OverlayFS = nullptr);
class CompilerInstanceWithContext {
// Context
dependencies::DependencyScanningWorker &Worker;
llvm::StringRef CWD;
std::vector<std::string> CommandLine;
// Context - Diagnostics engine.
DiagnosticConsumer *DiagConsumer = nullptr;
std::unique_ptr<dependencies::DiagnosticsEngineWithDiagOpts>
DiagEngineWithCmdAndOpts;
std::unique_ptr<dependencies::TextDiagnosticsPrinterWithOutput>
DiagPrinterWithOS;
// Context - compiler invocation
std::unique_ptr<CompilerInvocation> OriginalInvocation;
// Context - output options
std::unique_ptr<DependencyOutputOptions> OutputOpts;
// Context - stable directory handling
llvm::SmallVector<StringRef> StableDirs;
dependencies::PrebuiltModulesAttrsMap PrebuiltModuleASTMap;
// Compiler Instance
std::unique_ptr<CompilerInstance> CIPtr;
// Source location offset.
int32_t SrcLocOffset = 0;
CompilerInstanceWithContext(dependencies::DependencyScanningWorker &Worker,
StringRef CWD,
const std::vector<std::string> &CMD)
: Worker(Worker), CWD(CWD), CommandLine(CMD) {};
bool initialize(dependencies::DependencyActionController &Controller,
std::unique_ptr<dependencies::DiagnosticsEngineWithDiagOpts>
DiagEngineWithDiagOpts,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> OverlayFS);
public:
/// @brief Initialize the tool's compiler instance from the commandline.
/// The compiler instance only takes a `-cc1` job, so this method
/// builds the `-cc1` job from the CommandLine input.
/// @param Tool The dependency scanning tool whose compiler instance
/// with context is initialized.
/// @param CWD The current working directory.
/// @param CommandLine This command line may be a driver command or a cc1
/// command.
/// @param DC A diagnostics consumer to report error if the initialization
/// fails.
static std::optional<CompilerInstanceWithContext> initializeFromCommandline(
DependencyScanningTool &Tool, StringRef CWD,
ArrayRef<std::string> CommandLine,
dependencies::DependencyActionController &Controller,
DiagnosticConsumer &DC);
/// @brief Initializing the context and the compiler instance.
/// This method must be called before calling
/// computeDependenciesByNameWithContext.
/// @param CWD The current working directory used during the scan.
/// @param CommandLine The commandline used for the scan.
/// @return Error if the initializaiton fails.
static llvm::Expected<CompilerInstanceWithContext>
initializeOrError(DependencyScanningTool &Tool, StringRef CWD,
ArrayRef<std::string> CommandLine,
dependencies::DependencyActionController &Controller);
bool
computeDependencies(StringRef ModuleName,
dependencies::DependencyConsumer &Consumer,
dependencies::DependencyActionController &Controller);
/// @brief Computes the dependeny for the module named ModuleName.
/// @param ModuleName The name of the module for which this method computes
///. dependencies.
/// @param AlreadySeen This stores modules which have previously been
/// reported. Use the same instance for all calls to this
/// function for a single \c DependencyScanningTool in a
/// single build. Note that this parameter is not part of
/// the context because it can be shared across different
/// worker threads and each worker thread may update it.
/// @param LookupModuleOutput This function is called to fill in
/// "-fmodule-file=", "-o" and other output
/// arguments for dependencies.
/// @return An instance of \c TranslationUnitDeps if the scan is successful.
/// Otherwise it returns an error.
llvm::Expected<dependencies::TranslationUnitDeps>
computeDependenciesByNameOrError(
StringRef ModuleName,
const llvm::DenseSet<dependencies::ModuleID> &AlreadySeen,
dependencies::DependencyActionController &Controller);
// MaxNumOfQueries is the upper limit of the number of names the by-name
// scanning API (computeDependencies) can support after a
// CompilerInstanceWithContext is initialized. At the time of this commit, the
// estimated number of total unique importable names is around 3000 from
// Apple's SDKs. We usually import them in parallel, so it is unlikely that
// all names are all scanned by the same dependency scanning worker. Therefore
// the 64k (20x bigger than our estimate) size is sufficient to hold the
// unique source locations to report diagnostics per worker.
static const int32_t MaxNumOfQueries = 1 << 16;
};
} // end namespace tooling
} // end namespace clang

View file

@ -35,7 +35,6 @@ const uint16_t VERSION_MINOR = 41; // 39 for BoundsSafety;
const uint8_t kSwiftConforms = 1;
const uint8_t kSwiftDoesNotConform = 2;
using IdentifierID = llvm::PointerEmbeddedInt<unsigned, 31>;
using IdentifierIDField = llvm::BCVBR<16>;
using SelectorID = llvm::PointerEmbeddedInt<unsigned, 31>;
@ -365,47 +364,6 @@ constexpr uint8_t FunctionKeyHasParameterSelector = 0x01;
constexpr unsigned FunctionTableKeyBaseLength =
sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint16_t);
struct FunctionTableKey {
uint32_t parentContextID;
uint32_t nameID;
std::optional<llvm::SmallVector<IdentifierID, 2>> parameterTypeIDs;
FunctionTableKey() : parentContextID(-1), nameID(-1) {}
FunctionTableKey(uint32_t ParentContextID, uint32_t NameID)
: parentContextID(ParentContextID), nameID(NameID) {}
FunctionTableKey(uint32_t ParentContextID, uint32_t NameID,
const llvm::SmallVectorImpl<IdentifierID> &ParameterTypeIDs)
: parentContextID(ParentContextID), nameID(NameID) {
parameterTypeIDs.emplace(ParameterTypeIDs.begin(), ParameterTypeIDs.end());
}
FunctionTableKey(std::optional<Context> ParentCtx, IdentifierID NameID)
: parentContextID(ParentCtx ? ParentCtx->id.Value
: static_cast<uint32_t>(-1)),
nameID(NameID) {}
FunctionTableKey(std::optional<Context> ParentCtx, IdentifierID NameID,
const llvm::SmallVectorImpl<IdentifierID> &ParameterTypeIDs)
: parentContextID(ParentCtx ? ParentCtx->id.Value
: static_cast<uint32_t>(-1)),
nameID(NameID) {
parameterTypeIDs.emplace(ParameterTypeIDs.begin(), ParameterTypeIDs.end());
}
llvm::hash_code hashValue() const {
auto Hash = llvm::hash_combine(parentContextID, nameID,
static_cast<bool>(parameterTypeIDs));
if (parameterTypeIDs) {
Hash = llvm::hash_combine(Hash, parameterTypeIDs->size());
for (IdentifierID TypeID : *parameterTypeIDs)
Hash = llvm::hash_combine(Hash, static_cast<unsigned>(TypeID));
}
return Hash;
}
};
template <typename GetIdentifierFn>
std::optional<FunctionTableKey>
getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name,
@ -438,13 +396,6 @@ getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name,
return FunctionTableKey(ParentContextID, *NameID, ParameterTypeIDs);
}
inline bool operator==(const FunctionTableKey &lhs,
const FunctionTableKey &rhs) {
return lhs.parentContextID == rhs.parentContextID &&
lhs.nameID == rhs.nameID &&
lhs.parameterTypeIDs == rhs.parameterTypeIDs;
}
} // namespace api_notes
} // namespace clang
@ -492,18 +443,6 @@ template <> struct DenseMapInfo<clang::api_notes::SingleDeclTableKey> {
}
};
template <> struct DenseMapInfo<clang::api_notes::FunctionTableKey> {
static unsigned
getHashValue(const clang::api_notes::FunctionTableKey &value) {
return value.hashValue();
}
static bool isEqual(const clang::api_notes::FunctionTableKey &lhs,
const clang::api_notes::FunctionTableKey &rhs) {
return lhs == rhs;
}
};
} // namespace llvm
#endif

View file

@ -15,10 +15,14 @@
#include "clang/APINotes/APINotesReader.h"
#include "APINotesFormat.h"
#include "clang/APINotes/Types.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/Bitstream/BitstreamReader.h"
#include "llvm/Support/DJB.h"
#include "llvm/Support/OnDiskHashTable.h"
#include <string>
#include <type_traits>
#include <utility>
namespace clang {
namespace api_notes {
@ -758,6 +762,9 @@ public:
/// The identifier table.
std::unique_ptr<SerializedIdentifierTable> IdentifierTable;
/// Lazy reverse lookup cache from identifier ID to string.
std::optional<llvm::DenseMap<uint32_t, llvm::StringRef>> IdentifierStrings;
using SerializedContextIDTable =
llvm::OnDiskIterableChainedHashTable<ContextIDTableInfo>;
@ -833,6 +840,17 @@ public:
/// optional if the string is unknown.
std::optional<IdentifierID> getIdentifier(llvm::StringRef Str);
/// Retrieve the identifier string for the given ID, or an empty optional if
/// the ID is unknown.
std::optional<llvm::StringRef> getIdentifierString(IdentifierID ID);
/// Collect exact parameter selector keys stored in the given function-like
/// table.
template <typename TableT>
void collectExactFunctionParameterSelectors(
TableT &Table,
llvm::SmallVectorImpl<APINotesFunctionSelectorKey> &Selectors);
/// Retrieve the selector ID for the given selector, or an empty
/// optional if the string is unknown.
std::optional<SelectorID> getSelector(ObjCSelectorRef Selector);
@ -893,6 +911,54 @@ APINotesReader::Implementation::getIdentifier(llvm::StringRef Str) {
return *Known;
}
std::optional<llvm::StringRef>
APINotesReader::Implementation::getIdentifierString(IdentifierID ID) {
if (!IdentifierTable)
return std::nullopt;
if (ID == IdentifierID(0))
return llvm::StringRef();
if (!IdentifierStrings) {
IdentifierStrings.emplace();
// keys() and data() iterate over the same serialized entries in lockstep.
// The serialized hash-table order is not guaranteed to be identifier-ID
// order, so keep an explicit ID-to-string map rather than indexing a vector
// by ID.
auto Identifiers = IdentifierTable->keys();
auto IDs = IdentifierTable->data();
auto Identifier = Identifiers.begin();
auto KnownID = IDs.begin();
auto IdentifierEnd = Identifiers.end();
auto KnownIDEnd = IDs.end();
for (; Identifier != IdentifierEnd && KnownID != KnownIDEnd;
++Identifier, ++KnownID)
IdentifierStrings->try_emplace(static_cast<uint32_t>(*KnownID),
*Identifier);
}
auto Known = IdentifierStrings->find(static_cast<uint32_t>(ID));
if (Known == IdentifierStrings->end())
return std::nullopt;
return Known->second;
}
template <typename TableT>
void APINotesReader::Implementation::collectExactFunctionParameterSelectors(
TableT &Table,
llvm::SmallVectorImpl<APINotesFunctionSelectorKey> &Selectors) {
static_assert(std::is_same_v<TableT, SerializedGlobalFunctionTable> ||
std::is_same_v<TableT, SerializedCXXMethodTable>);
constexpr bool IsCXXMethod = std::is_same_v<TableT, SerializedCXXMethodTable>;
for (const FunctionTableKey &Key : Table.keys()) {
if (!Key.parameterTypeIDs)
continue;
Selectors.push_back(APINotesFunctionSelectorKey{Key, IsCXXMethod});
}
}
std::optional<FunctionTableKey>
APINotesReader::Implementation::getFunctionKey(uint32_t ParentContextID,
llvm::StringRef Name) {
@ -2351,6 +2417,26 @@ auto APINotesReader::lookupCXXMethod(ContextID CtxID, llvm::StringRef Name,
return lookupCXXMethodImpl(CtxID, Name, Parameters);
}
std::optional<APINotesFunctionSelectorKey>
APINotesReader::getCXXMethodSelectorKey(ContextID CtxID, llvm::StringRef Name) {
std::optional<FunctionTableKey> Key =
Implementation->getFunctionKey(CtxID.Value, Name);
if (!Key)
return std::nullopt;
return APINotesFunctionSelectorKey{*Key, /*IsCXXMethod=*/true};
}
std::optional<APINotesFunctionSelectorKey>
APINotesReader::getCXXMethodSelectorKey(
ContextID CtxID, llvm::StringRef Name,
llvm::ArrayRef<std::string> Parameters) {
std::optional<FunctionTableKey> Key =
Implementation->getFunctionKey(CtxID.Value, Name, Parameters);
if (!Key)
return std::nullopt;
return APINotesFunctionSelectorKey{*Key, /*IsCXXMethod=*/true};
}
auto APINotesReader::lookupCXXMethodImpl(ContextID CtxID, llvm::StringRef Name)
-> VersionedInfo<CXXMethodInfo> {
if (!Implementation->CXXMethodTable)
@ -2418,6 +2504,55 @@ auto APINotesReader::lookupGlobalFunction(
return lookupGlobalFunctionImpl(Name, Parameters, Ctx);
}
std::optional<APINotesFunctionSelectorKey>
APINotesReader::getGlobalFunctionSelectorKey(llvm::StringRef Name,
std::optional<Context> Ctx) {
std::optional<FunctionTableKey> Key =
Implementation->getFunctionKey(Ctx, Name);
if (!Key)
return std::nullopt;
return APINotesFunctionSelectorKey{*Key, /*IsCXXMethod=*/false};
}
std::optional<APINotesFunctionSelectorKey>
APINotesReader::getGlobalFunctionSelectorKey(
llvm::StringRef Name, llvm::ArrayRef<std::string> Parameters,
std::optional<Context> Ctx) {
std::optional<FunctionTableKey> Key =
Implementation->getFunctionKey(Ctx, Name, Parameters);
if (!Key)
return std::nullopt;
return APINotesFunctionSelectorKey{*Key, /*IsCXXMethod=*/false};
}
void APINotesReader::collectExactFunctionParameterSelectors(
llvm::SmallVectorImpl<APINotesFunctionSelectorKey> &Selectors) {
if (Implementation->GlobalFunctionTable)
Implementation->collectExactFunctionParameterSelectors(
*Implementation->GlobalFunctionTable, Selectors);
if (Implementation->CXXMethodTable)
Implementation->collectExactFunctionParameterSelectors(
*Implementation->CXXMethodTable, Selectors);
}
std::optional<llvm::SmallVector<std::string, 4>>
APINotesReader::getParameterSelectorSpellingsForDiagnostics(
const APINotesFunctionSelectorKey &Key) {
if (!Key.Key.parameterTypeIDs)
return std::nullopt;
llvm::SmallVector<std::string, 4> ParameterSpellings;
ParameterSpellings.reserve(Key.Key.parameterTypeIDs->size());
for (IdentifierID TypeID : *Key.Key.parameterTypeIDs) {
std::optional<llvm::StringRef> TypeName =
Implementation->getIdentifierString(TypeID);
if (!TypeName)
return std::nullopt;
ParameterSpellings.push_back(TypeName->str());
}
return ParameterSpellings;
}
auto APINotesReader::lookupGlobalFunctionImpl(llvm::StringRef Name,
std::optional<Context> Ctx)
-> VersionedInfo<GlobalFunctionInfo> {

View file

@ -11,6 +11,7 @@
namespace clang {
namespace api_notes {
LLVM_DUMP_METHOD void CommonEntityInfo::dump(llvm::raw_ostream &OS) const {
if (Unavailable)
OS << "[Unavailable] (" << UnavailableMsg << ")" << ' ';

View file

@ -18,11 +18,16 @@
#include "clang/APINotes/Types.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/Specifiers.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/VersionTuple.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
#include <optional>
#include <string>
#include <type_traits>
#include <vector>
@ -788,6 +793,22 @@ bool clang::api_notes::parseAndDumpAPINotes(StringRef YI,
namespace {
using namespace api_notes;
static std::string
getFunctionSelectorKey(llvm::StringRef Name,
llvm::ArrayRef<llvm::StringRef> Parameters) {
llvm::SmallString<64> Key;
llvm::raw_svector_ostream OS(Key);
auto AppendKeyPart = [&OS](llvm::StringRef Part) {
OS << Part.size() << ':' << Part;
};
AppendKeyPart(Name);
OS << ';';
for (llvm::StringRef Parameter : Parameters)
AppendKeyPart(Parameter);
return Key.str().str();
}
class YAMLConverter {
const Module &M;
APINotesWriter Writer;
@ -1162,11 +1183,24 @@ public:
Writer.addField(TagCtxID, Field.Name, FI, SwiftVersion);
}
llvm::StringSet<> KnownMethodSelectors;
for (const auto &CXXMethod : T.Methods) {
auto WhereParameters = getWhereParameters(CXXMethod);
if (!WhereParameters.first)
continue;
if (WhereParameters.second) {
if (!KnownMethodSelectors
.insert(getFunctionSelectorKey(CXXMethod.Name,
*WhereParameters.second))
.second) {
emitError(llvm::Twine("multiple API notes entries for C++ method '") +
CXXMethod.Name + "' with Where.Parameters " +
formatAPINotesParameterSelector(*WhereParameters.second));
continue;
}
}
CXXMethodInfo MI;
convertFunction(CXXMethod, MI);
if (WhereParameters.second)
@ -1243,13 +1277,26 @@ public:
// Write all global functions.
llvm::StringSet<> KnownNameOnlyFunctions;
llvm::StringSet<> KnownFunctionSelectors;
for (const auto &Function : TLItems.Functions) {
auto WhereParameters = getWhereParameters(Function);
if (!WhereParameters.first)
continue;
// Check for duplicate name-only global functions. Selector-aware
// duplicate diagnostics are handled by a later overload-matching PR.
if (WhereParameters.second) {
if (!KnownFunctionSelectors
.insert(getFunctionSelectorKey(Function.Name,
*WhereParameters.second))
.second) {
emitError(
llvm::Twine("multiple API notes entries for global function '") +
Function.Name + "' with Where.Parameters " +
formatAPINotesParameterSelector(*WhereParameters.second));
continue;
}
}
// Check for duplicate name-only global functions.
if (!WhereParameters.second &&
!KnownNameOnlyFunctions.insert(Function.Name).second) {
emitError(llvm::Twine("multiple definitions of global function '") +

View file

@ -4038,7 +4038,7 @@ bool Compiler<Emitter>::VisitOffsetOfExpr(const OffsetOfExpr *E) {
if (IndexT == PT_IntAP || IndexT == PT_IntAPS) {
if (!this->visit(ArrayIndexExpr))
return false;
if (!this->emitCastNoOverflow(IndexT, E))
if (!this->emitCastAPToOffsetIndex(IndexT, E))
return false;
continue;
}

View file

@ -476,7 +476,7 @@ bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc,
// If we're evaluating the initializer for a constexpr variable in C23, we may
// only read other contexpr variables. Abort here since this one isn't
// constexpr.
if (const auto *VD = dyn_cast_if_present<VarDecl>(S.EvaluatingDecl);
if (const auto *VD = S.EvaluatingDecl;
VD && VD->isConstexpr() && S.getLangOpts().C23)
return Invalid(S, OpPC);
@ -1572,10 +1572,7 @@ static bool diagnoseTypeIdField(InterpState &S, CodePtr OpPC,
Ptr.asTypeidPointer().TypeInfoType->getAsRecordDecl());
if (!R)
return false;
const Record::Field *Field =
llvm::find_if(R->fields(), [=](const Record::Field &F) -> bool {
return F.Offset == Offset;
});
const Record::Field *Field = R->findField(Offset);
if (!Field)
return false;
@ -1892,8 +1889,8 @@ bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func,
if (!CheckCallDepth(S, OpPC))
return false;
auto Memory = new char[InterpFrame::allocSize(Func)];
auto NewFrame = new (Memory) InterpFrame(S, Func, S.PC, VarArgSize);
auto *Memory = new char[InterpFrame::allocSize(Func)];
auto *NewFrame = new (Memory) InterpFrame(S, Func, S.PC, VarArgSize);
InterpFrame *FrameBefore = S.Current;
S.Current = NewFrame;
@ -1985,8 +1982,8 @@ bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
if (!CheckCallDepth(S, OpPC))
return cleanup();
auto Memory = new char[InterpFrame::allocSize(Func)];
auto NewFrame = new (Memory) InterpFrame(S, Func, S.PC, VarArgSize);
auto *Memory = new char[InterpFrame::allocSize(Func)];
auto *NewFrame = new (Memory) InterpFrame(S, Func, S.PC, VarArgSize);
InterpFrame *FrameBefore = S.Current;
S.Current = NewFrame;
@ -2064,6 +2061,7 @@ static bool getDynamicDecl(InterpState &S, CodePtr OpPC, PtrView TypePtr,
return DynamicDecl != nullptr;
}
namespace {
struct DynamicCastResult {
UnsignedOrNone Offset = std::nullopt;
bool Ambiguous = false;
@ -2088,6 +2086,7 @@ struct DynamicCastResult {
}
}
};
} // namespace
// Walk UP the type hierarchy, starting at the decl of R to find Needle.
static DynamicCastResult findRecordBase(const ASTContext &Ctx, const Record *R,
@ -2286,7 +2285,7 @@ bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
return false;
assert(DynamicDecl);
const auto *StaticDecl = cast<CXXRecordDecl>(Func->getParentDecl());
const auto *StaticDecl = Func->getParentDecl();
const auto *InitialFunction = cast<CXXMethodDecl>(Callee);
const CXXMethodDecl *Overrider;
@ -3080,7 +3079,7 @@ static bool castBackMemberPointer(InterpState &S,
unsigned OldPathLength = MemberPtr.getPathLength();
unsigned NewPathLength = OldPathLength - 1;
bool IsDerivedMember = NewPathLength != 0;
auto NewPath = S.allocMemberPointerPath(NewPathLength);
auto *NewPath = S.allocMemberPointerPath(NewPathLength);
std::copy_n(MemberPtr.path(), NewPathLength, NewPath);
S.Stk.push<MemberPointer>(MemberPtr.atInstanceBase(BaseOffset, NewPathLength,
@ -3096,7 +3095,7 @@ static bool appendToMemberPointer(InterpState &S,
unsigned OldPathLength = MemberPtr.getPathLength();
unsigned NewPathLength = OldPathLength + 1;
auto NewPath = S.allocMemberPointerPath(NewPathLength);
auto *NewPath = S.allocMemberPointerPath(NewPathLength);
std::copy_n(MemberPtr.path(), OldPathLength, NewPath);
NewPath[OldPathLength] = cast<CXXRecordDecl>(BaseDecl);
@ -3180,7 +3179,7 @@ bool CopyMemberPtrPath(InterpState &S, const RecordDecl *Entry,
unsigned OldPathLength = MemberPtr.getPathLength();
unsigned NewPathLength = OldPathLength + 1;
auto NewPath = S.allocMemberPointerPath(NewPathLength);
auto *NewPath = S.allocMemberPointerPath(NewPathLength);
std::copy_n(MemberPtr.path(), OldPathLength, NewPath);
NewPath[OldPathLength] = cast<CXXRecordDecl>(Entry);
@ -3223,7 +3222,7 @@ bool CastFloatingIntegralAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth,
}
// FIXME: Would be nice to generate this instead of hardcoding it here.
constexpr bool OpReturns(Opcode Op) {
[[maybe_unused]] static constexpr bool OpReturns(Opcode Op) {
return Op == OP_RetVoid || Op == OP_RetValue || Op == OP_NoRet ||
Op == OP_RetSint8 || Op == OP_RetUint8 || Op == OP_RetSint16 ||
Op == OP_RetUint16 || Op == OP_RetSint32 || Op == OP_RetUint32 ||

View file

@ -2910,10 +2910,11 @@ bool CastAPS(InterpState &S, uint32_t BitWidth) {
return true;
}
// Cast an AP integer to Sint64, failing constant evaluation if the value is
// negative or too large to fit (i.e. truncation would change the value).
// Cast an AP integer to Sint64 for use as an offsetof array index, failing
// constant evaluation if the value is negative or too large to fit in Sint64
// (i.e. truncation would change the value).
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool CastNoOverflow(InterpState &S, CodePtr OpPC) {
bool CastAPToOffsetIndex(InterpState &S, CodePtr OpPC) {
T Source = S.Stk.pop<T>();
APSInt Val = Source.toAPSInt();
if (Val.isNegative() || Val.getActiveBits() > 63)

Some files were not shown because too many files have changed in this diff Show more