Fix windows build failure and clang-format

Created using spr 1.3.6
This commit is contained in:
Steven Wu 2025-08-18 12:31:20 -07:00
commit 9b7be34863
1666 changed files with 40331 additions and 25769 deletions

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

@ -90,9 +90,6 @@ LTO:
- llvm/lib/Transforms/*/FunctionImport*
- llvm/tools/gold/**
mc:
- llvm/*/MC/**
clang:driver:
- clang/*/Driver/**
@ -621,6 +618,12 @@ llvm:adt:
llvm:support:
- llvm/**/Support/**
# Skip llvm/test/MC and llvm/unittests/MC, which includes target-specific directories.
llvm:mc:
- llvm/include/llvm/MC/**
- llvm/lib/MC/**
- llvm/tools/llvm-mc/**
llvm:transforms:
- llvm/lib/Transforms/**
- llvm/include/llvm/Transforms/**

View File

@ -81,6 +81,8 @@ RUN curl -L 'https://github.com/mozilla/sccache/releases/download/v0.10.0/sccach
ENV LLVM_SYSROOT=$LLVM_SYSROOT
ENV PATH=${LLVM_SYSROOT}/bin:${PATH}
ENV CC=clang
ENV CXX=clang++
# Create a new user to avoid test failures related to a lack of expected
# permissions issues in some tests. Set the user id to 1001 as that is the

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

@ -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

@ -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

5
.gitignore vendored
View File

@ -52,6 +52,11 @@ autoconf/autom4te.cache
# CLion project configuration
/.idea
/cmake-build*
# Coding assistants' stuff
/CLAUDE.md
/.claude/
/GEMINI.md
/.gemini/
#==============================================================================#
# Directories to ignore (do not add trailing '/'s, they skip symlinks).

View File

@ -740,6 +740,10 @@ public:
return false;
}
/// Return true if the hlt instruction under the x86, otherwise, default to
/// false.
virtual bool isX86HLT(const MCInst &Inst) const { return false; }
/// Return the width, in bytes, of the memory access performed by \p Inst, if
/// this is a pop instruction. Return zero otherwise.
virtual int getPopSize(const MCInst &Inst) const {

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

@ -132,8 +132,10 @@ bool MCPlusBuilder::equals(const MCSpecifierExpr &A, const MCSpecifierExpr &B,
}
bool MCPlusBuilder::isTerminator(const MCInst &Inst) const {
return Analysis->isTerminator(Inst) ||
(opts::TerminalTrap && Info->get(Inst.getOpcode()).isTrap());
return (opts::TerminalTrap && Info->get(Inst.getOpcode()).isTrap()) ||
Analysis->isTerminator(Inst)
? !isX86HLT(Inst)
: false;
}
void MCPlusBuilder::setTailCall(MCInst &Inst) const {

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

@ -223,6 +223,10 @@ public:
return Inst.getOpcode() == X86::ENDBR32 || Inst.getOpcode() == X86::ENDBR64;
}
bool isX86HLT(const MCInst &Inst) const override {
return Inst.getOpcode() == X86::HLT;
}
int getPopSize(const MCInst &Inst) const override {
switch (Inst.getOpcode()) {
case X86::POP16r:

View File

@ -0,0 +1,17 @@
## Check CFG for halt instruction
# RUN: %clang %cflags %s -static -o %t.exe -nostdlib
# RUN: llvm-bolt %t.exe --print-cfg --print-only=main -o %t 2>&1 | FileCheck %s --check-prefix=CHECK-CFG
# RUN: llvm-objdump -d %t --print-imm-hex | FileCheck %s --check-prefix=CHECK-BIN
# CHECK-CFG: BB Count : 1
# CHECK-BIN: <main>:
# CHECK-BIN-NEXT: f4 hlt
# CHECK-BIN-NEXT: c3 retq
.global main
.type main, %function
main:
hlt
retq
.size main, .-main

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)));

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

@ -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

@ -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.
@ -130,6 +134,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 +173,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

@ -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

@ -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

@ -0,0 +1,15 @@
// RUN: %check_clang_tidy -std=c++20 %s modernize-type-traits %t
namespace std {
template <class> struct tuple_size {
static const int value = 1;
};
template <int, class> struct tuple_element {
using type = int;
};
}
struct A {};
template <int> int get(const A&);
auto [a] = A();

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

@ -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

@ -193,6 +193,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 +232,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 +311,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

@ -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

@ -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

@ -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

@ -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">;

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

@ -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

@ -504,8 +504,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

@ -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
//===----------------------------------------------------------------------===//
@ -1749,6 +1805,39 @@ def CIR_VTableAddrPointOp : CIR_Op<"vtable.address_point", [
}];
}
//===----------------------------------------------------------------------===//
// VTableGetVPtr
//===----------------------------------------------------------------------===//
def CIR_VTableGetVPtrOp : CIR_Op<"vtable.get_vptr", [Pure]> {
let summary = "Get a the address of the vtable pointer for an object";
let description = [{
The `vtable.get_vptr` operation retrieves the address of the vptr for a
C++ object. This operation requires that the object pointer points to
the start of a complete object. (TODO: Describe how we get that).
The vptr will always be at offset zero in the object, but this operation
is more explicit about what is being retrieved than a direct bitcast.
The return type is always `!cir.ptr<!cir.vptr>`.
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>
```
}];
let arguments = (ins
Arg<CIR_PointerType, "the vptr address", [MemRead]>:$src
);
let results = (outs CIR_PtrToVPtr:$result);
let assemblyFormat = [{
$src `:` qualified(type($src)) `->` qualified(type($result)) attr-dict
}];
}
//===----------------------------------------------------------------------===//
// SetBitfieldOp
//===----------------------------------------------------------------------===//
@ -2210,6 +2299,68 @@ def CIR_CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> {
];
}
//===----------------------------------------------------------------------===//
// ReturnAddrOp and FrameAddrOp
//===----------------------------------------------------------------------===//
class CIR_FuncAddrBuiltinOp<string mnemonic> : CIR_Op<mnemonic, []> {
let arguments = (ins CIR_UInt32:$level);
let results = (outs CIR_VoidPtrType:$result);
let assemblyFormat = [{
`(` $level `)` attr-dict
}];
}
def CIR_ReturnAddrOp : CIR_FuncAddrBuiltinOp<"return_address"> {
let summary =
"The return address of the current function, or of one of its callers";
let description = [{
Represents a call to builtin function ` __builtin_return_address` in CIR.
This builtin function returns the return address of the current function,
or of one of its callers.
The `level` argument is number of frames to scan up the call stack.
For instance, value of 0 yields the return address of the current function,
value of 1 yields the return address of the caller of the current function,
and so forth.
Examples:
```mlir
%p = return_address(%level) -> !cir.ptr<!void>
```
}];
}
def CIR_FrameAddrOp : CIR_FuncAddrBuiltinOp<"frame_address"> {
let summary =
"The frame address of the current function, or of one of its callers";
let description = [{
Represents a call to builtin function ` __builtin_frame_address` in CIR.
This builtin function returns the frame address of the current function,
or of one of its callers. The frame is the area on the stack that holds
local variables and saved registers. The frame address is normally the
address of the first word pushed on to the stack by the function.
However, the exact definition depends upon the processor and the calling
convention. If the processor has a dedicated frame pointer register, and
the function has a frame, then __builtin_frame_address returns the value of
the frame pointer register.
The `level` argument is number of frames to scan up the call stack.
For instance, value of 0 yields the frame address of the current function,
value of 1 yields the frame address of the caller of the current function,
and so forth.
Examples:
```mlir
%p = frame_address(%level) -> !cir.ptr<!void>
```
}];
}
//===----------------------------------------------------------------------===//
// StackSaveOp & StackRestoreOp
//===----------------------------------------------------------------------===//

View File

@ -289,6 +289,14 @@ def CIR_AnyFloatOrVecOfFloatType
let cppFunctionName = "isFPOrVectorOfFPType";
}
//===----------------------------------------------------------------------===//
// VPtr type predicates
//===----------------------------------------------------------------------===//
def CIR_AnyVPtrType : CIR_TypeBase<"::cir::VPtrType", "vptr type">;
def CIR_PtrToVPtr : CIR_PtrToType<CIR_AnyVPtrType>;
//===----------------------------------------------------------------------===//
// Scalar Type predicates
//===----------------------------------------------------------------------===//

View File

@ -296,10 +296,10 @@ def CIR_VPtrType : CIR_Type<"VPtr", "vptr", [
access to the vptr.
This type will be the element type of the 'vptr' member of structures that
require a vtable pointer. A pointer to this type is returned by the
`cir.vtable.address_point` and `cir.vtable.get_vptr` operations, and this
pointer may be passed to the `cir.vtable.get_virtual_fn_addr` operation to
get the address of a virtual function pointer.
require a vtable pointer. The `cir.vtable.address_point` operation returns
this type. The `cir.vtable.get_vptr` operations returns a pointer to this
type. This pointer may be passed to the `cir.vtable.get_virtual_fn_addr`
operation to get the address of a virtual function pointer.
The pointer may also be cast to other pointer types in order to perform
pointer arithmetic based on information encoded in the AST layout to get

View File

@ -6987,7 +6987,6 @@ def static_libgfortran : Flag<["-"], "static-libgfortran">, Group<gfortran_Group
// "f" options with values for gfortran.
def fblas_matmul_limit_EQ : Joined<["-"], "fblas-matmul-limit=">, Group<gfortran_Group>;
def fcheck_EQ : Joined<["-"], "fcheck=">, Group<gfortran_Group>;
def fcoarray_EQ : Joined<["-"], "fcoarray=">, Group<gfortran_Group>;
def ffpe_trap_EQ : Joined<["-"], "ffpe-trap=">, Group<gfortran_Group>;
def ffree_line_length_VALUE : Joined<["-"], "ffree-line-length-">, Group<gfortran_Group>;
def finit_character_EQ : Joined<["-"], "finit-character=">, Group<gfortran_Group>;
@ -8695,6 +8694,15 @@ def fopenmp_host_ir_file_path : Separate<["-"], "fopenmp-host-ir-file-path">,
} // let Visibility = [CC1Option, FC1Option]
//===----------------------------------------------------------------------===//
// Coarray Options
//===----------------------------------------------------------------------===//
def fcoarray : Flag<["-"], "fcoarray">,
Group<f_Group>,
Visibility<[FlangOption, FC1Option]>,
HelpText<"Enable Coarray features">;
//===----------------------------------------------------------------------===//
// SYCL Options
//===----------------------------------------------------------------------===//

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

@ -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

@ -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

@ -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);
@ -2974,20 +2980,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.
@ -4140,13 +4151,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);
}
@ -4174,7 +4185,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 +4194,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);
}
@ -4944,7 +4961,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()) {
@ -4989,11 +5005,31 @@ bool Compiler<Emitter>::VisitBuiltinCallExpr(const CallExpr *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;
}
}
}
@ -5146,7 +5182,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;
@ -6745,6 +6782,22 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
// value.
bool IsReference = D->getType()->isReferenceType();
// Function parameters.
// Note that it's important to check them first since we might have a local
// variable created for a ParmVarDecl as well.
if (const auto *PVD = dyn_cast<ParmVarDecl>(D)) {
if (Ctx.getLangOpts().CPlusPlus && !Ctx.getLangOpts().CPlusPlus11 &&
!D->getType()->isIntegralOrEnumerationType()) {
return this->emitInvalidDeclRef(cast<DeclRefExpr>(E),
/*InitializerFailed=*/false, E);
}
if (auto It = this->Params.find(PVD); It != this->Params.end()) {
if (IsReference || !It->second.IsPtr)
return this->emitGetParam(classifyPrim(E), It->second.Offset, E);
return this->emitGetPtrParam(It->second.Offset, E);
}
}
// Local variables.
if (auto It = Locals.find(D); It != Locals.end()) {
const unsigned Offset = It->second.Offset;
@ -6762,20 +6815,6 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
return this->emitGetPtrGlobal(*GlobalIndex, E);
}
// Function parameters.
if (const auto *PVD = dyn_cast<ParmVarDecl>(D)) {
if (Ctx.getLangOpts().CPlusPlus && !Ctx.getLangOpts().CPlusPlus11 &&
!D->getType()->isIntegralOrEnumerationType()) {
return this->emitInvalidDeclRef(cast<DeclRefExpr>(E),
/*InitializerFailed=*/false, E);
}
if (auto It = this->Params.find(PVD); It != this->Params.end()) {
if (IsReference || !It->second.IsPtr)
return this->emitGetParam(classifyPrim(E), It->second.Offset, E);
return this->emitGetPtrParam(It->second.Offset, E);
}
}
// In case we need to re-visit a declaration.
auto revisit = [&](const VarDecl *VD) -> bool {

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
@ -426,6 +427,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().

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

@ -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));
@ -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

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), IsDynamic(false) {
assert(Desc);
AccessFlags |= (ExternFlag * IsExtern);
AccessFlags |= (WeakFlag * IsWeak);
@ -87,7 +86,7 @@ public:
/// 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; }
@ -177,7 +176,7 @@ 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;

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;
}

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

