merge: branch 'main' into users/krishna2803/nextfnbf16

This commit is contained in:
Krishna Pandey 2025-08-20 01:39:03 +05:30
commit 6092968769
No known key found for this signature in database
GPG Key ID: 20FD86C0096672D5
2113 changed files with 53054 additions and 22050 deletions

View File

@ -49,8 +49,7 @@ DEPENDENTS_TO_TEST = {
"flang",
},
"lld": {"bolt", "cross-project-tests"},
# TODO(issues/132795): LLDB should be enabled on clang changes.
"clang": {"clang-tools-extra", "cross-project-tests"},
"clang": {"clang-tools-extra", "cross-project-tests", "lldb"},
"mlir": {"flang"},
# Test everything if ci scripts are changed.
".ci": {

View File

@ -83,11 +83,11 @@ class TestComputeProjects(unittest.TestCase):
)
self.assertEqual(
env_variables["projects_to_build"],
"clang;clang-tools-extra;lld;llvm",
"clang;clang-tools-extra;lld;lldb;llvm",
)
self.assertEqual(
env_variables["project_check_targets"],
"check-clang check-clang-tools",
"check-clang check-clang-tools check-lldb",
)
self.assertEqual(
env_variables["runtimes_to_build"], "compiler-rt;libcxx;libcxxabi;libunwind"
@ -158,11 +158,11 @@ class TestComputeProjects(unittest.TestCase):
)
self.assertEqual(
env_variables["projects_to_build"],
"clang;clang-tools-extra;lld;llvm;mlir",
"clang;clang-tools-extra;lld;lldb;llvm;mlir",
)
self.assertEqual(
env_variables["project_check_targets"],
"check-clang check-clang-cir check-clang-tools",
"check-clang check-clang-cir check-clang-tools check-lldb",
)
self.assertEqual(
env_variables["runtimes_to_build"], "compiler-rt;libcxx;libcxxabi;libunwind"

View File

@ -24,6 +24,7 @@ function at-exit {
retcode=$?
mkdir -p artifacts
sccache --show-stats
sccache --show-stats >> artifacts/sccache_stats.txt
cp "${BUILD_DIR}"/.ninja_log artifacts/.ninja_log
cp "${MONOREPO_ROOT}"/*.log artifacts/ || :

View File

@ -4,7 +4,6 @@ permissions:
contents: read
on:
workflow_dispatch:
push:
branches:
- 'main'
@ -13,29 +12,46 @@ on:
- 'clang/tools/libclang/**'
- 'clang/CMakeList.txt'
- '.github/workflows/libclang-python-tests.yml'
- '.github/workflows/llvm-project-tests.yml'
pull_request:
paths:
- 'clang/bindings/python/**'
- 'clang/tools/libclang/**'
- 'clang/CMakeList.txt'
- '.github/workflows/libclang-python-tests.yml'
- '.github/workflows/llvm-project-tests.yml'
jobs:
check-clang-python:
# Build libclang and then run the libclang Python binding's unit tests.
# There is an issue running on "windows-2019".
# See https://github.com/llvm/llvm-project/issues/76601#issuecomment-1873049082.
name: Build and run Python unit tests
if: github.repository == 'llvm/llvm-project'
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.13"]
uses: ./.github/workflows/llvm-project-tests.yml
with:
build_target: check-clang-python
projects: clang
# There is an issue running on "windows-2019".
# See https://github.com/llvm/llvm-project/issues/76601#issuecomment-1873049082.
os_list: '["ubuntu-24.04"]'
python_version: ${{ matrix.python-version }}
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup Python
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
with:
python-version: ${{ matrix.python-version }}
- name: Setup ccache
uses: hendrikmuhs/ccache-action@a1209f81afb8c005c13b4296c32e363431bffea5 # v1.2.17
with:
max-size: 2G
key: spirv-ubuntu-24.04
variant: sccache
- name: Build and Test
run: |
mkdir build
cmake -GNinja \
-S llvm \
-B build \
-DCMAKE_BUILD_TYPE=Release \
-DLLVM_ENABLE_ASSERTIONS=ON \
-DCMAKE_C_COMPILER_LAUNCHER=sccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache \
-DLLVM_ENABLE_PROJECTS=clang
ninja -C build check-clang-python

View File

@ -1,149 +0,0 @@
name: LLVM Project Tests
permissions:
contents: read
on:
workflow_dispatch:
inputs:
build_target:
required: false
projects:
required: false
extra_cmake_args:
required: false
os_list:
required: false
default: '["ubuntu-24.04", "windows-2019", "macOS-13"]'
python_version:
required: false
type: string
default: '3.11'
workflow_call:
inputs:
build_target:
required: false
type: string
default: "all"
projects:
required: true
type: string
extra_cmake_args:
required: false
type: string
os_list:
required: false
type: string
# Use windows-2019 due to:
# https://developercommunity.visualstudio.com/t/Prev-Issue---with-__assume-isnan-/1597317
default: '["ubuntu-24.04", "windows-2019", "macOS-13"]'
python_version:
required: false
type: string
default: '3.11'
concurrency:
# Skip intermediate builds: always.
# Cancel intermediate builds: only if it is a pull request build.
# If the group name here is the same as the group name in the workflow that includes
# this one, then the action will try to wait on itself and get stuck.
group: llvm-project-${{ github.workflow }}-${{ inputs.projects }}-${{ inputs.python_version }}${{ github.ref }}
cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}
jobs:
lit-tests:
name: Lit Tests
runs-on: ${{ matrix.os }}
container:
image: ${{(startsWith(matrix.os, 'ubuntu') && 'ghcr.io/llvm/ci-ubuntu-24.04:latest') || null}}
volumes:
- /mnt/:/mnt/
strategy:
fail-fast: false
matrix:
os: ${{ fromJSON(inputs.os_list) }}
steps:
- name: Setup Windows
if: startsWith(matrix.os, 'windows')
uses: llvm/actions/setup-windows@main
with:
arch: amd64
# On Windows, starting with win19/20220814.1, cmake choose the 32-bit
# python3.10.6 libraries instead of the 64-bit libraries when building
# lldb. Using this setup-python action to make 3.10 the default
# python fixes this.
- name: Setup Python
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
with:
python-version: ${{ inputs.python_version }}
- name: Install Ninja
if: runner.os != 'Linux'
uses: llvm/actions/install-ninja@main
# actions/checkout deletes any existing files in the new git directory,
# so this needs to either run before ccache-action or it has to use
# clean: false.
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 250
- name: Setup ccache
uses: hendrikmuhs/ccache-action@a1209f81afb8c005c13b4296c32e363431bffea5 # v1.2.17
with:
# A full build of llvm, clang, lld, and lldb takes about 250MB
# of ccache space. There's not much reason to have more than this,
# because we usually won't need to save cache entries from older
# builds. Also, there is an overall 10GB cache limit, and each
# run creates a new cache entry so we want to ensure that we have
# enough cache space for all the tests to run at once and still
# fit under the 10 GB limit.
# Default to 2G to workaround: https://github.com/hendrikmuhs/ccache-action/issues/174
max-size: 2G
key: ${{ matrix.os }}
variant: sccache
- name: Build and Test
env:
# Workaround for https://github.com/actions/virtual-environments/issues/5900.
# This should be a no-op for non-mac OSes
PKG_CONFIG_PATH: /usr/local/Homebrew/Library/Homebrew/os/mac/pkgconfig//12
shell: bash
id: build-llvm
run: |
if [ "${{ runner.os }}" == "Linux" ]; then
builddir="/mnt/build/"
sudo mkdir -p $builddir
sudo chown gha $builddir
extra_cmake_args="-DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang"
else
builddir="$(pwd)"/build
fi
if [ "${{ runner.os }}" == "macOS" ]; then
# Workaround test failure on some lld tests on MacOS
# https://github.com/llvm/llvm-project/issues/81967
extra_cmake_args="-DLLVM_DISABLE_ASSEMBLY_FILES=ON"
fi
echo "llvm-builddir=$builddir" >> "$GITHUB_OUTPUT"
cmake -G Ninja \
-B "$builddir" \
-S llvm \
-DLLVM_ENABLE_PROJECTS="${{ inputs.projects }}" \
-DCMAKE_BUILD_TYPE=Release \
-DLLVM_ENABLE_ASSERTIONS=ON \
-DLLDB_INCLUDE_TESTS=OFF \
-DLIBCLC_TARGETS_TO_BUILD="amdgcn--;amdgcn--amdhsa;r600--;nvptx--;nvptx64--;nvptx--nvidiacl;nvptx64--nvidiacl" \
-DCMAKE_C_COMPILER_LAUNCHER=sccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache \
$extra_cmake_args \
${{ inputs.extra_cmake_args }}
ninja -C "$builddir" '${{ inputs.build_target }}'
- name: Build and Test libclc
if: "!startsWith(matrix.os, 'windows') && contains(inputs.projects, 'libclc')"
env:
LLVM_BUILDDIR: ${{ steps.build-llvm.outputs.llvm-builddir }}
run: |
# The libclc tests don't have a generated check target so all we can
# do is build it.
ninja -C "$LLVM_BUILDDIR"

View File

@ -1,32 +0,0 @@
# This workflow will test the llvm-project-tests workflow in PRs
# targetting the main branch. Since this workflow doesn't normally
# run on main PRs, we need some way to test it to ensure new updates
# don't break it.
name: LLVM Workflow Test
permissions:
contents: read
on:
pull_request:
branches:
- 'main'
paths:
- '.github/workflows/llvm-project-tests.yml'
- '.github/workflows/llvm-project-workflow-tests.yml'
concurrency:
# Skip intermediate builds: always.
# Cancel intermediate builds: only if it is a pull request build.
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}
jobs:
llvm-test:
if: github.repository_owner == 'llvm'
name: Build and Test
uses: ./.github/workflows/llvm-project-tests.yml
with:
build_target: check-all
projects: clang;lld;libclc;lldb

View File

@ -24,9 +24,28 @@ jobs:
check_spirv:
if: github.repository_owner == 'llvm'
name: Test MLIR SPIR-V
uses: ./.github/workflows/llvm-project-tests.yml
with:
build_target: check-mlir
projects: mlir
extra_cmake_args: '-DLLVM_TARGETS_TO_BUILD="host" -DLLVM_INCLUDE_SPIRV_TOOLS_TESTS=ON'
os_list: '["ubuntu-24.04"]'
runs-on: ubuntu-24.04
container:
image: ghcr.io/llvm/ci-ubuntu-24.04:latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup ccache
uses: hendrikmuhs/ccache-action@a1209f81afb8c005c13b4296c32e363431bffea5 # v1.2.17
with:
max-size: 2G
key: spirv-mlir-ubuntu-24.04
variant: sccache
- name: Build and Test
run: |
mkdir build
cmake -GNinja \
-S llvm \
-B build \
-DCMAKE_BUILD_TYPE=Release \
-DLLVM_ENABLE_ASSERTIONS=ON \
-DCMAKE_C_COMPILER_LAUNCHER=sccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache \
-DLLVM_TARGETS_TO_BUILD="host" \
-DLLVM_INCLUDE_SPIRV_TOOLS_TESTS=ON \
-DLLVM_ENABLE_PROJECTS=mlir
ninja -C build check-mlir

View File

@ -70,8 +70,6 @@ jobs:
- name: Run code formatter
env:
GITHUB_PR_NUMBER: ${{ github.event.pull_request.number }}
START_REV: ${{ github.event.pull_request.base.sha }}
END_REV: ${{ github.event.pull_request.head.sha }}
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
# Create an empty comments file so the pr-write job doesn't fail.
run: |

View File

@ -69,6 +69,11 @@ jobs:
./.ci/monolithic-linux.sh "${projects_to_build}" "${project_check_targets}" "${runtimes_to_build}" "${runtimes_check_targets}" "${runtimes_check_targets_needs_reconfig}" "${enable_cir}"
- 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
# a checks failure.
# https://github.com/actions/upload-artifact/issues/569
continue-on-error: true
if: '!cancelled()'
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
@ -114,6 +119,11 @@ jobs:
# these environment variables.
bash -c "export SCCACHE_GCS_BUCKET=$CACHE_GCS_BUCKET; export SCCACHE_GCS_RW_MODE=READ_WRITE; export SCCACHE_IDLE_TIMEOUT=0; sccache --start-server; .ci/monolithic-windows.sh \"${{ steps.vars.outputs.windows-projects }}\" \"${{ steps.vars.outputs.windows-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
# a checks failure.
# https://github.com/actions/upload-artifact/issues/569
continue-on-error: true
if: '!cancelled()'
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:

View File

@ -4,7 +4,6 @@ permissions:
contents: read
on:
workflow_dispatch:
pull_request:
paths:
- 'llvm/lib/Target/SPIRV/**'
@ -21,9 +20,27 @@ jobs:
check_spirv:
if: github.repository_owner == 'llvm'
name: Test SPIR-V
uses: ./.github/workflows/llvm-project-tests.yml
with:
build_target: check-llvm-codegen-spirv
projects:
extra_cmake_args: '-DLLVM_TARGETS_TO_BUILD="SPIRV" -DLLVM_INCLUDE_SPIRV_TOOLS_TESTS=ON'
os_list: '["ubuntu-24.04"]'
runs-on: ubuntu-24.04
container:
image: ghcr.io/llvm/ci-ubuntu-24.04:latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup ccache
uses: hendrikmuhs/ccache-action@a1209f81afb8c005c13b4296c32e363431bffea5 # v1.2.17
with:
max-size: 2G
key: spirv-ubuntu-24.04
variant: sccache
- name: Build and Test
run: |
mkdir build
cmake -GNinja \
-S llvm \
-B build \
-DCMAKE_BUILD_TYPE=Release \
-DLLVM_ENABLE_ASSERTIONS=ON \
-DCMAKE_C_COMPILER_LAUNCHER=sccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache \
-DLLVM_TARGETS_TO_BUILD="SPIRV" \
-DLLVM_INCLUDE_SPIRV_TOOLS_TESTS=ON
ninja -C build check-llvm-codegen-spirv

View File

@ -2517,7 +2517,7 @@ BinaryContext::calculateEmittedSize(BinaryFunction &BF, bool FixBranches) {
// Clean-up the effect of the code emission.
for (const MCSymbol &Symbol : Assembler.symbols()) {
MCSymbol *MutableSymbol = const_cast<MCSymbol *>(&Symbol);
MutableSymbol->setUndefined();
MutableSymbol->setFragment(nullptr);
MutableSymbol->setIsRegistered(false);
}

View File

@ -662,7 +662,7 @@ Error CleanMCState::runOnFunctions(BinaryContext &BC) {
if (S->isDefined()) {
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: Symbol \"" << S->getName()
<< "\" is already defined\n");
const_cast<MCSymbol *>(S)->setUndefined();
const_cast<MCSymbol *>(S)->setFragment(nullptr);
}
if (S->isRegistered()) {
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: Symbol \"" << S->getName()

View File

@ -424,6 +424,10 @@ ClangTidyASTConsumerFactory::createASTConsumer(
FinderOptions.CheckProfiling.emplace(Profiling->Records);
}
// Avoid processing system headers, unless the user explicitly requests it
if (!Context.getOptions().SystemHeaders.value_or(false))
FinderOptions.IgnoreSystemHeaders = true;
std::unique_ptr<ast_matchers::MatchFinder> Finder(
new ast_matchers::MatchFinder(std::move(FinderOptions)));
@ -540,7 +544,7 @@ runClangTidy(clang::tidy::ClangTidyContext &Context,
ArrayRef<std::string> InputFiles,
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS,
bool ApplyAnyFix, bool EnableCheckProfile,
llvm::StringRef StoreCheckProfile) {
llvm::StringRef StoreCheckProfile, bool Quiet) {
ClangTool Tool(Compilations, InputFiles,
std::make_shared<PCHContainerOperations>(), BaseFS);
@ -577,8 +581,9 @@ runClangTidy(clang::tidy::ClangTidyContext &Context,
class ActionFactory : public FrontendActionFactory {
public:
ActionFactory(ClangTidyContext &Context,
IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS)
: ConsumerFactory(Context, std::move(BaseFS)) {}
IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS,
bool Quiet)
: ConsumerFactory(Context, std::move(BaseFS)), Quiet(Quiet) {}
std::unique_ptr<FrontendAction> create() override {
return std::make_unique<Action>(&ConsumerFactory);
}
@ -589,6 +594,8 @@ runClangTidy(clang::tidy::ClangTidyContext &Context,
DiagnosticConsumer *DiagConsumer) override {
// Explicitly ask to define __clang_analyzer__ macro.
Invocation->getPreprocessorOpts().SetUpStaticAnalyzer = true;
if (Quiet)
Invocation->getDiagnosticOpts().ShowCarets = false;
return FrontendActionFactory::runInvocation(
Invocation, Files, PCHContainerOps, DiagConsumer);
}
@ -607,9 +614,10 @@ runClangTidy(clang::tidy::ClangTidyContext &Context,
};
ClangTidyASTConsumerFactory ConsumerFactory;
bool Quiet;
};
ActionFactory Factory(Context, std::move(BaseFS));
ActionFactory Factory(Context, std::move(BaseFS), Quiet);
Tool.run(&Factory);
return DiagConsumer.take();
}

View File

@ -94,7 +94,8 @@ runClangTidy(clang::tidy::ClangTidyContext &Context,
ArrayRef<std::string> InputFiles,
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS,
bool ApplyAnyFix, bool EnableCheckProfile = false,
llvm::StringRef StoreCheckProfile = StringRef());
llvm::StringRef StoreCheckProfile = StringRef(),
bool Quiet = false);
/// Controls what kind of fixes clang-tidy is allowed to apply.
enum FixBehaviour {

View File

@ -89,13 +89,9 @@ def write_header(
+ check_name_camel.upper()
+ "_H"
)
f.write("//===--- ")
f.write(os.path.basename(filename))
f.write(" - clang-tidy ")
f.write("-" * max(0, 42 - len(os.path.basename(filename))))
f.write("*- C++ -*-===//")
f.write(
"""
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@ -145,13 +141,9 @@ def write_implementation(
filename = os.path.join(module_path, check_name_camel) + ".cpp"
print("Creating %s..." % filename)
with io.open(filename, "w", encoding="utf8", newline="\n") as f:
f.write("//===--- ")
f.write(os.path.basename(filename))
f.write(" - clang-tidy ")
f.write("-" * max(0, 51 - len(os.path.basename(filename))))
f.write("-===//")
f.write(
"""
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.

View File

@ -188,7 +188,7 @@ static bool isKnownToHaveValue(const Expr &Cond, const ASTContext &Ctx,
/// \return true iff all `CallExprs` visited have callees; false otherwise
/// indicating there is an unresolved indirect call.
static bool populateCallees(const Stmt *StmtNode,
llvm::SmallSet<const Decl *, 16> &Callees) {
llvm::SmallPtrSet<const Decl *, 16> &Callees) {
if (const auto *Call = dyn_cast<CallExpr>(StmtNode)) {
const Decl *Callee = Call->getDirectCallee();
@ -212,7 +212,7 @@ static bool populateCallees(const Stmt *StmtNode,
/// returns true iff `SCC` contains `Func` and its' function set overlaps with
/// `Callees`
static bool overlap(ArrayRef<CallGraphNode *> SCC,
const llvm::SmallSet<const Decl *, 16> &Callees,
const llvm::SmallPtrSet<const Decl *, 16> &Callees,
const Decl *Func) {
bool ContainsFunc = false, Overlap = false;
@ -264,7 +264,7 @@ static bool hasRecursionOverStaticLoopCondVariables(const Expr *Cond,
if (!hasStaticLocalVariable(Cond))
return false;
llvm::SmallSet<const Decl *, 16> CalleesInLoop;
llvm::SmallPtrSet<const Decl *, 16> CalleesInLoop;
if (!populateCallees(LoopStmt, CalleesInLoop)) {
// If there are unresolved indirect calls, we assume there could

View File

@ -191,6 +191,9 @@ void PreferMemberInitializerCheck::check(
if (!AssignmentToMember)
continue;
const FieldDecl *Field = AssignmentToMember->Field;
// Skip if the field is inherited from a base class.
if (Field->getParent() != Class)
continue;
const Expr *InitValue = AssignmentToMember->Init;
updateAssignmentLevel(Field, InitValue, Ctor, AssignedFields);
if (!canAdvanceAssignment(AssignedFields[Field]))

View File

@ -32,6 +32,7 @@ add_clang_library(clangTidyMiscModule STATIC
NoRecursionCheck.cpp
NonCopyableObjects.cpp
NonPrivateMemberVariablesInClassesCheck.cpp
OverrideWithDifferentVisibilityCheck.cpp
RedundantExpressionCheck.cpp
StaticAssertCheck.cpp
ThrowByValueCatchByReferenceCheck.cpp

View File

@ -22,6 +22,7 @@
#include "NoRecursionCheck.h"
#include "NonCopyableObjects.h"
#include "NonPrivateMemberVariablesInClassesCheck.h"
#include "OverrideWithDifferentVisibilityCheck.h"
#include "RedundantExpressionCheck.h"
#include "StaticAssertCheck.h"
#include "ThrowByValueCatchByReferenceCheck.h"
@ -81,6 +82,8 @@ public:
"misc-use-anonymous-namespace");
CheckFactories.registerCheck<UseInternalLinkageCheck>(
"misc-use-internal-linkage");
CheckFactories.registerCheck<OverrideWithDifferentVisibilityCheck>(
"misc-override-with-different-visibility");
}
};

View File

@ -0,0 +1,150 @@
//===--- OverrideWithDifferentVisibilityCheck.cpp - clang-tidy ------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "OverrideWithDifferentVisibilityCheck.h"
#include "../utils/Matchers.h"
#include "../utils/OptionsUtils.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
using namespace clang;
namespace {
AST_MATCHER(NamedDecl, isOperatorDecl) {
DeclarationName::NameKind const NK = Node.getDeclName().getNameKind();
return NK != DeclarationName::Identifier &&
NK != DeclarationName::CXXConstructorName &&
NK != DeclarationName::CXXDestructorName;
}
} // namespace
namespace clang::tidy {
template <>
struct OptionEnumMapping<
misc::OverrideWithDifferentVisibilityCheck::ChangeKind> {
static llvm::ArrayRef<std::pair<
misc::OverrideWithDifferentVisibilityCheck::ChangeKind, StringRef>>
getEnumMapping() {
static constexpr std::pair<
misc::OverrideWithDifferentVisibilityCheck::ChangeKind, StringRef>
Mapping[] = {
{misc::OverrideWithDifferentVisibilityCheck::ChangeKind::Any,
"any"},
{misc::OverrideWithDifferentVisibilityCheck::ChangeKind::Widening,
"widening"},
{misc::OverrideWithDifferentVisibilityCheck::ChangeKind::Narrowing,
"narrowing"},
};
return {Mapping};
}
};
namespace misc {
OverrideWithDifferentVisibilityCheck::OverrideWithDifferentVisibilityCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
DetectVisibilityChange(
Options.get("DisallowedVisibilityChange", ChangeKind::Any)),
CheckDestructors(Options.get("CheckDestructors", false)),
CheckOperators(Options.get("CheckOperators", false)),
IgnoredFunctions(utils::options::parseStringList(
Options.get("IgnoredFunctions", ""))) {}
void OverrideWithDifferentVisibilityCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "DisallowedVisibilityChange", DetectVisibilityChange);
Options.store(Opts, "CheckDestructors", CheckDestructors);
Options.store(Opts, "CheckOperators", CheckOperators);
Options.store(Opts, "IgnoredFunctions",
utils::options::serializeStringList(IgnoredFunctions));
}
void OverrideWithDifferentVisibilityCheck::registerMatchers(
MatchFinder *Finder) {
const auto IgnoredDecl =
namedDecl(matchers::matchesAnyListedName(IgnoredFunctions));
const auto FilterDestructors =
CheckDestructors ? decl() : decl(unless(cxxDestructorDecl()));
const auto FilterOperators =
CheckOperators ? namedDecl() : namedDecl(unless(isOperatorDecl()));
Finder->addMatcher(
cxxMethodDecl(
isVirtual(), FilterDestructors, FilterOperators,
ofClass(
cxxRecordDecl(unless(isExpansionInSystemHeader())).bind("class")),
forEachOverridden(cxxMethodDecl(ofClass(cxxRecordDecl().bind("base")),
unless(IgnoredDecl))
.bind("base_func")))
.bind("func"),
this);
}
void OverrideWithDifferentVisibilityCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *const MatchedFunction =
Result.Nodes.getNodeAs<FunctionDecl>("func");
if (!MatchedFunction->isCanonicalDecl())
return;
const auto *const ParentClass =
Result.Nodes.getNodeAs<CXXRecordDecl>("class");
const auto *const BaseClass = Result.Nodes.getNodeAs<CXXRecordDecl>("base");
CXXBasePaths Paths;
if (!ParentClass->isDerivedFrom(BaseClass, Paths))
return;
const auto *const OverriddenFunction =
Result.Nodes.getNodeAs<FunctionDecl>("base_func");
AccessSpecifier const ActualAccess = MatchedFunction->getAccess();
AccessSpecifier OverriddenAccess = OverriddenFunction->getAccess();
const CXXBaseSpecifier *InheritanceWithStrictVisibility = nullptr;
for (const CXXBasePath &Path : Paths) {
for (const CXXBasePathElement &Elem : Path) {
if (Elem.Base->getAccessSpecifier() > OverriddenAccess) {
OverriddenAccess = Elem.Base->getAccessSpecifier();
InheritanceWithStrictVisibility = Elem.Base;
}
}
}
if (ActualAccess != OverriddenAccess) {
if (DetectVisibilityChange == ChangeKind::Widening &&
ActualAccess > OverriddenAccess)
return;
if (DetectVisibilityChange == ChangeKind::Narrowing &&
ActualAccess < OverriddenAccess)
return;
if (InheritanceWithStrictVisibility) {
diag(MatchedFunction->getLocation(),
"visibility of function %0 is changed from %1 (through %1 "
"inheritance of class %2) to %3")
<< MatchedFunction << OverriddenAccess
<< InheritanceWithStrictVisibility->getType() << ActualAccess;
diag(InheritanceWithStrictVisibility->getBeginLoc(),
"%0 is inherited as %1 here", DiagnosticIDs::Note)
<< InheritanceWithStrictVisibility->getType() << OverriddenAccess;
} else {
diag(MatchedFunction->getLocation(),
"visibility of function %0 is changed from %1 in class %2 to %3")
<< MatchedFunction << OverriddenAccess << BaseClass << ActualAccess;
}
diag(OverriddenFunction->getLocation(), "function declared here as %0",
DiagnosticIDs::Note)
<< OverriddenFunction->getAccess();
}
}
} // namespace misc
} // namespace clang::tidy

View File

@ -0,0 +1,43 @@
//===--- OverrideWithDifferentVisibilityCheck.h - clang-tidy --*- C++ -*---===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_OVERRIDEWITHDIFFERENTVISIBILITYCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_OVERRIDEWITHDIFFERENTVISIBILITYCHECK_H
#include "../ClangTidyCheck.h"
namespace clang::tidy::misc {
/// Finds virtual function overrides with different visibility than the function
/// in the base class.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/misc/override-with-different-visibility.html
class OverrideWithDifferentVisibilityCheck : public ClangTidyCheck {
public:
enum class ChangeKind { Any, Widening, Narrowing };
OverrideWithDifferentVisibilityCheck(StringRef Name,
ClangTidyContext *Context);
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}
private:
ChangeKind DetectVisibilityChange;
bool CheckDestructors;
bool CheckOperators;
std::vector<llvm::StringRef> IgnoredFunctions;
};
} // namespace clang::tidy::misc
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_OVERRIDEWITHDIFFERENTVISIBILITYCHECK_H

View File

@ -717,7 +717,7 @@ int clangTidyMain(int argc, const char **argv) {
EnableModuleHeadersParsing);
std::vector<ClangTidyError> Errors =
runClangTidy(Context, OptionsParser->getCompilations(), PathList, BaseFS,
FixNotes, EnableCheckProfile, ProfilePrefix);
FixNotes, EnableCheckProfile, ProfilePrefix, Quiet);
bool FoundErrors = llvm::any_of(Errors, [](const ClangTidyError &E) {
return E.DiagLevel == ClangTidyError::Error;
});

View File

@ -28,6 +28,7 @@ import glob
import json
import multiprocessing
import os
import queue
import re
import shutil
import subprocess
@ -42,13 +43,6 @@ try:
except ImportError:
yaml = None
is_py2 = sys.version[0] == "2"
if is_py2:
import Queue as queue
else:
import queue as queue
def run_tidy(task_queue, lock, timeout, failed_files):
watchdog = None

View File

@ -985,7 +985,7 @@ resolveForwardingParameters(const FunctionDecl *D, unsigned MaxDepth) {
// Recurse on pack parameters
size_t Depth = 0;
const FunctionDecl *CurrentFunction = D;
llvm::SmallSet<const FunctionTemplateDecl *, 4> SeenTemplates;
llvm::SmallPtrSet<const FunctionTemplateDecl *, 4> SeenTemplates;
if (const auto *Template = D->getPrimaryTemplate()) {
SeenTemplates.insert(Template);
}

View File

@ -833,6 +833,10 @@ bool OverlayCDB::setCompileCommand(PathRef File,
std::unique_ptr<ProjectModules>
OverlayCDB::getProjectModules(PathRef File) const {
auto MDB = DelegatingCDB::getProjectModules(File);
if (!MDB) {
log("Failed to get compilation Database for {0}", File);
return {};
}
MDB->setCommandMangler([&Mangler = Mangler](tooling::CompileCommand &Command,
PathRef CommandPath) {
Mangler(Command, CommandPath);

View File

@ -1876,7 +1876,7 @@ static void fillSubTypes(const SymbolID &ID,
});
}
using RecursionProtectionSet = llvm::SmallSet<const CXXRecordDecl *, 4>;
using RecursionProtectionSet = llvm::SmallPtrSet<const CXXRecordDecl *, 4>;
// Extracts parents from AST and populates the type hierarchy item.
static void fillSuperTypes(const CXXRecordDecl &CXXRD, llvm::StringRef TUPath,

View File

@ -181,7 +181,7 @@ struct ExtractionZone {
bool requiresHoisting(const SourceManager &SM,
const HeuristicResolver *Resolver) const {
// First find all the declarations that happened inside extraction zone.
llvm::SmallSet<const Decl *, 1> DeclsInExtZone;
llvm::SmallPtrSet<const Decl *, 1> DeclsInExtZone;
for (auto *RootStmt : RootStmts) {
findExplicitReferences(
RootStmt,

View File

@ -0,0 +1,66 @@
# A smoke test to check that clangd works without compilation database
#
# Windows have different escaping modes.
# FIXME: We should add one for windows.
# UNSUPPORTED: system-windows
#
# RUN: rm -fr %t
# RUN: mkdir -p %t
# RUN: split-file %s %t
#
# RUN: sed -e "s|DIR|%/t|g" %t/definition.jsonrpc.tmpl > %t/definition.jsonrpc
#
# RUN: clangd -experimental-modules-support -lit-test < %t/definition.jsonrpc \
# RUN: | FileCheck -strict-whitespace %t/definition.jsonrpc
#--- A.h
void printA();
#--- Use.cpp
#include "A.h"
void foo() {
print
}
#--- definition.jsonrpc.tmpl
{
"jsonrpc": "2.0",
"id": 0,
"method": "initialize",
"params": {
"processId": 123,
"rootPath": "clangd",
"capabilities": {
"textDocument": {
"completion": {
"completionItem": {
"snippetSupport": true
}
}
}
},
"trace": "off"
}
}
---
{
"jsonrpc": "2.0",
"method": "textDocument/didOpen",
"params": {
"textDocument": {
"uri": "file://DIR/Use.cpp",
"languageId": "cpp",
"version": 1,
"text": "#include \"A.h\"\nvoid foo() {\n print\n}\n"
}
}
}
# CHECK: "message"{{.*}}printA{{.*}}(fix available)
---
{"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"file://DIR/Use.cpp"},"context":{"triggerKind":1},"position":{"line":2,"character":6}}}
---
{"jsonrpc":"2.0","id":2,"method":"shutdown"}
---
{"jsonrpc":"2.0","method":"exit"}

View File

@ -104,6 +104,10 @@ Improvements to clang-query
Improvements to clang-tidy
--------------------------
- :program:`clang-tidy` no longer attemps to analyze code from system headers
by default, greatly improving performance. This behavior is disabled if the
`SystemHeaders` option is enabled.
- The :program:`run-clang-tidy.py` and :program:`clang-tidy-diff.py` scripts
now run checks in parallel by default using all available hardware threads.
Both scripts display the number of threads being used in their output.
@ -115,6 +119,9 @@ Improvements to clang-tidy
- Improved documentation of the `-line-filter` command-line flag of
:program:`clang-tidy` and :program:`run-clang-tidy.py`.
- Improved :program:`clang-tidy` option `-quiet` by suppressing diagnostic
count messages.
New checks
^^^^^^^^^^
@ -130,6 +137,12 @@ New checks
Checks for uses of MLIR's old/to be deprecated ``OpBuilder::create<T>`` form
and suggests using ``T::create`` instead.
- New :doc:`misc-override-with-different-visibility
<clang-tidy/checks/misc/override-with-different-visibility>` check.
Finds virtual function overrides with different visibility than the function
in the base class.
New check aliases
^^^^^^^^^^^^^^^^^
@ -163,6 +176,10 @@ Changes in existing checks
an additional matcher that generalizes the copy-and-swap idiom pattern
detection.
- Improved :doc:`cppcoreguidelines-prefer-member-initializer
<clang-tidy/checks/cppcoreguidelines/prefer-member-initializer>` check to
avoid false positives on inherited members in class templates.
- Improved :doc:`misc-header-include-cycle
<clang-tidy/checks/misc/header-include-cycle>` check performance.

View File

@ -0,0 +1,314 @@
======================
Clang-Change-Namespace
======================
.. contents::
.. toctree::
:maxdepth: 1
:program:`clang-change-namespace` can be used to change the surrounding
namespaces of class/function definitions.
Classes/functions in the moved namespace will have new namespaces while
references to symbols (e.g. types, functions) which are not defined in the
changed namespace will be correctly qualified by prepending namespace specifiers
before them. This will try to add shortest namespace specifiers possible.
When a symbol reference needs to be fully-qualified, this adds a `::` prefix to
the namespace specifiers unless the new namespace is the global namespace. For
classes, only classes that are declared/defined in the given namespace in
specified files will be moved: forward declarations will remain in the old
namespace. The will be demonstrated in the next example.
Example usage
-------------
For example, consider this `test.cc` example here with the forward declared
class `FWD` and the defined class `A`, both in the namespace `a`.
.. code-block:: c++
namespace a {
class FWD;
class A {
FWD *fwd;
};
} // namespace a
And now let's change the namespace `a` to `x`.
.. code-block:: console
clang-change-namespace \
--old_namespace "a" \
--new_namespace "x" \
--file_pattern "test.cc" \
--i \
test.cc
Note that in the code below there's still the forward decalred class `FWD` that
stayed in the namespace `a`. It wasn't moved to the new namespace because it
wasn't defined/declared here in `a` but only forward declared.
.. code-block:: c++
namespace a {
class FWD;
} // namespace a
namespace x {
class A {
a::FWD *fwd;
};
} // namespace x
Another example
---------------
Consider this `test.cc` file:
.. code-block:: c++
namespace na {
class X {};
namespace nb {
class Y {
X x;
};
} // namespace nb
} // namespace na
To move the definition of class `Y` from namespace `na::nb` to `x::y`, run:
.. code-block:: console
clang-change-namespace \
--old_namespace "na::nb" \
--new_namespace "x::y" \
--file_pattern "test.cc" \
--i \
test.cc
This will overwrite `test.cc` to look like this:
.. code-block:: c++
namespace na {
class X {};
} // namespace na
namespace x {
namespace y {
class Y {
na::X x;
};
} // namespace y
} // namespace x
Note, that we've successfully moved the class `Y` from namespace `na::nb` to
namespace `x::y`.
Caveats
=======
Content already exists in new namespace
---------------------------------------
Consider this `test.cc` example that defines two `class A` one inside the
namespace `a` and one in namespace `b`:
.. code-block:: c++
namespace a {
class A {
int classAFromWithinNamespace_a;
};
} // namespace a
namespace b {
class A {
int classAFromWithinNamespace_b;
};
} //namespace b
Let's move everything from namespace `a` to namespace `b`:
.. code-block:: console
clang-change-namespace \
--old_namespace "a" \
--new_namespace "b" \
--file_pattern test.cc \
test.cc
As expected we now have to definitions of `class A` inside the namespace `b`:
.. code-block:: c++
namespace b {
class A {
int classAFromWithinNamespace_a;
};
} // namespace b
namespace b {
class A {
int classAFromWithinNamespace_b;
};
} //namespace b
The re-factoring looks correct but the code will not compile due to the name
duplication. It is not up to the tool to ensure compilability in that sense.
But one has to be aware of that.
Inline namespace doesn't work
-----------------------------
Consider this usage of two versions of implementations for a `greet` function:
.. code-block:: c++
#include <cstdio>
namespace Greeter {
inline namespace Version1 {
const char* greet() { return "Hello from version 1!"; }
} // namespace Version1
namespace Version2 {
const char* greet() { return "Hello from version 2!"; }
} // namespace Version2
} // namespace Greeter
int main(int argc, char* argv[]) {
printf("%s\n", Greeter::greet());
return 0;
}
Note, that currently `Greeter::greet()` will result in a call to
`Greeter::Version1::greet()` because that's the inlined namespace.
Let's say you want to move one and make `Version2` the default now and remove
the `inline` from the `Version1`. First let's try to turn `namespace Version2`
into `inline namespace Version2`:
.. code-block:: console
clang-change-namespace \
--old_namespace "Greeter::Version2" \
--new_namespace "inline Version2" \
--file_pattern main.cc main.cc
But this will put the `inline` keyword in the wrong place resulting in:
.. code-block:: c++
#include <cstdio>
namespace Greeter {
inline namespace Version1 {
const char* greet() { return "Hello from version 1!"; }
} // namespace Version1
} // namespace Greeter
namespace inline Greeter {
namespace Version2 {
const char *greet() { return "Hello from version 2!"; }
} // namespace Version2
} // namespace inline Greeter
int main(int argc, char* argv[]) {
printf("%s\n", Greeter::greet());
return 0;
}
One cannot use `:program:`clang-change-namespace` to inline a namespace.
Symbol references not updated
-----------------------------
Consider this `test.cc` file:
.. code-block:: c++
namespace old {
struct foo {};
} // namespace old
namespace b {
old::foo g_foo;
} // namespace b
Notice that namespace `b` defines a global variable of type `old::foo`. If we
now change the name of the `old` namespace to `modern`, the reference will not
be updated:
.. code-block:: console
clang-change-namespace \
--old_namespace "old" \
--new_namespace "modern" \
--file_pattern test.cc \
test.cc
.. code-block:: c++
namespace modern {
struct foo {};
} // namespace modern
namespace b {
old::foo g_foo;
} // namespace b
`g_foo` is still of the no longer existing type `old::foo` while instead it
should use `modern::foo`.
Only symbol references in the moved namespace are updated, not outside of it.
:program:`clang-change-namespace` Command Line Options
======================================================
.. option:: --allowed_file=<string>
A file containing regexes of symbol names that are not expected to be updated
when changing namespaces around them.
.. option:: --dump_result
Dump new file contents in YAML, if specified.
.. option:: --extra-arg=<string>
Additional argument to append to the compiler command line
.. option:: --extra-arg-before=<string>
Additional argument to prepend to the compiler command line
.. option:: --file_pattern=<string>
Only rename namespaces in files that match the given regular expression
pattern.
.. option:: -i
Inplace edit <file>s, if specified.
.. option:: --new_namespace=<string>
New namespace. Use `""` when you target the global namespace.
.. option:: --old_namespace=<string>
Old namespace.
.. option:: -p <string>
Build path
.. option:: --style=<string>
The style name used for reformatting.

View File

@ -271,6 +271,7 @@ Clang-Tidy Checks
:doc:`misc-no-recursion <misc/no-recursion>`,
:doc:`misc-non-copyable-objects <misc/non-copyable-objects>`,
:doc:`misc-non-private-member-variables-in-classes <misc/non-private-member-variables-in-classes>`,
:doc:`misc-override-with-different-visibility <misc/override-with-different-visibility>`,
:doc:`misc-redundant-expression <misc/redundant-expression>`, "Yes"
:doc:`misc-static-assert <misc/static-assert>`, "Yes"
:doc:`misc-throw-by-value-catch-by-reference <misc/throw-by-value-catch-by-reference>`,

View File

@ -0,0 +1,87 @@
.. title:: clang-tidy - misc-override-with-different-visibility
misc-override-with-different-visibility
=======================================
Finds virtual function overrides with different visibility than the function
in the base class. This includes for example if a virtual function declared as
``private`` is overridden and declared as ``public`` in a subclass. The detected
change is the modification of visibility resulting from keywords ``public``,
``protected``, ``private`` at overridden virtual functions. The check applies to
any normal virtual function and optionally to destructors or operators. Use of
the ``using`` keyword is not considered as visibility change by this check.
.. code-block:: c++
class A {
public:
virtual void f_pub();
private:
virtual void f_priv();
};
class B: public A {
public:
void f_priv(); // warning: changed visibility from private to public
private:
void f_pub(); // warning: changed visibility from public to private
};
class C: private A {
// no warning: f_pub becomes private in this case but this is from the
// private inheritance
};
class D: private A {
public:
void f_pub(); // warning: changed visibility from private to public
// 'f_pub' would have private access but is forced to be
// public
};
If the visibility is changed in this way, it can indicate bad design or
programming error.
If a virtual function is private in a subclass but public in the base class, it
can still be accessed from a pointer to the subclass if the pointer is converted
to the base type. Probably private inheritance can be used instead.
A protected virtual function that is made public in a subclass may have valid
use cases but similar (not exactly same) effect can be achieved with the
``using`` keyword.
Options
-------
.. option:: DisallowedVisibilityChange
Controls what kind of change to the visibility will be detected by the check.
Possible values are `any`, `widening`, `narrowing`. For example the
`widening` option will produce warning only if the visibility is changed
from more restrictive (``private``) to less restrictive (``public``).
Default value is `any`.
.. option:: CheckDestructors
If `true`, the check does apply to destructors too. Otherwise destructors
are ignored by the check.
Default value is `false`.
.. option:: CheckOperators
If `true`, the check does apply to overloaded C++ operators (as virtual
member functions) too. This includes other special member functions (like
conversions) too. This option is probably useful only in rare cases because
operators and conversions are not often virtual functions.
Default value is `false`.
.. option:: IgnoredFunctions
This option can be used to ignore the check at specific functions.
To configure this option, a semicolon-separated list of function names
should be provided. The list can contain regular expressions, in this way it
is possible to select all functions of a specific class (like `MyClass::.*`)
or a specific function of any class (like `my_function` or
`::.*::my_function`). The function names are matched at the base class.
Default value is empty string.

View File

@ -111,6 +111,13 @@ Diagnostics which have a corresponding warning option, are named
``-Wliteral-conversion`` will be reported with check name
``clang-diagnostic-literal-conversion``.
Clang compiler errors (such as syntax errors, semantic errors, or other failures
that prevent Clang from compiling the code) are reported with the check name
``clang-diagnostic-error``. These represent fundamental compilation failures that
must be fixed before :program:`clang-tidy` can perform its analysis. Unlike other
diagnostics, ``clang-diagnostic-error`` cannot be disabled, as :program:`clang-tidy`
requires valid code to function.
The ``-fix`` flag instructs :program:`clang-tidy` to fix found errors if
supported by corresponding checks.

View File

@ -17,6 +17,7 @@ Contents
clang-tidy/index
clang-include-fixer
clang-change-namespace
modularize
pp-trace
clangd <https://clangd.llvm.org/>

View File

@ -650,3 +650,16 @@ struct InitFromBindingDecl {
}
};
} // namespace GH82970
struct A {
int m;
};
struct B : A {
B() { m = 0; }
};
template <class T>
struct C : A {
C() { m = 0; }
};

View File

@ -0,0 +1,14 @@
#pragma clang system_header
namespace sys {
struct Base {
virtual void publicF();
};
struct Derived: public Base {
private:
void publicF() override;
};
}

View File

@ -0,0 +1,60 @@
// RUN: %check_clang_tidy %s misc-override-with-different-visibility %t -- \
// RUN: -config="{CheckOptions: {misc-override-with-different-visibility.IgnoredFunctions: 'IgnoreAlways::.*;::a::IgnoreSelected::.*;IgnoreFunctions::f1;ignored_f'}}"
class IgnoreAlways {
virtual void f();
};
class IgnoreSelected {
virtual void f();
};
namespace a {
class IgnoreAlways {
virtual void f();
};
class IgnoreSelected {
virtual void f();
};
}
namespace ignore_always {
class Test1: public IgnoreAlways {
public:
void f();
void ignored_f(int);
};
class Test2: public a::IgnoreAlways {
public:
void f();
};
}
namespace ignore_selected {
class Test1: public IgnoreSelected {
public:
void f();
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'f'
// CHECK-MESSAGES: :9:16: note: function declared here
void ignored_f(int);
};
class Test2: public a::IgnoreSelected {
public:
void f();
};
}
class IgnoreFunctions {
virtual void f1();
virtual void f2();
virtual void ignored_f();
};
class IgnoreFunctionsTest: public IgnoreFunctions {
public:
void f1();
void f2();
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'f2'
// CHECK-MESSAGES: :[[@LINE-9]]:16: note: function declared here
void ignored_f();
};

View File

@ -0,0 +1,75 @@
// RUN: %check_clang_tidy -check-suffixes=DTORS,WIDENING,NARROWING %s misc-override-with-different-visibility %t -- \
// RUN: -config="{CheckOptions: {misc-override-with-different-visibility.CheckDestructors: true}}"
// RUN: %check_clang_tidy -check-suffixes=OPS,WIDENING,NARROWING %s misc-override-with-different-visibility %t -- \
// RUN: -config="{CheckOptions: {misc-override-with-different-visibility.CheckOperators: true}}"
// RUN: %check_clang_tidy -check-suffixes=WIDENING %s misc-override-with-different-visibility %t -- \
// RUN: -config="{CheckOptions: {misc-override-with-different-visibility.DisallowedVisibilityChange: 'widening'}}"
// RUN: %check_clang_tidy -check-suffixes=NARROWING %s misc-override-with-different-visibility %t -- \
// RUN: -config="{CheckOptions: {misc-override-with-different-visibility.DisallowedVisibilityChange: 'narrowing'}}"
namespace test_change {
class A {
protected:
virtual void f1();
virtual void f2();
};
class B: public A {
public:
void f1();
// CHECK-MESSAGES-WIDENING: :[[@LINE-1]]:8: warning: visibility of function 'f1'
// CHECK-MESSAGES-WIDENING: :[[@LINE-8]]:16: note: function declared here
private:
void f2();
// CHECK-MESSAGES-NARROWING: :[[@LINE-1]]:8: warning: visibility of function 'f2'
// CHECK-MESSAGES-NARROWING: :[[@LINE-11]]:16: note: function declared here
};
}
namespace test_destructor {
class A {
public:
virtual ~A();
};
class B: public A {
protected:
~B();
// CHECK-MESSAGES-DTORS: :[[@LINE-1]]:3: warning: visibility of function '~B'
// CHECK-MESSAGES-DTORS: :[[@LINE-7]]:11: note: function declared here
};
}
namespace test_operator {
class A {
virtual A& operator=(const A&);
virtual A& operator++();
virtual int operator()(int);
virtual operator double() const;
};
class B: public A {
protected:
A& operator=(const A&);
// CHECK-MESSAGES-OPS: :[[@LINE-1]]:6: warning: visibility of function 'operator='
// CHECK-MESSAGES-OPS: :[[@LINE-10]]:14: note: function declared here
A& operator++();
// CHECK-MESSAGES-OPS: :[[@LINE-1]]:6: warning: visibility of function 'operator++'
// CHECK-MESSAGES-OPS: :[[@LINE-12]]:14: note: function declared here
int operator()(int);
// CHECK-MESSAGES-OPS: :[[@LINE-1]]:7: warning: visibility of function 'operator()'
// CHECK-MESSAGES-OPS: :[[@LINE-14]]:15: note: function declared here
operator double() const;
// CHECK-MESSAGES-OPS: :[[@LINE-1]]:3: warning: visibility of function 'operator double'
// CHECK-MESSAGES-OPS: :[[@LINE-16]]:11: note: function declared here
};
}

View File

@ -0,0 +1,289 @@
// RUN: %check_clang_tidy %s misc-override-with-different-visibility %t -- -config="{CheckOptions: {misc-override-with-different-visibility.CheckDestructors: true,misc-override-with-different-visibility.CheckOperators: true}}" -- -I %S/Inputs/override-with-different-visibility
#include <test-system-header.h>
class A {
public:
virtual void pub_foo1() {}
virtual void pub_foo2() {}
virtual void pub_foo3() {}
protected:
virtual void prot_foo1();
virtual void prot_foo2();
virtual void prot_foo3();
private:
virtual void priv_foo1() {}
virtual void priv_foo2() {}
virtual void priv_foo3() {}
};
void A::prot_foo1() {}
void A::prot_foo2() {}
void A::prot_foo3() {}
namespace test1 {
class B: public A {
public:
void pub_foo1() override {}
void prot_foo1() override {}
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo1' is changed from protected in class 'A' to public [misc-override-with-different-visibility]
// CHECK-MESSAGES: :9:16: note: function declared here as protected
void priv_foo1() override {}
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'priv_foo1' is changed from private in class 'A' to public [misc-override-with-different-visibility]
// CHECK-MESSAGES: :13:16: note: function declared here as private
protected:
void pub_foo2() override {}
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'pub_foo2' is changed from public in class 'A' to protected [misc-override-with-different-visibility]
// CHECK-MESSAGES: :6:16: note: function declared here as public
void prot_foo2() override {}
void priv_foo2() override {}
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'priv_foo2' is changed from private in class 'A' to protected [misc-override-with-different-visibility]
// CHECK-MESSAGES: :14:16: note: function declared here as private
private:
void pub_foo3() override {}
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'pub_foo3' is changed from public in class 'A' to private [misc-override-with-different-visibility]
// CHECK-MESSAGES: :7:16: note: function declared here as public
void prot_foo3() override {}
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo3' is changed from protected in class 'A' to private [misc-override-with-different-visibility]
// CHECK-MESSAGES: :11:16: note: function declared here as protected
void priv_foo3() override {}
};
class C: public B {
public:
void pub_foo1() override;
protected:
void prot_foo1() override;
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo1' is changed from public in class 'B' to protected [misc-override-with-different-visibility]
// CHECK-MESSAGES: :27:8: note: function declared here as public
private:
void priv_foo1() override;
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'priv_foo1' is changed from public in class 'B' to private [misc-override-with-different-visibility]
// CHECK-MESSAGES: :30:8: note: function declared here as public
};
void C::prot_foo1() {}
void C::priv_foo1() {}
}
namespace test2 {
class B: public A {
public:
void pub_foo1() override;
protected:
void prot_foo1() override;
private:
void priv_foo1() override;
};
class C: public B {
public:
void pub_foo1() override;
void prot_foo1() override;
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo1' is changed from protected in class 'B' to public
// CHECK-MESSAGES: :75:8: note: function declared here as protected
void priv_foo1() override;
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'priv_foo1' is changed from private in class 'B' to public
// CHECK-MESSAGES: :77:8: note: function declared here as private
void pub_foo2() override;
void prot_foo2() override;
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo2' is changed from protected in class 'A' to public
// CHECK-MESSAGES: :10:16: note: function declared here as protected
void priv_foo2() override;
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'priv_foo2' is changed from private in class 'A' to public
// CHECK-MESSAGES: :14:16: note: function declared here as private
};
}
namespace test3 {
class B: private A {
public:
void pub_foo1() override;
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'pub_foo1' is changed from private (through private inheritance of class 'A') to public
// CHECK-MESSAGES: :103:10: note: 'A' is inherited as private here
// CHECK-MESSAGES: :5:16: note: function declared here as public
protected:
void prot_foo1() override;
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo1' is changed from private (through private inheritance of class 'A') to protected
// CHECK-MESSAGES: :103:10: note: 'A' is inherited as private here
// CHECK-MESSAGES: :9:16: note: function declared here as protected
private:
void priv_foo1() override;
public:
void prot_foo2() override;
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo2' is changed from private (through private inheritance of class 'A') to public
// CHECK-MESSAGES: :103:10: note: 'A' is inherited as private here
// CHECK-MESSAGES: :10:16: note: function declared here as protected
void priv_foo2() override;
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'priv_foo2' is changed from private in class 'A' to public
// CHECK-MESSAGES: :14:16: note: function declared here as private
private:
void pub_foo3() override;
void prot_foo3() override;
};
class C: private A {
};
class D: public C {
public:
void pub_foo1() override;
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'pub_foo1' is changed from private (through private inheritance of class 'A') to public
// CHECK-MESSAGES: :131:10: note: 'A' is inherited as private here
// CHECK-MESSAGES: :5:16: note: function declared here as public
};
}
namespace test4 {
struct Base1 {
public:
virtual void foo1();
private:
virtual void foo2();
};
struct Base2 {
public:
virtual void foo2();
private:
virtual void foo1();
};
struct A : public Base1, public Base2 {
protected:
void foo1() override;
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'foo1' is changed from private in class 'Base2' to protected
// CHECK-MESSAGES: :158:16: note: function declared here as private
// CHECK-MESSAGES: :[[@LINE-3]]:8: warning: visibility of function 'foo1' is changed from public in class 'Base1' to protected
// CHECK-MESSAGES: :149:16: note: function declared here as public
private:
void foo2() override;
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'foo2' is changed from public in class 'Base2' to private
// CHECK-MESSAGES: :156:16: note: function declared here as public
};
}
namespace test5 {
struct B1: virtual public A {};
struct B2: virtual private A {};
struct B: public B1, public B2 {
public:
void pub_foo1() override;
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'pub_foo1' is changed from private (through private inheritance of class 'A') to public
// CHECK-MESSAGES: :179:12: note: 'A' is inherited as private here
// CHECK-MESSAGES: :5:16: note: function declared here as public
};
}
namespace test_using {
class A {
private:
A(int);
protected:
virtual void f();
};
class B: public A {
public:
using A::A;
using A::f;
};
}
namespace test_template {
template <typename T>
class A {
protected:
virtual T foo();
};
template <typename T>
class B: public A<T> {
private:
T foo() override;
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: visibility of function 'foo' is changed from protected in class 'A<int>' to private
// CHECK-MESSAGES: :[[@LINE-8]]:13: note: function declared here as protected
};
template <typename T>
class C: private A<T> {
public:
T foo() override;
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: visibility of function 'foo' is changed from private (through private inheritance of class 'A<int>') to public
// CHECK-MESSAGES: :[[@LINE-4]]:10: note: 'A<int>' is inherited as private here
// CHECK-MESSAGES: :[[@LINE-17]]:13: note: function declared here as protected
};
B<int> fB() {
return B<int>{};
}
C<int> fC() {
return C<int>{};
}
}
namespace test_system_header {
struct SysDerived: public sys::Base {
private:
void publicF();
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'publicF' is changed from public in class 'Base' to private
};
}
namespace test_destructor {
class A {
public:
virtual ~A();
};
class B: public A {
protected:
~B();
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: visibility of function '~B'
// CHECK-MESSAGES: :[[@LINE-7]]:11: note: function declared here
};
}
namespace test_operator {
class A {
virtual int operator()(int);
virtual A& operator++();
virtual operator double() const;
};
class B: public A {
protected:
int operator()(int);
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: visibility of function 'operator()'
// CHECK-MESSAGES: :[[@LINE-9]]:15: note: function declared here
A& operator++();
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: visibility of function 'operator++'
// CHECK-MESSAGES: :[[@LINE-11]]:14: note: function declared here
operator double() const;
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: visibility of function 'operator double'
// CHECK-MESSAGES: :[[@LINE-13]]:11: note: function declared here
};
}

View File

@ -10,7 +10,7 @@
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header_alias\.h' %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER_ALIAS %s
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header_alias\.h' -quiet %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER_ALIAS %s
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header\.h' %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER %s
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header\.h' -quiet %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER %s
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header\.h' -quiet %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER --allow-empty %s
// Check that `-header-filter` operates on the same file paths as paths in
// diagnostics printed by ClangTidy.

View File

@ -66,19 +66,14 @@ class A { A(int); };
// CHECK4-NOT: warning:
// CHECK4-QUIET-NOT: warning:
// CHECK: Suppressed 3 warnings (3 in non-user code)
// CHECK: Use -header-filter=.* to display errors from all non-system headers.
// CHECK-QUIET-NOT: Suppressed
// CHECK2: Suppressed 1 warnings (1 in non-user code)
// CHECK2: Use -header-filter=.* {{.*}}
// CHECK2-QUIET-NOT: Suppressed
// CHECK3: Suppressed 2 warnings (2 in non-user code)
// CHECK3: Use -header-filter=.* {{.*}}
// CHECK3-QUIET-NOT: Suppressed
// CHECK4-NOT: Suppressed {{.*}} warnings
// CHECK4-NOT: Use -header-filter=.* {{.*}}
// CHECK4-QUIET-NOT: Suppressed
// CHECK6: Suppressed 2 warnings (2 in non-user code)
// CHECK6: Use -header-filter=.* {{.*}}
int x = 123;

View File

@ -0,0 +1,26 @@
// This test ensures that the --quiet flag only suppresses the "X warnings generated"
// message while keeping all diagnostic information including caret indicators (^).
// RUN: clang-tidy -checks=-*,readability-magic-numbers,clang-diagnostic-sign-compare %s -- \
// RUN: -Wsign-compare 2>&1 | FileCheck %s --check-prefix=CHECK-NORMAL
// RUN: clang-tidy -checks=-*,readability-magic-numbers,clang-diagnostic-sign-compare -quiet %s -- \
// RUN: -Wsign-compare 2>&1 | FileCheck %s --check-prefix=CHECK-QUIET
// CHECK-NORMAL: 2 warnings generated
// CHECK-NORMAL-DAG: warning: 42 is a magic number
// CHECK-NORMAL-DAG: {{[ ]*\^}}
// CHECK-NORMAL-DAG: warning: comparison of integers of different signs
// CHECK-NORMAL-DAG: {{[ ]*~ \^ ~}}
// CHECK-QUIET-NOT: {{[0-9]+}} warning{{s?}} generated
// CHECK-QUIET-DAG: warning: 42 is a magic number
// CHECK-QUIET-DAG: {{[ ]*\^}}
// CHECK-QUIET-DAG: warning: comparison of integers of different signs
// CHECK-QUIET-DAG: {{[ ]*~ \^ ~}}
int main() {
const int CONST_VAL = 10;
int x = 42; // trigger 'readability-magic-numbers' with caret: ^
unsigned int y = CONST_VAL;
return x < y; // trigger 'clang-diagnostic-sign-compare' with caret: ^
}

View File

@ -11,9 +11,9 @@
// RUN: clang-tidy -help | FileCheck -check-prefix=CHECK-OPT-PRESENT %s
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -system-headers=true %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-SYSTEM-HEADERS %s
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -system-headers=false %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-NO-SYSTEM-HEADERS %s
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -system-headers=false %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-NO-SYSTEM-HEADERS --allow-empty %s
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -config='SystemHeaders: true' %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-SYSTEM-HEADERS %s
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -config='SystemHeaders: false' %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-NO-SYSTEM-HEADERS %s
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -config='SystemHeaders: false' %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-NO-SYSTEM-HEADERS --allow-empty %s
#include <system_header.h>
// CHECK-SYSTEM-HEADERS: system_header.h:1:13: warning: single-argument constructors must be marked explicit

View File

@ -126,6 +126,9 @@ clang-format is turned off or back on.
// clang-format on
void formatted_code_again;
In addition, the ``OneLineFormatOffRegex`` option gives you a concise way to
disable formatting for all of the lines that match the regular expression.
Configuring Style in Code
=========================
@ -6483,13 +6486,51 @@ the configuration (without a prefix: ``Auto``).
.. _SpaceInEmptyBlock:
**SpaceInEmptyBlock** (``Boolean``) :versionbadge:`clang-format 10` :ref:`¶ <SpaceInEmptyBlock>`
If ``true``, spaces will be inserted into ``{}``.
This option is **deprecated**. See ``Block`` of ``SpaceInEmptyBraces``.
.. _SpaceInEmptyBraces:
**SpaceInEmptyBraces** (``SpaceInEmptyBracesStyle``) :versionbadge:`clang-format 22` :ref:`¶ <SpaceInEmptyBraces>`
Specifies when to insert a space in empty braces.
.. note::
This option doesn't apply to initializer braces if
``Cpp11BracedListStyle`` is set to ``true``.
Possible values:
* ``SIEB_Always`` (in configuration: ``Always``)
Always insert a space in empty braces.
.. code-block:: c++
void f() { }
class Unit { };
auto a = [] { };
int x{ };
* ``SIEB_Block`` (in configuration: ``Block``)
Only insert a space in empty blocks.
.. code-block:: c++
void f() { }
class Unit { };
auto a = [] { };
int x{};
* ``SIEB_Never`` (in configuration: ``Never``)
Never insert a space in empty braces.
.. code-block:: c++
void f() {}
class Unit {};
auto a = [] {};
int x{};
.. code-block:: c++
true: false:
void f() { } vs. void f() {}
while (true) { } while (true) {}
.. _SpaceInEmptyParentheses:

View File

@ -60,6 +60,10 @@ only for the linker wrapper will be forwarded to the wrapped linker job.
--v Display the version number and exit
-- The separator for the wrapped linker arguments
The linker wrapper will generate the appropriate runtime calls to register the
generated device binary with the offloading runtime. To do this step manually we
provide the ``llvm-offload-wrapper`` utility.
Relocatable Linking
===================

File diff suppressed because it is too large Load Diff

View File

@ -137,6 +137,12 @@ Non-comprehensive list of changes in this release
- ``__builtin_elementwise_max`` and ``__builtin_elementwise_min`` functions for integer types can
now be used in constant expressions.
- Use of ``__has_feature`` to detect the ``ptrauth_qualifier`` and ``ptrauth_intrinsics``
features has been deprecated, and is restricted to the arm64e target only. The
correct method to check for these features is to test for the ``__PTRAUTH__``
macro.
New Compiler Flags
------------------
- New option ``-fno-sanitize-annotate-debug-info-traps`` added to disable emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``).
@ -193,6 +199,8 @@ Bug Fixes in This Version
targets that treat ``_Float16``/``__fp16`` as native scalar types. Previously
the warning was silently lost because the operands differed only by an implicit
cast chain. (#GH149967).
- Fixed a crash with incompatible pointer to integer conversions in designated
initializers involving string literals. (#GH154046)
Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -230,6 +238,7 @@ Bug Fixes to AST Handling
- Fix incorrect name qualifiers applied to alias CTAD. (#GH136624)
- Fixed ElaboratedTypes appearing within NestedNameSpecifier, which was not a
legal representation. This is fixed because ElaboratedTypes don't exist anymore. (#GH43179) (#GH68670) (#GH92757)
- Fix unrecognized html tag causing undesirable comment lexing (#GH152944)
- Fix comment lexing of special command names (#GH152943)
Miscellaneous Bug Fixes
@ -308,8 +317,12 @@ AST Matchers
- Ensure ``hasBitWidth`` doesn't crash on bit widths that are dependent on template
parameters.
- Add a boolean member ``IgnoreSystemHeaders`` to ``MatchFinderOptions``. This
allows it to ignore nodes in system headers when traversing the AST.
clang-format
------------
- Add ``SpaceInEmptyBraces`` option and set it to ``Always`` for WebKit style.
libclang
--------

View File

@ -66,17 +66,17 @@ supported. Uninstrumented code simply won't be accounted for in reports.
To compile code with Modified Condition/Decision Coverage (MC/DC) enabled,
pass ``-fcoverage-mcdc`` in addition to the clang options specified above.
MC/DC is an advanced form of code coverage most applicable in the embedded
MC/DC is an advanced form of code coverage most applicable to the embedded
space.
Running the instrumented program
================================
The next step is to run the instrumented program. When the program exits it
The next step is to run the instrumented program. When the program exits, it
will write a **raw profile** to the path specified by the ``LLVM_PROFILE_FILE``
environment variable. If that variable does not exist, the profile is written
to ``default.profraw`` in the current directory of the program. If
``LLVM_PROFILE_FILE`` contains a path to a non-existent directory, the missing
``LLVM_PROFILE_FILE`` specifies a path to a non-existent directory, the missing
directory structure will be created. Additionally, the following special
**pattern strings** are rewritten:
@ -97,7 +97,7 @@ directory structure will be created. Additionally, the following special
* "%b" expands out to the binary ID (build ID). It can be used with "%Nm" to
avoid binary signature collisions. To use it, the program should be compiled
with the build ID linker option (``--build-id`` for GNU ld or LLD,
``/build-id`` for lld-link on Windows). Linux, Windows and AIX are supported.
``/build-id`` for lld-link on Windows). Linux, Windows, and AIX are supported.
* "%c" expands out to nothing, but enables a mode in which profile counter
updates are continuously synced to a file. This means that if the
@ -128,7 +128,7 @@ and set bias to the offset between the original and the new counter location,
at which point every subsequent counter access will be to the new location,
which allows updating profile directly akin to the continuous mode.
The advantage of this approach is that doesn't require any special OS support.
The advantage of this approach is that it doesn't require any special OS support.
The disadvantage is the extra overhead due to additional instructions required
for each counter access (overhead both in terms of binary size and performance)
plus duplication of counters (i.e. one copy in the binary itself and another
@ -137,7 +137,7 @@ other platforms by passing the ``-runtime-counter-relocation`` option to the
backend during compilation.
For a program such as the `Lit <https://llvm.org/docs/CommandGuide/lit.html>`_
testing tool which invokes other programs, it may be necessary to set
testing tool, which invokes other programs, it may be necessary to set
``LLVM_PROFILE_FILE`` for each invocation. The pattern strings "%p" or "%Nm"
may help to avoid corruption due to concurrency. Note that "%p" is also a Lit
token and needs to be escaped as "%%p".
@ -149,7 +149,7 @@ token and needs to be escaped as "%%p".
Creating coverage reports
=========================
Raw profiles have to be **indexed** before they can be used to generate
Raw profiles must be **indexed** before they can be used to generate
coverage reports. This is done using the "merge" tool in ``llvm-profdata``
(which can combine multiple raw profiles and index them at the same time):
@ -240,13 +240,13 @@ line-oriented report, try:
TOTAL 13 0 100.00% 3 0 100.00% 13 0 100.00% 12 2 83.33%
The ``llvm-cov`` tool supports specifying a custom demangler, writing out
reports in a directory structure, and generating html reports. For the full
reports in a directory structure, and generating HTML reports. For the full
list of options, please refer to the `command guide
<https://llvm.org/docs/CommandGuide/llvm-cov.html>`_.
A few final notes:
* The ``-sparse`` flag is optional but can result in dramatically smaller
* The ``-sparse`` flag is optional but can produce dramatically smaller
indexed profiles. This option should not be used if the indexed profile will
be reused for PGO.
@ -255,7 +255,7 @@ A few final notes:
information directly into an existing raw profile on disk. The details are
out of scope.
* The ``llvm-profdata`` tool can be used to merge together multiple raw or
* The ``llvm-profdata`` tool can be used to merge multiple raw or
indexed profiles. To combine profiling data from multiple runs of a program,
try e.g:
@ -299,7 +299,7 @@ There are six statistics tracked in a coverage summary:
source code that may each evaluate to either "true" or "false". These
conditions may comprise larger boolean expressions linked by boolean logical
operators. For example, "x = (y == 2) || (z < 10)" is a boolean expression
that is comprised of two individual conditions, each of which evaluates to
comprised of two individual conditions, each of which evaluates to
either true or false, producing four total branch outcomes.
* Modified Condition/Decision Coverage (MC/DC) is the percentage of individual
@ -316,7 +316,7 @@ There are six statistics tracked in a coverage summary:
``-show-mcdc-summary`` option as long as code was also compiled using the
clang option ``-fcoverage-mcdc``.
* Boolean expressions that are only comprised of one condition (and therefore
* Boolean expressions comprised of only one condition (and therefore
have no logical operators) are not included in MC/DC analysis and are
trivially deducible using branch coverage.
@ -366,7 +366,7 @@ By default the compiler runtime uses a static initializer to determine the
profile output path and to register a writer function. To collect profiles
without using static initializers, do this manually:
* Export a ``int __llvm_profile_runtime`` symbol from each instrumented shared
* Export an ``int __llvm_profile_runtime`` symbol from each instrumented shared
library and executable. When the linker finds a definition of this symbol, it
knows to skip loading the object which contains the profiling runtime's
static initializer.
@ -380,7 +380,7 @@ without using static initializers, do this manually:
to ``__llvm_profile_write_file``.
* Forward-declare ``int __llvm_profile_write_file(void)`` and call it to write
out a profile. This function returns 0 when it succeeds, and a non-zero value
out a profile. This function returns 0 on success, and a non-zero value
otherwise. Calling this function multiple times appends profile data to an
existing on-disk raw profile.
@ -418,7 +418,7 @@ Collecting coverage reports for the llvm project
================================================
To prepare a coverage report for llvm (and any of its sub-projects), add
``-DLLVM_BUILD_INSTRUMENTED_COVERAGE=On`` to the cmake configuration. Raw
``-DLLVM_BUILD_INSTRUMENTED_COVERAGE=On`` to the CMake configuration. Raw
profiles will be written to ``$BUILD_DIR/profiles/``. To prepare an html
report, run ``llvm/utils/prepare-code-coverage-artifact.py``.
@ -429,7 +429,7 @@ To specify an alternate directory for raw profiles, use
Drawbacks and limitations
=========================
* Prior to version 2.26, the GNU binutils BFD linker is not able link programs
* Prior to version 2.26, the GNU binutils BFD linker cannot link programs
compiled with ``-fcoverage-mapping`` in its ``--gc-sections`` mode. Possible
workarounds include disabling ``--gc-sections``, upgrading to a newer version
of BFD, or using the Gold linker.

View File

@ -359,7 +359,7 @@ class CXXFinalOverriderMap
/// A set of all the primary bases for a class.
class CXXIndirectPrimaryBaseSet
: public llvm::SmallSet<const CXXRecordDecl*, 32> {};
: public llvm::SmallPtrSet<const CXXRecordDecl *, 32> {};
inline bool
inheritanceModelHasVBPtrOffsetField(MSInheritanceModel Inheritance) {

View File

@ -51,6 +51,11 @@ def Col : Tag<"col"> { let EndTagForbidden = 1; }
def Tr : Tag<"tr"> { let EndTagOptional = 1; }
def Th : Tag<"th"> { let EndTagOptional = 1; }
def Td : Tag<"td"> { let EndTagOptional = 1; }
def Summary : Tag<"summary">;
def Details : Tag<"details">;
def Mark : Tag<"mark">;
def Figure : Tag<"figure">;
def FigCaption : Tag<"figcaption">;
// Define a list of attributes that are not safe to pass through to HTML
// output if the input is untrusted.

View File

@ -3526,7 +3526,7 @@ protected:
public:
// Low-level accessor. If you just want the type defined by this node,
// check out ASTContext::getTypeDeclType or one of
// ASTContext::getTypedefType, ASTContext::getRecordType, etc. if you
// ASTContext::getTypedefType, ASTContext::getTagType, etc. if you
// already know the specific kind of node this is.
const Type *getTypeForDecl() const {
assert(!isa<TagDecl>(this));

View File

@ -1250,19 +1250,32 @@ public:
SourceLocation EndLoc);
};
// A structure to stand in for the recipe on a reduction. RecipeDecl is the
// 'main' declaration used for initializaiton, which is fixed.
struct OpenACCReductionRecipe {
VarDecl *RecipeDecl;
// TODO: OpenACC: this should eventually have the operations here too.
};
class OpenACCReductionClause final
: public OpenACCClauseWithVarList,
private llvm::TrailingObjects<OpenACCReductionClause, Expr *> {
private llvm::TrailingObjects<OpenACCReductionClause, Expr *,
OpenACCReductionRecipe> {
friend TrailingObjects;
OpenACCReductionOperator Op;
OpenACCReductionClause(SourceLocation BeginLoc, SourceLocation LParenLoc,
OpenACCReductionOperator Operator,
ArrayRef<Expr *> VarList, SourceLocation EndLoc)
ArrayRef<Expr *> VarList,
ArrayRef<OpenACCReductionRecipe> Recipes,
SourceLocation EndLoc)
: OpenACCClauseWithVarList(OpenACCClauseKind::Reduction, BeginLoc,
LParenLoc, EndLoc),
Op(Operator) {
setExprs(getTrailingObjects(VarList.size()), VarList);
assert(VarList.size() == Recipes.size());
setExprs(getTrailingObjects<Expr *>(VarList.size()), VarList);
llvm::uninitialized_copy(Recipes, getTrailingObjects<
OpenACCReductionRecipe > ());
}
public:
@ -1270,12 +1283,26 @@ public:
return C->getClauseKind() == OpenACCClauseKind::Reduction;
}
ArrayRef<OpenACCReductionRecipe> getRecipes() {
return ArrayRef<OpenACCReductionRecipe>{
getTrailingObjects<OpenACCReductionRecipe>(), getExprs().size()};
}
ArrayRef<OpenACCReductionRecipe> getRecipes() const {
return ArrayRef<OpenACCReductionRecipe>{
getTrailingObjects<OpenACCReductionRecipe>(), getExprs().size()};
}
static OpenACCReductionClause *
Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc,
OpenACCReductionOperator Operator, ArrayRef<Expr *> VarList,
SourceLocation EndLoc);
ArrayRef<OpenACCReductionRecipe> Recipes, SourceLocation EndLoc);
OpenACCReductionOperator getReductionOp() const { return Op; }
size_t numTrailingObjects(OverloadToken<Expr *>) const {
return getExprs().size();
}
};
class OpenACCLinkClause final

View File

@ -6401,6 +6401,9 @@ protected:
bool IsInjected, const Type *CanonicalType);
public:
// FIXME: Temporarily renamed from `getDecl` in order to facilitate
// rebasing, due to change in behaviour. This should be renamed back
// to `getDecl` once the change is settled.
TagDecl *getOriginalDecl() const { return decl; }
NestedNameSpecifier getQualifier() const;
@ -6466,6 +6469,9 @@ class RecordType final : public TagType {
using TagType::TagType;
public:
// FIXME: Temporarily renamed from `getDecl` in order to facilitate
// rebasing, due to change in behaviour. This should be renamed back
// to `getDecl` once the change is settled.
RecordDecl *getOriginalDecl() const {
return reinterpret_cast<RecordDecl *>(TagType::getOriginalDecl());
}
@ -6483,6 +6489,9 @@ class EnumType final : public TagType {
using TagType::TagType;
public:
// FIXME: Temporarily renamed from `getDecl` in order to facilitate
// rebasing, due to change in behaviour. This should be renamed back
// to `getDecl` once the change is settled.
EnumDecl *getOriginalDecl() const {
return reinterpret_cast<EnumDecl *>(TagType::getOriginalDecl());
}
@ -6515,6 +6524,9 @@ class InjectedClassNameType final : public TagType {
bool IsInjected, const Type *CanonicalType);
public:
// FIXME: Temporarily renamed from `getDecl` in order to facilitate
// rebasing, due to change in behaviour. This should be renamed back
// to `getDecl` once the change is settled.
CXXRecordDecl *getOriginalDecl() const {
return reinterpret_cast<CXXRecordDecl *>(TagType::getOriginalDecl());
}

View File

@ -135,10 +135,15 @@ public:
llvm::StringMap<llvm::TimeRecord> &Records;
};
MatchFinderOptions() {}
/// Enables per-check timers.
///
/// It prints a report after match.
std::optional<Profiling> CheckProfiling;
/// Avoids matching declarations in system headers.
bool IgnoreSystemHeaders{false};
};
MatchFinder(MatchFinderOptions Options = MatchFinderOptions());

View File

@ -19,14 +19,35 @@
#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/ADT/ImmutableMap.h"
#include "llvm/ADT/ImmutableSet.h"
#include "llvm/ADT/StringMap.h"
#include <memory>
namespace clang::lifetimes {
/// Enum to track the confidence level of a potential error.
enum class Confidence {
None,
Maybe, // Reported as a potential error (-Wlifetime-safety-strict)
Definite // Reported as a definite error (-Wlifetime-safety-permissive)
};
class LifetimeSafetyReporter {
public:
LifetimeSafetyReporter() = default;
virtual ~LifetimeSafetyReporter() = default;
virtual void reportUseAfterFree(const Expr *IssueExpr, const Expr *UseExpr,
SourceLocation FreeLoc,
Confidence Confidence) {}
};
/// The main entry point for the analysis.
void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC);
void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC,
LifetimeSafetyReporter *Reporter);
namespace internal {
// Forward declarations of internal types.
@ -53,6 +74,7 @@ template <typename Tag> struct ID {
IDBuilder.AddInteger(Value);
}
};
template <typename Tag>
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ID<Tag> ID) {
return OS << ID.Value;
@ -78,7 +100,8 @@ using ProgramPoint = const Fact *;
/// encapsulates the various dataflow analyses.
class LifetimeSafetyAnalysis {
public:
LifetimeSafetyAnalysis(AnalysisDeclContext &AC);
LifetimeSafetyAnalysis(AnalysisDeclContext &AC,
LifetimeSafetyReporter *Reporter);
~LifetimeSafetyAnalysis();
void run();
@ -87,7 +110,7 @@ public:
LoanSet getLoansAtPoint(OriginID OID, ProgramPoint PP) const;
/// Returns the set of loans that have expired at a specific program point.
LoanSet getExpiredLoansAtPoint(ProgramPoint PP) const;
std::vector<LoanID> getExpiredLoansAtPoint(ProgramPoint PP) const;
/// Finds the OriginID for a given declaration.
/// Returns a null optional if not found.
@ -110,6 +133,7 @@ public:
private:
AnalysisDeclContext &AC;
LifetimeSafetyReporter *Reporter;
std::unique_ptr<LifetimeFactory> Factory;
std::unique_ptr<FactManager> FactMgr;
std::unique_ptr<LoanPropagationAnalysis> LoanPropagation;
@ -118,4 +142,25 @@ private:
} // namespace internal
} // namespace clang::lifetimes
namespace llvm {
template <typename Tag>
struct DenseMapInfo<clang::lifetimes::internal::ID<Tag>> {
using ID = clang::lifetimes::internal::ID<Tag>;
static inline ID getEmptyKey() {
return {DenseMapInfo<uint32_t>::getEmptyKey()};
}
static inline ID getTombstoneKey() {
return {DenseMapInfo<uint32_t>::getTombstoneKey()};
}
static unsigned getHashValue(const ID &Val) {
return DenseMapInfo<uint32_t>::getHashValue(Val.Value);
}
static bool isEqual(const ID &LHS, const ID &RHS) { return LHS == RHS; }
};
} // namespace llvm
#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H

View File

@ -42,6 +42,18 @@ struct ContextSensitiveOptions {
unsigned Depth = 2;
};
/// A simple representation of essential elements of the logical context used in
/// environments. Designed for import/export for applications requiring
/// serialization support.
struct SimpleLogicalContext {
// Global invariant that applies for all definitions in the context.
const Formula *Invariant;
// Flow-condition tokens in the context.
llvm::DenseMap<Atom, const Formula *> TokenDefs;
// Dependencies between flow-condition definitions.
llvm::DenseMap<Atom, llvm::DenseSet<Atom>> TokenDeps;
};
/// Owns objects that encompass the state of a program and stores context that
/// is used during dataflow analysis.
class DataflowAnalysisContext {
@ -140,6 +152,15 @@ public:
/// Adds `Constraint` to the flow condition identified by `Token`.
void addFlowConditionConstraint(Atom Token, const Formula &Constraint);
/// Adds `Deps` to the dependencies of the flow condition identified by
/// `Token`. Intended for use in deserializing contexts. The formula alone
/// doesn't have enough information to indicate its deps.
void addFlowConditionDeps(Atom Token, const llvm::DenseSet<Atom> &Deps) {
// Avoid creating an entry for `Token` with an empty set.
if (!Deps.empty())
FlowConditionDeps[Token].insert(Deps.begin(), Deps.end());
}
/// Creates a new flow condition with the same constraints as the flow
/// condition identified by `Token` and returns its token.
Atom forkFlowCondition(Atom Token);
@ -207,6 +228,14 @@ public:
return {};
}
/// Export the logical-context portions of `AC`, limited to the given target
/// flow-condition tokens.
SimpleLogicalContext
exportLogicalContext(llvm::DenseSet<dataflow::Atom> TargetTokens) const;
/// Initializes this context's "logical" components with `LC`.
void initLogicalContext(SimpleLogicalContext LC);
private:
friend class Environment;
@ -228,6 +257,11 @@ private:
DataflowAnalysisContext(Solver &S, std::unique_ptr<Solver> &&OwnedSolver,
Options Opts);
/// Computes the transitive closure of dependencies of (flow-condition)
/// `Tokens`. That is, the set of flow-condition tokens reachable from
/// `Tokens` in the dependency graph.
llvm::DenseSet<Atom> collectDependencies(llvm::DenseSet<Atom> Tokens) const;
// Extends the set of modeled field declarations.
void addModeledFields(const FieldSet &Fields);

View File

@ -157,10 +157,18 @@ public:
};
/// Creates an environment that uses `DACtx` to store objects that encompass
/// the state of a program.
/// the state of a program. `FlowConditionToken` sets the flow condition
/// associated with the environment. Generally, new environments should be
/// initialized with a fresh token, by using one of the other
/// constructors. This constructor is for specialized use, including
/// deserialization and delegation from other constructors.
Environment(DataflowAnalysisContext &DACtx, Atom FlowConditionToken)
: DACtx(&DACtx), FlowConditionToken(FlowConditionToken) {}
/// Creates an environment that uses `DACtx` to store objects that encompass
/// the state of a program. Populates a fresh atom as flow condition token.
explicit Environment(DataflowAnalysisContext &DACtx)
: DACtx(&DACtx),
FlowConditionToken(DACtx.arena().makeFlowConditionToken()) {}
: Environment(DACtx, DACtx.arena().makeFlowConditionToken()) {}
/// Creates an environment that uses `DACtx` to store objects that encompass
/// the state of a program, with `S` as the statement to analyze.

View File

@ -85,21 +85,17 @@ public:
}
using AtomNames = llvm::DenseMap<Atom, std::string>;
// Produce a stable human-readable representation of this formula.
// For example: (V3 | !(V1 & V2))
// If AtomNames is provided, these override the default V0, V1... names.
/// Produces a stable human-readable representation of this formula.
/// For example: (V3 | !(V1 & V2))
/// If AtomNames is provided, these override the default V0, V1... names.
void print(llvm::raw_ostream &OS, const AtomNames * = nullptr) const;
// Allocate Formulas using Arena rather than calling this function directly.
/// Allocates Formulas using Arena rather than calling this function directly.
static const Formula &create(llvm::BumpPtrAllocator &Alloc, Kind K,
ArrayRef<const Formula *> Operands,
unsigned Value = 0);
private:
Formula() = default;
Formula(const Formula &) = delete;
Formula &operator=(const Formula &) = delete;
/// Count of operands (sub-formulas) associated with Formulas of kind `K`.
static unsigned numOperands(Kind K) {
switch (K) {
case AtomRef:
@ -116,6 +112,11 @@ private:
llvm_unreachable("Unhandled Formula::Kind enum");
}
private:
Formula() = default;
Formula(const Formula &) = delete;
Formula &operator=(const Formula &) = delete;
Kind FormulaKind;
// Some kinds of formula have scalar values, e.g. AtomRef's atom number.
unsigned Value;

View File

@ -0,0 +1,40 @@
//=== FormulaSerialization.h - Formula De/Serialization support -*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_FORMULA_SERIALIZATION_H
#define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_FORMULA_SERIALIZATION_H
#include "clang/Analysis/FlowSensitive/Arena.h"
#include "clang/Analysis/FlowSensitive/Formula.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
#include <string>
namespace clang::dataflow {
/// Prints `F` to `OS` in a compact format, optimized for easy parsing
/// (deserialization) rather than human use.
void serializeFormula(const Formula &F, llvm::raw_ostream &OS);
/// Parses `Str` to build a serialized Formula.
/// @returns error on parse failure or if parsing does not fully consume `Str`.
/// @param A used to construct the formula components.
/// @param AtomMap maps serialized Atom identifiers (unsigned ints) to Atoms.
/// This map is provided by the caller to enable consistency across
/// multiple formulas in a single file.
llvm::Expected<const Formula *>
parseFormula(llvm::StringRef Str, Arena &A,
llvm::DenseMap<unsigned, Atom> &AtomMap);
} // namespace clang::dataflow
#endif

View File

@ -580,6 +580,8 @@ TARGET_BUILTIN(__builtin_ppc_bcdsub_p, "iiV16UcV16Uc", "",
"isa-v207-instructions")
// P9 Binary-coded decimal (BCD) builtins.
TARGET_BUILTIN(__builtin_ppc_bcdcopysign, "V16UcV16UcV16Uc", "", "power9-vector")
TARGET_BUILTIN(__builtin_ppc_bcdsetsign, "V16UcV16UcUc", "t", "power9-vector")
TARGET_BUILTIN(__builtin_ppc_national2packed, "V16UcV16UcUc", "t", "power9-vector")
TARGET_BUILTIN(__builtin_ppc_packed2national, "V16UcV16Uc", "", "power9-vector")
TARGET_BUILTIN(__builtin_ppc_packed2zoned, "V16UcV16UcUc", "t", "power9-vector")

View File

@ -268,7 +268,6 @@ let Header = "emmintrin.h", Attributes = [NoThrow, RequireDeclaration] in {
}
let Features = "sse2", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
def pmuludq128 : X86Builtin<"_Vector<2, long long int>(_Vector<4, int>, _Vector<4, int>)">;
def psraw128 : X86Builtin<"_Vector<8, short>(_Vector<8, short>, _Vector<8, short>)">;
def psrad128 : X86Builtin<"_Vector<4, int>(_Vector<4, int>, _Vector<4, int>)">;
def psrlw128 : X86Builtin<"_Vector<8, short>(_Vector<8, short>, _Vector<8, short>)">;
@ -290,6 +289,10 @@ let Features = "sse2", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] i
def psrldqi128_byteshift : X86Builtin<"_Vector<2, long long int>(_Vector<2, long long int>, _Constant int)">;
}
let Features = "sse2", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<128>] in {
def pmuludq128 : X86Builtin<"_Vector<2, long long int>(_Vector<4, int>, _Vector<4, int>)">;
}
let Features = "sse3", Attributes = [NoThrow] in {
def monitor : X86Builtin<"void(void const *, unsigned int, unsigned int)">;
def mwait : X86Builtin<"void(unsigned int, unsigned int)">;
@ -312,7 +315,6 @@ let Features = "sse4.1", Attributes = [NoThrow, Const, RequiredVectorWidth<128>]
def blendvpd : X86Builtin<"_Vector<2, double>(_Vector<2, double>, _Vector<2, double>, _Vector<2, double>)">;
def blendvps : X86Builtin<"_Vector<4, float>(_Vector<4, float>, _Vector<4, float>, _Vector<4, float>)">;
def packusdw128 : X86Builtin<"_Vector<8, short>(_Vector<4, int>, _Vector<4, int>)">;
def pmuldq128 : X86Builtin<"_Vector<2, long long int>(_Vector<4, int>, _Vector<4, int>)">;
def roundps : X86Builtin<"_Vector<4, float>(_Vector<4, float>, _Constant int)">;
def roundss : X86Builtin<"_Vector<4, float>(_Vector<4, float>, _Vector<4, float>, _Constant int)">;
def roundsd : X86Builtin<"_Vector<2, double>(_Vector<2, double>, _Vector<2, double>, _Constant int)">;
@ -329,6 +331,10 @@ let Features = "sse4.1", Attributes = [NoThrow, Const, RequiredVectorWidth<128>]
def vec_set_v4si : X86Builtin<"_Vector<4, int>(_Vector<4, int>, int, _Constant int)">;
}
let Features = "sse4.1", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<128>] in {
def pmuldq128 : X86Builtin<"_Vector<2, long long int>(_Vector<4, int>, _Vector<4, int>)">;
}
let Features = "sse4.2", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
def pcmpistrm128 : X86Builtin<"_Vector<16, char>(_Vector<16, char>, _Vector<16, char>, _Constant char)">;
def pcmpistri128 : X86Builtin<"int(_Vector<16, char>, _Vector<16, char>, _Constant char)">;
@ -580,9 +586,7 @@ let Features = "avx2", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] i
def pmaddubsw256 : X86Builtin<"_Vector<16, short>(_Vector<32, char>, _Vector<32, char>)">;
def pmaddwd256 : X86Builtin<"_Vector<8, int>(_Vector<16, short>, _Vector<16, short>)">;
def pmovmskb256 : X86Builtin<"int(_Vector<32, char>)">;
def pmuldq256 : X86Builtin<"_Vector<4, long long int>(_Vector<8, int>, _Vector<8, int>)">;
def pmulhrsw256 : X86Builtin<"_Vector<16, short>(_Vector<16, short>, _Vector<16, short>)">;
def pmuludq256 : X86Builtin<"_Vector<4, long long int>(_Vector<8, int>, _Vector<8, int>)">;
def psadbw256 : X86Builtin<"_Vector<4, long long int>(_Vector<32, char>, _Vector<32, char>)">;
def pshufb256 : X86Builtin<"_Vector<32, char>(_Vector<32, char>, _Vector<32, char>)">;
def pshufd256 : X86Builtin<"_Vector<8, int>(_Vector<8, int>, _Constant int)">;
@ -620,6 +624,11 @@ let Features = "avx2", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] i
def insert128i256 : X86Builtin<"_Vector<4, long long int>(_Vector<4, long long int>, _Vector<2, long long int>, _Constant int)">;
}
let Features = "avx2", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<256>] in {
def pmuldq256 : X86Builtin<"_Vector<4, long long int>(_Vector<8, int>, _Vector<8, int>)">;
def pmuludq256 : X86Builtin<"_Vector<4, long long int>(_Vector<8, int>, _Vector<8, int>)">;
}
let Features = "avx2", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<256>] in {
def pmulhuw256 : X86Builtin<"_Vector<16, unsigned short>(_Vector<16, unsigned short>, _Vector<16, unsigned short>)">;
def pmulhw256 : X86Builtin<"_Vector<16, short>(_Vector<16, short>, _Vector<16, short>)">;
@ -1078,6 +1087,9 @@ let Features = "avx512f,evex512", Attributes = [NoThrow, Const, RequiredVectorWi
def cvtpd2ps512_mask : X86Builtin<"_Vector<8, float>(_Vector<8, double>, _Vector<8, float>, unsigned char, _Constant int)">;
def vcvtps2ph512_mask : X86Builtin<"_Vector<16, short>(_Vector<16, float>, _Constant int, _Vector<16, short>, unsigned short)">;
def vcvtph2ps512_mask : X86Builtin<"_Vector<16, float>(_Vector<16, short>, _Vector<16, float>, unsigned short, _Constant int)">;
}
let Features = "avx512f,evex512", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<512>] in {
def pmuldq512 : X86Builtin<"_Vector<8, long long int>(_Vector<16, int>, _Vector<16, int>)">;
def pmuludq512 : X86Builtin<"_Vector<8, long long int>(_Vector<16, int>, _Vector<16, int>)">;
}
@ -4118,99 +4130,99 @@ let Features = "avx512fp16,evex512", Attributes = [NoThrow, Const, RequiredVecto
def vfcmulcph512_mask : X86Builtin<"_Vector<16, float>(_Vector<16, float>, _Vector<16, float>, _Vector<16, float>, unsigned short, _Constant int)">;
}
let Features = "avx512bw,avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
let Features = "avx512bw,avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<128>] in {
def selectb_128 : X86Builtin<"_Vector<16, char>(unsigned short, _Vector<16, char>, _Vector<16, char>)">;
}
let Features = "avx512bw,avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] in {
let Features = "avx512bw,avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<256>] in {
def selectb_256 : X86Builtin<"_Vector<32, char>(unsigned int, _Vector<32, char>, _Vector<32, char>)">;
}
let Features = "avx512bw,evex512", Attributes = [NoThrow, Const, RequiredVectorWidth<512>] in {
let Features = "avx512bw,evex512", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<512>] in {
def selectb_512 : X86Builtin<"_Vector<64, char>(unsigned long long int, _Vector<64, char>, _Vector<64, char>)">;
}
let Features = "avx512bw,avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
let Features = "avx512bw,avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<128>] in {
def selectw_128 : X86Builtin<"_Vector<8, short>(unsigned char, _Vector<8, short>, _Vector<8, short>)">;
}
let Features = "avx512bw,avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] in {
let Features = "avx512bw,avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<256>] in {
def selectw_256 : X86Builtin<"_Vector<16, short>(unsigned short, _Vector<16, short>, _Vector<16, short>)">;
}
let Features = "avx512bw,evex512", Attributes = [NoThrow, Const, RequiredVectorWidth<512>] in {
let Features = "avx512bw,evex512", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<512>] in {
def selectw_512 : X86Builtin<"_Vector<32, short>(unsigned int, _Vector<32, short>, _Vector<32, short>)">;
}
let Features = "avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
let Features = "avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<128>] in {
def selectd_128 : X86Builtin<"_Vector<4, int>(unsigned char, _Vector<4, int>, _Vector<4, int>)">;
}
let Features = "avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] in {
let Features = "avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<256>] in {
def selectd_256 : X86Builtin<"_Vector<8, int>(unsigned char, _Vector<8, int>, _Vector<8, int>)">;
}
let Features = "avx512f,evex512", Attributes = [NoThrow, Const, RequiredVectorWidth<512>] in {
let Features = "avx512f,evex512", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<512>] in {
def selectd_512 : X86Builtin<"_Vector<16, int>(unsigned short, _Vector<16, int>, _Vector<16, int>)">;
}
let Features = "avx512fp16,avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
let Features = "avx512fp16,avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<128>] in {
def selectph_128 : X86Builtin<"_Vector<8, _Float16>(unsigned char, _Vector<8, _Float16>, _Vector<8, _Float16>)">;
}
let Features = "avx512fp16,avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] in {
let Features = "avx512fp16,avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<256>] in {
def selectph_256 : X86Builtin<"_Vector<16, _Float16>(unsigned short, _Vector<16, _Float16>, _Vector<16, _Float16>)">;
}
let Features = "avx512fp16,evex512", Attributes = [NoThrow, Const, RequiredVectorWidth<512>] in {
let Features = "avx512fp16,evex512", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<512>] in {
def selectph_512 : X86Builtin<"_Vector<32, _Float16>(unsigned int, _Vector<32, _Float16>, _Vector<32, _Float16>)">;
}
let Features = "avx512bf16,avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
let Features = "avx512bf16,avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<128>] in {
def selectpbf_128 : X86Builtin<"_Vector<8, __bf16>(unsigned char, _Vector<8, __bf16>, _Vector<8, __bf16>)">;
}
let Features = "avx512bf16,avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] in {
let Features = "avx512bf16,avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<256>] in {
def selectpbf_256 : X86Builtin<"_Vector<16, __bf16>(unsigned short, _Vector<16, __bf16>, _Vector<16, __bf16>)">;
}
let Features = "avx512bf16,evex512", Attributes = [NoThrow, Const, RequiredVectorWidth<512>] in {
let Features = "avx512bf16,evex512", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<512>] in {
def selectpbf_512 : X86Builtin<"_Vector<32, __bf16>(unsigned int, _Vector<32, __bf16>, _Vector<32, __bf16>)">;
}
let Features = "avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
let Features = "avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<128>] in {
def selectq_128 : X86Builtin<"_Vector<2, long long int>(unsigned char, _Vector<2, long long int>, _Vector<2, long long int>)">;
}
let Features = "avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] in {
let Features = "avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<256>] in {
def selectq_256 : X86Builtin<"_Vector<4, long long int>(unsigned char, _Vector<4, long long int>, _Vector<4, long long int>)">;
}
let Features = "avx512f,evex512", Attributes = [NoThrow, Const, RequiredVectorWidth<512>] in {
let Features = "avx512f,evex512", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<512>] in {
def selectq_512 : X86Builtin<"_Vector<8, long long int>(unsigned char, _Vector<8, long long int>, _Vector<8, long long int>)">;
}
let Features = "avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
let Features = "avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<128>] in {
def selectps_128 : X86Builtin<"_Vector<4, float>(unsigned char, _Vector<4, float>, _Vector<4, float>)">;
}
let Features = "avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] in {
let Features = "avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<256>] in {
def selectps_256 : X86Builtin<"_Vector<8, float>(unsigned char, _Vector<8, float>, _Vector<8, float>)">;
}
let Features = "avx512f,evex512", Attributes = [NoThrow, Const, RequiredVectorWidth<512>] in {
let Features = "avx512f,evex512", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<512>] in {
def selectps_512 : X86Builtin<"_Vector<16, float>(unsigned short, _Vector<16, float>, _Vector<16, float>)">;
}
let Features = "avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
let Features = "avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<128>] in {
def selectpd_128 : X86Builtin<"_Vector<2, double>(unsigned char, _Vector<2, double>, _Vector<2, double>)">;
}
let Features = "avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] in {
let Features = "avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<256>] in {
def selectpd_256 : X86Builtin<"_Vector<4, double>(unsigned char, _Vector<4, double>, _Vector<4, double>)">;
}
let Features = "avx512f,evex512", Attributes = [NoThrow, Const, RequiredVectorWidth<512>] in {
let Features = "avx512f,evex512", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<512>] in {
def selectpd_512 : X86Builtin<"_Vector<8, double>(unsigned char, _Vector<8, double>, _Vector<8, double>)">;
}

View File

@ -581,6 +581,13 @@ def err_drv_reduced_module_output_overrided : Warning<
"please consider use '-fmodule-output=' to specify the output file for reduced BMI explicitly">,
InGroup<DiagGroup<"reduced-bmi-output-overrided">>;
def remark_found_cxx20_module_usage : Remark<
"found C++20 module usage in file '%0'">,
InGroup<ModulesDriver>;
def remark_performing_driver_managed_module_build : Remark<
"performing driver managed module build">,
InGroup<ModulesDriver>;
def warn_drv_delayed_template_parsing_after_cxx20 : Warning<
"-fdelayed-template-parsing is deprecated after C++20">,
InGroup<DiagGroup<"delayed-template-parsing-in-cxx20">>;

View File

@ -533,7 +533,14 @@ def Dangling : DiagGroup<"dangling", [DanglingAssignment,
DanglingGsl,
ReturnStackAddress]>;
def LifetimeSafety : DiagGroup<"experimental-lifetime-safety">;
def LifetimeSafetyPermissive : DiagGroup<"experimental-lifetime-safety-permissive">;
def LifetimeSafetyStrict : DiagGroup<"experimental-lifetime-safety-strict">;
def LifetimeSafety : DiagGroup<"experimental-lifetime-safety",
[LifetimeSafetyPermissive, LifetimeSafetyStrict]> {
code Documentation = [{
Experimental warnings to detect use-after-free and related temporal safety bugs based on lifetime safety analysis.
}];
}
def DistributedObjectModifiers : DiagGroup<"distributed-object-modifiers">;
def DllexportExplicitInstantiationDecl : DiagGroup<"dllexport-explicit-instantiation-decl">;
@ -628,6 +635,7 @@ def ModuleConflict : DiagGroup<"module-conflict">;
def ModuleFileExtension : DiagGroup<"module-file-extension">;
def ModuleIncludeDirectiveTranslation : DiagGroup<"module-include-translation">;
def ModuleMap : DiagGroup<"module-map">;
def ModulesDriver : DiagGroup<"modules-driver">;
def RoundTripCC1Args : DiagGroup<"round-trip-cc1-args">;
def NewlineEOF : DiagGroup<"newline-eof">;
def Nullability : DiagGroup<"nullability">;

View File

@ -10671,9 +10671,15 @@ def warn_dangling_reference_captured_by_unknown : Warning<
"object whose reference is captured will be destroyed at the end of "
"the full-expression">, InGroup<DanglingCapture>;
def warn_experimental_lifetime_safety_dummy_warning : Warning<
"todo: remove this warning after we have atleast one warning based on the lifetime analysis">,
InGroup<LifetimeSafety>, DefaultIgnore;
// Diagnostics based on the Lifetime safety analysis.
def warn_lifetime_safety_loan_expires_permissive : Warning<
"object whose reference is captured does not live long enough">,
InGroup<LifetimeSafetyPermissive>, DefaultIgnore;
def warn_lifetime_safety_loan_expires_strict : Warning<
"object whose reference is captured may not live long enough">,
InGroup<LifetimeSafetyStrict>, DefaultIgnore;
def note_lifetime_safety_used_here : Note<"later used here">;
def note_lifetime_safety_destroyed_here : Note<"destroyed here">;
// For non-floating point, expressions of the form x == x or x != x
// should result in a warning, since these always evaluate to a constant.

View File

@ -147,8 +147,10 @@ FEATURE(type_sanitizer, LangOpts.Sanitize.has(SanitizerKind::Type))
FEATURE(thread_sanitizer, LangOpts.Sanitize.has(SanitizerKind::Thread))
FEATURE(dataflow_sanitizer, LangOpts.Sanitize.has(SanitizerKind::DataFlow))
FEATURE(scudo, LangOpts.Sanitize.hasOneOf(SanitizerKind::Scudo))
FEATURE(ptrauth_intrinsics, LangOpts.PointerAuthIntrinsics)
EXTENSION(ptrauth_qualifier, LangOpts.PointerAuthIntrinsics)
FEATURE(ptrauth_intrinsics, LangOpts.PointerAuthIntrinsics &&
PP.getTargetInfo().getTriple().isOSDarwin())
FEATURE(ptrauth_qualifier, LangOpts.PointerAuthIntrinsics &&
PP.getTargetInfo().getTriple().isOSDarwin())
FEATURE(ptrauth_calls, LangOpts.PointerAuthCalls)
FEATURE(ptrauth_returns, LangOpts.PointerAuthReturns)
FEATURE(ptrauth_vtable_pointer_address_discrimination, LangOpts.PointerAuthVTPtrAddressDiscrimination)

View File

@ -233,8 +233,9 @@ protected:
bool TLSSupported;
bool VLASupported;
bool NoAsmVariants; // True if {|} are normal characters.
bool HasLegalHalfType; // True if the backend supports operations on the half
// LLVM IR type.
bool HasFastHalfType; // True if the backend has native half float support,
// and performing calculations in float instead does
// not have a performance advantage.
bool HalfArgsAndReturns; // OpenCL 6.1.1.1, NEON (IEEE 754-2008 half) type.
bool HasFloat128;
bool HasFloat16;
@ -700,8 +701,9 @@ public:
return 128;
}
/// Determine whether _Float16 is supported on this target.
virtual bool hasLegalHalfType() const { return HasLegalHalfType; }
/// Determine whether the target has fast native support for operations
/// on half types.
virtual bool hasFastHalfType() const { return HasFastHalfType; }
/// Whether half args and returns are supported.
virtual bool allowHalfArgsAndReturns() const { return HalfArgsAndReturns; }

View File

@ -157,6 +157,20 @@ public:
return create<cir::ComplexImagOp>(loc, operandTy.getElementType(), operand);
}
cir::LoadOp createLoad(mlir::Location loc, mlir::Value ptr,
uint64_t alignment = 0) {
mlir::IntegerAttr alignmentAttr = getAlignmentAttr(alignment);
assert(!cir::MissingFeatures::opLoadStoreVolatile());
assert(!cir::MissingFeatures::opLoadStoreMemOrder());
return cir::LoadOp::create(*this, loc, ptr, /*isDeref=*/false,
alignmentAttr);
}
mlir::Value createAlignedLoad(mlir::Location loc, mlir::Value ptr,
uint64_t alignment) {
return createLoad(loc, ptr, alignment);
}
mlir::Value createNot(mlir::Value value) {
return create<cir::UnaryOp>(value.getLoc(), value.getType(),
cir::UnaryOpKind::Not, value);
@ -504,8 +518,7 @@ public:
static OpBuilder::InsertPoint getBestAllocaInsertPoint(mlir::Block *block) {
auto last =
std::find_if(block->rbegin(), block->rend(), [](mlir::Operation &op) {
// TODO: Add LabelOp missing feature here
return mlir::isa<cir::AllocaOp>(&op);
return mlir::isa<cir::AllocaOp, cir::LabelOp>(&op);
});
if (last != block->rend())

View File

@ -341,6 +341,44 @@ def CIR_ConstVectorAttr : CIR_Attr<"ConstVector", "const_vector", [
let genVerifyDecl = 1;
}
//===----------------------------------------------------------------------===//
// ConstRecordAttr
//===----------------------------------------------------------------------===//
def CIR_ConstRecordAttr : CIR_Attr<"ConstRecord", "const_record", [
TypedAttrInterface
]> {
let summary = "Represents a constant record";
let description = [{
Effectively supports "struct-like" constants. It's must be built from
an `mlir::ArrayAttr` instance where each element is a typed attribute
(`mlir::TypedAttribute`).
Example:
```
cir.global external @rgb2 = #cir.const_record<{0 : i8,
5 : i64, #cir.null : !cir.ptr<i8>
}> : !cir.record<"", i8, i64, !cir.ptr<i8>>
```
}];
let parameters = (ins AttributeSelfTypeParameter<"">:$type,
"mlir::ArrayAttr":$members);
let builders = [
AttrBuilderWithInferredContext<(ins "cir::RecordType":$type,
"mlir::ArrayAttr":$members), [{
return $_get(type.getContext(), type, members);
}]>
];
let assemblyFormat = [{
`<` custom<RecordMembers>($members) `>`
}];
let genVerifyDecl = 1;
}
//===----------------------------------------------------------------------===//
// ConstPtrAttr
//===----------------------------------------------------------------------===//

View File

@ -1060,6 +1060,62 @@ def CIR_BrOp : CIR_Op<"br",[
}];
}
//===----------------------------------------------------------------------===//
// GotoOp
//===----------------------------------------------------------------------===//
def CIR_GotoOp : CIR_Op<"goto", [Terminator]> {
let description = [{
Transfers control to the specified `label`. This requires a corresponding
`cir.label` to exist and is used by to represent source level `goto`s
that jump across region boundaries. Alternatively, `cir.br` is used to
construct goto's that don't violate such boundaries.
`cir.goto` is completely symbolic (i.e. it "jumps" on a label that isn't
yet materialized) and should be taken into account by passes and analysis
when deciding if it's safe to make some assumptions about a given region
or basic block.
Example:
```C++
int test(int x) {
if (x)
goto label;
{
x = 10;
label:
return x;
}
}
```
```mlir
cir.scope { // REGION #1
%2 = cir.load %0 : !cir.ptr<!s32i>, !s32i
%3 = cir.cast(int_to_bool, %2 : !s32i), !cir.bool
cir.if %3 {
cir.goto "label"
}
}
cir.scope { // REGION #2
%2 = cir.const #cir.int<10> : !s32i
cir.store %2, %0 : !s32i, !cir.ptr<!s32i>
cir.br ^bb1
^bb1: // pred: ^bb0
cir.label "label"
%3 = cir.load %0 : !cir.ptr<!s32i>, !s32i
cir.store %3, %1 : !s32i, !cir.ptr<!s32i>
%4 = cir.load %1 : !cir.ptr<!s32i>, !s32i
cir.return %4 : !s32i
}
cir.unreachable
```
}];
let arguments = (ins StrAttr:$label);
let assemblyFormat = [{ $label attr-dict }];
}
//===----------------------------------------------------------------------===//
// LabelOp
//===----------------------------------------------------------------------===//
@ -1782,6 +1838,54 @@ def CIR_VTableGetVPtrOp : CIR_Op<"vtable.get_vptr", [Pure]> {
}];
}
//===----------------------------------------------------------------------===//
// VTableGetVirtualFnAddrOp
//===----------------------------------------------------------------------===//
def CIR_VTableGetVirtualFnAddrOp : CIR_Op<"vtable.get_virtual_fn_addr", [
Pure
]> {
let summary = "Get a the address of a virtual function pointer";
let description = [{
The `vtable.get_virtual_fn_addr` operation retrieves the address of a
virtual function pointer from an object's vtable (__vptr).
This is an abstraction to perform the basic pointer arithmetic to get
the address of the virtual function pointer, which can then be loaded and
called.
The `vptr` operand must be a `!cir.ptr<!cir.vptr>` value, which would
have been returned by a previous call to `cir.vatble.get_vptr`. The
`index` operand is an index of the virtual function in the vtable.
The return type is a pointer-to-pointer to the function type.
Example:
```mlir
%2 = cir.load %0 : !cir.ptr<!cir.ptr<!rec_C>>, !cir.ptr<!rec_C>
%3 = cir.vtable.get_vptr %2 : !cir.ptr<!rec_C> -> !cir.ptr<!cir.vptr>
%4 = cir.load %3 : !cir.ptr<!cir.vptr>, !cir.vptr
%5 = cir.vtable.get_virtual_fn_addr %4[2] : !cir.vptr
-> !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!rec_C>) -> !s32i>>>
%6 = cir.load align(8) %5 : !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!rec_C>)
-> !s32i>>>,
!cir.ptr<!cir.func<(!cir.ptr<!rec_C>) -> !s32i>>
%7 = cir.call %6(%2) : (!cir.ptr<!cir.func<(!cir.ptr<!rec_C>) -> !s32i>>,
!cir.ptr<!rec_C>) -> !s32i
```
}];
let arguments = (ins
Arg<CIR_VPtrType, "vptr", [MemRead]>:$vptr,
I64Attr:$index);
let results = (outs CIR_PointerType:$result);
let assemblyFormat = [{
$vptr `[` $index `]` attr-dict
`:` qualified(type($vptr)) `->` qualified(type($result))
}];
}
//===----------------------------------------------------------------------===//
// SetBitfieldOp
//===----------------------------------------------------------------------===//
@ -3510,4 +3614,86 @@ def CIR_FAbsOp : CIR_UnaryFPToFPBuiltinOp<"fabs", "FAbsOp"> {
}];
}
//===----------------------------------------------------------------------===//
// Variadic Operations
//===----------------------------------------------------------------------===//
def CIR_VAStartOp : CIR_Op<"va_start"> {
let summary = "Starts a variable argument list";
let description = [{
The cir.va_start operation models the C/C++ va_start macro by
initializing a variable argument list at the given va_list storage
location.
The first operand must be a pointer to the target's `va_list`
representation. This operation has no results and produces its effect by
mutating the storage referenced by the pointer operand. The second operand
must be an integer value that contains the expected number of arguments in
that list.
Each `cir.va_start` must be paired with a corresponding `cir.va_end`
on the same logical `va_list` object along all control-flow paths. After
`cir.va_end`, the `va_list` must not be accessed unless reinitialized
with another `cir.va_start`.
Lowering maps this to the LLVM intrinsic `llvm.va_start`, passing the
appropriately decayed pointer to the underlying `va_list` storage.
Example:
```mlir
// %args : !cir.ptr<!cir.array<!rec___va_list_tag x 1>>
%p = cir.cast(array_to_ptrdecay, %args
: !cir.ptr<!cir.array<!rec___va_list_tag x 1>>),
!cir.ptr<!rec___va_list_tag>
%count = cir.load %0 : !cir.ptr<!s32i>, !s32i
cir.va_start %p %count : !cir.ptr<!rec___va_list_tag>, !s32i
```
}];
let arguments = (ins
CIR_PointerType:$arg_list,
CIR_AnyFundamentalIntType:$count
);
let assemblyFormat = [{
$arg_list $count attr-dict `:` type(operands)
}];
}
def CIR_VAEndOp : CIR_Op<"va_end"> {
let summary = "Ends a variable argument list";
let description = [{
The `cir.va_end` operation models the C/C++ va_end macro by finalizing
and cleaning up a variable argument list previously initialized with
`cir.va_start`.
The operand must be a pointer to the target's `va_list` representation.
This operation has no results and produces its effect by mutating the
storage referenced by the pointer operand.
`cir.va_end` must only be called after a matching `cir.va_start` on the
same `va_list` along all control-flow paths. After `cir.va_end`, the
`va_list` is invalid and must not be accessed unless reinitialized.
Lowering typically maps this to the LLVM intrinsic `llvm.va_end`,
passing the appropriately decayed pointer to the underlying `va_list`
storage.
Example:
```mlir
// %args : !cir.ptr<!cir.array<!rec___va_list_tag x 1>>
%p = cir.cast(array_to_ptrdecay, %args
: !cir.ptr<!cir.array<!rec___va_list_tag x 1>>),
!cir.ptr<!rec___va_list_tag>
cir.va_end %p : !cir.ptr<!rec___va_list_tag>
```
}];
let arguments = (ins CIR_PointerType:$arg_list);
let assemblyFormat = [{
$arg_list attr-dict `:` type(operands)
}];
}
#endif // CLANG_CIR_DIALECT_IR_CIROPS_TD