@ -29,7 +29,7 @@ class DeadBlock;
class Pointer;
class Context;
template <unsigned A, bool B> class Integral;
enum PrimType : unsigned;
enum PrimType : uint8_t;
class Pointer;
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P);
@ -593,7 +593,7 @@ public:
}
/// Returns the declaration ID.
std::optional<unsigned> getDeclID() const {
UnsignedOrNone getDeclID() const {
if (isBlockPointer()) {
assert(asBlockPointer().Pointee);
return asBlockPointer().Pointee->getDeclID();

View File

@ -31,7 +31,7 @@ template <bool Signed> class IntegralAP;
template <unsigned Bits, bool Signed> class Integral;
/// Enumeration of the primitive types of the VM.
enum PrimType : unsigned {
enum PrimType : uint8_t {
PT_Sint8 = 0,
PT_Uint8 = 1,
PT_Sint16 = 2,
@ -51,14 +51,15 @@ enum PrimType : unsigned {
// Like std::optional<PrimType>, but only sizeof(PrimType).
class OptPrimType final {
unsigned V = ~0u;
static constexpr uint8_t None = 0xFF;
uint8_t V = None;
public:
OptPrimType() = default;
OptPrimType(std::nullopt_t) {}
OptPrimType(PrimType T) : V(static_cast<unsigned>(T)) {}
explicit constexpr operator bool() const { return V != ~0u; }
explicit constexpr operator bool() const { return V != None; }
PrimType operator*() const {
assert(operator bool());
return static_cast<PrimType>(V);

View File

@ -164,8 +164,8 @@ unsigned Program::getOrCreateDummy(const DeclTy &D) {
const auto *VD = cast<ValueDecl>(cast<const Decl *>(D));
IsWeak = VD->isWeak();
QT = VD->getType();
if (const auto *RT = QT->getAs<ReferenceType>())
QT = RT->getPointeeType();
if (QT->isPointerOrReferenceType())
QT = QT->getPointeeType();
}
assert(!QT.isNull());

View File

@ -73,6 +73,10 @@ public:
return Globals[Idx]->block();
}
bool isGlobalInitialized(unsigned Index) const {
return getPtrGlobal(Index).isInitialized();
}
/// Finds a global's index.
std::optional<unsigned> getGlobal(const ValueDecl *VD);
std::optional<unsigned> getGlobal(const Expr *E);
@ -152,7 +156,7 @@ public:
};
/// Returns the current declaration ID.
std::optional<unsigned> getCurrentDecl() const {
UnsignedOrNone getCurrentDecl() const {
if (CurrentDeclaration == NoDeclaration)
return std::nullopt;
return CurrentDeclaration;

View File

@ -2805,32 +2805,20 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc,
case CXXTemporaryObjectExprClass:
case CXXConstructExprClass: {
if (const CXXRecordDecl *Type = getType()->getAsCXXRecordDecl()) {
const auto *WarnURAttr = Type->getAttr<WarnUnusedResultAttr>();
if (Type->hasAttr<WarnUnusedAttr>() ||
(WarnURAttr && WarnURAttr->IsCXX11NoDiscard())) {
WarnE = this;
Loc = getBeginLoc();
R1 = getSourceRange();
return true;
}
}
const auto *CE = cast<CXXConstructExpr>(this);
if (const CXXConstructorDecl *Ctor = CE->getConstructor()) {
const auto *WarnURAttr = Ctor->getAttr<WarnUnusedResultAttr>();
if (WarnURAttr && WarnURAttr->IsCXX11NoDiscard()) {
WarnE = this;
Loc = getBeginLoc();
R1 = getSourceRange();
const CXXRecordDecl *Type = getType()->getAsCXXRecordDecl();
if (unsigned NumArgs = CE->getNumArgs())
R2 = SourceRange(CE->getArg(0)->getBeginLoc(),
CE->getArg(NumArgs - 1)->getEndLoc());
return true;
}
if ((Type && Type->hasAttr<WarnUnusedAttr>()) ||
CE->hasUnusedResultAttr(Ctx)) {
WarnE = this;
Loc = getBeginLoc();
R1 = getSourceRange();
if (unsigned NumArgs = CE->getNumArgs())
R2 = SourceRange(CE->getArg(0)->getBeginLoc(),
CE->getArg(NumArgs - 1)->getEndLoc());
return true;
}
return false;
}

View File

@ -11711,6 +11711,43 @@ bool VectorExprEvaluator::VisitCallExpr(const CallExpr *E) {
return Success(APValue(ResultElements.data(), ResultElements.size()), E);
}
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:
case clang::X86::BI__builtin_ia32_pmuludq512: {
APValue SourceLHS, SourceRHS;
if (!EvaluateAsRValue(Info, E->getArg(0), SourceLHS) ||
!EvaluateAsRValue(Info, E->getArg(1), SourceRHS))
return false;
unsigned SourceLen = SourceLHS.getVectorLength();
SmallVector<APValue, 4> ResultElements;
ResultElements.reserve(SourceLen / 2);
for (unsigned EltNum = 0; EltNum < SourceLen; EltNum += 2) {
APSInt LHS = SourceLHS.getVectorElt(EltNum).getInt();
APSInt RHS = SourceRHS.getVectorElt(EltNum).getInt();
switch (E->getBuiltinCallee()) {
case clang::X86::BI__builtin_ia32_pmuludq128:
case clang::X86::BI__builtin_ia32_pmuludq256:
case clang::X86::BI__builtin_ia32_pmuludq512:
ResultElements.push_back(
APValue(APSInt(llvm::APIntOps::muluExtended(LHS, RHS), true)));
break;
case clang::X86::BI__builtin_ia32_pmuldq128:
case clang::X86::BI__builtin_ia32_pmuldq256:
case clang::X86::BI__builtin_ia32_pmuldq512:
ResultElements.push_back(
APValue(APSInt(llvm::APIntOps::mulsExtended(LHS, RHS), false)));
break;
}
}
return Success(APValue(ResultElements.data(), ResultElements.size()), E);
}
case Builtin::BI__builtin_elementwise_max:
case Builtin::BI__builtin_elementwise_min: {
APValue SourceLHS, SourceRHS;
@ -11746,6 +11783,50 @@ bool VectorExprEvaluator::VisitCallExpr(const CallExpr *E) {
return Success(APValue(ResultElements.data(), ResultElements.size()), E);
}
case X86::BI__builtin_ia32_selectb_128:
case X86::BI__builtin_ia32_selectb_256:
case X86::BI__builtin_ia32_selectb_512:
case X86::BI__builtin_ia32_selectw_128:
case X86::BI__builtin_ia32_selectw_256:
case X86::BI__builtin_ia32_selectw_512:
case X86::BI__builtin_ia32_selectd_128:
case X86::BI__builtin_ia32_selectd_256:
case X86::BI__builtin_ia32_selectd_512:
case X86::BI__builtin_ia32_selectq_128:
case X86::BI__builtin_ia32_selectq_256:
case X86::BI__builtin_ia32_selectq_512:
case X86::BI__builtin_ia32_selectph_128:
case X86::BI__builtin_ia32_selectph_256:
case X86::BI__builtin_ia32_selectph_512:
case X86::BI__builtin_ia32_selectpbf_128:
case X86::BI__builtin_ia32_selectpbf_256:
case X86::BI__builtin_ia32_selectpbf_512:
case X86::BI__builtin_ia32_selectps_128:
case X86::BI__builtin_ia32_selectps_256:
case X86::BI__builtin_ia32_selectps_512:
case X86::BI__builtin_ia32_selectpd_128:
case X86::BI__builtin_ia32_selectpd_256:
case X86::BI__builtin_ia32_selectpd_512: {
// AVX512 predicated move: "Result = Mask[] ? LHS[] : RHS[]".
APValue SourceMask, SourceLHS, SourceRHS;
if (!EvaluateAsRValue(Info, E->getArg(0), SourceMask) ||
!EvaluateAsRValue(Info, E->getArg(1), SourceLHS) ||
!EvaluateAsRValue(Info, E->getArg(2), SourceRHS))
return false;
APSInt Mask = SourceMask.getInt();
unsigned SourceLen = SourceLHS.getVectorLength();
SmallVector<APValue, 4> ResultElements;
ResultElements.reserve(SourceLen);
for (unsigned EltNum = 0; EltNum < SourceLen; ++EltNum) {
const APValue &LHS = SourceLHS.getVectorElt(EltNum);
const APValue &RHS = SourceRHS.getVectorElt(EltNum);
ResultElements.push_back(Mask[EltNum] ? LHS : RHS);
}
return Success(APValue(ResultElements.data(), ResultElements.size()), E);
}
}
}

View File

@ -440,37 +440,37 @@ public:
#define GEN_CLANG_CLAUSE_CLASS
#define CLAUSE_CLASS(Enum, Str, Class) void Visit##Class(const Class *C);
#include "llvm/Frontend/OpenMP/OMP.inc"
void VistOMPClauseWithPreInit(const OMPClauseWithPreInit *C);
void VistOMPClauseWithPostUpdate(const OMPClauseWithPostUpdate *C);
void VisitOMPClauseWithPreInit(const OMPClauseWithPreInit *C);
void VisitOMPClauseWithPostUpdate(const OMPClauseWithPostUpdate *C);
};
void OMPClauseProfiler::VistOMPClauseWithPreInit(
void OMPClauseProfiler::VisitOMPClauseWithPreInit(
const OMPClauseWithPreInit *C) {
if (auto *S = C->getPreInitStmt())
Profiler->VisitStmt(S);
}
void OMPClauseProfiler::VistOMPClauseWithPostUpdate(
void OMPClauseProfiler::VisitOMPClauseWithPostUpdate(
const OMPClauseWithPostUpdate *C) {
VistOMPClauseWithPreInit(C);
VisitOMPClauseWithPreInit(C);
if (auto *E = C->getPostUpdateExpr())
Profiler->VisitStmt(E);
}
void OMPClauseProfiler::VisitOMPIfClause(const OMPIfClause *C) {
VistOMPClauseWithPreInit(C);
VisitOMPClauseWithPreInit(C);
if (C->getCondition())
Profiler->VisitStmt(C->getCondition());
}
void OMPClauseProfiler::VisitOMPFinalClause(const OMPFinalClause *C) {
VistOMPClauseWithPreInit(C);
VisitOMPClauseWithPreInit(C);
if (C->getCondition())
Profiler->VisitStmt(C->getCondition());
}
void OMPClauseProfiler::VisitOMPNumThreadsClause(const OMPNumThreadsClause *C) {
VistOMPClauseWithPreInit(C);
VisitOMPClauseWithPreInit(C);
if (C->getNumThreads())
Profiler->VisitStmt(C->getNumThreads());
}
@ -526,13 +526,13 @@ void OMPClauseProfiler::VisitOMPDetachClause(const OMPDetachClause *C) {
}
void OMPClauseProfiler::VisitOMPNovariantsClause(const OMPNovariantsClause *C) {
VistOMPClauseWithPreInit(C);
VisitOMPClauseWithPreInit(C);
if (C->getCondition())
Profiler->VisitStmt(C->getCondition());
}
void OMPClauseProfiler::VisitOMPNocontextClause(const OMPNocontextClause *C) {
VistOMPClauseWithPreInit(C);
VisitOMPClauseWithPreInit(C);
if (C->getCondition())
Profiler->VisitStmt(C->getCondition());
}
@ -568,7 +568,7 @@ void OMPClauseProfiler::VisitOMPMessageClause(const OMPMessageClause *C) {
}
void OMPClauseProfiler::VisitOMPScheduleClause(const OMPScheduleClause *C) {
VistOMPClauseWithPreInit(C);
VisitOMPClauseWithPreInit(C);
if (auto *S = C->getChunkSize())
Profiler->VisitStmt(S);
}
@ -646,7 +646,7 @@ void OMPClauseProfiler::VisitOMPDestroyClause(const OMPDestroyClause *C) {
}
void OMPClauseProfiler::VisitOMPFilterClause(const OMPFilterClause *C) {
VistOMPClauseWithPreInit(C);
VisitOMPClauseWithPreInit(C);
if (C->getThreadID())
Profiler->VisitStmt(C->getThreadID());
}
@ -669,7 +669,7 @@ void OMPClauseProfiler::VisitOMPPrivateClause(const OMPPrivateClause *C) {
void
OMPClauseProfiler::VisitOMPFirstprivateClause(const OMPFirstprivateClause *C) {
VisitOMPClauseList(C);
VistOMPClauseWithPreInit(C);
VisitOMPClauseWithPreInit(C);
for (auto *E : C->private_copies()) {
if (E)
Profiler->VisitStmt(E);
@ -682,7 +682,7 @@ OMPClauseProfiler::VisitOMPFirstprivateClause(const OMPFirstprivateClause *C) {
void
OMPClauseProfiler::VisitOMPLastprivateClause(const OMPLastprivateClause *C) {
VisitOMPClauseList(C);
VistOMPClauseWithPostUpdate(C);
VisitOMPClauseWithPostUpdate(C);
for (auto *E : C->source_exprs()) {
if (E)
Profiler->VisitStmt(E);
@ -705,7 +705,7 @@ void OMPClauseProfiler::VisitOMPReductionClause(
C->getQualifierLoc().getNestedNameSpecifier());
Profiler->VisitName(C->getNameInfo().getName());
VisitOMPClauseList(C);
VistOMPClauseWithPostUpdate(C);
VisitOMPClauseWithPostUpdate(C);
for (auto *E : C->privates()) {
if (E)
Profiler->VisitStmt(E);
@ -743,7 +743,7 @@ void OMPClauseProfiler::VisitOMPTaskReductionClause(
C->getQualifierLoc().getNestedNameSpecifier());
Profiler->VisitName(C->getNameInfo().getName());
VisitOMPClauseList(C);
VistOMPClauseWithPostUpdate(C);
VisitOMPClauseWithPostUpdate(C);
for (auto *E : C->privates()) {
if (E)
Profiler->VisitStmt(E);
@ -767,7 +767,7 @@ void OMPClauseProfiler::VisitOMPInReductionClause(
C->getQualifierLoc().getNestedNameSpecifier());
Profiler->VisitName(C->getNameInfo().getName());
VisitOMPClauseList(C);
VistOMPClauseWithPostUpdate(C);
VisitOMPClauseWithPostUpdate(C);
for (auto *E : C->privates()) {
if (E)
Profiler->VisitStmt(E);
@ -791,7 +791,7 @@ void OMPClauseProfiler::VisitOMPInReductionClause(
}
void OMPClauseProfiler::VisitOMPLinearClause(const OMPLinearClause *C) {
VisitOMPClauseList(C);
VistOMPClauseWithPostUpdate(C);
VisitOMPClauseWithPostUpdate(C);
for (auto *E : C->privates()) {
if (E)
Profiler->VisitStmt(E);
@ -873,25 +873,25 @@ void OMPClauseProfiler::VisitOMPAllocateClause(const OMPAllocateClause *C) {
}
void OMPClauseProfiler::VisitOMPNumTeamsClause(const OMPNumTeamsClause *C) {
VisitOMPClauseList(C);
VistOMPClauseWithPreInit(C);
VisitOMPClauseWithPreInit(C);
}
void OMPClauseProfiler::VisitOMPThreadLimitClause(
const OMPThreadLimitClause *C) {
VisitOMPClauseList(C);
VistOMPClauseWithPreInit(C);
VisitOMPClauseWithPreInit(C);
}
void OMPClauseProfiler::VisitOMPPriorityClause(const OMPPriorityClause *C) {
VistOMPClauseWithPreInit(C);
VisitOMPClauseWithPreInit(C);
if (C->getPriority())
Profiler->VisitStmt(C->getPriority());
}
void OMPClauseProfiler::VisitOMPGrainsizeClause(const OMPGrainsizeClause *C) {
VistOMPClauseWithPreInit(C);
VisitOMPClauseWithPreInit(C);
if (C->getGrainsize())
Profiler->VisitStmt(C->getGrainsize());
}
void OMPClauseProfiler::VisitOMPNumTasksClause(const OMPNumTasksClause *C) {
VistOMPClauseWithPreInit(C);
VisitOMPClauseWithPreInit(C);
if (C->getNumTasks())
Profiler->VisitStmt(C->getNumTasks());
}
@ -952,7 +952,7 @@ void OMPClauseProfiler::VisitOMPOrderClause(const OMPOrderClause *C) {}
void OMPClauseProfiler::VisitOMPBindClause(const OMPBindClause *C) {}
void OMPClauseProfiler::VisitOMPXDynCGroupMemClause(
const OMPXDynCGroupMemClause *C) {
VistOMPClauseWithPreInit(C);
VisitOMPClauseWithPreInit(C);
if (Expr *Size = C->getSize())
Profiler->VisitStmt(Size);
}
@ -1229,7 +1229,7 @@ void StmtProfiler::VisitOMPDistributeDirective(
void OMPClauseProfiler::VisitOMPDistScheduleClause(
const OMPDistScheduleClause *C) {
VistOMPClauseWithPreInit(C);
VisitOMPClauseWithPreInit(C);
if (auto *S = C->getChunkSize())
Profiler->VisitStmt(S);
}

View File

@ -1627,7 +1627,7 @@ bool QualType::UseExcessPrecision(const ASTContext &Ctx) {
switch (BT->getKind()) {
case BuiltinType::Kind::Float16: {
const TargetInfo &TI = Ctx.getTargetInfo();
if (TI.hasFloat16Type() && !TI.hasLegalHalfType() &&
if (TI.hasFloat16Type() && !TI.hasFastHalfType() &&
Ctx.getLangOpts().getFloat16ExcessPrecision() !=
Ctx.getLangOpts().ExcessPrecisionKind::FPP_None)
return true;

View File

@ -1344,6 +1344,41 @@ private:
return false;
}
template <typename T> static SourceLocation getNodeLocation(const T &Node) {
return Node.getBeginLoc();
}
static SourceLocation getNodeLocation(const CXXCtorInitializer &Node) {
return Node.getSourceLocation();
}
static SourceLocation getNodeLocation(const TemplateArgumentLoc &Node) {
return Node.getLocation();
}
static SourceLocation getNodeLocation(const Attr &Node) {
return Node.getLocation();
}
bool isInSystemHeader(SourceLocation Loc) {
const SourceManager &SM = getASTContext().getSourceManager();
return SM.isInSystemHeader(Loc);
}
template <typename T> bool shouldSkipNode(T &Node) {
if (Options.IgnoreSystemHeaders && isInSystemHeader(getNodeLocation(Node)))
return true;
return false;
}
template <typename T> bool shouldSkipNode(T *Node) {
return (Node == nullptr) || shouldSkipNode(*Node);
}
bool shouldSkipNode(QualType &) { return false; }
bool shouldSkipNode(NestedNameSpecifier &) { return false; }
/// Bucket to record map.
///
/// Used to get the appropriate bucket for each matcher.
@ -1473,9 +1508,8 @@ bool MatchASTVisitor::objcClassIsDerivedFrom(
}
bool MatchASTVisitor::TraverseDecl(Decl *DeclNode) {
if (!DeclNode) {
if (shouldSkipNode(DeclNode))
return true;
}
bool ScopedTraversal =
TraversingASTNodeNotSpelledInSource || DeclNode->isImplicit();
@ -1503,9 +1537,9 @@ bool MatchASTVisitor::TraverseDecl(Decl *DeclNode) {
}
bool MatchASTVisitor::TraverseStmt(Stmt *StmtNode, DataRecursionQueue *Queue) {
if (!StmtNode) {
if (shouldSkipNode(StmtNode))
return true;
}
bool ScopedTraversal = TraversingASTNodeNotSpelledInSource ||
TraversingASTChildrenNotSpelledInSource;
@ -1515,6 +1549,9 @@ bool MatchASTVisitor::TraverseStmt(Stmt *StmtNode, DataRecursionQueue *Queue) {
}
bool MatchASTVisitor::TraverseType(QualType TypeNode, bool TraverseQualifier) {
if (shouldSkipNode(TypeNode))
return true;
match(TypeNode);
return RecursiveASTVisitor<MatchASTVisitor>::TraverseType(TypeNode,
TraverseQualifier);
@ -1522,6 +1559,8 @@ bool MatchASTVisitor::TraverseType(QualType TypeNode, bool TraverseQualifier) {
bool MatchASTVisitor::TraverseTypeLoc(TypeLoc TypeLocNode,
bool TraverseQualifier) {
if (shouldSkipNode(TypeLocNode))
return true;
// The RecursiveASTVisitor only visits types if they're not within TypeLocs.
// We still want to find those types via matchers, so we match them here. Note
// that the TypeLocs are structurally a shadow-hierarchy to the expressed
@ -1534,6 +1573,9 @@ bool MatchASTVisitor::TraverseTypeLoc(TypeLoc TypeLocNode,
}
bool MatchASTVisitor::TraverseNestedNameSpecifier(NestedNameSpecifier NNS) {
if (shouldSkipNode(NNS))
return true;
match(NNS);
return RecursiveASTVisitor<MatchASTVisitor>::TraverseNestedNameSpecifier(NNS);
}
@ -1543,6 +1585,9 @@ bool MatchASTVisitor::TraverseNestedNameSpecifierLoc(
if (!NNS)
return true;
if (shouldSkipNode(NNS))
return true;
match(NNS);
// We only match the nested name specifier here (as opposed to traversing it)
@ -1555,7 +1600,7 @@ bool MatchASTVisitor::TraverseNestedNameSpecifierLoc(
bool MatchASTVisitor::TraverseConstructorInitializer(
CXXCtorInitializer *CtorInit) {
if (!CtorInit)
if (shouldSkipNode(CtorInit))
return true;
bool ScopedTraversal = TraversingASTNodeNotSpelledInSource ||
@ -1573,11 +1618,17 @@ bool MatchASTVisitor::TraverseConstructorInitializer(
}
bool MatchASTVisitor::TraverseTemplateArgumentLoc(TemplateArgumentLoc Loc) {
if (shouldSkipNode(Loc))
return true;
match(Loc);
return RecursiveASTVisitor<MatchASTVisitor>::TraverseTemplateArgumentLoc(Loc);
}
bool MatchASTVisitor::TraverseAttr(Attr *AttrNode) {
if (shouldSkipNode(AttrNode))
return true;
match(*AttrNode);
return RecursiveASTVisitor<MatchASTVisitor>::TraverseAttr(AttrNode);
}

View File

@ -6,6 +6,7 @@ add_clang_library(clangAnalysisFlowSensitive
DataflowAnalysisContext.cpp
DataflowEnvironment.cpp
Formula.cpp
FormulaSerialization.cpp
HTMLLogger.cpp
Logger.cpp
RecordOps.cpp

View File

@ -208,6 +208,24 @@ bool DataflowAnalysisContext::equivalentFormulas(const Formula &Val1,
return isUnsatisfiable(std::move(Constraints));
}
llvm::DenseSet<Atom> DataflowAnalysisContext::collectDependencies(
llvm::DenseSet<Atom> Tokens) const {
// Use a worklist algorithm, with `Remaining` holding the worklist and
// `Tokens` tracking which atoms have already been added to the worklist.
std::vector<Atom> Remaining(Tokens.begin(), Tokens.end());
while (!Remaining.empty()) {
Atom CurrentToken = Remaining.back();
Remaining.pop_back();
if (auto DepsIt = FlowConditionDeps.find(CurrentToken);
DepsIt != FlowConditionDeps.end())
for (Atom A : DepsIt->second)
if (Tokens.insert(A).second)
Remaining.push_back(A);
}
return Tokens;
}
void DataflowAnalysisContext::addTransitiveFlowConditionConstraints(
Atom Token, llvm::SetVector<const Formula *> &Constraints) {
llvm::DenseSet<Atom> AddedTokens;
@ -224,6 +242,8 @@ void DataflowAnalysisContext::addTransitiveFlowConditionConstraints(
auto ConstraintsIt = FlowConditionConstraints.find(Token);
if (ConstraintsIt == FlowConditionConstraints.end()) {
// The flow condition is unconstrained. Just add the atom directly, which
// is equivalent to asserting it is true.
Constraints.insert(&arena().makeAtomRef(Token));
} else {
// Bind flow condition token via `iff` to its set of constraints:
@ -239,6 +259,65 @@ void DataflowAnalysisContext::addTransitiveFlowConditionConstraints(
}
}
static void getReferencedAtoms(const Formula &F,
llvm::DenseSet<dataflow::Atom> &Refs) {
switch (F.kind()) {
case Formula::AtomRef:
Refs.insert(F.getAtom());
break;
case Formula::Literal:
break;
case Formula::Not:
getReferencedAtoms(*F.operands()[0], Refs);
break;
case Formula::And:
case Formula::Or:
case Formula::Implies:
case Formula::Equal:
ArrayRef<const Formula *> Operands = F.operands();
getReferencedAtoms(*Operands[0], Refs);
getReferencedAtoms(*Operands[1], Refs);
break;
}
}
SimpleLogicalContext DataflowAnalysisContext::exportLogicalContext(
llvm::DenseSet<dataflow::Atom> TargetTokens) const {
SimpleLogicalContext LC;
if (Invariant != nullptr) {
LC.Invariant = Invariant;
getReferencedAtoms(*Invariant, TargetTokens);
}
llvm::DenseSet<dataflow::Atom> Dependencies =
collectDependencies(std::move(TargetTokens));
for (dataflow::Atom Token : Dependencies) {
// Only process the token if it is constrained. Unconstrained tokens don't
// have dependencies.
const Formula *Constraints = FlowConditionConstraints.lookup(Token);
if (Constraints == nullptr)
continue;
LC.TokenDefs[Token] = Constraints;
if (auto DepsIt = FlowConditionDeps.find(Token);
DepsIt != FlowConditionDeps.end())
LC.TokenDeps[Token] = DepsIt->second;
}
return LC;
}
void DataflowAnalysisContext::initLogicalContext(SimpleLogicalContext LC) {
Invariant = LC.Invariant;
FlowConditionConstraints = std::move(LC.TokenDefs);
// TODO: The dependencies in `LC.TokenDeps` can be reconstructed from
// `LC.TokenDefs`. Give the caller the option to reconstruct, rather than
// providing them directly, to save caller space (memory/disk).
FlowConditionDeps = std::move(LC.TokenDeps);
}
static void printAtomList(const llvm::SmallVector<Atom> &Atoms,
llvm::raw_ostream &OS) {
OS << "(";

View File

@ -0,0 +1,153 @@
//===- FormulaSerialization.cpp ---------------------------------*- 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
//
//===----------------------------------------------------------------------===//
#include "clang/Analysis/FlowSensitive/FormulaSerialization.h"
#include "clang/Analysis/FlowSensitive/Arena.h"
#include "clang/Analysis/FlowSensitive/Formula.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include <cassert>
namespace clang::dataflow {
// Returns the leading indicator of operation formulas. `AtomRef` and `Literal`
// are handled differently.
static char compactSigil(Formula::Kind K) {
switch (K) {
case Formula::AtomRef:
case Formula::Literal:
// No sigil.
return '\0';
case Formula::Not:
return '!';
case Formula::And:
return '&';
case Formula::Or:
return '|';
case Formula::Implies:
return '>';
case Formula::Equal:
return '=';
}
llvm_unreachable("unhandled formula kind");
}
void serializeFormula(const Formula &F, llvm::raw_ostream &OS) {
switch (Formula::numOperands(F.kind())) {
case 0:
switch (F.kind()) {
case Formula::AtomRef:
OS << F.getAtom();
break;
case Formula::Literal:
OS << (F.literal() ? 'T' : 'F');
break;
default:
llvm_unreachable("unhandled formula kind");
}
break;
case 1:
OS << compactSigil(F.kind());
serializeFormula(*F.operands()[0], OS);
break;
case 2:
OS << compactSigil(F.kind());
serializeFormula(*F.operands()[0], OS);
serializeFormula(*F.operands()[1], OS);
break;
default:
llvm_unreachable("unhandled formula arity");
}
}
static llvm::Expected<const Formula *>
parsePrefix(llvm::StringRef &Str, Arena &A,
llvm::DenseMap<unsigned, Atom> &AtomMap) {
if (Str.empty())
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"unexpected end of input");
char Prefix = Str[0];
Str = Str.drop_front();
switch (Prefix) {
case 'T':
return &A.makeLiteral(true);
case 'F':
return &A.makeLiteral(false);
case 'V': {
unsigned AtomID;
if (Str.consumeInteger(10, AtomID))
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"expected atom id");
auto [It, Inserted] = AtomMap.try_emplace(AtomID, Atom());
if (Inserted)
It->second = A.makeAtom();
return &A.makeAtomRef(It->second);
}
case '!': {
auto OperandOrErr = parsePrefix(Str, A, AtomMap);
if (!OperandOrErr)
return OperandOrErr.takeError();
return &A.makeNot(**OperandOrErr);
}
case '&':
case '|':
case '>':
case '=': {
auto LeftOrErr = parsePrefix(Str, A, AtomMap);
if (!LeftOrErr)
return LeftOrErr.takeError();
auto RightOrErr = parsePrefix(Str, A, AtomMap);
if (!RightOrErr)
return RightOrErr.takeError();
const Formula &LHS = **LeftOrErr;
const Formula &RHS = **RightOrErr;
switch (Prefix) {
case '&':
return &A.makeAnd(LHS, RHS);
case '|':
return &A.makeOr(LHS, RHS);
case '>':
return &A.makeImplies(LHS, RHS);
case '=':
return &A.makeEquals(LHS, RHS);
default:
llvm_unreachable("unexpected binary op");
}
}
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"unexpected prefix character: %c", Prefix);
}
}
llvm::Expected<const Formula *>
parseFormula(llvm::StringRef Str, Arena &A,
llvm::DenseMap<unsigned, Atom> &AtomMap) {
size_t OriginalSize = Str.size();
llvm::Expected<const Formula *> F = parsePrefix(Str, A, AtomMap);
if (!F)
return F.takeError();
if (!Str.empty())
return llvm::createStringError(llvm::inconvertibleErrorCode(),
("unexpected suffix of length: " +
llvm::Twine(Str.size() - OriginalSize))
.str());
return F;
}
} // namespace clang::dataflow

View File

@ -45,10 +45,11 @@ struct Loan {
/// is represented as empty LoanSet
LoanID ID;
AccessPath Path;
SourceLocation IssueLoc;
/// The expression that creates the loan, e.g., &x.
const Expr *IssueExpr;
Loan(LoanID id, AccessPath path, SourceLocation loc)
: ID(id), Path(path), IssueLoc(loc) {}
Loan(LoanID id, AccessPath path, const Expr *IssueExpr)
: ID(id), Path(path), IssueExpr(IssueExpr) {}
};
/// An Origin is a symbolic identifier that represents the set of possible
@ -82,8 +83,8 @@ class LoanManager {
public:
LoanManager() = default;
Loan &addLoan(AccessPath Path, SourceLocation Loc) {
AllLoans.emplace_back(getNextLoanID(), Path, Loc);
Loan &addLoan(AccessPath Path, const Expr *IssueExpr) {
AllLoans.emplace_back(getNextLoanID(), Path, IssueExpr);
return AllLoans.back();
}
@ -199,6 +200,8 @@ public:
AssignOrigin,
/// An origin escapes the function by flowing into the return value.
ReturnOfOrigin,
/// An origin is used (eg. dereferencing a pointer).
Use,
/// A marker for a specific point in the code, for testing.
TestPoint,
};
@ -242,12 +245,17 @@ public:
class ExpireFact : public Fact {
LoanID LID;
SourceLocation ExpiryLoc;
public:
static bool classof(const Fact *F) { return F->getKind() == Kind::Expire; }
ExpireFact(LoanID LID) : Fact(Kind::Expire), LID(LID) {}
ExpireFact(LoanID LID, SourceLocation ExpiryLoc)
: Fact(Kind::Expire), LID(LID), ExpiryLoc(ExpiryLoc) {}
LoanID getLoanID() const { return LID; }
SourceLocation getExpiryLoc() const { return ExpiryLoc; }
void dump(llvm::raw_ostream &OS) const override {
OS << "Expire (LoanID: " << getLoanID() << ")\n";
}
@ -287,6 +295,24 @@ public:
}
};
class UseFact : public Fact {
OriginID UsedOrigin;
const Expr *UseExpr;
public:
static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
UseFact(OriginID UsedOrigin, const Expr *UseExpr)
: Fact(Kind::Use), UsedOrigin(UsedOrigin), UseExpr(UseExpr) {}
OriginID getUsedOrigin() const { return UsedOrigin; }
const Expr *getUseExpr() const { return UseExpr; }
void dump(llvm::raw_ostream &OS) const override {
OS << "Use (OriginID: " << UsedOrigin << ")\n";
}
};
/// A dummy-fact used to mark a specific point in the code for testing.
/// It is generated by recognizing a `void("__lifetime_test_point_...")` cast.
class TestPointFact : public Fact {
@ -417,13 +443,17 @@ public:
if (VD->hasLocalStorage()) {
OriginID OID = FactMgr.getOriginMgr().getOrCreate(*UO);
AccessPath AddrOfLocalVarPath(VD);
const Loan &L = FactMgr.getLoanMgr().addLoan(AddrOfLocalVarPath,
UO->getOperatorLoc());
const Loan &L =
FactMgr.getLoanMgr().addLoan(AddrOfLocalVarPath, UO);
CurrentBlockFacts.push_back(
FactMgr.createFact<IssueFact>(L.ID, OID));
}
}
}
} else if (UO->getOpcode() == UO_Deref) {
// This is a pointer use, like '*p'.
OriginID OID = FactMgr.getOriginMgr().get(*UO->getSubExpr());
CurrentBlockFacts.push_back(FactMgr.createFact<UseFact>(OID, UO));
}
}
@ -492,7 +522,8 @@ private:
// Check if the loan is for a stack variable and if that variable
// is the one being destructed.
if (LoanPath.D == DestructedVD)
CurrentBlockFacts.push_back(FactMgr.createFact<ExpireFact>(L.ID));
CurrentBlockFacts.push_back(FactMgr.createFact<ExpireFact>(
L.ID, DtorOpt.getTriggerStmt()->getEndLoc()));
}
}
@ -618,6 +649,7 @@ public:
}
}
protected:
Lattice getState(ProgramPoint P) const { return PerPointStates.lookup(P); }
Lattice getInState(const CFGBlock *B) const { return InStates.lookup(B); }
@ -665,6 +697,8 @@ private:
return D->transfer(In, *F->getAs<AssignOriginFact>());
case Fact::Kind::ReturnOfOrigin:
return D->transfer(In, *F->getAs<ReturnOfOriginFact>());
case Fact::Kind::Use:
return D->transfer(In, *F->getAs<UseFact>());
case Fact::Kind::TestPoint:
return D->transfer(In, *F->getAs<TestPointFact>());
}
@ -676,6 +710,7 @@ public:
Lattice transfer(Lattice In, const ExpireFact &) { return In; }
Lattice transfer(Lattice In, const AssignOriginFact &) { return In; }
Lattice transfer(Lattice In, const ReturnOfOriginFact &) { return In; }
Lattice transfer(Lattice In, const UseFact &) { return In; }
Lattice transfer(Lattice In, const TestPointFact &) { return In; }
};
@ -693,6 +728,20 @@ static llvm::ImmutableSet<T> join(llvm::ImmutableSet<T> A,
return A;
}
/// Checks if set A is a subset of set B.
template <typename T>
static bool isSubsetOf(const llvm::ImmutableSet<T> &A,
const llvm::ImmutableSet<T> &B) {
// Empty set is a subset of all sets.
if (A.isEmpty())
return true;
for (const T &Elem : A)
if (!B.contains(Elem))
return false;
return true;
}
/// Computes the key-wise union of two ImmutableMaps.
// TODO(opt): This key-wise join is a performance bottleneck. A more
// efficient merge could be implemented using a Patricia Trie or HAMT
@ -700,7 +749,7 @@ static llvm::ImmutableSet<T> join(llvm::ImmutableSet<T> A,
template <typename K, typename V, typename Joiner>
static llvm::ImmutableMap<K, V>
join(llvm::ImmutableMap<K, V> A, llvm::ImmutableMap<K, V> B,
typename llvm::ImmutableMap<K, V>::Factory &F, Joiner joinValues) {
typename llvm::ImmutableMap<K, V>::Factory &F, Joiner JoinValues) {
if (A.getHeight() < B.getHeight())
std::swap(A, B);
@ -710,7 +759,7 @@ join(llvm::ImmutableMap<K, V> A, llvm::ImmutableMap<K, V> B,
const K &Key = Entry.first;
const V &ValB = Entry.second;
if (const V *ValA = A.lookup(Key))
A = F.add(A, Key, joinValues(*ValA, ValB));
A = F.add(A, Key, JoinValues(*ValA, ValB));
else
A = F.add(A, Key, ValB);
}
@ -723,17 +772,14 @@ join(llvm::ImmutableMap<K, V> A, llvm::ImmutableMap<K, V> B,
// ========================================================================= //
using OriginLoanMap = llvm::ImmutableMap<OriginID, LoanSet>;
using ExpiredLoanMap = llvm::ImmutableMap<LoanID, const ExpireFact *>;
/// An object to hold the factories for immutable collections, ensuring
/// that all created states share the same underlying memory management.
struct LifetimeFactory {
OriginLoanMap::Factory OriginMapFactory;
LoanSet::Factory LoanSetFactory;
/// Creates a singleton set containing only the given loan ID.
LoanSet createLoanSet(LoanID LID) {
return LoanSetFactory.add(LoanSetFactory.getEmptySet(), LID);
}
ExpiredLoanMap::Factory ExpiredLoanMapFactory;
};
/// Represents the dataflow lattice for loan propagation.
@ -774,13 +820,15 @@ struct LoanPropagationLattice {
class LoanPropagationAnalysis
: public DataflowAnalysis<LoanPropagationAnalysis, LoanPropagationLattice,
Direction::Forward> {
LifetimeFactory &Factory;
OriginLoanMap::Factory &OriginLoanMapFactory;
LoanSet::Factory &LoanSetFactory;
public:
LoanPropagationAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F,
LifetimeFactory &Factory)
: DataflowAnalysis(C, AC, F), Factory(Factory) {}
LifetimeFactory &LFactory)
: DataflowAnalysis(C, AC, F),
OriginLoanMapFactory(LFactory.OriginMapFactory),
LoanSetFactory(LFactory.LoanSetFactory) {}
using Base::transfer;
@ -792,9 +840,9 @@ public:
// TODO(opt): Keep the state small by removing origins which become dead.
Lattice join(Lattice A, Lattice B) {
OriginLoanMap JoinedOrigins =
utils::join(A.Origins, B.Origins, Factory.OriginMapFactory,
[this](LoanSet S1, LoanSet S2) {
return utils::join(S1, S2, Factory.LoanSetFactory);
utils::join(A.Origins, B.Origins, OriginLoanMapFactory,
[&](LoanSet S1, LoanSet S2) {
return utils::join(S1, S2, LoanSetFactory);
});
return Lattice(JoinedOrigins);
}
@ -803,8 +851,9 @@ public:
Lattice transfer(Lattice In, const IssueFact &F) {
OriginID OID = F.getOriginID();
LoanID LID = F.getLoanID();
return LoanPropagationLattice(Factory.OriginMapFactory.add(
In.Origins, OID, Factory.createLoanSet(LID)));
return LoanPropagationLattice(OriginLoanMapFactory.add(
In.Origins, OID,
LoanSetFactory.add(LoanSetFactory.getEmptySet(), LID)));
}
/// The destination origin's loan set is replaced by the source's.
@ -814,7 +863,7 @@ public:
OriginID SrcOID = F.getSrcOriginID();
LoanSet SrcLoans = getLoans(In, SrcOID);
return LoanPropagationLattice(
Factory.OriginMapFactory.add(In.Origins, DestOID, SrcLoans));
OriginLoanMapFactory.add(In.Origins, DestOID, SrcLoans));
}
LoanSet getLoans(OriginID OID, ProgramPoint P) {
@ -825,7 +874,7 @@ private:
LoanSet getLoans(Lattice L, OriginID OID) {
if (auto *Loans = L.Origins.lookup(OID))
return *Loans;
return Factory.LoanSetFactory.getEmptySet();
return LoanSetFactory.getEmptySet();
}
};
@ -835,10 +884,11 @@ private:
/// The dataflow lattice for tracking the set of expired loans.
struct ExpiredLattice {
LoanSet Expired;
/// Map from an expired `LoanID` to the `ExpireFact` that made it expire.
ExpiredLoanMap Expired;
ExpiredLattice() : Expired(nullptr) {};
explicit ExpiredLattice(LoanSet S) : Expired(S) {}
explicit ExpiredLattice(ExpiredLoanMap M) : Expired(M) {}
bool operator==(const ExpiredLattice &Other) const {
return Expired == Other.Expired;
@ -851,8 +901,8 @@ struct ExpiredLattice {
OS << "ExpiredLattice State:\n";
if (Expired.isEmpty())
OS << " <empty>\n";
for (const LoanID &LID : Expired)
OS << " Loan " << LID << " is expired\n";
for (const auto &[ID, _] : Expired)
OS << " Loan " << ID << " is expired\n";
}
};
@ -861,26 +911,31 @@ class ExpiredLoansAnalysis
: public DataflowAnalysis<ExpiredLoansAnalysis, ExpiredLattice,
Direction::Forward> {
LoanSet::Factory &Factory;
ExpiredLoanMap::Factory &Factory;
public:
ExpiredLoansAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F,
LifetimeFactory &Factory)
: DataflowAnalysis(C, AC, F), Factory(Factory.LoanSetFactory) {}
: DataflowAnalysis(C, AC, F), Factory(Factory.ExpiredLoanMapFactory) {}
using Base::transfer;
StringRef getAnalysisName() const { return "ExpiredLoans"; }
Lattice getInitialState() { return Lattice(Factory.getEmptySet()); }
Lattice getInitialState() { return Lattice(Factory.getEmptyMap()); }
/// Merges two lattices by taking the union of the expired loan sets.
Lattice join(Lattice L1, Lattice L2) const {
return Lattice(utils::join(L1.Expired, L2.Expired, Factory));
/// Merges two lattices by taking the union of the two expired loans.
Lattice join(Lattice L1, Lattice L2) {
return Lattice(
utils::join(L1.Expired, L2.Expired, Factory,
// Take the last expiry fact to make this hermetic.
[](const ExpireFact *F1, const ExpireFact *F2) {
return F1->getExpiryLoc() > F2->getExpiryLoc() ? F1 : F2;
}));
}
Lattice transfer(Lattice In, const ExpireFact &F) {
return Lattice(Factory.add(In.Expired, F.getLoanID()));
return Lattice(Factory.add(In.Expired, F.getLoanID(), &F));
}
// Removes the loan from the set of expired loans.
@ -912,15 +967,116 @@ public:
Lattice transfer(Lattice In, const IssueFact &F) {
return Lattice(Factory.remove(In.Expired, F.getLoanID()));
}
ExpiredLoanMap getExpiredLoans(ProgramPoint P) { return getState(P).Expired; }
};
// ========================================================================= //
// TODO:
// - Modify loan expiry analysis to answer `bool isExpired(Loan L, Point P)`
// - Modify origin liveness analysis to answer `bool isLive(Origin O, Point P)`
// - Using the above three to perform the final error reporting.
// Lifetime checker and Error reporter
// ========================================================================= //
/// Struct to store the complete context for a potential lifetime violation.
struct PendingWarning {
SourceLocation ExpiryLoc; // Where the loan expired.
const Expr *UseExpr; // Where the origin holding this loan was used.
Confidence ConfidenceLevel;
};
class LifetimeChecker {
private:
llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
LoanPropagationAnalysis &LoanPropagation;
ExpiredLoansAnalysis &ExpiredLoans;
FactManager &FactMgr;
AnalysisDeclContext &ADC;
LifetimeSafetyReporter *Reporter;
public:
LifetimeChecker(LoanPropagationAnalysis &LPA, ExpiredLoansAnalysis &ELA,
FactManager &FM, AnalysisDeclContext &ADC,
LifetimeSafetyReporter *Reporter)
: LoanPropagation(LPA), ExpiredLoans(ELA), FactMgr(FM), ADC(ADC),
Reporter(Reporter) {}
void run() {
llvm::TimeTraceScope TimeProfile("LifetimeChecker");
for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
for (const Fact *F : FactMgr.getFacts(B))
if (const auto *UF = F->getAs<UseFact>())
checkUse(UF);
issuePendingWarnings();
}
/// Checks for use-after-free errors for a given use of an Origin.
///
/// This method is called for each 'UseFact' identified in the control flow
/// graph. It determines if the loans held by the used origin have expired
/// at the point of use.
void checkUse(const UseFact *UF) {
OriginID O = UF->getUsedOrigin();
// Get the set of loans that the origin might hold at this program point.
LoanSet HeldLoans = LoanPropagation.getLoans(O, UF);
// Get the set of all loans that have expired at this program point.
ExpiredLoanMap AllExpiredLoans = ExpiredLoans.getExpiredLoans(UF);
// If the pointer holds no loans or no loans have expired, there's nothing
// to check.
if (HeldLoans.isEmpty() || AllExpiredLoans.isEmpty())
return;
// Identify loans that which have expired but are held by the pointer. Using
// them is a use-after-free.
llvm::SmallVector<LoanID> DefaultedLoans;
// A definite UaF error occurs if all loans the origin might hold have
// expired.
bool IsDefiniteError = true;
for (LoanID L : HeldLoans) {
if (AllExpiredLoans.contains(L))
DefaultedLoans.push_back(L);
else
// If at least one loan is not expired, this use is not a definite UaF.
IsDefiniteError = false;
}
// If there are no defaulted loans, the use is safe.
if (DefaultedLoans.empty())
return;
// Determine the confidence level of the error (definite or maybe).
Confidence CurrentConfidence =
IsDefiniteError ? Confidence::Definite : Confidence::Maybe;
// For each expired loan, create a pending warning.
for (LoanID DefaultedLoan : DefaultedLoans) {
// If we already have a warning for this loan with a higher or equal
// confidence, skip this one.
if (FinalWarningsMap.count(DefaultedLoan) &&
CurrentConfidence <= FinalWarningsMap[DefaultedLoan].ConfidenceLevel)
continue;
auto *EF = AllExpiredLoans.lookup(DefaultedLoan);
assert(EF && "Could not find ExpireFact for an expired loan.");
FinalWarningsMap[DefaultedLoan] = {/*ExpiryLoc=*/(*EF)->getExpiryLoc(),
/*UseExpr=*/UF->getUseExpr(),
/*ConfidenceLevel=*/CurrentConfidence};
}
}
void issuePendingWarnings() {
if (!Reporter)
return;
for (const auto &[LID, Warning] : FinalWarningsMap) {
const Loan &L = FactMgr.getLoanMgr().getLoan(LID);
const Expr *IssueExpr = L.IssueExpr;
Reporter->reportUseAfterFree(IssueExpr, Warning.UseExpr,
Warning.ExpiryLoc, Warning.ConfidenceLevel);
}
}
};
// ========================================================================= //
// LifetimeSafetyAnalysis Class Implementation
// ========================================================================= //
@ -928,8 +1084,9 @@ public:
// We need this here for unique_ptr with forward declared class.
LifetimeSafetyAnalysis::~LifetimeSafetyAnalysis() = default;
LifetimeSafetyAnalysis::LifetimeSafetyAnalysis(AnalysisDeclContext &AC)
: AC(AC), Factory(std::make_unique<LifetimeFactory>()),
LifetimeSafetyAnalysis::LifetimeSafetyAnalysis(AnalysisDeclContext &AC,
LifetimeSafetyReporter *Reporter)
: AC(AC), Reporter(Reporter), Factory(std::make_unique<LifetimeFactory>()),
FactMgr(std::make_unique<FactManager>()) {}
void LifetimeSafetyAnalysis::run() {
@ -952,6 +1109,8 @@ void LifetimeSafetyAnalysis::run() {
/// blocks; only Decls are visible. Therefore, loans in a block that
/// never reach an Origin associated with a Decl can be safely dropped by
/// the analysis.
/// 3. Collapse ExpireFacts belonging to same source location into a single
/// Fact.
LoanPropagation =
std::make_unique<LoanPropagationAnalysis>(Cfg, AC, *FactMgr, *Factory);
LoanPropagation->run();
@ -959,6 +1118,10 @@ void LifetimeSafetyAnalysis::run() {
ExpiredLoans =
std::make_unique<ExpiredLoansAnalysis>(Cfg, AC, *FactMgr, *Factory);
ExpiredLoans->run();
LifetimeChecker Checker(*LoanPropagation, *ExpiredLoans, *FactMgr, AC,
Reporter);
Checker.run();
}
LoanSet LifetimeSafetyAnalysis::getLoansAtPoint(OriginID OID,
@ -967,9 +1130,13 @@ LoanSet LifetimeSafetyAnalysis::getLoansAtPoint(OriginID OID,
return LoanPropagation->getLoans(OID, PP);
}
LoanSet LifetimeSafetyAnalysis::getExpiredLoansAtPoint(ProgramPoint PP) const {
std::vector<LoanID>
LifetimeSafetyAnalysis::getExpiredLoansAtPoint(ProgramPoint PP) const {
assert(ExpiredLoans && "ExpiredLoansAnalysis has not been run.");
return ExpiredLoans->getState(PP).Expired;
std::vector<LoanID> Result;
for (const auto &pair : ExpiredLoans->getExpiredLoans(PP))
Result.push_back(pair.first);
return Result;
}
std::optional<OriginID>
@ -1009,8 +1176,9 @@ llvm::StringMap<ProgramPoint> LifetimeSafetyAnalysis::getTestPoints() const {
}
} // namespace internal
void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC) {
internal::LifetimeSafetyAnalysis Analysis(AC);
void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC,
LifetimeSafetyReporter *Reporter) {
internal::LifetimeSafetyAnalysis Analysis(AC, Reporter);
Analysis.run();
}
} // namespace clang::lifetimes

View File

@ -62,7 +62,7 @@ TargetInfo::TargetInfo(const llvm::Triple &T) : Triple(T) {
TLSSupported = true;
VLASupported = true;
NoAsmVariants = false;
HasLegalHalfType = false;
HasFastHalfType = false;
HalfArgsAndReturns = false;
HasFloat128 = false;
HasIbm128 = false;

View File

@ -142,7 +142,7 @@ AArch64TargetInfo::AArch64TargetInfo(const llvm::Triple &Triple,
AddrSpaceMap = &ARM64AddrSpaceMap;
// All AArch64 implementations support ARMv8 FP, which makes half a legal type.
HasLegalHalfType = true;
HasFastHalfType = true;
HalfArgsAndReturns = true;
HasFloat16 = true;
HasStrictFP = true;

View File

@ -251,7 +251,7 @@ AMDGPUTargetInfo::AMDGPUTargetInfo(const llvm::Triple &Triple,
BFloat16Format = &llvm::APFloat::BFloat();
}
HasLegalHalfType = true;
HasFastHalfType = true;
HasFloat16 = true;
WavefrontSize = (GPUFeatures & llvm::AMDGPU::FEATURE_WAVE32) ? 32 : 64;

View File

@ -585,13 +585,13 @@ bool ARMTargetInfo::handleTargetFeatures(std::vector<std::string> &Features,
} else if (Feature == "+fp16") {
HW_FP |= HW_FP_HP;
} else if (Feature == "+fullfp16") {
HasLegalHalfType = true;
HasFastHalfType = true;
} else if (Feature == "+dotprod") {
DotProd = true;
} else if (Feature == "+mve") {
MVE |= MVE_INT;
} else if (Feature == "+mve.fp") {
HasLegalHalfType = true;
HasFastHalfType = true;
FPU |= FPARMV8;
MVE |= MVE_INT | MVE_FP;
HW_FP |= HW_FP_SP | HW_FP_HP;
@ -1014,11 +1014,11 @@ void ARMTargetInfo::getTargetDefines(const LangOptions &Opts,
Builder.defineMacro("__ARM_FP_FAST", "1");
// Armv8.2-A FP16 vector intrinsic
if ((FPU & NeonFPU) && HasLegalHalfType)
if ((FPU & NeonFPU) && HasFastHalfType)
Builder.defineMacro("__ARM_FEATURE_FP16_VECTOR_ARITHMETIC", "1");
// Armv8.2-A FP16 scalar intrinsics
if (HasLegalHalfType)
if (HasFastHalfType)
Builder.defineMacro("__ARM_FEATURE_FP16_SCALAR_ARITHMETIC", "1");
// Armv8.2-A dot product intrinsics

View File

@ -59,7 +59,7 @@ public:
VLASupported = false;
AddrSpaceMap = &DirectXAddrSpaceMap;
UseAddrSpaceMapMangling = true;
HasLegalHalfType = true;
HasFastHalfType = true;
HasFloat16 = true;
NoAsmVariants = true;
PlatformMinVersion = Triple.getOSVersion();

View File

@ -149,7 +149,7 @@ bool HexagonTargetInfo::handleTargetFeatures(std::vector<std::string> &Features,
HasAudio = true;
}
if (CPU.compare("hexagonv68") >= 0) {
HasLegalHalfType = true;
HasFastHalfType = true;
HasFloat16 = true;
}
return true;

View File

@ -65,7 +65,7 @@ NVPTXTargetInfo::NVPTXTargetInfo(const llvm::Triple &Triple,
GPU = OffloadArch::UNUSED;
// PTX supports f16 as a fundamental type.
HasLegalHalfType = true;
HasFastHalfType = true;
HasFloat16 = true;
if (TargetPointerWidth == 32)

View File

@ -427,7 +427,7 @@ bool RISCVTargetInfo::handleTargetFeatures(std::vector<std::string> &Features,
ABI = ISAInfo->computeDefaultABI().str();
if (ISAInfo->hasExtension("zfh") || ISAInfo->hasExtension("zhinx"))
HasLegalHalfType = true;
HasFastHalfType = true;
FastScalarUnalignedAccess =
llvm::is_contained(Features, "+unaligned-scalar-mem");

View File

@ -106,7 +106,7 @@ protected:
LongWidth = LongAlign = 64;
AddrSpaceMap = &SPIRDefIsPrivMap;
UseAddrSpaceMapMangling = true;
HasLegalHalfType = true;
HasFastHalfType = true;
HasFloat16 = true;
// Define available target features
// These must be defined in sorted order!
@ -427,7 +427,7 @@ public:
BFloat16Width = BFloat16Align = 16;
BFloat16Format = &llvm::APFloat::BFloat();
HasLegalHalfType = true;
HasFastHalfType = true;
HasFloat16 = true;
HalfArgsAndReturns = true;

View File

@ -104,7 +104,7 @@ public:
// -ffloat16-excess-precision=none is given, no conversions will be made
// and instead the backend will promote each half operation to float
// individually.
HasLegalHalfType = false;
HasFastHalfType = false;
HasStrictFP = true;
}

View File

@ -348,7 +348,7 @@ bool X86TargetInfo::handleTargetFeatures(std::vector<std::string> &Features,
HasAVX512BF16 = true;
} else if (Feature == "+avx512fp16") {
HasAVX512FP16 = true;
HasLegalHalfType = true;
HasFastHalfType = true;
} else if (Feature == "+avx512dq") {
HasAVX512DQ = true;
} else if (Feature == "+avx512bitalg") {

View File

@ -84,6 +84,10 @@ public:
llvm_unreachable("Unsupported format for long double");
}
mlir::Type getPtrToVPtrType() {
return getPointerTo(cir::VPtrType::get(getContext()));
}
/// Get a CIR record kind from a AST declaration tag.
cir::RecordType::RecordKind getRecordKind(const clang::TagTypeKind kind) {
switch (kind) {
@ -263,6 +267,9 @@ public:
cir::ConstantOp getSInt32(int32_t c, mlir::Location loc) {
return getConstantInt(loc, getSInt32Ty(), c);
}
cir::ConstantOp getUInt32(uint32_t c, mlir::Location loc) {
return getConstantInt(loc, getUInt32Ty(), c);
}
// Creates constant nullptr for pointer type ty.
cir::ConstantOp getNullPtr(mlir::Type ty, mlir::Location loc) {

View File

@ -312,6 +312,20 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
case Builtin::BI__builtin_rotateright64:
return emitRotate(e, /*isRotateLeft=*/false);
case Builtin::BI__builtin_return_address:
case Builtin::BI__builtin_frame_address: {
mlir::Location loc = getLoc(e->getExprLoc());
llvm::APSInt level = e->getArg(0)->EvaluateKnownConstInt(getContext());
if (builtinID == Builtin::BI__builtin_return_address) {
return RValue::get(cir::ReturnAddrOp::create(
builder, loc,
builder.getConstAPInt(loc, builder.getUInt32Ty(), level)));
}
return RValue::get(cir::FrameAddrOp::create(
builder, loc,
builder.getConstAPInt(loc, builder.getUInt32Ty(), level)));
}
case Builtin::BI__builtin_trap:
emitTrap(loc, /*createNewBlock=*/true);
return RValue::get(nullptr);

View File

@ -289,7 +289,7 @@ void CIRGenFunction::initializeVTablePointer(mlir::Location loc,
}
// Apply the offsets.
Address vtableField = loadCXXThisAddress();
Address classAddr = loadCXXThisAddress();
if (!nonVirtualOffset.isZero() || virtualOffset) {
cgm.errorNYI(loc,
"initializeVTablePointer: non-virtual and virtual offset");
@ -300,9 +300,9 @@ void CIRGenFunction::initializeVTablePointer(mlir::Location loc,
// vtable field is derived from `this` pointer, therefore they should be in
// the same addr space.
assert(!cir::MissingFeatures::addressSpace());
// TODO(cir): This should be cir.vtable.get_vptr.
vtableField = builder.createElementBitCast(loc, vtableField,
vtableAddressPoint.getType());
auto vtablePtr = cir::VTableGetVPtrOp::create(
builder, loc, builder.getPtrToVPtrType(), classAddr.getPointer());
Address vtableField = Address(vtablePtr, classAddr.getAlignment());
builder.createStore(loc, vtableAddressPoint, vtableField);
assert(!cir::MissingFeatures::opTBAA());
assert(!cir::MissingFeatures::createInvariantGroup());
@ -657,6 +657,23 @@ Address CIRGenFunction::getAddressOfBaseClass(
return value;
}
mlir::Value CIRGenFunction::getVTablePtr(mlir::Location loc, Address thisAddr,
const CXXRecordDecl *rd) {
auto vtablePtr = cir::VTableGetVPtrOp::create(
builder, loc, builder.getPtrToVPtrType(), thisAddr.getPointer());
Address vtablePtrAddr = Address(vtablePtr, thisAddr.getAlignment());
auto vtable = builder.createLoad(loc, vtablePtrAddr);
assert(!cir::MissingFeatures::opTBAA());
if (cgm.getCodeGenOpts().OptimizationLevel > 0 &&
cgm.getCodeGenOpts().StrictVTablePointers) {
assert(!cir::MissingFeatures::createInvariantGroup());
}
return vtable;
}
void CIRGenFunction::emitCXXConstructorCall(const clang::CXXConstructorDecl *d,
clang::CXXCtorType type,
bool forVirtualBase,

View File

@ -80,7 +80,7 @@ public:
// initializer or to propagate to another context; for example,
// side effects, or emitting an initialization that requires a
// reference to its current location.
mlir::Attribute emitForMemory(mlir::Attribute c, QualType t);
mlir::Attribute emitForMemory(mlir::Attribute c, QualType destType);
/// Try to emit the initializer of the given declaration as an abstract
/// constant.
@ -90,8 +90,9 @@ public:
/// asserting that it succeeded. This is only safe to do when the
/// expression is known to be a constant expression with either a fairly
/// simple type or a known simple form.
mlir::Attribute emitAbstract(const Expr *e, QualType destType);
mlir::Attribute emitAbstract(SourceLocation loc, const APValue &value,
QualType t);
QualType destType);
mlir::Attribute tryEmitConstantExpr(const ConstantExpr *ce);
@ -101,6 +102,7 @@ public:
mlir::Attribute tryEmitPrivateForVarInit(const VarDecl &d);
mlir::TypedAttr tryEmitPrivate(const Expr *e, QualType destType);
mlir::Attribute tryEmitPrivate(const APValue &value, QualType destType);
mlir::Attribute tryEmitPrivateForMemory(const APValue &value, QualType t);

View File

@ -710,6 +710,16 @@ mlir::Attribute ConstantEmitter::tryEmitPrivateForMemory(const APValue &value,
return (c ? emitForMemory(c, destType) : nullptr);
}
mlir::Attribute ConstantEmitter::emitAbstract(const Expr *e,
QualType destType) {
AbstractStateRAII state{*this, true};
mlir::Attribute c = mlir::cast<mlir::Attribute>(tryEmitPrivate(e, destType));
if (!c)
cgm.errorNYI(e->getSourceRange(),
"emitAbstract failed, emit null constaant");
return c;
}
mlir::Attribute ConstantEmitter::emitAbstract(SourceLocation loc,
const APValue &value,
QualType destType) {
@ -731,6 +741,32 @@ mlir::Attribute ConstantEmitter::emitForMemory(mlir::Attribute c,
return c;
}
mlir::TypedAttr ConstantEmitter::tryEmitPrivate(const Expr *e,
QualType destType) {
assert(!destType->isVoidType() && "can't emit a void constant");
if (mlir::Attribute c =
ConstExprEmitter(*this).Visit(const_cast<Expr *>(e), destType))
return llvm::dyn_cast<mlir::TypedAttr>(c);
Expr::EvalResult result;
bool success = false;
if (destType->isReferenceType())
success = e->EvaluateAsLValue(result, cgm.getASTContext());
else
success =
e->EvaluateAsRValue(result, cgm.getASTContext(), inConstantContext);
if (success && !result.hasSideEffects()) {
mlir::Attribute c = tryEmitPrivate(result.Val, destType);
return llvm::dyn_cast<mlir::TypedAttr>(c);
}
return nullptr;
}
mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &value,
QualType destType) {
auto &builder = cgm.getBuilder();

View File

@ -1120,6 +1120,8 @@ public:
mlir::LogicalResult emitFunctionBody(const clang::Stmt *body);
mlir::LogicalResult emitGotoStmt(const clang::GotoStmt &s);
void emitImplicitAssignmentOperatorBody(FunctionArgList &args);
void emitInitializerForField(clang::FieldDecl *field, LValue lhs,

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