View File

@ -95,7 +95,6 @@ struct MissingFeatures {
static bool opCallArgEvaluationOrder() { return false; }
static bool opCallCallConv() { return false; }
static bool opCallMustTail() { return false; }
static bool opCallVirtual() { return false; }
static bool opCallInAlloca() { return false; }
static bool opCallAttrs() { return false; }
static bool opCallSurroundingTry() { return false; }
@ -176,7 +175,12 @@ struct MissingFeatures {
static bool aggValueSlotVolatile() { return false; }
static bool alignCXXRecordDecl() { return false; }
static bool armComputeVolatileBitfields() { return false; }
static bool asmGoto() { return false; }
static bool asmInputOperands() { return false; }
static bool asmLabelAttr() { return false; }
static bool asmMemoryEffects() { return false; }
static bool asmOutputOperands() { return false; }
static bool asmUnwindClobber() { return false; }
static bool assignMemcpyizer() { return false; }
static bool astVarDeclInterface() { return false; }
static bool attributeBuiltin() { return false; }
@ -204,6 +208,7 @@ struct MissingFeatures {
static bool dataLayoutTypeAllocSize() { return false; }
static bool dataLayoutTypeStoreSize() { return false; }
static bool deferredCXXGlobalInit() { return false; }
static bool devirtualizeMemberFunction() { return false; }
static bool ehCleanupFlags() { return false; }
static bool ehCleanupScope() { return false; }
static bool ehCleanupScopeRequiresEHCleanup() { return false; }
@ -215,6 +220,7 @@ struct MissingFeatures {
static bool emitLValueAlignmentAssumption() { return false; }
static bool emitNullabilityCheck() { return false; }
static bool emitTypeCheck() { return false; }
static bool emitTypeMetadataCodeForVCall() { return false; }
static bool fastMathFlags() { return false; }
static bool fpConstraints() { return false; }
static bool generateDebugInfo() { return false; }

View File

@ -512,6 +512,9 @@ public:
/// BuildActions - Construct the list of actions to perform for the
/// given arguments, which are only done for a single architecture.
/// If the compilation is an explicit module build, delegates to
/// BuildDriverManagedModuleBuildActions. Otherwise, BuildDefaultActions is
/// used.
///
/// \param C - The compilation that is being built.
/// \param Args - The input arguments.
@ -796,6 +799,35 @@ private:
/// compilation based on which -f(no-)?lto(=.*)? option occurs last.
void setLTOMode(const llvm::opt::ArgList &Args);
/// BuildDefaultActions - Constructs the list of actions to perform
/// for the provided arguments, which are only done for a single architecture.
///
/// \param C - The compilation that is being built.
/// \param Args - The input arguments.
/// \param Actions - The list to store the resulting actions onto.
void BuildDefaultActions(Compilation &C, llvm::opt::DerivedArgList &Args,
const InputList &Inputs, ActionList &Actions) const;
/// BuildDriverManagedModuleBuildActions - Performs a dependency
/// scan and constructs the list of actions to perform for dependency order
/// and the provided arguments. This is only done for a single a architecture.
///
/// \param C - The compilation that is being built.
/// \param Args - The input arguments.
/// \param Actions - The list to store the resulting actions onto.
void BuildDriverManagedModuleBuildActions(Compilation &C,
llvm::opt::DerivedArgList &Args,
const InputList &Inputs,
ActionList &Actions) const;
/// Scans the leading lines of the C++ source inputs to detect C++20 module
/// usage.
///
/// \returns True if module usage is detected, false otherwise, or an error on
/// read failure.
llvm::ErrorOr<bool>
ScanInputsForCXX20ModulesUsage(const InputList &Inputs) const;
/// Retrieves a ToolChain for a particular \p Target triple.
///
/// Will cache ToolChains for the life of the driver object, and create them

View File

@ -3296,6 +3296,13 @@ defm modules_reduced_bmi : BoolOption<"f", "modules-reduced-bmi",
PosFlag<SetTrue, [], [ClangOption, CC1Option],
"Generate the reduced BMI">>;
def fmodules_driver : Flag<["-"], "fmodules-driver">,
Group<f_Group>, Visibility<[ClangOption]>,
HelpText<"Enable support for driver managed module builds (experimental)">;
def fno_modules_driver : Flag<["-"], "fno-modules-driver">,
Group<f_Group>, Visibility<[ClangOption]>,
HelpText<"Disable support for driver managed module builds (experimental)">;
def experimental_modules_reduced_bmi : Flag<["-"], "fexperimental-modules-reduced-bmi">,
Group<f_Group>, Visibility<[ClangOption, CC1Option]>, Alias<fmodules_reduced_bmi>;

View File

@ -4813,14 +4813,45 @@ struct FormatStyle {
/// \version 7
bool SpaceBeforeRangeBasedForLoopColon;
/// If ``true``, spaces will be inserted into ``{}``.
/// \code
/// true: false:
/// void f() { } vs. void f() {}
/// while (true) { } while (true) {}
/// \endcode
/// This option is **deprecated**. See ``Block`` of ``SpaceInEmptyBraces``.
/// \version 10
bool SpaceInEmptyBlock;
// bool SpaceInEmptyBlock;
/// Style of when to insert a space in empty braces.
enum SpaceInEmptyBracesStyle : int8_t {
/// Always insert a space in empty braces.
/// \code
/// void f() { }
/// class Unit { };
/// auto a = [] { };
/// int x{ };
/// \endcode
SIEB_Always,
/// Only insert a space in empty blocks.
/// \code
/// void f() { }
/// class Unit { };
/// auto a = [] { };
/// int x{};
/// \endcode
SIEB_Block,
/// Never insert a space in empty braces.
/// \code
/// void f() {}
/// class Unit {};
/// auto a = [] {};
/// int x{};
/// \endcode
SIEB_Never
};
/// Specifies when to insert a space in empty braces.
/// \note
/// This option doesn't apply to initializer braces if
/// ``Cpp11BracedListStyle`` is set to ``true``.
/// \endnote
/// \version 22
SpaceInEmptyBracesStyle SpaceInEmptyBraces;
/// If ``true``, spaces may be inserted into ``()``.
/// This option is **deprecated**. See ``InEmptyParentheses`` of
@ -5494,7 +5525,7 @@ struct FormatStyle {
SpaceBeforeRangeBasedForLoopColon ==
R.SpaceBeforeRangeBasedForLoopColon &&
SpaceBeforeSquareBrackets == R.SpaceBeforeSquareBrackets &&
SpaceInEmptyBlock == R.SpaceInEmptyBlock &&
SpaceInEmptyBraces == R.SpaceInEmptyBraces &&
SpacesBeforeTrailingComments == R.SpacesBeforeTrailingComments &&
SpacesInAngles == R.SpacesInAngles &&
SpacesInContainerLiterals == R.SpacesInContainerLiterals &&

View File

@ -135,6 +135,13 @@ void printDependencyDirectivesAsSource(
ArrayRef<dependency_directives_scan::Directive> Directives,
llvm::raw_ostream &OS);
/// Scan an input source buffer for C++20 named module usage.
///
/// \param Source The input source buffer.
///
/// \returns true if any C++20 named modules related directive was found.
bool scanInputForCXX20ModulesUsage(StringRef Source);
/// Functor that returns the dependency directives for a given file.
class DependencyDirectivesGetter {
public:

View File

@ -143,9 +143,6 @@ class Lexer : public PreprocessorLexer {
/// True if this is the first time we're lexing the input file.
bool IsFirstTimeLexingFile;
/// True if current lexing token is the first pp-token.
bool IsFirstPPToken;
// NewLinePtr - A pointer to new line character '\n' being lexed. For '\r\n',
// it also points to '\n.'
const char *NewLinePtr;

View File

@ -0,0 +1,310 @@
//===--- NoTrivialPPDirectiveTracer.h ---------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines the NoTrivialPPDirectiveTracer interface.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_LEX_NO_TRIVIAL_PPDIRECTIVE_TRACER_H
#define LLVM_CLANG_LEX_NO_TRIVIAL_PPDIRECTIVE_TRACER_H
#include "clang/Lex/PPCallbacks.h"
namespace clang {
class Preprocessor;
/// Consider the following code:
///
/// # 1 __FILE__ 1 3
/// export module a;
///
/// According to the wording in
/// [P1857R3](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1857r3.html):
///
/// A module directive may only appear as the first preprocessing tokens in a
/// file (excluding the global module fragment.)
///
/// and the wording in
/// [[cpp.pre]](https://eel.is/c++draft/cpp.pre#nt:module-file):
/// module-file:
/// pp-global-module-fragment[opt] pp-module group[opt]
/// pp-private-module-fragment[opt]
///
/// `#` is the first pp-token in the translation unit, and it was rejected by
/// clang, but they really should be exempted from this rule. The goal is to not
/// allow any preprocessor conditionals or most state changes, but these don't
/// fit that.
///
/// State change would mean most semantically observable preprocessor state,
/// particularly anything that is order dependent. Global flags like being a
/// system header/module shouldn't matter.
///
/// We should exempt a brunch of directives, even though it violates the current
/// standard wording.
///
/// This class used to trace 'no-trivial' pp-directives in main file, which may
/// change the preprocessing state.
///
/// FIXME: Once the wording of the standard is revised, we need to follow the
/// wording of the standard. Currently this is just a workaround
class NoTrivialPPDirectiveTracer : public PPCallbacks {
Preprocessor &PP;
/// Whether preprocessing main file. We only focus on the main file.
bool InMainFile = true;
/// Whether one or more conditional, include or other 'no-trivial'
/// pp-directives has seen before.
bool SeenNoTrivialPPDirective = false;
void setSeenNoTrivialPPDirective();
public:
NoTrivialPPDirectiveTracer(Preprocessor &P) : PP(P) {}
bool hasSeenNoTrivialPPDirective() const;
/// Callback invoked whenever the \p Lexer moves to a different file for
/// lexing. Unlike \p FileChanged line number directives and other related
/// pragmas do not trigger callbacks to \p LexedFileChanged.
///
/// \param FID The \p FileID that the \p Lexer moved to.
///
/// \param Reason Whether the \p Lexer entered a new file or exited one.
///
/// \param FileType The \p CharacteristicKind of the file the \p Lexer moved
/// to.
///
/// \param PrevFID The \p FileID the \p Lexer was using before the change.
///
/// \param Loc The location where the \p Lexer entered a new file from or the
/// location that the \p Lexer moved into after exiting a file.
void LexedFileChanged(FileID FID, LexedFileChangeReason Reason,
SrcMgr::CharacteristicKind FileType, FileID PrevFID,
SourceLocation Loc) override;
/// Callback invoked whenever an embed directive has been processed,
/// regardless of whether the embed will actually find a file.
///
/// \param HashLoc The location of the '#' that starts the embed directive.
///
/// \param FileName The name of the file being included, as written in the
/// source code.
///
/// \param IsAngled Whether the file name was enclosed in angle brackets;
/// otherwise, it was enclosed in quotes.
///
/// \param File The actual file that may be included by this embed directive.
///
/// \param Params The parameters used by the directive.
void EmbedDirective(SourceLocation HashLoc, StringRef FileName, bool IsAngled,
OptionalFileEntryRef File,
const LexEmbedParametersResult &Params) override {
setSeenNoTrivialPPDirective();
}
/// Callback invoked whenever an inclusion directive of
/// any kind (\c \#include, \c \#import, etc.) has been processed, regardless
/// of whether the inclusion will actually result in an inclusion.
///
/// \param HashLoc The location of the '#' that starts the inclusion
/// directive.
///
/// \param IncludeTok The token that indicates the kind of inclusion
/// directive, e.g., 'include' or 'import'.
///
/// \param FileName The name of the file being included, as written in the
/// source code.
///
/// \param IsAngled Whether the file name was enclosed in angle brackets;
/// otherwise, it was enclosed in quotes.
///
/// \param FilenameRange The character range of the quotes or angle brackets
/// for the written file name.
///
/// \param File The actual file that may be included by this inclusion
/// directive.
///
/// \param SearchPath Contains the search path which was used to find the file
/// in the file system. If the file was found via an absolute include path,
/// SearchPath will be empty. For framework includes, the SearchPath and
/// RelativePath will be split up. For example, if an include of "Some/Some.h"
/// is found via the framework path
/// "path/to/Frameworks/Some.framework/Headers/Some.h", SearchPath will be
/// "path/to/Frameworks/Some.framework/Headers" and RelativePath will be
/// "Some.h".
///
/// \param RelativePath The path relative to SearchPath, at which the include
/// file was found. This is equal to FileName except for framework includes.
///
/// \param SuggestedModule The module suggested for this header, if any.
///
/// \param ModuleImported Whether this include was translated into import of
/// \p SuggestedModule.
///
/// \param FileType The characteristic kind, indicates whether a file or
/// directory holds normal user code, system code, or system code which is
/// implicitly 'extern "C"' in C++ mode.
///
void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
StringRef FileName, bool IsAngled,
CharSourceRange FilenameRange,
OptionalFileEntryRef File, StringRef SearchPath,
StringRef RelativePath, const Module *SuggestedModule,
bool ModuleImported,
SrcMgr::CharacteristicKind FileType) override {
setSeenNoTrivialPPDirective();
}
/// Callback invoked whenever there was an explicit module-import
/// syntax.
///
/// \param ImportLoc The location of import directive token.
///
/// \param Path The identifiers (and their locations) of the module
/// "path", e.g., "std.vector" would be split into "std" and "vector".
///
/// \param Imported The imported module; can be null if importing failed.
///
void moduleImport(SourceLocation ImportLoc, ModuleIdPath Path,
const Module *Imported) override {
setSeenNoTrivialPPDirective();
}
/// Callback invoked when the end of the main file is reached.
///
/// No subsequent callbacks will be made.
void EndOfMainFile() override { setSeenNoTrivialPPDirective(); }
/// Callback invoked when start reading any pragma directive.
void PragmaDirective(SourceLocation Loc,
PragmaIntroducerKind Introducer) override {}
/// Called by Preprocessor::HandleMacroExpandedIdentifier when a
/// macro invocation is found.
void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
SourceRange Range, const MacroArgs *Args) override;
/// Hook called whenever a macro definition is seen.
void MacroDefined(const Token &MacroNameTok,
const MacroDirective *MD) override {
setSeenNoTrivialPPDirective();
}
/// Hook called whenever a macro \#undef is seen.
/// \param MacroNameTok The active Token
/// \param MD A MacroDefinition for the named macro.
/// \param Undef New MacroDirective if the macro was defined, null otherwise.
///
/// MD is released immediately following this callback.
void MacroUndefined(const Token &MacroNameTok, const MacroDefinition &MD,
const MacroDirective *Undef) override {
setSeenNoTrivialPPDirective();
}
/// Hook called whenever the 'defined' operator is seen.
/// \param MD The MacroDirective if the name was a macro, null otherwise.
void Defined(const Token &MacroNameTok, const MacroDefinition &MD,
SourceRange Range) override {
setSeenNoTrivialPPDirective();
}
/// Hook called whenever an \#if is seen.
/// \param Loc the source location of the directive.
/// \param ConditionRange The SourceRange of the expression being tested.
/// \param ConditionValue The evaluated value of the condition.
///
// FIXME: better to pass in a list (or tree!) of Tokens.
void If(SourceLocation Loc, SourceRange ConditionRange,
ConditionValueKind ConditionValue) override {
setSeenNoTrivialPPDirective();
}
/// Hook called whenever an \#elif is seen.
/// \param Loc the source location of the directive.
/// \param ConditionRange The SourceRange of the expression being tested.
/// \param ConditionValue The evaluated value of the condition.
/// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive.
// FIXME: better to pass in a list (or tree!) of Tokens.
void Elif(SourceLocation Loc, SourceRange ConditionRange,
ConditionValueKind ConditionValue, SourceLocation IfLoc) override {
setSeenNoTrivialPPDirective();
}
/// Hook called whenever an \#ifdef is seen.
/// \param Loc the source location of the directive.
/// \param MacroNameTok Information on the token being tested.
/// \param MD The MacroDefinition if the name was a macro, null otherwise.
void Ifdef(SourceLocation Loc, const Token &MacroNameTok,
const MacroDefinition &MD) override {
setSeenNoTrivialPPDirective();
}
/// Hook called whenever an \#elifdef branch is taken.
/// \param Loc the source location of the directive.
/// \param MacroNameTok Information on the token being tested.
/// \param MD The MacroDefinition if the name was a macro, null otherwise.
void Elifdef(SourceLocation Loc, const Token &MacroNameTok,
const MacroDefinition &MD) override {
setSeenNoTrivialPPDirective();
}
/// Hook called whenever an \#elifdef is skipped.
/// \param Loc the source location of the directive.
/// \param ConditionRange The SourceRange of the expression being tested.
/// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive.
// FIXME: better to pass in a list (or tree!) of Tokens.
void Elifdef(SourceLocation Loc, SourceRange ConditionRange,
SourceLocation IfLoc) override {
setSeenNoTrivialPPDirective();
}
/// Hook called whenever an \#ifndef is seen.
/// \param Loc the source location of the directive.
/// \param MacroNameTok Information on the token being tested.
/// \param MD The MacroDefiniton if the name was a macro, null otherwise.
void Ifndef(SourceLocation Loc, const Token &MacroNameTok,
const MacroDefinition &MD) override {
setSeenNoTrivialPPDirective();
}
/// Hook called whenever an \#elifndef branch is taken.
/// \param Loc the source location of the directive.
/// \param MacroNameTok Information on the token being tested.
/// \param MD The MacroDefinition if the name was a macro, null otherwise.
void Elifndef(SourceLocation Loc, const Token &MacroNameTok,
const MacroDefinition &MD) override {
setSeenNoTrivialPPDirective();
}
/// Hook called whenever an \#elifndef is skipped.
/// \param Loc the source location of the directive.
/// \param ConditionRange The SourceRange of the expression being tested.
/// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive.
// FIXME: better to pass in a list (or tree!) of Tokens.
void Elifndef(SourceLocation Loc, SourceRange ConditionRange,
SourceLocation IfLoc) override {
setSeenNoTrivialPPDirective();
}
/// Hook called whenever an \#else is seen.
/// \param Loc the source location of the directive.
/// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive.
void Else(SourceLocation Loc, SourceLocation IfLoc) override {
setSeenNoTrivialPPDirective();
}
/// Hook called whenever an \#endif is seen.
/// \param Loc the source location of the directive.
/// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive.
void Endif(SourceLocation Loc, SourceLocation IfLoc) override {
setSeenNoTrivialPPDirective();
}
};
} // namespace clang
#endif // LLVM_CLANG_LEX_NO_TRIVIAL_PPDIRECTIVE_TRACER_H

View File

@ -82,6 +82,7 @@ class PreprocessorLexer;
class PreprocessorOptions;
class ScratchBuffer;
class TargetInfo;
class NoTrivialPPDirectiveTracer;
namespace Builtin {
class Context;
@ -353,6 +354,11 @@ private:
/// First pp-token source location in current translation unit.
SourceLocation FirstPPTokenLoc;
/// A preprocessor directive tracer to trace whether the preprocessing
/// state changed. These changes would mean most semantically observable
/// preprocessor state, particularly anything that is order dependent.
NoTrivialPPDirectiveTracer *DirTracer = nullptr;
/// A position within a C++20 import-seq.
class StdCXXImportSeq {
public:
@ -609,6 +615,8 @@ private:
return State == NamedModuleImplementation && !getName().contains(':');
}
bool isNotAModuleDecl() const { return State == NotAModuleDecl; }
StringRef getName() const {
assert(isNamedModule() && "Can't get name from a non named module");
return Name;
@ -3091,6 +3099,10 @@ public:
bool setDeserializedSafeBufferOptOutMap(
const SmallVectorImpl<SourceLocation> &SrcLocSeqs);
/// Whether we've seen pp-directives which may have changed the preprocessing
/// state.
bool hasSeenNoTrivialPPDirective() const;
private:
/// Helper functions to forward lexing to the actual lexer. They all share the
/// same signature.

View File

@ -86,12 +86,12 @@ public:
// macro stringizing or charizing operator.
CommaAfterElided = 0x200, // The comma following this token was elided (MS).
IsEditorPlaceholder = 0x400, // This identifier is a placeholder.
IsReinjected = 0x800, // A phase 4 token that was produced before and
// re-added, e.g. via EnterTokenStream. Annotation
// tokens are *not* reinjected.
FirstPPToken = 0x1000, // This token is the first pp token in the
// translation unit.
IsReinjected = 0x800, // A phase 4 token that was produced before and
// re-added, e.g. via EnterTokenStream. Annotation
// tokens are *not* reinjected.
HasSeenNoTrivialPPDirective =
0x1000, // Whether we've seen any 'no-trivial' pp-directives before
// current position.
};
tok::TokenKind getKind() const { return Kind; }
@ -321,8 +321,9 @@ public:
/// lexer uses identifier tokens to represent placeholders.
bool isEditorPlaceholder() const { return getFlag(IsEditorPlaceholder); }
/// Returns true if this token is the first pp-token.
bool isFirstPPToken() const { return getFlag(FirstPPToken); }
bool hasSeenNoTrivialPPDirective() const {
return getFlag(HasSeenNoTrivialPPDirective);
}
};
/// Information about the conditional stack (\#if directives)

View File

@ -933,7 +933,7 @@ public:
/// to local variables that are usable as constant expressions and
/// do not involve an odr-use (they may still need to be captured
/// if the enclosing full-expression is instantiation dependent).
llvm::SmallSet<Expr *, 8> NonODRUsedCapturingExprs;
llvm::SmallPtrSet<Expr *, 8> NonODRUsedCapturingExprs;
/// A map of explicit capture indices to their introducer source ranges.
llvm::DenseMap<unsigned, SourceRange> ExplicitCaptureRanges;

View File

@ -9837,7 +9837,7 @@ public:
SourceLocation ModuleLoc, ModuleDeclKind MDK,
ModuleIdPath Path, ModuleIdPath Partition,
ModuleImportState &ImportState,
bool IntroducerIsFirstPPToken);
bool SeenNoTrivialPPDirective);
/// The parser has processed a global-module-fragment declaration that begins
/// the definition of the global module fragment of the current module unit.

View File

@ -229,10 +229,17 @@ private:
void diagnoseAvailabilityViolations(TranslationUnitDecl *TU);
bool initGlobalResourceDecl(VarDecl *VD);
uint32_t getNextImplicitBindingOrderID() {
return ImplicitBindingNextOrderID++;
}
bool initGlobalResourceDecl(VarDecl *VD);
bool initGlobalResourceArrayDecl(VarDecl *VD);
void createResourceRecordCtorArgs(const Type *ResourceTy, StringRef VarName,
HLSLResourceBindingAttr *RBA,
HLSLVkBindingAttr *VkBinding,
uint32_t ArrayIndex,
llvm::SmallVectorImpl<Expr *> &Args);
};
} // namespace clang

View File

@ -947,12 +947,12 @@ public:
ArrayRef<Expr *> IntExprs, SourceLocation EndLoc);
// Does the checking for a 'reduction ' clause that needs to be done in
// dependent and not dependent cases.
OpenACCClause *
CheckReductionClause(ArrayRef<const OpenACCClause *> ExistingClauses,
OpenACCDirectiveKind DirectiveKind,
SourceLocation BeginLoc, SourceLocation LParenLoc,
OpenACCReductionOperator ReductionOp,
ArrayRef<Expr *> Vars, SourceLocation EndLoc);
OpenACCClause *CheckReductionClause(
ArrayRef<const OpenACCClause *> ExistingClauses,
OpenACCDirectiveKind DirectiveKind, SourceLocation BeginLoc,
SourceLocation LParenLoc, OpenACCReductionOperator ReductionOp,
ArrayRef<Expr *> Vars, ArrayRef<OpenACCReductionRecipe> Recipes,
SourceLocation EndLoc);
ExprResult BuildOpenACCAsteriskSizeExpr(SourceLocation AsteriskLoc);
ExprResult ActOnOpenACCAsteriskSizeExpr(SourceLocation AsteriskLoc);

View File

@ -320,7 +320,7 @@ protected:
/// A set of location contexts that correspoind to call sites which should be
/// considered "interesting".
llvm::SmallSet<const LocationContext *, 2> InterestingLocationContexts;
llvm::SmallPtrSet<const LocationContext *, 2> InterestingLocationContexts;
/// A set of custom visitors which generate "event" diagnostics at
/// interesting points in the path.
@ -348,7 +348,7 @@ protected:
llvm::SmallSet<InvalidationRecord, 4> Invalidations;
/// Conditions we're already tracking.
llvm::SmallSet<const ExplodedNode *, 4> TrackedConditions;
llvm::SmallPtrSet<const ExplodedNode *, 4> TrackedConditions;
/// Reports with different uniqueing locations are considered to be different
/// for the purposes of deduplication.

View File

@ -60,16 +60,18 @@ template <class Emitter> class OptionScope final {
public:
/// Root constructor, compiling or discarding primitives.
OptionScope(Compiler<Emitter> *Ctx, bool NewDiscardResult,
bool NewInitializing)
bool NewInitializing, bool NewToLValue)
: Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult),
OldInitializing(Ctx->Initializing) {
OldInitializing(Ctx->Initializing), OldToLValue(NewToLValue) {
Ctx->DiscardResult = NewDiscardResult;
Ctx->Initializing = NewInitializing;
Ctx->ToLValue = NewToLValue;
}
~OptionScope() {
Ctx->DiscardResult = OldDiscardResult;
Ctx->Initializing = OldInitializing;
Ctx->ToLValue = OldToLValue;
}
private:
@ -78,6 +80,7 @@ private:
/// Old discard flag to restore.
bool OldDiscardResult;
bool OldInitializing;
bool OldToLValue;
};
template <class Emitter>
@ -222,6 +225,9 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
switch (CE->getCastKind()) {
case CK_LValueToRValue: {
if (ToLValue && CE->getType()->isPointerType())
return this->delegate(SubExpr);
if (SubExpr->getType().isVolatileQualified())
return this->emitInvalidCast(CastKind::Volatile, /*Fatal=*/true, CE);
@ -250,7 +256,7 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
// Prepare storage for the result.
if (!Initializing && !SubExprT) {
std::optional<unsigned> LocalIndex = allocateLocal(SubExpr);
UnsignedOrNone LocalIndex = allocateLocal(SubExpr);
if (!LocalIndex)
return false;
if (!this->emitGetPtrLocal(*LocalIndex, CE))
@ -603,7 +609,7 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
// We're creating a complex value here, so we need to
// allocate storage for it.
if (!Initializing) {
std::optional<unsigned> LocalIndex = allocateTemporary(CE);
UnsignedOrNone LocalIndex = allocateTemporary(CE);
if (!LocalIndex)
return false;
if (!this->emitGetPtrLocal(*LocalIndex, CE))
@ -627,7 +633,7 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
assert(CE->getType()->isAnyComplexType());
assert(SubExpr->getType()->isAnyComplexType());
if (!Initializing) {
std::optional<unsigned> LocalIndex = allocateLocal(CE);
UnsignedOrNone LocalIndex = allocateLocal(CE);
if (!LocalIndex)
return false;
if (!this->emitGetPtrLocal(*LocalIndex, CE))
@ -672,7 +678,7 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
assert(CE->getType()->isVectorType());
if (!Initializing) {
std::optional<unsigned> LocalIndex = allocateLocal(CE);
UnsignedOrNone LocalIndex = allocateLocal(CE);
if (!LocalIndex)
return false;
if (!this->emitGetPtrLocal(*LocalIndex, CE))
@ -716,7 +722,7 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
assert(CE->getType()->isVectorType());
if (!Initializing) {
std::optional<unsigned> LocalIndex = allocateTemporary(CE);
UnsignedOrNone LocalIndex = allocateTemporary(CE);
if (!LocalIndex)
return false;
if (!this->emitGetPtrLocal(*LocalIndex, CE))
@ -804,7 +810,7 @@ bool Compiler<Emitter>::VisitImaginaryLiteral(const ImaginaryLiteral *E) {
return true;
if (!Initializing) {
std::optional<unsigned> LocalIndex = allocateTemporary(E);
UnsignedOrNone LocalIndex = allocateTemporary(E);
if (!LocalIndex)
return false;
if (!this->emitGetPtrLocal(*LocalIndex, E))
@ -905,7 +911,7 @@ bool Compiler<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
// We need a temporary variable holding our return value.
if (!Initializing) {
std::optional<unsigned> ResultIndex = this->allocateLocal(BO);
UnsignedOrNone ResultIndex = this->allocateLocal(BO);
if (!this->emitGetPtrLocal(*ResultIndex, BO))
return false;
}
@ -1145,7 +1151,7 @@ template <class Emitter>
bool Compiler<Emitter>::VisitComplexBinOp(const BinaryOperator *E) {
// Prepare storage for result.
if (!Initializing) {
std::optional<unsigned> LocalIndex = allocateTemporary(E);
UnsignedOrNone LocalIndex = allocateTemporary(E);
if (!LocalIndex)
return false;
if (!this->emitGetPtrLocal(*LocalIndex, E))
@ -1204,7 +1210,7 @@ bool Compiler<Emitter>::VisitComplexBinOp(const BinaryOperator *E) {
if (!LHSIsComplex) {
// This is using the RHS type for the fake-complex LHS.
std::optional<unsigned> LocalIndex = allocateTemporary(RHS);
UnsignedOrNone LocalIndex = allocateTemporary(RHS);
if (!LocalIndex)
return false;
LHSOffset = *LocalIndex;
@ -1379,7 +1385,7 @@ bool Compiler<Emitter>::VisitVectorBinOp(const BinaryOperator *E) {
// Prepare storage for result.
if (!Initializing && !E->isCompoundAssignmentOp()) {
std::optional<unsigned> LocalIndex = allocateTemporary(E);
UnsignedOrNone LocalIndex = allocateTemporary(E);
if (!LocalIndex)
return false;
if (!this->emitGetPtrLocal(*LocalIndex, E))
@ -1933,8 +1939,17 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
PrimType TargetT = classifyPrim(Init->getType());
auto Eval = [&](const IntegerLiteral *IL, unsigned ElemIndex) {
if (!this->emitConst(IL->getValue(), Init))
return false;
if (TargetT == PT_Float) {
if (!this->emitConst(IL->getValue(), classifyPrim(IL), Init))
return false;
const auto *Sem = &Ctx.getFloatSemantics(CAT->getElementType());
if (!this->emitCastIntegralFloating(classifyPrim(IL), Sem,
getFPOptions(E), E))
return false;
} else {
if (!this->emitConst(IL->getValue(), TargetT, Init))
return false;
}
return this->emitInitElem(TargetT, ElemIndex, IL);
};
if (!EmbedS->doForEachDataElement(Eval, ElementIndex))
@ -2084,7 +2099,7 @@ bool Compiler<Emitter>::visitCallArgs(ArrayRef<const Expr *> Args,
ExplicitMemberFn);
}
std::optional<unsigned> LocalIndex =
UnsignedOrNone LocalIndex =
allocateLocal(std::move(Source), Arg->getType(),
/*ExtendingDecl=*/nullptr, ScopeKind::Call);
if (!LocalIndex)
@ -2927,7 +2942,7 @@ bool Compiler<Emitter>::VisitMaterializeTemporaryExpr(
return false;
const Expr *Inner = E->getSubExpr()->skipRValueSubobjectAdjustments();
if (std::optional<unsigned> LocalIndex =
if (UnsignedOrNone LocalIndex =
allocateLocal(E, Inner->getType(), E->getExtendingDecl())) {
InitLinkScope<Emitter> ILS(this, InitLink::Temp(*LocalIndex));
if (!this->emitGetPtrLocal(*LocalIndex, E))
@ -2974,20 +2989,25 @@ bool Compiler<Emitter>::VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) {
if (T && !E->isLValue())
return this->delegate(Init);
if (std::optional<unsigned> GlobalIndex = P.createGlobal(E)) {
if (!this->emitGetPtrGlobal(*GlobalIndex, E))
std::optional<unsigned> GlobalIndex = P.createGlobal(E);
if (!GlobalIndex)
return false;
if (!this->emitGetPtrGlobal(*GlobalIndex, E))
return false;
// Since this is a global variable, we might've already seen,
// don't do it again.
if (P.isGlobalInitialized(*GlobalIndex))
return true;
if (T) {
if (!this->visit(Init))
return false;
if (T) {
if (!this->visit(Init))
return false;
return this->emitInitGlobal(*T, *GlobalIndex, E);
}
return this->visitInitializer(Init) && this->emitFinishInit(E);
return this->emitInitGlobal(*T, *GlobalIndex, E);
}
return false;
return this->visitInitializer(Init) && this->emitFinishInit(E);
}
// Otherwise, use a local variable.
@ -2999,7 +3019,7 @@ bool Compiler<Emitter>::VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) {
unsigned LocalIndex;
if (T)
LocalIndex = this->allocateLocalPrimitive(Init, *T, /*IsConst=*/false);
else if (std::optional<unsigned> MaybeIndex = this->allocateLocal(Init))
else if (UnsignedOrNone MaybeIndex = this->allocateLocal(Init))
LocalIndex = *MaybeIndex;
else
return false;
@ -3170,20 +3190,13 @@ bool Compiler<Emitter>::VisitCXXConstructExpr(const CXXConstructExpr *E) {
if (T->isRecordType()) {
const CXXConstructorDecl *Ctor = E->getConstructor();
// Trivial copy/move constructor. Avoid copy.
if (Ctor->isDefaulted() && Ctor->isCopyOrMoveConstructor() &&
Ctor->isTrivial() &&
E->getArg(0)->isTemporaryObject(Ctx.getASTContext(),
T->getAsCXXRecordDecl()))
return this->visitInitializer(E->getArg(0));
// If we're discarding a construct expression, we still need
// to allocate a variable and call the constructor and destructor.
if (DiscardResult) {
if (Ctor->isTrivial())
return true;
assert(!Initializing);
std::optional<unsigned> LocalIndex = allocateLocal(E);
UnsignedOrNone LocalIndex = allocateLocal(E);
if (!LocalIndex)
return false;
@ -3192,6 +3205,13 @@ bool Compiler<Emitter>::VisitCXXConstructExpr(const CXXConstructExpr *E) {
return false;
}
// Trivial copy/move constructor. Avoid copy.
if (Ctor->isDefaulted() && Ctor->isCopyOrMoveConstructor() &&
Ctor->isTrivial() &&
E->getArg(0)->isTemporaryObject(Ctx.getASTContext(),
T->getAsCXXRecordDecl()))
return this->visitInitializer(E->getArg(0));
// Zero initialization.
if (E->requiresZeroInitialization()) {
const Record *R = getRecord(E->getType());
@ -3386,7 +3406,7 @@ bool Compiler<Emitter>::VisitCXXScalarValueInitExpr(
if (const auto *CT = Ty->getAs<ComplexType>()) {
if (!Initializing) {
std::optional<unsigned> LocalIndex = allocateLocal(E);
UnsignedOrNone LocalIndex = allocateLocal(E);
if (!LocalIndex)
return false;
if (!this->emitGetPtrLocal(*LocalIndex, E))
@ -3409,7 +3429,7 @@ bool Compiler<Emitter>::VisitCXXScalarValueInitExpr(
if (const auto *VT = Ty->getAs<VectorType>()) {
// FIXME: Code duplication with the _Complex case above.
if (!Initializing) {
std::optional<unsigned> LocalIndex = allocateLocal(E);
UnsignedOrNone LocalIndex = allocateLocal(E);
if (!LocalIndex)
return false;
if (!this->emitGetPtrLocal(*LocalIndex, E))
@ -4035,8 +4055,7 @@ bool Compiler<Emitter>::VisitExtVectorElementExpr(
// Now the vector variable for the return value.
if (!Initializing) {
std::optional<unsigned> ResultIndex;
ResultIndex = allocateLocal(E);
UnsignedOrNone ResultIndex = allocateLocal(E);
if (!ResultIndex)
return false;
if (!this->emitGetPtrLocal(*ResultIndex, E))
@ -4097,8 +4116,7 @@ bool Compiler<Emitter>::VisitCXXStdInitializerListExpr(
PrimType SecondFieldT = classifyPrim(R->getField(1u)->Decl->getType());
if (isIntegralType(SecondFieldT)) {
if (!this->emitConst(static_cast<APSInt>(ArrayType->getSize()),
SecondFieldT, E))
if (!this->emitConst(ArrayType->getSize(), SecondFieldT, E))
return false;
return this->emitInitField(SecondFieldT, R->getField(1u)->Offset, E);
}
@ -4108,7 +4126,7 @@ bool Compiler<Emitter>::VisitCXXStdInitializerListExpr(
return false;
if (!this->emitExpandPtr(E))
return false;
if (!this->emitConst(static_cast<APSInt>(ArrayType->getSize()), PT_Uint64, E))
if (!this->emitConst(ArrayType->getSize(), PT_Uint64, E))
return false;
if (!this->emitArrayElemPtrPop(PT_Uint64, E))
return false;
@ -4140,13 +4158,13 @@ bool Compiler<Emitter>::VisitStmtExpr(const StmtExpr *E) {
template <class Emitter> bool Compiler<Emitter>::discard(const Expr *E) {
OptionScope<Emitter> Scope(this, /*NewDiscardResult=*/true,
/*NewInitializing=*/false);
/*NewInitializing=*/false, /*ToLValue=*/false);
return this->Visit(E);
}
template <class Emitter> bool Compiler<Emitter>::delegate(const Expr *E) {
// We're basically doing:
// OptionScope<Emitter> Scope(this, DicardResult, Initializing);
// OptionScope<Emitter> Scope(this, DicardResult, Initializing, ToLValue);
// but that's unnecessary of course.
return this->Visit(E);
}
@ -4161,7 +4179,7 @@ template <class Emitter> bool Compiler<Emitter>::visit(const Expr *E) {
// Create local variable to hold the return value.
if (!E->isGLValue() && !E->getType()->isAnyComplexType() &&
!canClassify(E->getType())) {
std::optional<unsigned> LocalIndex = allocateLocal(E);
UnsignedOrNone LocalIndex = allocateLocal(E);
if (!LocalIndex)
return false;
@ -4174,7 +4192,7 @@ template <class Emitter> bool Compiler<Emitter>::visit(const Expr *E) {
// Otherwise,we have a primitive return value, produce the value directly
// and push it on the stack.
OptionScope<Emitter> Scope(this, /*NewDiscardResult=*/false,
/*NewInitializing=*/false);
/*NewInitializing=*/false, /*ToLValue=*/ToLValue);
return this->Visit(E);
}
@ -4183,7 +4201,13 @@ bool Compiler<Emitter>::visitInitializer(const Expr *E) {
assert(!canClassify(E->getType()));
OptionScope<Emitter> Scope(this, /*NewDiscardResult=*/false,
/*NewInitializing=*/true);
/*NewInitializing=*/true, /*ToLValue=*/false);
return this->Visit(E);
}
template <class Emitter> bool Compiler<Emitter>::visitAsLValue(const Expr *E) {
OptionScope<Emitter> Scope(this, /*NewDiscardResult=*/false,
/*NewInitializing=*/false, /*ToLValue=*/true);
return this->Visit(E);
}
@ -4480,12 +4504,18 @@ bool Compiler<Emitter>::emitConst(T Value, const Expr *E) {
template <class Emitter>
bool Compiler<Emitter>::emitConst(const APSInt &Value, PrimType Ty,
const Expr *E) {
return this->emitConst(static_cast<const APInt &>(Value), Ty, E);
}
template <class Emitter>
bool Compiler<Emitter>::emitConst(const APInt &Value, PrimType Ty,
const Expr *E) {
if (Ty == PT_IntAPS)
return this->emitConstIntAPS(Value, E);
if (Ty == PT_IntAP)
return this->emitConstIntAP(Value, E);
if (Value.isSigned())
if (isSignedType(Ty))
return this->emitConst(Value.getSExtValue(), Ty, E);
return this->emitConst(Value.getZExtValue(), Ty, E);
}
@ -4516,10 +4546,10 @@ unsigned Compiler<Emitter>::allocateLocalPrimitive(
}
template <class Emitter>
std::optional<unsigned>
Compiler<Emitter>::allocateLocal(DeclTy &&Src, QualType Ty,
const ValueDecl *ExtendingDecl, ScopeKind SC,
bool IsConstexprUnknown) {
UnsignedOrNone Compiler<Emitter>::allocateLocal(DeclTy &&Src, QualType Ty,
const ValueDecl *ExtendingDecl,
ScopeKind SC,
bool IsConstexprUnknown) {
const ValueDecl *Key = nullptr;
const Expr *Init = nullptr;
bool IsTemporary = false;
@ -4553,7 +4583,7 @@ Compiler<Emitter>::allocateLocal(DeclTy &&Src, QualType Ty,
}
template <class Emitter>
std::optional<unsigned> Compiler<Emitter>::allocateTemporary(const Expr *E) {
UnsignedOrNone Compiler<Emitter>::allocateTemporary(const Expr *E) {
QualType Ty = E->getType();
assert(!Ty->isRecordType());
@ -4632,7 +4662,7 @@ bool Compiler<Emitter>::visitExpr(const Expr *E, bool DestroyToplevelScope) {
// Expressions with a composite return type.
// For us, that means everything we don't
// have a PrimType for.
if (std::optional<unsigned> LocalOffset = this->allocateLocal(E)) {
if (UnsignedOrNone LocalOffset = this->allocateLocal(E)) {
InitLinkScope<Emitter> ILS(this, InitLink::Temp(*LocalOffset));
if (!this->emitGetPtrLocal(*LocalOffset, E))
return false;
@ -4829,7 +4859,7 @@ VarCreationState Compiler<Emitter>::visitVarDecl(const VarDecl *VD,
return this->emitSetLocal(*VarT, Offset, VD);
}
} else {
if (std::optional<unsigned> Offset = this->allocateLocal(
if (UnsignedOrNone Offset = this->allocateLocal(
VD, VD->getType(), nullptr, ScopeKind::Block, IsConstexprUnknown)) {
if (!Init)
return true;
@ -4944,7 +4974,6 @@ bool Compiler<Emitter>::visitAPValueInitializer(const APValue &Val,
template <class Emitter>
bool Compiler<Emitter>::VisitBuiltinCallExpr(const CallExpr *E,
unsigned BuiltinID) {
if (BuiltinID == Builtin::BI__builtin_constant_p) {
// Void argument is always invalid and harder to handle later.
if (E->getArg(0)->getType()->isVoidType()) {
@ -4982,18 +5011,38 @@ bool Compiler<Emitter>::VisitBuiltinCallExpr(const CallExpr *E,
// Non-primitive return type. Prepare storage.
if (!Initializing && !ReturnT && !ReturnType->isVoidType()) {
std::optional<unsigned> LocalIndex = allocateLocal(E);
UnsignedOrNone LocalIndex = allocateLocal(E);
if (!LocalIndex)
return false;
if (!this->emitGetPtrLocal(*LocalIndex, E))
return false;
}
if (!Context::isUnevaluatedBuiltin(BuiltinID)) {
// Put arguments on the stack.
for (const auto *Arg : E->arguments()) {
if (!this->visit(Arg))
// Prepare function arguments including special cases.
switch (BuiltinID) {
case Builtin::BI__builtin_object_size:
case Builtin::BI__builtin_dynamic_object_size: {
assert(E->getNumArgs() == 2);
const Expr *Arg0 = E->getArg(0);
if (Arg0->isGLValue()) {
if (!this->visit(Arg0))
return false;
} else {
if (!this->visitAsLValue(Arg0))
return false;
}
if (!this->visit(E->getArg(1)))
return false;
} break;
default:
if (!Context::isUnevaluatedBuiltin(BuiltinID)) {
// Put arguments on the stack.
for (const auto *Arg : E->arguments()) {
if (!this->visit(Arg))
return false;
}
}
}
@ -5058,7 +5107,7 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
// If we need to discard the return value but the function returns its
// value via an RVO pointer, we need to create one such pointer just
// for this call.
if (std::optional<unsigned> LocalIndex = allocateLocal(E)) {
if (UnsignedOrNone LocalIndex = allocateLocal(E)) {
if (!this->emitGetPtrLocal(*LocalIndex, E))
return false;
}
@ -5066,7 +5115,7 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
// We need the result. Prepare a pointer to return or
// dup the current one.
if (!Initializing) {
if (std::optional<unsigned> LocalIndex = allocateLocal(E)) {
if (UnsignedOrNone LocalIndex = allocateLocal(E)) {
if (!this->emitGetPtrLocal(*LocalIndex, E))
return false;
}
@ -5102,7 +5151,7 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
}
bool Devirtualized = false;
std::optional<unsigned> CalleeOffset;
UnsignedOrNone CalleeOffset = std::nullopt;
// Add the (optional, implicit) This pointer.
if (const auto *MC = dyn_cast<CXXMemberCallExpr>(E)) {
if (!FuncDecl && classifyPrim(E->getCallee()) == PT_MemberPtr) {
@ -5146,7 +5195,8 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
if (!this->emitCheckPseudoDtor(E))
return false;
const Expr *Base = PD->getBase();
if (!Base->isGLValue())
// E.g. `using T = int; 0.~T();`.
if (OptPrimType BaseT = classify(Base); !BaseT || BaseT != PT_Ptr)
return this->discard(Base);
if (!this->visit(Base))
return false;
@ -6509,7 +6559,7 @@ bool Compiler<Emitter>::VisitComplexUnaryOperator(const UnaryOperator *E) {
OptPrimType ResT = classify(E);
auto prepareResult = [=]() -> bool {
if (!ResT && !Initializing) {
std::optional<unsigned> LocalIndex = allocateLocal(SubExpr);
UnsignedOrNone LocalIndex = allocateLocal(SubExpr);
if (!LocalIndex)
return false;
return this->emitGetPtrLocal(*LocalIndex, E);
@ -6627,7 +6677,7 @@ bool Compiler<Emitter>::VisitVectorUnaryOperator(const UnaryOperator *E) {
return this->delegate(SubExpr);
if (!Initializing) {
std::optional<unsigned> LocalIndex = allocateLocal(SubExpr);
UnsignedOrNone LocalIndex = allocateLocal(SubExpr);
if (!LocalIndex)
return false;
if (!this->emitGetPtrLocal(*LocalIndex, E))
@ -7231,7 +7281,7 @@ bool Compiler<Emitter>::emitBuiltinBitCast(const CastExpr *E) {
// Prepare storage for the result in case we discard.
if (DiscardResult && !Initializing && !ToT) {
std::optional<unsigned> LocalIndex = allocateLocal(E);
UnsignedOrNone LocalIndex = allocateLocal(E);
if (!LocalIndex)
return false;
if (!this->emitGetPtrLocal(*LocalIndex, E))

View File

@ -282,6 +282,7 @@ protected:
/// been created. visitInitializer() then relies on a pointer to this
/// variable being on top of the stack.
bool visitInitializer(const Expr *E);
bool visitAsLValue(const Expr *E);
/// Evaluates an expression for side effects and discards the result.
bool discard(const Expr *E);
/// Just pass evaluation on to \p E. This leaves all the parsing flags
@ -315,11 +316,11 @@ protected:
bool IsConstexprUnknown = false);
/// Allocates a space storing a local given its type.
std::optional<unsigned>
allocateLocal(DeclTy &&Decl, QualType Ty = QualType(),
const ValueDecl *ExtendingDecl = nullptr,
ScopeKind = ScopeKind::Block, bool IsConstexprUnknown = false);
std::optional<unsigned> allocateTemporary(const Expr *E);
UnsignedOrNone allocateLocal(DeclTy &&Decl, QualType Ty = QualType(),
const ValueDecl *ExtendingDecl = nullptr,
ScopeKind = ScopeKind::Block,
bool IsConstexprUnknown = false);
UnsignedOrNone allocateTemporary(const Expr *E);
private:
friend class VariableScope<Emitter>;
@ -346,9 +347,10 @@ private:
/// Emits an APSInt constant.
bool emitConst(const llvm::APSInt &Value, PrimType Ty, const Expr *E);
bool emitConst(const llvm::APInt &Value, PrimType Ty, const Expr *E);
bool emitConst(const llvm::APSInt &Value, const Expr *E);
bool emitConst(const llvm::APInt &Value, const Expr *E) {
return emitConst(static_cast<llvm::APSInt>(Value), E);
return emitConst(Value, classifyPrim(E), E);
}
/// Emits an integer constant.
@ -426,6 +428,7 @@ protected:
bool DiscardResult = false;
bool InStmtExpr = false;
bool ToLValue = false;
/// Flag inidicating if we're initializing an already created
/// variable. This is set in visitInitializer().
@ -565,7 +568,7 @@ public:
void addLocal(const Scope::Local &Local) override {
if (!Idx) {
Idx = this->Ctx->Descriptors.size();
Idx = static_cast<unsigned>(this->Ctx->Descriptors.size());
this->Ctx->Descriptors.emplace_back();
this->Ctx->emitInitScope(*Idx, {});
}
@ -613,7 +616,7 @@ public:
}
/// Index of the scope in the chain.
std::optional<unsigned> Idx;
UnsignedOrNone Idx = std::nullopt;
};
/// Scope for storage declared in a compound statement.

View File

@ -398,17 +398,11 @@ const llvm::fltSemantics &Context::getFloatSemantics(QualType T) const {
}
bool Context::Run(State &Parent, const Function *Func) {
{
InterpState State(Parent, *P, Stk, *this, Func);
if (Interpret(State)) {
assert(Stk.empty());
return true;
}
// State gets destroyed here, so the Stk.clear() below doesn't accidentally
// remove values the State's destructor might access.
InterpState State(Parent, *P, Stk, *this, Func);
if (Interpret(State)) {
assert(Stk.empty());
return true;
}
Stk.clear();
return false;
}

View File

@ -30,7 +30,7 @@ namespace interp {
class Function;
class Program;
class State;
enum PrimType : unsigned;
enum PrimType : uint8_t;
struct ParamOffset {
unsigned Offset;

View File

@ -24,7 +24,7 @@ class Record;
class SourceInfo;
struct InitMap;
struct Descriptor;
enum PrimType : unsigned;
enum PrimType : uint8_t;
using DeclTy = llvm::PointerUnion<const Decl *, const Expr *>;
using InitMapPtr = std::optional<std::pair<bool, std::shared_ptr<InitMap>>>;

View File

@ -545,7 +545,7 @@ LLVM_DUMP_METHOD void Block::dump(llvm::raw_ostream &OS) const {
OS << " Initialized: " << IsInitialized << "\n";
OS << " Weak: " << isWeak() << "\n";
OS << " Dummy: " << isDummy() << '\n';
OS << " Dynamic: " << IsDynamic << "\n";
OS << " Dynamic: " << isDynamic() << "\n";
}
LLVM_DUMP_METHOD void EvaluationResult::dump() const {

View File

@ -101,13 +101,17 @@ Block *DynamicAllocator::allocate(const Descriptor *D, unsigned EvalID,
ID->LifeState =
AllocForm == Form::Operator ? Lifetime::Ended : Lifetime::Started;
B->IsDynamic = true;
if (auto It = AllocationSites.find(D->asExpr()); It != AllocationSites.end())
if (auto It = AllocationSites.find(D->asExpr());
It != AllocationSites.end()) {
It->second.Allocations.emplace_back(std::move(Memory));
else
B->setDynAllocId(It->second.NumAllocs);
++It->second.NumAllocs;
} else {
AllocationSites.insert(
{D->asExpr(), AllocationSite(std::move(Memory), AllocForm)});
B->setDynAllocId(0);
}
assert(B->isDynamic());
return B;
}

View File

@ -48,11 +48,13 @@ private:
struct AllocationSite {
llvm::SmallVector<Allocation> Allocations;
unsigned NumAllocs = 0;
Form AllocForm;
AllocationSite(std::unique_ptr<std::byte[]> Memory, Form AllocForm)
: AllocForm(AllocForm) {
Allocations.push_back({std::move(Memory)});
++NumAllocs;
}
size_t size() const { return Allocations.size(); }

View File

@ -28,7 +28,7 @@ namespace interp {
class Program;
class ByteCodeEmitter;
class Pointer;
enum PrimType : uint32_t;
enum PrimType : uint8_t;
/// Describes a scope block.
///

View File

@ -1852,6 +1852,11 @@ bool EndLifetime(InterpState &S, CodePtr OpPC) {
const auto &Ptr = S.Stk.peek<Pointer>();
if (Ptr.isBlockPointer() && !CheckDummy(S, OpPC, Ptr.block(), AK_Destroy))
return false;
// FIXME: We need per-element lifetime information for primitive arrays.
if (Ptr.isArrayElement())
return true;
endLifetimeRecurse(Ptr.narrow());
return true;
}
@ -1861,6 +1866,11 @@ bool EndLifetimePop(InterpState &S, CodePtr OpPC) {
const auto &Ptr = S.Stk.pop<Pointer>();
if (Ptr.isBlockPointer() && !CheckDummy(S, OpPC, Ptr.block(), AK_Destroy))
return false;
// FIXME: We need per-element lifetime information for primitive arrays.
if (Ptr.isArrayElement())
return true;
endLifetimeRecurse(Ptr.narrow());
return true;
}

View File

@ -18,10 +18,6 @@ using namespace clang::interp;
void Block::addPointer(Pointer *P) {
assert(P);
if (IsStatic) {
assert(!Pointers);
return;
}
#ifndef NDEBUG
assert(!hasPointer(P));
@ -39,10 +35,6 @@ void Block::addPointer(Pointer *P) {
void Block::removePointer(Pointer *P) {
assert(P->isBlockPointer());
assert(P);
if (IsStatic) {
assert(!Pointers);
return;
}
#ifndef NDEBUG
assert(hasPointer(P));
@ -64,7 +56,7 @@ void Block::removePointer(Pointer *P) {
}
void Block::cleanup() {
if (Pointers == nullptr && !IsDynamic && isDead())
if (Pointers == nullptr && !isDynamic() && isDead())
(reinterpret_cast<DeadBlock *>(this + 1) - 1)->free();
}
@ -74,10 +66,6 @@ void Block::replacePointer(Pointer *Old, Pointer *New) {
assert(New);
assert(New->isBlockPointer());
assert(Old != New);
if (IsStatic) {
assert(!Pointers);
return;
}
#ifndef NDEBUG
assert(hasPointer(Old));
#endif
@ -123,7 +111,7 @@ DeadBlock::DeadBlock(DeadBlock *&Root, Block *Blk)
Prev = nullptr;
Root = this;
B.IsDynamic = Blk->IsDynamic;
B.DynAllocId = Blk->DynAllocId;
// Transfer pointers.
B.Pointers = Blk->Pointers;

View File

@ -22,7 +22,7 @@ class Block;
class DeadBlock;
class InterpState;
class Pointer;
enum PrimType : unsigned;
enum PrimType : uint8_t;
/// A memory block, either on the stack or in the heap.
///
@ -50,9 +50,9 @@ private:
public:
/// Creates a new block.
Block(unsigned EvalID, const std::optional<unsigned> &DeclID,
const Descriptor *Desc, bool IsStatic = false, bool IsExtern = false,
bool IsWeak = false, bool IsDummy = false)
Block(unsigned EvalID, UnsignedOrNone DeclID, const Descriptor *Desc,
bool IsStatic = false, bool IsExtern = false, bool IsWeak = false,
bool IsDummy = false)
: Desc(Desc), DeclID(DeclID), EvalID(EvalID), IsStatic(IsStatic) {
assert(Desc);
AccessFlags |= (ExternFlag * IsExtern);
@ -62,8 +62,7 @@ public:
Block(unsigned EvalID, const Descriptor *Desc, bool IsStatic = false,
bool IsExtern = false, bool IsWeak = false, bool IsDummy = false)
: Desc(Desc), DeclID((unsigned)-1), EvalID(EvalID), IsStatic(IsStatic),
IsDynamic(false) {
: Desc(Desc), EvalID(EvalID), IsStatic(IsStatic) {
assert(Desc);
AccessFlags |= (ExternFlag * IsExtern);
AccessFlags |= (WeakFlag * IsWeak);
@ -81,13 +80,13 @@ public:
/// Checks if the block is temporary.
bool isTemporary() const { return Desc->IsTemporary; }
bool isWeak() const { return AccessFlags & WeakFlag; }
bool isDynamic() const { return IsDynamic; }
bool isDynamic() const { return (DynAllocId != std::nullopt); }
bool isDummy() const { return AccessFlags & DummyFlag; }
bool isDead() const { return AccessFlags & DeadFlag; }
/// Returns the size of the block.
unsigned getSize() const { return Desc->getAllocSize(); }
/// Returns the declaration ID.
std::optional<unsigned> getDeclID() const { return DeclID; }
UnsignedOrNone getDeclID() const { return DeclID; }
/// Returns whether the data of this block has been initialized via
/// invoking the Ctor func.
bool isInitialized() const { return IsInitialized; }
@ -150,6 +149,7 @@ private:
friend class DeadBlock;
friend class InterpState;
friend class DynamicAllocator;
friend class Program;
Block(unsigned EvalID, const Descriptor *Desc, bool IsExtern, bool IsStatic,
bool IsWeak, bool IsDummy, bool IsDead)
@ -161,6 +161,9 @@ private:
AccessFlags |= (DummyFlag * IsDummy);
}
/// To be called by DynamicAllocator.
void setDynAllocId(unsigned ID) { DynAllocId = ID; }
/// Deletes a dead block at the end of its lifetime.
void cleanup();
@ -177,16 +180,15 @@ private:
/// Start of the chain of pointers.
Pointer *Pointers = nullptr;
/// Unique identifier of the declaration.
std::optional<unsigned> DeclID;
UnsignedOrNone DeclID = std::nullopt;
const unsigned EvalID = ~0u;
/// Flag indicating if the block has static storage duration.
bool IsStatic = false;
/// Flag indicating if the block contents have been initialized
/// via invokeCtor.
bool IsInitialized = false;
/// Flag indicating if this block has been allocated via dynamic
/// memory allocation (e.g. malloc).
bool IsDynamic = false;
/// Allocation ID for this dynamic allocation, if it is one.
UnsignedOrNone DynAllocId = std::nullopt;
/// AccessFlags containing IsExtern, IsDead, IsWeak, and IsDummy bits.
uint8_t AccessFlags = 0;
};

View File

@ -2170,29 +2170,32 @@ static bool interp__builtin_memchr(InterpState &S, CodePtr OpPC,
return true;
}
static unsigned computeFullDescSize(const ASTContext &ASTCtx,
const Descriptor *Desc) {
static std::optional<unsigned> computeFullDescSize(const ASTContext &ASTCtx,
const Descriptor *Desc) {
if (Desc->isPrimitive())
return ASTCtx.getTypeSizeInChars(Desc->getType()).getQuantity();
if (Desc->isArray())
return ASTCtx.getTypeSizeInChars(Desc->getElemQualType()).getQuantity() *
Desc->getNumElems();
if (Desc->isRecord()) {
// Can't use Descriptor::getType() as that may return a pointer type. Look
// at the decl directly.
return ASTCtx
.getTypeSizeInChars(
ASTCtx.getCanonicalTagType(Desc->ElemRecord->getDecl()))
.getQuantity();
}
if (Desc->isRecord())
return ASTCtx.getTypeSizeInChars(Desc->getType()).getQuantity();
llvm_unreachable("Unhandled descriptor type");
return 0;
return std::nullopt;
}
/// Compute the byte offset of \p Ptr in the full declaration.
static unsigned computePointerOffset(const ASTContext &ASTCtx,
const Pointer &Ptr) {
unsigned Result = 0;
Pointer P = Ptr;
while (P.isArrayElement() || P.isField()) {
while (P.isField() || P.isArrayElement()) {
P = P.expand();
const Descriptor *D = P.getFieldDesc();
@ -2205,7 +2208,6 @@ static unsigned computePointerOffset(const ASTContext &ASTCtx,
Result += ElemSize * P.getIndex();
P = P.expand().getArray();
} else if (P.isBaseClass()) {
const auto *RD = cast<CXXRecordDecl>(D->asDecl());
bool IsVirtual = Ptr.isVirtualBaseClass();
P = P.getBase();
@ -2234,30 +2236,136 @@ static unsigned computePointerOffset(const ASTContext &ASTCtx,
return Result;
}
/// Does Ptr point to the last subobject?
static bool pointsToLastObject(const Pointer &Ptr) {
Pointer P = Ptr;
while (!P.isRoot()) {
if (P.isArrayElement()) {
P = P.expand().getArray();
continue;
}
if (P.isBaseClass()) {
if (P.getRecord()->getNumFields() > 0)
return false;
P = P.getBase();
continue;
}
Pointer Base = P.getBase();
if (const Record *R = Base.getRecord()) {
assert(P.getField());
if (P.getField()->getFieldIndex() != R->getNumFields() - 1)
return false;
}
P = Base;
}
return true;
}
/// Does Ptr point to the last object AND to a flexible array member?
static bool isUserWritingOffTheEnd(const ASTContext &Ctx, const Pointer &Ptr) {
auto isFlexibleArrayMember = [&](const Descriptor *FieldDesc) {
using FAMKind = LangOptions::StrictFlexArraysLevelKind;
FAMKind StrictFlexArraysLevel =
Ctx.getLangOpts().getStrictFlexArraysLevel();
if (StrictFlexArraysLevel == FAMKind::Default)
return true;
unsigned NumElems = FieldDesc->getNumElems();
if (NumElems == 0 && StrictFlexArraysLevel != FAMKind::IncompleteOnly)
return true;
if (NumElems == 1 && StrictFlexArraysLevel == FAMKind::OneZeroOrIncomplete)
return true;
return false;
};
const Descriptor *FieldDesc = Ptr.getFieldDesc();
if (!FieldDesc->isArray())
return false;
return Ptr.isDummy() && pointsToLastObject(Ptr) &&
isFlexibleArrayMember(FieldDesc);
}
static bool interp__builtin_object_size(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const CallExpr *Call) {
const ASTContext &ASTCtx = S.getASTContext();
PrimType KindT = *S.getContext().classify(Call->getArg(1));
[[maybe_unused]] unsigned Kind = popToAPSInt(S.Stk, KindT).getZExtValue();
// From the GCC docs:
// Kind is an integer constant from 0 to 3. If the least significant bit is
// clear, objects are whole variables. If it is set, a closest surrounding
// subobject is considered the object a pointer points to. The second bit
// determines if maximum or minimum of remaining bytes is computed.
unsigned Kind = popToAPSInt(S.Stk, KindT).getZExtValue();
assert(Kind <= 3 && "unexpected kind");
bool UseFieldDesc = (Kind & 1u);
bool ReportMinimum = (Kind & 2u);
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (Ptr.isZero())
if (Call->getArg(0)->HasSideEffects(ASTCtx)) {
// "If there are any side effects in them, it returns (size_t) -1
// for type 0 or 1 and (size_t) 0 for type 2 or 3."
pushInteger(S, Kind <= 1 ? -1 : 0, Call->getType());
return true;
}
if (Ptr.isZero() || !Ptr.isBlockPointer())
return false;
// We can't load through pointers.
if (Ptr.isDummy() && Ptr.getType()->isPointerType())
return false;
bool DetermineForCompleteObject = Ptr.getFieldDesc() == Ptr.getDeclDesc();
const Descriptor *DeclDesc = Ptr.getDeclDesc();
if (!DeclDesc)
assert(DeclDesc);
if (!UseFieldDesc || DetermineForCompleteObject) {
// Lower bound, so we can't fall back to this.
if (ReportMinimum && !DetermineForCompleteObject)
return false;
// Can't read beyond the pointer decl desc.
if (!UseFieldDesc && !ReportMinimum && DeclDesc->getType()->isPointerType())
return false;
} else {
if (isUserWritingOffTheEnd(ASTCtx, Ptr.expand())) {
// If we cannot determine the size of the initial allocation, then we
// can't given an accurate upper-bound. However, we are still able to give
// conservative lower-bounds for Type=3.
if (Kind == 1)
return false;
}
}
const Descriptor *Desc = UseFieldDesc ? Ptr.getFieldDesc() : DeclDesc;
assert(Desc);
std::optional<unsigned> FullSize = computeFullDescSize(ASTCtx, Desc);
if (!FullSize)
return false;
const ASTContext &ASTCtx = S.getASTContext();
unsigned ByteOffset;
if (UseFieldDesc) {
if (Ptr.isBaseClass())
ByteOffset = computePointerOffset(ASTCtx, Ptr.getBase()) -
computePointerOffset(ASTCtx, Ptr);
else
ByteOffset =
computePointerOffset(ASTCtx, Ptr) -
computePointerOffset(ASTCtx, Ptr.expand().atIndex(0).narrow());
} else
ByteOffset = computePointerOffset(ASTCtx, Ptr);
unsigned ByteOffset = computePointerOffset(ASTCtx, Ptr);
unsigned FullSize = computeFullDescSize(ASTCtx, DeclDesc);
pushInteger(S, FullSize - ByteOffset, Call->getType());
assert(ByteOffset <= *FullSize);
unsigned Result = *FullSize - ByteOffset;
pushInteger(S, Result, Call->getType());
return true;
}
@ -2366,15 +2474,30 @@ static bool interp__builtin_elementwise_sat(InterpState &S, CodePtr OpPC,
});
APSInt Result;
if (BuiltinID == Builtin::BI__builtin_elementwise_add_sat) {
switch (BuiltinID) {
case Builtin::BI__builtin_elementwise_add_sat:
Result = APSInt(Elem1.isSigned() ? Elem1.sadd_sat(Elem2)
: Elem1.uadd_sat(Elem2),
Call->getType()->isUnsignedIntegerOrEnumerationType());
} else if (BuiltinID == Builtin::BI__builtin_elementwise_sub_sat) {
break;
case Builtin::BI__builtin_elementwise_sub_sat:
Result = APSInt(Elem1.isSigned() ? Elem1.ssub_sat(Elem2)
: Elem1.usub_sat(Elem2),
Call->getType()->isUnsignedIntegerOrEnumerationType());
} else {
break;
case clang::X86::BI__builtin_ia32_pmulhuw128:
case clang::X86::BI__builtin_ia32_pmulhuw256:
case clang::X86::BI__builtin_ia32_pmulhuw512:
Result = APSInt(llvm::APIntOps::mulhu(Elem1, Elem2),
/*isUnsigned=*/true);
break;
case clang::X86::BI__builtin_ia32_pmulhw128:
case clang::X86::BI__builtin_ia32_pmulhw256:
case clang::X86::BI__builtin_ia32_pmulhw512:
Result = APSInt(llvm::APIntOps::mulhs(Elem1, Elem2),
/*isUnsigned=*/false);
break;
default:
llvm_unreachable("Wrong builtin ID");
}
@ -2460,6 +2583,50 @@ static bool interp__builtin_elementwise_maxmin(InterpState &S, CodePtr OpPC,
return true;
}
static bool interp__builtin_ia32_pmul(InterpState &S, CodePtr OpPC,
const CallExpr *Call,
unsigned BuiltinID) {
assert(Call->getArg(0)->getType()->isVectorType() &&
Call->getArg(1)->getType()->isVectorType());
const Pointer &RHS = S.Stk.pop<Pointer>();
const Pointer &LHS = S.Stk.pop<Pointer>();
const Pointer &Dst = S.Stk.peek<Pointer>();
const auto *VT = Call->getArg(0)->getType()->castAs<VectorType>();
PrimType ElemT = *S.getContext().classify(VT->getElementType());
unsigned SourceLen = VT->getNumElements();
SmallVector<APValue, 4> ResultElements;
ResultElements.reserve(SourceLen / 2);
for (unsigned I = 0; I != SourceLen; I += 2) {
APSInt Elem1;
APSInt Elem2;
INT_TYPE_SWITCH_NO_BOOL(ElemT, {
Elem1 = LHS.elem<T>(I).toAPSInt();
Elem2 = RHS.elem<T>(I).toAPSInt();
});
APSInt Result;
switch (BuiltinID) {
case clang::X86::BI__builtin_ia32_pmuludq128:
case clang::X86::BI__builtin_ia32_pmuludq256:
case clang::X86::BI__builtin_ia32_pmuludq512:
Result = APSInt(llvm::APIntOps::muluExtended(Elem1, Elem2), true);
break;
case clang::X86::BI__builtin_ia32_pmuldq128:
case clang::X86::BI__builtin_ia32_pmuldq256:
case clang::X86::BI__builtin_ia32_pmuldq512:
Result = APSInt(llvm::APIntOps::mulsExtended(Elem1, Elem2), false);
break;
}
INT_TYPE_SWITCH_NO_BOOL(ElemT,
{ Dst.elem<T>(I) = static_cast<T>(Result); });
}
Dst.initializeAllElements();
return true;
}
bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
uint32_t BuiltinID) {
if (!S.getASTContext().BuiltinInfo.isConstantEvaluated(BuiltinID))
@ -2868,12 +3035,25 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
case Builtin::BI__builtin_elementwise_add_sat:
case Builtin::BI__builtin_elementwise_sub_sat:
case clang::X86::BI__builtin_ia32_pmulhuw128:
case clang::X86::BI__builtin_ia32_pmulhuw256:
case clang::X86::BI__builtin_ia32_pmulhuw512:
case clang::X86::BI__builtin_ia32_pmulhw128:
case clang::X86::BI__builtin_ia32_pmulhw256:
case clang::X86::BI__builtin_ia32_pmulhw512:
return interp__builtin_elementwise_sat(S, OpPC, Call, BuiltinID);
case Builtin::BI__builtin_elementwise_max:
case Builtin::BI__builtin_elementwise_min:
return interp__builtin_elementwise_maxmin(S, OpPC, Call, BuiltinID);
case clang::X86::BI__builtin_ia32_pmuldq128:
case clang::X86::BI__builtin_ia32_pmuldq256:
case clang::X86::BI__builtin_ia32_pmuldq512:
case clang::X86::BI__builtin_ia32_pmuludq128:
case clang::X86::BI__builtin_ia32_pmuludq256:
return interp__builtin_ia32_pmul(S, OpPC, Call, BuiltinID);
default:
S.FFDiag(S.Current->getLocation(OpPC),
diag::note_invalid_subexpr_in_const_expr)

View File

@ -26,33 +26,33 @@ InterpStack::~InterpStack() {
std::free(Chunk);
Chunk = nullptr;
StackSize = 0;
#ifndef NDEBUG
ItemTypes.clear();
#endif
}
// We keep the last chunk around to reuse.
void InterpStack::clear() {
if (!Chunk)
return;
if (Chunk->Next)
std::free(Chunk->Next);
assert(Chunk);
StackSize = 0;
#ifndef NDEBUG
ItemTypes.clear();
#endif
for (PrimType Item : llvm::reverse(ItemTypes)) {
TYPE_SWITCH(Item, { this->discard<T>(); });
}
assert(ItemTypes.empty());
assert(empty());
}
void InterpStack::clearTo(size_t NewSize) {
assert(NewSize <= size());
size_t ToShrink = size() - NewSize;
if (ToShrink == 0)
if (NewSize == 0)
return clear();
if (NewSize == size())
return;
shrink(ToShrink);
assert(NewSize <= size());
for (PrimType Item : llvm::reverse(ItemTypes)) {
TYPE_SWITCH(Item, { this->discard<T>(); });
if (size() == NewSize)
break;
}
// Note: discard() above already removed the types from ItemTypes.
assert(size() == NewSize);
}
@ -105,25 +105,9 @@ void InterpStack::shrink(size_t Size) {
Chunk->End -= Size;
StackSize -= Size;
#ifndef NDEBUG
size_t TypesSize = 0;
for (PrimType T : ItemTypes)
TYPE_SWITCH(T, { TypesSize += aligned_size<T>(); });
size_t StackSize = size();
while (TypesSize > StackSize) {
TYPE_SWITCH(ItemTypes.back(), {
TypesSize -= aligned_size<T>();
ItemTypes.pop_back();
});
}
assert(TypesSize == StackSize);
#endif
}
void InterpStack::dump() const {
#ifndef NDEBUG
llvm::errs() << "Items: " << ItemTypes.size() << ". Size: " << size() << '\n';
if (ItemTypes.empty())
return;
@ -133,11 +117,11 @@ void InterpStack::dump() const {
// The type of the item on the top of the stack is inserted to the back
// of the vector, so the iteration has to happen backwards.
for (auto TyIt = ItemTypes.rbegin(); TyIt != ItemTypes.rend(); ++TyIt) {
Offset += align(primSize(*TyIt));
for (PrimType Item : llvm::reverse(ItemTypes)) {
Offset += align(primSize(Item));
llvm::errs() << Index << '/' << Offset << ": ";
TYPE_SWITCH(*TyIt, {
TYPE_SWITCH(Item, {
const T &V = peek<T>(Offset);
llvm::errs() << V;
});
@ -145,5 +129,4 @@ void InterpStack::dump() const {
++Index;
}
#endif
}

View File

@ -17,7 +17,6 @@
#include "IntegralAP.h"
#include "MemberPointer.h"
#include "PrimType.h"
#include <vector>
namespace clang {
namespace interp {
@ -33,18 +32,14 @@ public:
/// Constructs a value in place on the top of the stack.
template <typename T, typename... Tys> void push(Tys &&...Args) {
new (grow(aligned_size<T>())) T(std::forward<Tys>(Args)...);
#ifndef NDEBUG
ItemTypes.push_back(toPrimType<T>());
#endif
}
/// Returns the value from the top of the stack and removes it.
template <typename T> T pop() {
#ifndef NDEBUG
assert(!ItemTypes.empty());
assert(ItemTypes.back() == toPrimType<T>());
ItemTypes.pop_back();
#endif
T *Ptr = &peekInternal<T>();
T Value = std::move(*Ptr);
shrink(aligned_size<T>());
@ -53,22 +48,20 @@ public:
/// Discards the top value from the stack.
template <typename T> void discard() {
#ifndef NDEBUG
assert(!ItemTypes.empty());
assert(ItemTypes.back() == toPrimType<T>());
ItemTypes.pop_back();
#endif
T *Ptr = &peekInternal<T>();
Ptr->~T();
if constexpr (!std::is_trivially_destructible_v<T>) {
Ptr->~T();
}
shrink(aligned_size<T>());
}
/// Returns a reference to the value on the top of the stack.
template <typename T> T &peek() const {
#ifndef NDEBUG
assert(!ItemTypes.empty());
assert(ItemTypes.back() == toPrimType<T>());
#endif
return peekInternal<T>();
}
@ -83,7 +76,7 @@ public:
/// Returns the size of the stack in bytes.
size_t size() const { return StackSize; }
/// Clears the stack without calling any destructors.
/// Clears the stack.
void clear();
void clearTo(size_t NewSize);
@ -146,9 +139,11 @@ private:
/// Total size of the stack.
size_t StackSize = 0;
#ifndef NDEBUG
/// vector recording the type of data we pushed into the stack.
std::vector<PrimType> ItemTypes;
/// SmallVector recording the type of data we pushed into the stack.
/// We don't usually need this during normal code interpretation but
/// when aborting, we need type information to call the destructors
/// for what's left on the stack.
llvm::SmallVector<PrimType> ItemTypes;
template <typename T> static constexpr PrimType toPrimType() {
if constexpr (std::is_same_v<T, Pointer>)
@ -192,7 +187,6 @@ private:
llvm_unreachable("unknown type push()'ed into InterpStack");
}
#endif
};
} // namespace interp

View File

@ -45,6 +45,12 @@ InterpState::~InterpState() {
while (DeadBlocks) {
DeadBlock *Next = DeadBlocks->Next;
// There might be a pointer in a global structure pointing to the dead
// block.
for (Pointer *P = DeadBlocks->B.Pointers; P; P = P->asBlockPointer().Next)
DeadBlocks->B.removePointer(P);
std::free(DeadBlocks);
DeadBlocks = Next;
}
@ -53,12 +59,6 @@ InterpState::~InterpState() {
void InterpState::cleanup() {
// As a last resort, make sure all pointers still pointing to a dead block
// don't point to it anymore.
for (DeadBlock *DB = DeadBlocks; DB; DB = DB->Next) {
for (Pointer *P = DB->B.Pointers; P; P = P->asBlockPointer().Next) {
P->PointeeStorage.BS.Pointee = nullptr;
}
}
Alloc.cleanup();
}

View File

@ -179,10 +179,7 @@ APValue Pointer::toAPValue(const ASTContext &ASTCtx) const {
else if (const auto *E = Desc->asExpr()) {
if (block()->isDynamic()) {
QualType AllocatedType = getDeclPtr().getFieldDesc()->getDataType(ASTCtx);
// FIXME: Suboptimal counting of dynamic allocations. Move this to Context
// or InterpState?
static int ReportedDynamicAllocs = 0;
DynamicAllocLValue DA(ReportedDynamicAllocs++);
DynamicAllocLValue DA(*block()->DynAllocId);
Base = APValue::LValueBase::getDynamicAlloc(DA, AllocatedType);
} else {
Base = E;

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