Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#include "Calculator.h"
#include <stdexcept>

int Calculator::add(int a, int b) {
return a + b;
Expand All @@ -14,8 +13,5 @@ int Calculator::multiply(int a, int b) {
}

double Calculator::divide(int a, int b) {
if (b == 0) {
throw std::invalid_argument("Division by zero");
}
return static_cast<double>(a) / b;
}
8 changes: 4 additions & 4 deletions clang-tools-extra/test/clang-doc/basic-project.test
Original file line number Diff line number Diff line change
Expand Up @@ -139,25 +139,25 @@
// HTML-CALC-NEXT: <div>
// HTML-CALC-NEXT: <h3 id="{{([0-9A-F]{40})}}">add</h3>
// HTML-CALC-NEXT: <p>public int add(int a, int b)</p>
// HTML-CALC-NEXT: <p>Defined at line 4 of file {{.*}}Calculator.cpp</p>
// HTML-CALC-NEXT: <p>Defined at line 3 of file {{.*}}Calculator.cpp</p>
// HTML-CALC-NEXT: <div>
// HTML-CALC-NEXT: <div></div>
// HTML-CALC-NEXT: </div>
// HTML-CALC-NEXT: <h3 id="{{([0-9A-F]{40})}}">subtract</h3>
// HTML-CALC-NEXT: <p>public int subtract(int a, int b)</p>
// HTML-CALC-NEXT: <p>Defined at line 8 of file {{.*}}Calculator.cpp</p>
// HTML-CALC-NEXT: <p>Defined at line 7 of file {{.*}}Calculator.cpp</p>
// HTML-CALC-NEXT: <div>
// HTML-CALC-NEXT: <div></div>
// HTML-CALC-NEXT: </div>
// HTML-CALC-NEXT: <h3 id="{{([0-9A-F]{40})}}">multiply</h3>
// HTML-CALC-NEXT: <p>public int multiply(int a, int b)</p>
// HTML-CALC-NEXT: <p>Defined at line 12 of file {{.*}}Calculator.cpp</p>
// HTML-CALC-NEXT: <p>Defined at line 11 of file {{.*}}Calculator.cpp</p>
// HTML-CALC-NEXT: <div>
// HTML-CALC-NEXT: <div></div>
// HTML-CALC-NEXT: </div>
// HTML-CALC-NEXT: <h3 id="{{([0-9A-F]{40})}}">divide</h3>
// HTML-CALC-NEXT: <p>public double divide(int a, int b)</p>
// HTML-CALC-NEXT: <p>Defined at line 16 of file {{.*}}Calculator.cpp</p>
// HTML-CALC-NEXT: <p>Defined at line 15 of file {{.*}}Calculator.cpp</p>
// HTML-CALC-NEXT: <div>
// HTML-CALC-NEXT: <div></div>
// HTML-CALC-NEXT: </div>
Expand Down
2 changes: 0 additions & 2 deletions clang-tools-extra/test/clang-doc/single-source-html.cpp

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -906,3 +906,12 @@ void negativeNonConstMemberExpr() {
}
}


bool operator==(ExpensiveToCopyType, ExpensiveToCopyType);

template<typename T> bool OperatorWithNoDirectCallee(T t) {
ExpensiveToCopyType a1;
ExpensiveToCopyType a2 = a1;
return a1 == t;
}

7 changes: 4 additions & 3 deletions clang/docs/BoundsSafety.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
Overview
========

**NOTE:** This is a design document and the feature is not available for users yet.
Please see :doc:`BoundsSafetyImplPlans` for more details.

``-fbounds-safety`` is a C extension to enforce bounds safety to prevent
out-of-bounds (OOB) memory accesses, which remain a major source of security
vulnerabilities in C. ``-fbounds-safety`` aims to eliminate this class of bugs
Expand Down Expand Up @@ -55,9 +58,7 @@ adopt, offering these properties that make it widely adoptable in practice:
* It has a relatively low adoption cost.

This document discusses the key designs of ``-fbounds-safety``. The document is
subject to be actively updated with a more detailed specification. The
implementation plan can be found in :doc:`BoundsSafetyImplPlans`.

subject to be actively updated with a more detailed specification.

Programming Model
=================
Expand Down
65 changes: 32 additions & 33 deletions clang/docs/BoundsSafetyImplPlans.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,39 @@ Implementation plans for ``-fbounds-safety``
.. contents::
:local:

Gradual updates with experimental flag
======================================

The feature will be implemented as a series of smaller PRs and we will guard our
implementation with an experimental flag ``-fexperimental-bounds-safety`` until
the usable model is fully available. Once the model is ready for use, we will
expose the flag ``-fbounds-safety``.

Possible patch sets
-------------------

* External bounds annotations and the (late) parsing logic.
* Internal bounds annotations (wide pointers) and their parsing logic.
* Clang code generation for wide pointers with debug information.
* Pointer cast semantics involving bounds annotations (this could be divided
into multiple sub-PRs).
* CFG analysis for pairs of related pointer and count assignments and the likes.
* Bounds check expressions in AST and the Clang code generation (this could also
be divided into multiple sub-PRs).

Proposed implementation
=======================

External bounds annotations
===========================
---------------------------

The bounds annotations are C type attributes appertaining to pointer types. If
an attribute is added to the position of a declaration attribute, e.g., ``int
*ptr __counted_by(size)``, the attribute appertains to the outermost pointer
type of the declaration (``int *``).

New sugar types
===============
---------------

An external bounds annotation creates a type sugar of the underlying pointer
types. We will introduce a new sugar type, ``DynamicBoundsPointerType`` to
Expand All @@ -29,7 +52,7 @@ overloading. However, this design requires a separate logic to walk through the
entire type hierarchy to check type compatibility of bounds annotations.

Late parsing for C
==================
------------------

A bounds annotation such as ``__counted_by(count)`` can be added to type of a
struct field declaration where count is another field of the same struct
Expand All @@ -43,7 +66,7 @@ same logic. This requires introducing late parsing logic for C/C++ type
attributes.

Internal bounds annotations
===========================
---------------------------

``__indexable`` and ``__bidi_indexable`` alter pointer representations to be
equivalent to a struct with the pointer and the corresponding bounds fields.
Expand All @@ -65,7 +88,7 @@ operations returning wide pointers. Alternatively, a new ``TEK`` and an
expression emitter dedicated to wide pointers could be introduced.

Default bounds annotations
==========================
--------------------------

The model may implicitly add ``__bidi_indexable`` or ``__single`` depending on
the context of the declaration that has the pointer type. ``__bidi_indexable``
Expand All @@ -79,7 +102,7 @@ This also requires the parser to reset the type of the declaration with the
newly created type with the right default attribute.

Promotion expression
====================
--------------------

A new expression will be introduced to represent the conversion from a pointer
with an external bounds annotation, such as ``__counted_by``, to
Expand All @@ -88,7 +111,7 @@ CastExprs because it requires an extra subexpression(s) to provide the bounds
information necessary to create a wide pointer.

Bounds check expression
=======================
-----------------------

Bounds checks are part of semantics defined in the ``-fbounds-safety`` language
model. Hence, exposing the bounds checks and other semantic actions in the AST
Expand All @@ -98,7 +121,7 @@ and has the additional sub-expressions that are necessary to perform the check
according to the kind.

Paired assignment check
=======================
-----------------------

``-fbounds-safety`` enforces that variables or fields related with the same
external bounds annotation (e.g., ``buf`` and ``count`` related with
Expand All @@ -123,7 +146,7 @@ provides a linear view of statements within each ``CFGBlock`` (Clang
``CFGBlock`` represents a single basic block in a source-level CFG).

Bounds check optimizations
==========================
--------------------------

In ``-fbounds-safety``, the Clang frontend emits run-time checks for every
memory dereference if the type system or analyses in the frontend couldn’t
Expand Down Expand Up @@ -229,27 +252,3 @@ solution.

``-fbounds-safety`` is not currently supported in C++, but we believe the
general approach would be applicable for future efforts.

Upstreaming plan
================

Gradual updates with experimental flag
--------------------------------------

The upstreaming will take place as a series of smaller PRs and we will guard our
implementation with an experimental flag ``-fexperimental-bounds-safety`` until
the usable model is fully upstreamed. Once the model is ready for use, we will
expose the flag ``-fbounds-safety``.

Possible patch sets
-------------------

* External bounds annotations and the (late) parsing logic.
* Internal bounds annotations (wide pointers) and their parsing logic.
* Clang code generation for wide pointers with debug information.
* Pointer cast semantics involving bounds annotations (this could be divided
into multiple sub-PRs).
* CFG analysis for pairs of related pointer and count assignments and the likes.
* Bounds check expressions in AST and the Clang code generation (this could also
be divided into multiple sub-PRs).

62 changes: 61 additions & 1 deletion clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,12 @@ Unless specified otherwise operation(±0) = ±0 and operation(±infinity) = ±in
T __builtin_elementwise_sin(T x) return the sine of x interpreted as an angle in radians floating point types
T __builtin_elementwise_cos(T x) return the cosine of x interpreted as an angle in radians floating point types
T __builtin_elementwise_tan(T x) return the tangent of x interpreted as an angle in radians floating point types
T __builtin_elementwise_asin(T x) return the arcsine of x interpreted as an angle in radians floating point types
T __builtin_elementwise_acos(T x) return the arccosine of x interpreted as an angle in radians floating point types
T __builtin_elementwise_atan(T x) return the arctangent of x interpreted as an angle in radians floating point types
T __builtin_elementwise_sinh(T x) return the hyperbolic sine of angle x in radians floating point types
T __builtin_elementwise_cosh(T x) return the hyperbolic cosine of angle x in radians floating point types
T __builtin_elementwise_tanh(T x) return the hyperbolic tangent of angle x in radians floating point types
T __builtin_elementwise_floor(T x) return the largest integral value less than or equal to x floating point types
T __builtin_elementwise_log(T x) return the natural logarithm of x floating point types
T __builtin_elementwise_log2(T x) return the base 2 logarithm of x floating point types
Expand Down Expand Up @@ -3477,6 +3483,60 @@ Query for this feature with ``__has_builtin(__builtin_trap)``.
``__builtin_arm_trap`` is lowered to the ``llvm.aarch64.break`` builtin, and then to ``brk #payload``.
``__builtin_verbose_trap``
--------------------------
``__builtin_verbose_trap`` causes the program to stop its execution abnormally
and shows a human-readable description of the reason for the termination when a
debugger is attached or in a symbolicated crash log.
**Syntax**:
.. code-block:: c++
__builtin_verbose_trap(const char *category, const char *reason)
**Description**
``__builtin_verbose_trap`` is lowered to the ` ``llvm.trap`` <https://llvm.org/docs/LangRef.html#llvm-trap-intrinsic>`_ builtin.
Additionally, clang emits debugging information that represents an artificial
inline frame whose name encodes the category and reason strings passed to the builtin,
prefixed by a "magic" prefix.
For example, consider the following code:
.. code-block:: c++
void foo(int* p) {
if (p == nullptr)
__builtin_verbose_trap("check null", "Argument must not be null!");
}
The debugging information would look as if it were produced for the following code:
.. code-block:: c++
__attribute__((always_inline))
inline void "__clang_trap_msg$check null$Argument must not be null!"() {
__builtin_trap();
}
void foo(int* p) {
if (p == nullptr)
"__clang_trap_msg$check null$Argument must not be null!"();
}
However, the generated code would not actually contain a call to the artificial
function — it only exists in the debugging information.
Query for this feature with ``__has_builtin(__builtin_verbose_trap)``. Note that
users need to enable debug information to enable this feature. A call to this
builtin is equivalent to a call to ``__builtin_trap`` if debug information isn't
enabled.
The optimizer can merge calls to trap with different messages, which degrades
the debugging experience.
``__builtin_allow_runtime_check``
---------------------------------
Expand Down Expand Up @@ -5339,7 +5399,7 @@ The ``#pragma clang section`` directive obeys the following rules:
* The pragma clang section is enabled automatically, without need of any flags.
* This feature is only defined to work sensibly for ELF targets.
* This feature is only defined to work sensibly for ELF and Mach-O targets.
* If section name is specified through _attribute_((section("myname"))), then
the attribute name gains precedence.
Expand Down
33 changes: 32 additions & 1 deletion clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ C++ Specific Potentially Breaking Changes
To fix this, update libstdc++ to version 14.1.1 or greater.

- Clang now emits errors when Thread Safety Analysis trylock attributes are
applied to functions or methods with incompatible return values, such as
constructors, destructors, and void-returning functions. This only affects the
``TRY_ACQUIRE`` and ``TRY_ACQUIRE_SHARED`` attributes (and any synonyms).

ABI Changes in This Version
---------------------------
- Fixed Microsoft name mangling of implicitly defined variables used for thread
Expand All @@ -95,6 +100,17 @@ ABI Changes in This Version
- Fixed Microsoft calling convention when returning classes that have a deleted
copy assignment operator. Such a class should be returned indirectly.

- Removed the global alias that was pointing to AArch64 Function Multiversioning
ifuncs. Its purpose was to preserve backwards compatibility when the ".ifunc"
suffix got removed from the name mangling. The alias interacts badly with
GlobalOpt (see the issue #96197).

- Fixed Microsoft name mangling for auto non-type template arguments of pointer
type for MSVC 1920+. This change resolves incompatibilities with code compiled
by MSVC 1920+ but will introduce incompatibilities with code compiled by
earlier versions of Clang unless such code is built with the compiler option
`-fms-compatibility-version=19.14` to imitate the MSVC 1914 mangling behavior.

AST Dumping Potentially Breaking Changes
----------------------------------------

Expand Down Expand Up @@ -280,6 +296,9 @@ Resolutions to C++ Defect Reports
- P0522 implementation is enabled by default in all language versions, and
provisional wording for CWG2398 is implemented.

- Clang now performs type-only lookup for the name in ``using enum`` declaration.
(`CWG2877: Type-only lookup for using-enum-declarator <https://cplusplus.github.io/CWG/issues/2877.html>`_).

- Clang now requires a template argument list after a template keyword.
(`CWG96: Syntactic disambiguation using the template keyword <https://cplusplus.github.io/CWG/issues/96.html>`_).

Expand Down Expand Up @@ -501,6 +520,11 @@ Attribute Changes in Clang
};
- Introduced new function type attributes ``[[clang::nonblocking]]``, ``[[clang::nonallocating]]``,
``[[clang::blocking]]``, and ``[[clang::allocating]]``, with GNU-style variants as well.
The attributes declare constraints about a function's behavior pertaining to blocking and
heap memory allocation.

Improvements to Clang's diagnostics
-----------------------------------
- Clang now applies syntax highlighting to the code snippets it
Expand Down Expand Up @@ -710,6 +734,11 @@ Bug Fixes in This Version

- Fixed `static_cast` to array of unknown bound. Fixes (#GH62863).

- Clang's Thread Safety Analysis now evaluates trylock success arguments of enum
types rather than silently defaulting to false. This fixes a class of false
negatives where the analysis failed to detect unchecked access to guarded
data.

Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down Expand Up @@ -902,7 +931,9 @@ Bug Fixes to C++ Support
between the addresses of two labels (a GNU extension) to a pointer within a constant expression. (#GH95366).
- Fix immediate escalation bugs in the presence of dependent call arguments. (#GH94935)
- Clang now diagnoses explicit specializations with storage class specifiers in all contexts.

- Fix an assertion failure caused by parsing a lambda used as a default argument for the value of a
forward-declared class. (#GH93512).
- Fixed a bug in access checking inside return-type-requirement of compound requirements. (#GH93788).

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
53 changes: 49 additions & 4 deletions clang/docs/ThreadSafetyAnalysis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -420,10 +420,17 @@ TRY_ACQUIRE(<bool>, ...), TRY_ACQUIRE_SHARED(<bool>, ...)
*Previously:* ``EXCLUSIVE_TRYLOCK_FUNCTION``, ``SHARED_TRYLOCK_FUNCTION``

These are attributes on a function or method that tries to acquire the given
capability, and returns a boolean value indicating success or failure.
The first argument must be ``true`` or ``false``, to specify which return value
indicates success, and the remaining arguments are interpreted in the same way
as ``ACQUIRE``. See :ref:`mutexheader`, below, for example uses.
capability, and returns a boolean, integer, or pointer value indicating success
or failure.

The attribute's first argument defines whether a zero or non-zero return value
indicates success. Syntactically, it accepts ``NULL`` or ``nullptr``, ``bool``
and ``int`` literals, as well as enumerator values. *The analysis only cares
whether this success value is zero or non-zero.* This leads to some subtle
consequences, discussed in the next section.

The remaining arguments are interpreted in the same way as ``ACQUIRE``. See
:ref:`mutexheader`, below, for example uses.

Because the analysis doesn't support conditional locking, a capability is
treated as acquired after the first branch on the return value of a try-acquire
Expand All @@ -445,6 +452,44 @@ function.
}
}

Subtle Consequences of Non-Boolean Success Values
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The trylock attributes accept non-boolean expressions for the success value, but
the analysis only cares whether the value is zero or non-zero.

Suppose you define an enum with two non-zero enumerators: ``LockAcquired = 1``
and ``LockNotAcquired = 2``. If your trylock function returns ``LockAcquired``
on success and ``LockNotAcquired`` on failure, the analysis may fail to detect
access to guarded data without holding the mutex because they are both non-zero.

.. code-block:: c++

// *** Beware: this code demonstrates incorrect usage. ***

enum TrylockResult { LockAcquired = 1, LockNotAcquired = 2 };

class CAPABILITY("mutex") Mutex {
public:
TrylockResult TryLock() TRY_ACQUIRE(LockAcquired);
};

Mutex mu;
int a GUARDED_BY(mu);

void foo() {
if (mu.TryLock()) { // This branch satisfies the analysis, but permits
// unguarded access to `a`!
a = 0;
mu.Unlock();
}
}

It's also possible to return a pointer from the trylock function. Similarly, all
that matters is whether the success value is zero or non-zero. For instance, a
success value of `true` means the function returns a non-null pointer on
success.


ASSERT_CAPABILITY(...) and ASSERT_SHARED_CAPABILITY(...)
--------------------------------------------------------
Expand Down
14 changes: 14 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,14 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// This is the top-level (C++20) Named module we are building.
Module *CurrentCXXNamedModule = nullptr;

/// Help structures to decide whether two `const Module *` belongs
/// to the same conceptual module to avoid the expensive to string comparison
/// if possible.
///
/// Not serialized intentionally.
llvm::StringMap<const Module *> PrimaryModuleNameMap;
llvm::DenseMap<const Module *, const Module *> SameModuleLookupSet;

static constexpr unsigned ConstantArrayTypesLog2InitSize = 8;
static constexpr unsigned GeneralTypesLog2InitSize = 9;
static constexpr unsigned FunctionProtoTypesLog2InitSize = 12;
Expand Down Expand Up @@ -1073,6 +1081,12 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// Get module under construction, nullptr if this is not a C++20 module.
Module *getCurrentNamedModule() const { return CurrentCXXNamedModule; }

/// If the two module \p M1 and \p M2 are in the same module.
///
/// FIXME: The signature may be confusing since `clang::Module` means to
/// a module fragment or a module unit but not a C++20 module.
bool isInSameModule(const Module *M1, const Module *M2);

TranslationUnitDecl *getTranslationUnitDecl() const {
return TUDecl->getMostRecentDecl();
}
Expand Down
9 changes: 9 additions & 0 deletions clang/include/clang/AST/AbstractBasicReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,15 @@ class DataStreamBasicReader : public BasicReaderBase<Impl> {
return FunctionProtoType::ExtParameterInfo::getFromOpaqueValue(value);
}

FunctionEffect readFunctionEffect() {
uint32_t value = asImpl().readUInt32();
return FunctionEffect::fromOpaqueInt32(value);
}

EffectConditionExpr readEffectConditionExpr() {
return EffectConditionExpr{asImpl().readExprRef()};
}

NestedNameSpecifier *readNestedNameSpecifier() {
auto &ctx = getASTContext();

Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/AST/AbstractBasicWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,14 @@ class DataStreamBasicWriter : public BasicWriterBase<Impl> {
asImpl().writeUInt32(epi.getOpaqueValue());
}

void writeFunctionEffect(FunctionEffect E) {
asImpl().writeUInt32(E.toOpaqueInt32());
}

void writeEffectConditionExpr(EffectConditionExpr CE) {
asImpl().writeExprRef(CE.getCondition());
}

void writeNestedNameSpecifier(NestedNameSpecifier *NNS) {
// Nested name specifiers usually aren't too long. I think that 8 would
// typically accommodate the vast majority.
Expand Down
17 changes: 17 additions & 0 deletions clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -3042,6 +3042,16 @@ class FunctionDecl : public DeclaratorDecl,
/// computed and stored.
unsigned getODRHash() const;

FunctionEffectsRef getFunctionEffects() const {
// Effects may differ between declarations, but they should be propagated
// from old to new on any redeclaration, so it suffices to look at
// getMostRecentDecl().
if (const auto *FPT =
getMostRecentDecl()->getType()->getAs<FunctionProtoType>())
return FPT->getFunctionEffects();
return {};
}

// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) {
Expand Down Expand Up @@ -4670,6 +4680,13 @@ class BlockDecl : public Decl, public DeclContext {

SourceRange getSourceRange() const override LLVM_READONLY;

FunctionEffectsRef getFunctionEffects() const {
if (const TypeSourceInfo *TSI = getSignatureAsWritten())
if (const auto *FPT = TSI->getType()->getAs<FunctionProtoType>())
return FPT->getFunctionEffects();
return {};
}

// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { return K == Block; }
Expand Down
8 changes: 3 additions & 5 deletions clang/include/clang/AST/DeclID.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ class LocalDeclID : public DeclIDBase {

static LocalDeclID get(ASTReader &Reader, serialization::ModuleFile &MF,
DeclID ID);
static LocalDeclID get(ASTReader &Reader, serialization::ModuleFile &MF,
unsigned ModuleFileIndex, unsigned LocalDeclID);

LocalDeclID &operator++() {
++ID;
Expand Down Expand Up @@ -259,11 +261,7 @@ template <> struct DenseMapInfo<clang::GlobalDeclID> {
}

static unsigned getHashValue(const GlobalDeclID &Key) {
// Our default hash algorithm for 64 bits integer may not be very good.
// In GlobalDeclID's case, it is pretty common that the lower 32 bits can
// be same.
// FIXME: Remove this when we fix the underlying issue.
return llvm::hash_value(Key.getRawValue());
return DenseMapInfo<DeclID>::getHashValue(Key.getRawValue());
}

static bool isEqual(const GlobalDeclID &L, const GlobalDeclID &R) {
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,11 @@ class Expr : public ValueStmt {
const Expr *PtrExpression, ASTContext &Ctx,
EvalResult &Status) const;

/// If the current Expr can be evaluated to a pointer to a null-terminated
/// constant string, return the constant string (without the terminating
/// null).
std::optional<std::string> tryEvaluateString(ASTContext &Ctx) const;

/// Enumeration used to describe the kind of Null pointer constant
/// returned from \c isNullPointerConstant().
enum NullPointerConstantKind {
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/AST/PropertiesBase.td
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ def ExtParameterInfo : PropertyType<"FunctionProtoType::ExtParameterInfo">;
def FixedPointSemantics : PropertyType<"llvm::FixedPointSemantics"> {
let PassByReference = 1;
}
def FunctionEffect : PropertyType<"FunctionEffect">;
def EffectConditionExpr : PropertyType<"EffectConditionExpr">;
def Identifier : RefPropertyType<"IdentifierInfo"> { let ConstWhenWriting = 1; }
def LValuePathEntry : PropertyType<"APValue::LValuePathEntry">;
def LValuePathSerializationHelper :
Expand Down
381 changes: 376 additions & 5 deletions clang/include/clang/AST/Type.h

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions clang/include/clang/AST/TypeProperties.td
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,12 @@ let Class = FunctionProtoType in {
def : Property<"AArch64SMEAttributes", UInt32> {
let Read = [{ node->getAArch64SMEAttributes() }];
}
def : Property<"functionEffects", Array<FunctionEffect>> {
let Read = [{ node->getFunctionEffectsWithoutConditions() }];
}
def : Property<"functionEffectConds", Array<EffectConditionExpr>> {
let Read = [{ node->getFunctionEffectConditions() }];
}

def : Creator<[{
auto extInfo = FunctionType::ExtInfo(noReturn, hasRegParm, regParm,
Expand All @@ -368,6 +374,7 @@ let Class = FunctionProtoType in {
epi.ExtParameterInfos =
extParameterInfo.empty() ? nullptr : extParameterInfo.data();
epi.AArch64SMEAttributes = AArch64SMEAttributes;
epi.FunctionEffects = FunctionEffectsRef::create(functionEffects, functionEffectConds);
return ctx.getFunctionType(returnType, parameters, epi);
}]>;
}
Expand Down
182 changes: 137 additions & 45 deletions clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,50 @@ template <typename LatticeT> struct DataflowAnalysisState {
Environment Env;
};

/// A callback to be called with the state before or after visiting a CFG
/// element.
template <typename AnalysisT>
using CFGEltCallback = std::function<void(
const CFGElement &,
const DataflowAnalysisState<typename AnalysisT::Lattice> &)>;

/// A pair of callbacks to be called with the state before and after visiting a
/// CFG element.
/// Either or both of the callbacks may be null.
template <typename AnalysisT> struct CFGEltCallbacks {
CFGEltCallback<AnalysisT> Before;
CFGEltCallback<AnalysisT> After;
};

/// A callback for performing diagnosis on a CFG element, called with the state
/// before or after visiting that CFG element. Returns a list of diagnostics
/// to emit (if any).
template <typename AnalysisT, typename Diagnostic>
using DiagnosisCallback = llvm::function_ref<llvm::SmallVector<Diagnostic>(
const CFGElement &, ASTContext &,
const TransferStateForDiagnostics<typename AnalysisT::Lattice> &)>;

/// A pair of callbacks for performing diagnosis on a CFG element, called with
/// the state before and after visiting that CFG element.
/// Either or both of the callbacks may be null.
template <typename AnalysisT, typename Diagnostic> struct DiagnosisCallbacks {
DiagnosisCallback<AnalysisT, Diagnostic> Before;
DiagnosisCallback<AnalysisT, Diagnostic> After;
};

/// Default for the maximum number of SAT solver iterations during analysis.
inline constexpr std::int64_t kDefaultMaxSATIterations = 1'000'000'000;

/// Default for the maximum number of block visits during analysis.
inline constexpr std::int32_t kDefaultMaxBlockVisits = 20'000;

/// Performs dataflow analysis and returns a mapping from basic block IDs to
/// dataflow analysis states that model the respective basic blocks. The
/// returned vector, if any, will have the same size as the number of CFG
/// blocks, with indices corresponding to basic block IDs. Returns an error if
/// the dataflow analysis cannot be performed successfully. Otherwise, calls
/// `PostVisitCFG` on each CFG element with the final analysis results at that
/// program point.
/// `PostAnalysisCallbacks` on each CFG element with the final analysis results
/// before and after that program point.
///
/// `MaxBlockVisits` caps the number of block visits during analysis. See
/// `runTypeErasedDataflowAnalysis` for a full description. The default value is
Expand All @@ -194,30 +231,40 @@ template <typename LatticeT> struct DataflowAnalysisState {
template <typename AnalysisT>
llvm::Expected<std::vector<
std::optional<DataflowAnalysisState<typename AnalysisT::Lattice>>>>
runDataflowAnalysis(
const AdornedCFG &ACFG, AnalysisT &Analysis, const Environment &InitEnv,
std::function<void(const CFGElement &, const DataflowAnalysisState<
typename AnalysisT::Lattice> &)>
PostVisitCFG = nullptr,
std::int32_t MaxBlockVisits = 20'000) {
std::function<void(const CFGElement &,
const TypeErasedDataflowAnalysisState &)>
PostVisitCFGClosure = nullptr;
if (PostVisitCFG) {
PostVisitCFGClosure = [&PostVisitCFG](
const CFGElement &Element,
const TypeErasedDataflowAnalysisState &State) {
auto *Lattice =
llvm::any_cast<typename AnalysisT::Lattice>(&State.Lattice.Value);
// FIXME: we should not be copying the environment here!
// Ultimately the PostVisitCFG only gets a const reference anyway.
PostVisitCFG(Element, DataflowAnalysisState<typename AnalysisT::Lattice>{
*Lattice, State.Env.fork()});
};
runDataflowAnalysis(const AdornedCFG &ACFG, AnalysisT &Analysis,
const Environment &InitEnv,
CFGEltCallbacks<AnalysisT> PostAnalysisCallbacks,
std::int32_t MaxBlockVisits = kDefaultMaxBlockVisits) {
CFGEltCallbacksTypeErased TypeErasedCallbacks;
if (PostAnalysisCallbacks.Before) {
TypeErasedCallbacks.Before =
[&PostAnalysisCallbacks](const CFGElement &Element,
const TypeErasedDataflowAnalysisState &State) {
auto *Lattice =
llvm::any_cast<typename AnalysisT::Lattice>(&State.Lattice.Value);
// FIXME: we should not be copying the environment here!
// Ultimately the `CFGEltCallback` only gets a const reference anyway.
PostAnalysisCallbacks.Before(
Element, DataflowAnalysisState<typename AnalysisT::Lattice>{
*Lattice, State.Env.fork()});
};
}
if (PostAnalysisCallbacks.After) {
TypeErasedCallbacks.After =
[&PostAnalysisCallbacks](const CFGElement &Element,
const TypeErasedDataflowAnalysisState &State) {
auto *Lattice =
llvm::any_cast<typename AnalysisT::Lattice>(&State.Lattice.Value);
// FIXME: we should not be copying the environment here!
// Ultimately the `CFGEltCallback` only gets a const reference anyway.
PostAnalysisCallbacks.After(
Element, DataflowAnalysisState<typename AnalysisT::Lattice>{
*Lattice, State.Env.fork()});
};
}

auto TypeErasedBlockStates = runTypeErasedDataflowAnalysis(
ACFG, Analysis, InitEnv, PostVisitCFGClosure, MaxBlockVisits);
ACFG, Analysis, InitEnv, TypeErasedCallbacks, MaxBlockVisits);
if (!TypeErasedBlockStates)
return TypeErasedBlockStates.takeError();

Expand All @@ -239,6 +286,22 @@ runDataflowAnalysis(
return std::move(BlockStates);
}

/// Overload that takes only one post-analysis callback, which is run on the
/// state after visiting the `CFGElement`. This is provided for backwards
/// compatibility; new callers should call the overload taking `CFGEltCallbacks`
/// instead.
template <typename AnalysisT>
llvm::Expected<std::vector<
std::optional<DataflowAnalysisState<typename AnalysisT::Lattice>>>>
runDataflowAnalysis(
const AdornedCFG &ACFG, AnalysisT &Analysis, const Environment &InitEnv,
CFGEltCallback<AnalysisT> PostAnalysisCallbackAfterElt = nullptr,
std::int32_t MaxBlockVisits = kDefaultMaxBlockVisits) {
return runDataflowAnalysis(ACFG, Analysis, InitEnv,
{nullptr, PostAnalysisCallbackAfterElt},
MaxBlockVisits);
}

// Create an analysis class that is derived from `DataflowAnalysis`. This is an
// SFINAE adapter that allows us to call two different variants of constructor
// (either with or without the optional `Environment` parameter).
Expand Down Expand Up @@ -271,14 +334,11 @@ auto createAnalysis(ASTContext &ASTCtx, Environment &Env)
/// `runDataflowAnalysis` for a full description and explanation of the default
/// value.
template <typename AnalysisT, typename Diagnostic>
llvm::Expected<llvm::SmallVector<Diagnostic>> diagnoseFunction(
const FunctionDecl &FuncDecl, ASTContext &ASTCtx,
llvm::function_ref<llvm::SmallVector<Diagnostic>(
const CFGElement &, ASTContext &,
const TransferStateForDiagnostics<typename AnalysisT::Lattice> &)>
Diagnoser,
std::int64_t MaxSATIterations = 1'000'000'000,
std::int32_t MaxBlockVisits = 20'000) {
llvm::Expected<llvm::SmallVector<Diagnostic>>
diagnoseFunction(const FunctionDecl &FuncDecl, ASTContext &ASTCtx,
DiagnosisCallbacks<AnalysisT, Diagnostic> Diagnoser,
std::int64_t MaxSATIterations = kDefaultMaxSATIterations,
std::int32_t MaxBlockVisits = kDefaultMaxBlockVisits) {
llvm::Expected<AdornedCFG> Context = AdornedCFG::build(FuncDecl);
if (!Context)
return Context.takeError();
Expand All @@ -288,21 +348,38 @@ llvm::Expected<llvm::SmallVector<Diagnostic>> diagnoseFunction(
Environment Env(AnalysisContext, FuncDecl);
AnalysisT Analysis = createAnalysis<AnalysisT>(ASTCtx, Env);
llvm::SmallVector<Diagnostic> Diagnostics;
CFGEltCallbacksTypeErased PostAnalysisCallbacks;
if (Diagnoser.Before) {
PostAnalysisCallbacks.Before =
[&ASTCtx, &Diagnoser,
&Diagnostics](const CFGElement &Elt,
const TypeErasedDataflowAnalysisState &State) mutable {
auto EltDiagnostics = Diagnoser.Before(
Elt, ASTCtx,
TransferStateForDiagnostics<typename AnalysisT::Lattice>(
llvm::any_cast<const typename AnalysisT::Lattice &>(
State.Lattice.Value),
State.Env));
llvm::move(EltDiagnostics, std::back_inserter(Diagnostics));
};
}
if (Diagnoser.After) {
PostAnalysisCallbacks.After =
[&ASTCtx, &Diagnoser,
&Diagnostics](const CFGElement &Elt,
const TypeErasedDataflowAnalysisState &State) mutable {
auto EltDiagnostics = Diagnoser.After(
Elt, ASTCtx,
TransferStateForDiagnostics<typename AnalysisT::Lattice>(
llvm::any_cast<const typename AnalysisT::Lattice &>(
State.Lattice.Value),
State.Env));
llvm::move(EltDiagnostics, std::back_inserter(Diagnostics));
};
}
if (llvm::Error Err =
runTypeErasedDataflowAnalysis(
*Context, Analysis, Env,
[&ASTCtx, &Diagnoser, &Diagnostics](
const CFGElement &Elt,
const TypeErasedDataflowAnalysisState &State) mutable {
auto EltDiagnostics = Diagnoser(
Elt, ASTCtx,
TransferStateForDiagnostics<typename AnalysisT::Lattice>(
llvm::any_cast<const typename AnalysisT::Lattice &>(
State.Lattice.Value),
State.Env));
llvm::move(EltDiagnostics, std::back_inserter(Diagnostics));
},
MaxBlockVisits)
runTypeErasedDataflowAnalysis(*Context, Analysis, Env,
PostAnalysisCallbacks, MaxBlockVisits)
.takeError())
return std::move(Err);

Expand All @@ -313,6 +390,21 @@ llvm::Expected<llvm::SmallVector<Diagnostic>> diagnoseFunction(
return Diagnostics;
}

/// Overload that takes only one diagnosis callback, which is run on the state
/// after visiting the `CFGElement`. This is provided for backwards
/// compatibility; new callers should call the overload taking
/// `DiagnosisCallbacks` instead.
template <typename AnalysisT, typename Diagnostic>
llvm::Expected<llvm::SmallVector<Diagnostic>>
diagnoseFunction(const FunctionDecl &FuncDecl, ASTContext &ASTCtx,
DiagnosisCallback<AnalysisT, Diagnostic> Diagnoser,
std::int64_t MaxSATIterations = kDefaultMaxSATIterations,
std::int32_t MaxBlockVisits = kDefaultMaxBlockVisits) {
DiagnosisCallbacks<AnalysisT, Diagnostic> Callbacks = {nullptr, Diagnoser};
return diagnoseFunction(FuncDecl, ASTCtx, Callbacks, MaxSATIterations,
MaxBlockVisits);
}

/// Abstract base class for dataflow "models": reusable analysis components that
/// model a particular aspect of program semantics in the `Environment`. For
/// example, a model may capture a type and its related functions.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,12 +132,25 @@ struct TypeErasedDataflowAnalysisState {
}
};

/// A callback to be called with the state before or after visiting a CFG
/// element.
using CFGEltCallbackTypeErased = std::function<void(
const CFGElement &, const TypeErasedDataflowAnalysisState &)>;

/// A pair of callbacks to be called with the state before and after visiting a
/// CFG element.
/// Either or both of the callbacks may be null.
struct CFGEltCallbacksTypeErased {
CFGEltCallbackTypeErased Before;
CFGEltCallbackTypeErased After;
};

/// Performs dataflow analysis and returns a mapping from basic block IDs to
/// dataflow analysis states that model the respective basic blocks. Indices of
/// the returned vector correspond to basic block IDs. Returns an error if the
/// dataflow analysis cannot be performed successfully. Otherwise, calls
/// `PostVisitCFG` on each CFG element with the final analysis results at that
/// program point.
/// `PostAnalysisCallbacks` on each CFG element with the final analysis results
/// before and after that program point.
///
/// `MaxBlockVisits` caps the number of block visits during analysis. It doesn't
/// distinguish between repeat visits to the same block and visits to distinct
Expand All @@ -148,9 +161,7 @@ llvm::Expected<std::vector<std::optional<TypeErasedDataflowAnalysisState>>>
runTypeErasedDataflowAnalysis(
const AdornedCFG &ACFG, TypeErasedDataflowAnalysis &Analysis,
const Environment &InitEnv,
std::function<void(const CFGElement &,
const TypeErasedDataflowAnalysisState &)>
PostVisitCFG,
const CFGEltCallbacksTypeErased &PostAnalysisCallbacks,
std::int32_t MaxBlockVisits);

} // namespace dataflow
Expand Down
85 changes: 57 additions & 28 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ class DefaultIntArgument<string name, int default> : IntArgument<name, 1> {
// possible values, and a list of enumerators to map them to.
class EnumArgument<string name, string type, bit is_string, list<string> values,
list<string> enums, bit opt = 0, bit fake = 0,
bit isExternalType = 0>
bit isExternalType = 0, bit isCovered = 1>
: Argument<name, opt, fake> {
string Type = type;
// When true, the argument will be parsed as an unevaluated string literal
Expand All @@ -296,13 +296,16 @@ class EnumArgument<string name, string type, bit is_string, list<string> values,
list<string> Values = values;
list<string> Enums = enums;
bit IsExternalType = isExternalType;
// We need to know whether an external enum is fully covered by the options
// in order to decide whether to emit unreachable default labels in a switch.
bit IsCovered = isCovered;
}

// FIXME: There should be a VariadicArgument type that takes any other type
// of argument and generates the appropriate type.
class VariadicEnumArgument<string name, string type, bit is_string,
list<string> values, list<string> enums,
bit isExternalType = 0>
bit isExternalType = 0, bit isCovered = 1>
: Argument<name, 1> {
string Type = type;
// When true, the argument will be parsed as an unevaluated string literal
Expand All @@ -311,6 +314,9 @@ class VariadicEnumArgument<string name, string type, bit is_string,
list<string> Values = values;
list<string> Enums = enums;
bit IsExternalType = isExternalType;
// We need to know whether an external enum is fully covered by the options
// in order to decide whether to emit unreachable default labels in a switch.
bit IsCovered = isCovered;
}

// Represents an attribute wrapped by another attribute.
Expand Down Expand Up @@ -1455,6 +1461,28 @@ def CXX11NoReturn : InheritableAttr {
let Documentation = [CXX11NoReturnDocs];
}

def NonBlocking : TypeAttr {
let Spellings = [Clang<"nonblocking">];
let Args = [ExprArgument<"Cond", /*optional*/1>];
let Documentation = [NonBlockingDocs];
}

def NonAllocating : TypeAttr {
let Spellings = [Clang<"nonallocating">];
let Args = [ExprArgument<"Cond", /*optional*/1>];
let Documentation = [NonAllocatingDocs];
}

def Blocking : TypeAttr {
let Spellings = [Clang<"blocking">];
let Documentation = [BlockingDocs];
}

def Allocating : TypeAttr {
let Spellings = [Clang<"allocating">];
let Documentation = [AllocatingDocs];
}

// Similar to CUDA, OpenCL attributes do not receive a [[]] spelling because
// the specification does not expose them with one currently.
def OpenCLKernel : InheritableAttr {
Expand Down Expand Up @@ -2913,7 +2941,7 @@ def CodeModel : InheritableAttr, TargetSpecificAttr<TargetLoongArch> {
let Spellings = [GCC<"model">];
let Args = [EnumArgument<"Model", "llvm::CodeModel::Model", /*is_string=*/1,
["normal", "medium", "extreme"], ["Small", "Medium", "Large"],
/*opt=*/0, /*fake=*/0, /*isExternalType=*/1>];
/*opt=*/0, /*fake=*/0, /*isExternalType=*/1, /*isCovered=*/0>];
let Subjects = SubjectList<[NonTLSGlobalVar], ErrorDiag>;
let Documentation = [CodeModelDocs];
}
Expand Down Expand Up @@ -4472,7 +4500,7 @@ def HLSLShader : InheritableAttr {
["Pixel", "Vertex", "Geometry", "Hull", "Domain", "Compute",
"RayGeneration", "Intersection", "AnyHit", "ClosestHit",
"Miss", "Callable", "Mesh", "Amplification"],
/*opt=*/0, /*fake=*/0, /*isExternalType=*/1>
/*opt=*/0, /*fake=*/0, /*isExternalType=*/1, /*isCovered=*/0>
];
let Documentation = [HLSLSV_ShaderTypeAttrDocs];
let AdditionalMembers =
Expand All @@ -4487,30 +4515,31 @@ def HLSLResource : InheritableAttr {
let Spellings = [];
let Subjects = SubjectList<[Struct]>;
let LangOpts = [HLSL];
let Args = [EnumArgument<"ResourceClass", "llvm::hlsl::ResourceClass",
/*is_string=*/0,
["SRV", "UAV", "CBuffer", "Sampler"],
["SRV", "UAV", "CBuffer", "Sampler"],
/*opt=*/0, /*fake=*/0, /*isExternalType=*/1>,
EnumArgument<"ResourceKind", "llvm::hlsl::ResourceKind",
/*is_string=*/0,
["Texture1D", "Texture2D", "Texture2DMS",
"Texture3D", "TextureCube", "Texture1DArray",
"Texture2DArray", "Texture2DMSArray",
"TextureCubeArray", "TypedBuffer", "RawBuffer",
"StructuredBuffer", "CBuffer", "Sampler",
"TBuffer", "RTAccelerationStructure",
"FeedbackTexture2D", "FeedbackTexture2DArray"],
["Texture1D", "Texture2D", "Texture2DMS",
"Texture3D", "TextureCube", "Texture1DArray",
"Texture2DArray", "Texture2DMSArray",
"TextureCubeArray", "TypedBuffer", "RawBuffer",
"StructuredBuffer", "CBuffer", "Sampler",
"TBuffer", "RTAccelerationStructure",
"FeedbackTexture2D", "FeedbackTexture2DArray"],
/*opt=*/0, /*fake=*/0, /*isExternalType=*/1>,
DefaultBoolArgument<"isROV", /*default=*/0>
];
let Args = [
EnumArgument<"ResourceClass", "llvm::hlsl::ResourceClass",
/*is_string=*/0, ["SRV", "UAV", "CBuffer", "Sampler"],
["SRV", "UAV", "CBuffer", "Sampler"],
/*opt=*/0, /*fake=*/0, /*isExternalType=*/1>,
EnumArgument<
"ResourceKind", "llvm::hlsl::ResourceKind",
/*is_string=*/0,
[
"Texture1D", "Texture2D", "Texture2DMS", "Texture3D", "TextureCube",
"Texture1DArray", "Texture2DArray", "Texture2DMSArray",
"TextureCubeArray", "TypedBuffer", "RawBuffer", "StructuredBuffer",
"CBuffer", "Sampler", "TBuffer", "RTAccelerationStructure",
"FeedbackTexture2D", "FeedbackTexture2DArray"
],
[
"Texture1D", "Texture2D", "Texture2DMS", "Texture3D", "TextureCube",
"Texture1DArray", "Texture2DArray", "Texture2DMSArray",
"TextureCubeArray", "TypedBuffer", "RawBuffer", "StructuredBuffer",
"CBuffer", "Sampler", "TBuffer", "RTAccelerationStructure",
"FeedbackTexture2D", "FeedbackTexture2DArray"
],
/*opt=*/0, /*fake=*/0, /*isExternalType=*/1, /*isCovered=*/0>,
DefaultBoolArgument<"isROV", /*default=*/0>
];
let Documentation = [InternalOnly];
}

Expand Down
76 changes: 73 additions & 3 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -5672,9 +5672,10 @@ may be changed in the future.
be used to pass function arguments. Floating-point registers (XMMs/YMMs) still
follow the C calling convention.
- On AArch64, only LR and FP are preserved by the callee.
Registers X19-X28, X0-X7, and X9-X15 are used to pass function arguments.
X8, X16-X18, SIMD and floating-point registers follow the AAPCS calling
convention.
Registers X20-X28, X0-X7, and X9-X14 are used to pass function arguments.
X8, X16-X19, SIMD and floating-point registers follow the AAPCS calling
convention. X15 is not available for argument passing on Windows, but is
used to pass arguments on other platforms.
}];
}

Expand Down Expand Up @@ -8106,3 +8107,72 @@ Attribute used by `clspv`_ (OpenCL-C to Vulkan SPIR-V compiler) to identify func
.. _`libclc`: https://libclc.llvm.org
}];
}

def DocCatNonBlockingNonAllocating : DocumentationCategory<"Performance Constraint Attributes"> {
let Content = [{
The ``nonblocking``, ``blocking``, ``nonallocating`` and ``allocating`` attributes can be attached
to function types, including blocks, C++ lambdas, and member functions. The attributes declare
constraints about a function's behavior pertaining to blocking and heap memory allocation.

There are several rules for function types with these attributes, enforced with
compiler warnings:

- When assigning or otherwise converting to a function pointer of ``nonblocking`` or
``nonallocating`` type, the source must also be a function or function pointer of
that type, unless it is a null pointer, i.e. the attributes should not be "spoofed". Conversions
that remove the attributes are transparent and valid.

- An override of a ``nonblocking`` or ``nonallocating`` virtual method must also be declared
with that same attribute (or a stronger one.) An overriding method may add an attribute.

- A redeclaration of a ``nonblocking`` or ``nonallocating`` function must also be declared with
the same attribute (or a stronger one). A redeclaration may add an attribute.

The warnings are controlled by ``-Wfunction-effects``, which is enabled by default.

In a future commit, the compiler will diagnose function calls from ``nonblocking`` and ``nonallocating``
functions to other functions which lack the appropriate attribute.
}];
}

def NonBlockingDocs : Documentation {
let Category = DocCatNonBlockingNonAllocating;
let Heading = "nonblocking";
let Content = [{
Declares that a function or function type either does or does not block in any way, according
to the optional, compile-time constant boolean argument, which defaults to true. When the argument
is false, the attribute is equivalent to ``blocking``.

For the purposes of diagnostics, ``nonblocking`` is considered to include the
``nonallocating`` guarantee and is therefore a "stronger" constraint or attribute.
}];
}

def NonAllocatingDocs : Documentation {
let Category = DocCatNonBlockingNonAllocating;
let Heading = "nonallocating";
let Content = [{
Declares that a function or function type either does or does not allocate heap memory, according
to the optional, compile-time constant boolean argument, which defaults to true. When the argument
is false, the attribute is equivalent to ``allocating``.
}];
}

def BlockingDocs : Documentation {
let Category = DocCatNonBlockingNonAllocating;
let Heading = "blocking";
let Content = [{
Declares that a function potentially blocks, and prevents any potential inference of ``nonblocking``
by the compiler.
}];
}

def AllocatingDocs : Documentation {
let Category = DocCatNonBlockingNonAllocating;
let Heading = "allocating";
let Content = [{
Declares that a function potentially allocates heap memory, and prevents any potential inference
of ``nonallocating`` by the compiler.
}];
}

42 changes: 42 additions & 0 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -1152,6 +1152,12 @@ def Trap : Builtin {
let Prototype = "void()";
}

def VerboseTrap : Builtin {
let Spellings = ["__builtin_verbose_trap"];
let Attributes = [NoThrow, NoReturn];
let Prototype = "void(char const*, char const*)";
}

def Debugtrap : Builtin {
let Spellings = ["__builtin_debugtrap"];
let Attributes = [NoThrow];
Expand Down Expand Up @@ -1218,6 +1224,24 @@ def ElementwiseAbs : Builtin {
let Prototype = "void(...)";
}

def ElementwiseACos : Builtin {
let Spellings = ["__builtin_elementwise_acos"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
let Prototype = "void(...)";
}

def ElementwiseASin : Builtin {
let Spellings = ["__builtin_elementwise_asin"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
let Prototype = "void(...)";
}

def ElementwiseATan : Builtin {
let Spellings = ["__builtin_elementwise_atan"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
let Prototype = "void(...)";
}

def ElementwiseBitreverse : Builtin {
let Spellings = ["__builtin_elementwise_bitreverse"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
Expand Down Expand Up @@ -1248,6 +1272,12 @@ def ElementwiseCos : Builtin {
let Prototype = "void(...)";
}

def ElementwiseCosh : Builtin {
let Spellings = ["__builtin_elementwise_cosh"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
let Prototype = "void(...)";
}

def ElementwiseExp : Builtin {
let Spellings = ["__builtin_elementwise_exp"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
Expand Down Expand Up @@ -1320,6 +1350,12 @@ def ElementwiseSin : Builtin {
let Prototype = "void(...)";
}

def ElementwiseSinh : Builtin {
let Spellings = ["__builtin_elementwise_sinh"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
let Prototype = "void(...)";
}

def ElementwiseSqrt : Builtin {
let Spellings = ["__builtin_elementwise_sqrt"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
Expand All @@ -1332,6 +1368,12 @@ def ElementwiseTan : Builtin {
let Prototype = "void(...)";
}

def ElementwiseTanh : Builtin {
let Spellings = ["__builtin_elementwise_tanh"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
let Prototype = "void(...)";
}

def ElementwiseTrunc : Builtin {
let Spellings = ["__builtin_elementwise_trunc"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/BuiltinsAMDGPU.def
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,12 @@ BUILTIN(__builtin_amdgcn_mqsad_pk_u16_u8, "WUiWUiUiWUi", "nc")
BUILTIN(__builtin_amdgcn_mqsad_u32_u8, "V4UiWUiUiV4Ui", "nc")

BUILTIN(__builtin_amdgcn_make_buffer_rsrc, "Qbv*sii", "nc")
BUILTIN(__builtin_amdgcn_raw_buffer_store_b8, "vcQbiiIi", "n")
BUILTIN(__builtin_amdgcn_raw_buffer_store_b16, "vsQbiiIi", "n")
BUILTIN(__builtin_amdgcn_raw_buffer_store_b32, "viQbiiIi", "n")
BUILTIN(__builtin_amdgcn_raw_buffer_store_b64, "vV2iQbiiIi", "n")
BUILTIN(__builtin_amdgcn_raw_buffer_store_b96, "vV3iQbiiIi", "n")
BUILTIN(__builtin_amdgcn_raw_buffer_store_b128, "vV4iQbiiIi", "n")

//===----------------------------------------------------------------------===//
// Ballot builtins.
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/CodeGenOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#ifndef LLVM_CLANG_BASIC_CODEGENOPTIONS_H
#define LLVM_CLANG_BASIC_CODEGENOPTIONS_H

#include "clang/Basic/PointerAuthOptions.h"
#include "clang/Basic/Sanitizers.h"
#include "clang/Basic/XRayInstr.h"
#include "llvm/ADT/FloatingPointMode.h"
Expand Down Expand Up @@ -391,6 +392,9 @@ class CodeGenOptions : public CodeGenOptionsBase {

std::vector<std::string> Reciprocals;

/// Configuration for pointer-signing.
PointerAuthOptions PointerAuth;

/// The preferred width for auto-vectorization transforms. This is intended to
/// override default transforms based on the width of the architected vector
/// registers.
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/Cuda.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ enum class CudaArch {
GFX12_GENERIC,
GFX1200,
GFX1201,
AMDGCNSPIRV,
Generic, // A processor model named 'generic' if the target backend defines a
// public one.
LAST,
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -1527,6 +1527,10 @@ def ReadOnlyPlacementChecks : DiagGroup<"read-only-types">;
def UnsafeBufferUsageInContainer : DiagGroup<"unsafe-buffer-usage-in-container">;
def UnsafeBufferUsage : DiagGroup<"unsafe-buffer-usage", [UnsafeBufferUsageInContainer]>;

// Warnings and notes related to the function effects system underlying
// the nonblocking and nonallocating attributes.
def FunctionEffects : DiagGroup<"function-effects">;

// Warnings and notes InstallAPI verification.
def InstallAPIViolation : DiagGroup<"installapi-violation">;

Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1508,6 +1508,8 @@ def err_omp_unexpected_append_op : Error<
"unexpected operation specified in 'append_args' clause, expected 'interop'">;
def err_omp_unexpected_execution_modifier : Error<
"unexpected 'execution' modifier in non-executable context">;
def err_omp_unknown_adjust_args_op : Error<
"incorrect adjust_args type, expected 'need_device_ptr' or 'nothing'">;
def err_omp_declare_variant_wrong_clause : Error<
"expected %select{'match'|'match', 'adjust_args', or 'append_args'}0 clause "
"on 'omp declare variant' directive">;
Expand Down
50 changes: 44 additions & 6 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -973,7 +973,7 @@ def err_main_global_variable :
def warn_main_redefined : Warning<"variable named 'main' with external linkage "
"has undefined behavior">, InGroup<Main>;
def ext_main_used : Extension<
"ISO C++ does not allow 'main' to be used by a program">, InGroup<Main>;
"referring to 'main' within an expression is a Clang extension">, InGroup<Main>;

/// parser diagnostics
def ext_no_declarators : ExtWarn<"declaration does not declare anything">,
Expand Down Expand Up @@ -3246,6 +3246,9 @@ def warn_unsupported_target_attribute
def err_attribute_unsupported
: Error<"%0 attribute is not supported on targets missing %1;"
" specify an appropriate -march= or -mcpu=">;
def err_attribute_unsupported_m_profile
: Error<"on M-profile architectures %0 attribute is not supported on targets missing %1;"
" specify an appropriate -march= or -mcpu=">;
def err_duplicate_target_attribute
: Error<"%select{unsupported|duplicate|unknown}0%select{| CPU|"
" tune CPU}1 '%2' in the '%select{target|target_clones|target_version}3' "
Expand All @@ -3272,11 +3275,23 @@ def warn_aligned_attr_underaligned : Warning<err_alignas_underaligned.Summary>,
def err_attribute_sizeless_type : Error<
"%0 attribute cannot be applied to sizeless type %1">;
def err_attribute_argument_n_type : Error<
"%0 attribute requires parameter %1 to be %select{int or bool|an integer "
"constant|a string|an identifier|a constant expression|a builtin function}2">;
"%0 attribute requires parameter %1 to be %select{"
"int or bool"
"|an integer constant"
"|a string"
"|an identifier"
"|a constant expression"
"|a builtin function"
"|nullptr or a bool, int, or enum literal}2">;
def err_attribute_argument_type : Error<
"%0 attribute requires %select{int or bool|an integer "
"constant|a string|an identifier}1">;
"%0 attribute requires %select{"
"int or bool"
"|an integer constant"
"|a string"
"|an identifier"
"|a constant expression"
"|a builtin function"
"|a pointer or a bool, int, or enum literal}1">;
def err_attribute_argument_out_of_range : Error<
"%0 attribute requires integer constant between %1 and %2 inclusive">;
def err_init_priority_object_attr : Error<
Expand Down Expand Up @@ -3728,7 +3743,8 @@ def warn_attribute_wrong_decl_type : Warning<
"|types and namespaces"
"|variables, functions and classes"
"|kernel functions"
"|non-K&R-style functions}2">,
"|non-K&R-style functions"
"|functions that return bool, integer, or a pointer type}2">,
InGroup<IgnoredAttributes>;
def err_attribute_wrong_decl_type : Error<warn_attribute_wrong_decl_type.Summary>;
def warn_type_attribute_wrong_type : Warning<
Expand Down Expand Up @@ -6075,6 +6091,9 @@ def err_thread_thread_different_kind : Error<
def err_mismatched_owning_module : Error<
"declaration of %0 in %select{the global module|module %2}1 follows "
"declaration in %select{the global module|module %4}3">;
def err_multiple_decl_in_different_modules : Error<
"declaration %0 attached to named module '%1' can't be attached to "
"other modules">;
def err_redefinition_different_type : Error<
"redefinition of %0 with a different type%diff{: $ vs $|}1,2">;
def err_redefinition_different_kind : Error<
Expand Down Expand Up @@ -8937,6 +8956,8 @@ def err_expected_callable_argument : Error<
"expected a callable expression as %ordinal0 argument to %1, found %2">;
def note_building_builtin_dump_struct_call : Note<
"in call to printing function with arguments '(%0)' while dumping struct">;
def err_builtin_verbose_trap_arg : Error<
"argument to __builtin_verbose_trap must %select{be a pointer to a constant string|not contain $}0">;

def err_atomic_load_store_uses_lib : Error<
"atomic %select{load|store}0 requires runtime support that is not "
Expand Down Expand Up @@ -10881,6 +10902,23 @@ def warn_imp_cast_drops_unaligned : Warning<
"implicit cast from type %0 to type %1 drops __unaligned qualifier">,
InGroup<DiagGroup<"unaligned-qualifier-implicit-cast">>;

// Function effects
// spoofing nonblocking/nonallocating
def warn_invalid_add_func_effects : Warning<
"attribute '%0' should not be added via type conversion">,
InGroup<FunctionEffects>;
def warn_mismatched_func_effect_override : Warning<
"attribute '%0' on overriding function does not match base declaration">,
InGroup<FunctionEffects>;
def warn_mismatched_func_effect_redeclaration : Warning<
"attribute '%0' on function does not match previous declaration">,
InGroup<FunctionEffects>;
def warn_conflicting_func_effects : Warning<
"effects conflict when merging declarations; kept '%0', discarded '%1'">,
InGroup<FunctionEffects>;
def err_func_with_effects_no_prototype : Error<
"'%0' function must have a prototype">;

} // end of sema category

let CategoryName = "API Notes Issue" in {
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,10 @@ class LangOptions : public LangOptionsBase {
// implementation on real-world examples.
std::string OpenACCMacroOverride;

// Indicates if the wasm-opt binary must be ignored in the case of a
// WebAssembly target.
bool NoWasmOpt = false;

LangOptions();

/// Set language defaults for the given input language and
Expand Down
134 changes: 134 additions & 0 deletions clang/include/clang/Basic/PointerAuthOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,144 @@
#ifndef LLVM_CLANG_BASIC_POINTERAUTHOPTIONS_H
#define LLVM_CLANG_BASIC_POINTERAUTHOPTIONS_H

#include "clang/Basic/LLVM.h"
#include "clang/Basic/LangOptions.h"
#include "llvm/ADT/STLForwardCompat.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Target/TargetOptions.h"
#include <optional>

namespace clang {

constexpr unsigned PointerAuthKeyNone = -1;

class PointerAuthSchema {
public:
enum class Kind : unsigned {
None,
ARM8_3,
};

/// Hardware pointer-signing keys in ARM8.3.
///
/// These values are the same used in ptrauth.h.
enum class ARM8_3Key : unsigned {
ASIA = 0,
ASIB = 1,
ASDA = 2,
ASDB = 3
};

/// Forms of extra discrimination.
enum class Discrimination : unsigned {
/// No additional discrimination.
None,

/// Discriminate using a constant value.
Constant,
};

private:
Kind TheKind : 2;
unsigned IsAddressDiscriminated : 1;
unsigned IsIsaPointer : 1;
unsigned AuthenticatesNullValues : 1;
PointerAuthenticationMode SelectedAuthenticationMode : 2;
Discrimination DiscriminationKind : 2;
unsigned Key : 2;
unsigned ConstantDiscriminator : 16;

public:
PointerAuthSchema() : TheKind(Kind::None) {}

PointerAuthSchema(
ARM8_3Key Key, bool IsAddressDiscriminated,
PointerAuthenticationMode AuthenticationMode,
Discrimination OtherDiscrimination,
std::optional<uint16_t> ConstantDiscriminatorOrNone = std::nullopt,
bool IsIsaPointer = false, bool AuthenticatesNullValues = false)
: TheKind(Kind::ARM8_3), IsAddressDiscriminated(IsAddressDiscriminated),
IsIsaPointer(IsIsaPointer),
AuthenticatesNullValues(AuthenticatesNullValues),
SelectedAuthenticationMode(AuthenticationMode),
DiscriminationKind(OtherDiscrimination), Key(llvm::to_underlying(Key)) {
assert((getOtherDiscrimination() != Discrimination::Constant ||
ConstantDiscriminatorOrNone) &&
"constant discrimination requires a constant!");
if (ConstantDiscriminatorOrNone)
ConstantDiscriminator = *ConstantDiscriminatorOrNone;
}

PointerAuthSchema(
ARM8_3Key Key, bool IsAddressDiscriminated,
Discrimination OtherDiscrimination,
std::optional<uint16_t> ConstantDiscriminatorOrNone = std::nullopt,
bool IsIsaPointer = false, bool AuthenticatesNullValues = false)
: PointerAuthSchema(Key, IsAddressDiscriminated,
PointerAuthenticationMode::SignAndAuth,
OtherDiscrimination, ConstantDiscriminatorOrNone,
IsIsaPointer, AuthenticatesNullValues) {}

Kind getKind() const { return TheKind; }

explicit operator bool() const { return isEnabled(); }

bool isEnabled() const { return getKind() != Kind::None; }

bool isAddressDiscriminated() const {
assert(getKind() != Kind::None);
return IsAddressDiscriminated;
}

bool isIsaPointer() const {
assert(getKind() != Kind::None);
return IsIsaPointer;
}

bool authenticatesNullValues() const {
assert(getKind() != Kind::None);
return AuthenticatesNullValues;
}

bool hasOtherDiscrimination() const {
return getOtherDiscrimination() != Discrimination::None;
}

Discrimination getOtherDiscrimination() const {
assert(getKind() != Kind::None);
return DiscriminationKind;
}

uint16_t getConstantDiscrimination() const {
assert(getOtherDiscrimination() == Discrimination::Constant);
return ConstantDiscriminator;
}

unsigned getKey() const {
switch (getKind()) {
case Kind::None:
llvm_unreachable("calling getKey() on disabled schema");
case Kind::ARM8_3:
return llvm::to_underlying(getARM8_3Key());
}
llvm_unreachable("bad key kind");
}

PointerAuthenticationMode getAuthenticationMode() const {
return SelectedAuthenticationMode;
}

ARM8_3Key getARM8_3Key() const {
assert(getKind() == Kind::ARM8_3);
return ARM8_3Key(Key);
}
};

struct PointerAuthOptions {
/// The ABI for C function pointers.
PointerAuthSchema FunctionPointers;
};

} // end namespace clang

#endif
1 change: 1 addition & 0 deletions clang/include/clang/Basic/SourceManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -1981,6 +1981,7 @@ class SourceManager : public RefCountedBase<SourceManager> {
SourceLocation SpellLoc,
SourceLocation ExpansionLoc,
unsigned ExpansionLength) const;
void updateSlocUsageStats() const;
};

/// Comparison function object.
Expand Down
82 changes: 43 additions & 39 deletions clang/include/clang/Basic/arm_sme.td

Large diffs are not rendered by default.

263 changes: 138 additions & 125 deletions clang/include/clang/Basic/arm_sve.td

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion clang/include/clang/Basic/arm_sve_sme_incl.td
Original file line number Diff line number Diff line change
Expand Up @@ -267,12 +267,15 @@ class ImmCheck<int arg, ImmCheckType kind, int eltSizeArg = -1> {
ImmCheckType Kind = kind;
}

defvar InvalidMode = "";

class Inst<string n, string p, string t, MergeType mt, string i,
list<FlagType> ft, list<ImmCheck> ch, MemEltType met = MemEltTyDefault> {
string Name = n;
string Prototype = p;
string Types = t;
string TargetGuard = "sve";
string SVETargetGuard = "sve";
string SMETargetGuard = "sme";
int Merge = mt.Value;
string MergeSuffix = mt.Suffix;
string LLVMIntrinsic = i;
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/CodeGen/ModuleBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include "clang/AST/ASTConsumer.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/StringRef.h"

namespace llvm {
class Constant;
Expand All @@ -27,6 +28,9 @@ namespace llvm {
}
}

// Prefix of the name of the artificial inline frame.
inline constexpr llvm::StringRef ClangTrapPrefix = "__clang_trap_msg";

namespace clang {
class CodeGenOptions;
class CoverageSourceInfo;
Expand Down
3 changes: 0 additions & 3 deletions clang/include/clang/Driver/Driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -747,9 +747,6 @@ class Driver {
/// option.
void setDriverMode(StringRef DriverModeValue);

/// Set the resource directory, depending on which driver is being used.
void setResourceDirectory();

/// Parse the \p Args list for LTO options and record the type of LTO
/// compilation based on which -f(no-)?lto(=.*)? option occurs last.
void setLTOMode(const llvm::opt::ArgList &Args);
Expand Down
30 changes: 19 additions & 11 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -5421,6 +5421,7 @@ def module_file_info : Flag<["-"], "module-file-info">, Flags<[]>,
HelpText<"Provide information about a particular module file">;
def mthumb : Flag<["-"], "mthumb">, Group<m_Group>;
def mtune_EQ : Joined<["-"], "mtune=">, Group<m_Group>,
Visibility<[ClangOption, FlangOption]>,
HelpText<"Only supported on AArch64, PowerPC, RISC-V, SPARC, SystemZ, and X86">;
def multi__module : Flag<["-"], "multi_module">;
def multiply__defined__unused : Separate<["-"], "multiply_defined_unused">;
Expand Down Expand Up @@ -5535,10 +5536,7 @@ def print_prog_name_EQ : Joined<["-", "--"], "print-prog-name=">,
Visibility<[ClangOption, CLOption]>;
def print_resource_dir : Flag<["-", "--"], "print-resource-dir">,
HelpText<"Print the resource directory pathname">,
HelpTextForVariants<[FlangOption],
"Print the resource directory pathname that contains lib and "
"include directories with the runtime libraries and MODULE files.">,
Visibility<[ClangOption, CLOption, FlangOption]>;
Visibility<[ClangOption, CLOption]>;
def print_search_dirs : Flag<["-", "--"], "print-search-dirs">,
HelpText<"Print the paths used for finding libraries and programs">,
Visibility<[ClangOption, CLOption]>;
Expand Down Expand Up @@ -6314,12 +6312,12 @@ def mno_gather : Flag<["-"], "mno-gather">, Group<m_Group>,
def mno_scatter : Flag<["-"], "mno-scatter">, Group<m_Group>,
HelpText<"Disable generation of scatter instructions in auto-vectorization(x86 only)">;
def mapx_features_EQ : CommaJoined<["-"], "mapx-features=">, Group<m_x86_Features_Group>,
HelpText<"Enable features of APX">, Values<"egpr,push2pop2,ppx,ndd,ccmp,nf,cf">;
HelpText<"Enable features of APX">, Values<"egpr,push2pop2,ppx,ndd,ccmp,nf,cf,zu">;
def mno_apx_features_EQ : CommaJoined<["-"], "mno-apx-features=">, Group<m_x86_Features_Group>,
HelpText<"Disable features of APX">, Values<"egpr,push2pop2,ppx,ndd,ccmp,nf,cf">;
HelpText<"Disable features of APX">, Values<"egpr,push2pop2,ppx,ndd,ccmp,nf,cf,zu">;
// For stability, we only add a feature to -mapxf after it passes the validation of llvm-test-suite && cpu2017 on Intel SDE.
def mapxf : Flag<["-"], "mapxf">, Alias<mapx_features_EQ>, AliasArgs<["egpr","push2pop2","ppx","ndd","ccmp","nf"]>;
def mno_apxf : Flag<["-"], "mno-apxf">, Alias<mno_apx_features_EQ>, AliasArgs<["egpr","push2pop2","ppx","ndd","ccmp","nf"]>;
def mapxf : Flag<["-"], "mapxf">, Alias<mapx_features_EQ>, AliasArgs<["egpr","push2pop2","ppx","ndd","ccmp","nf","cf"]>;
def mno_apxf : Flag<["-"], "mno-apxf">, Alias<mno_apx_features_EQ>, AliasArgs<["egpr","push2pop2","ppx","ndd","ccmp","nf","cf"]>;
def mapx_inline_asm_use_gpr32 : Flag<["-"], "mapx-inline-asm-use-gpr32">, Group<m_Group>,
HelpText<"Enable use of GPR32 in inline assembly for APX">;
} // let Flags = [TargetSpecific]
Expand Down Expand Up @@ -6740,9 +6738,6 @@ def emit_hlfir : Flag<["-"], "emit-hlfir">, Group<Action_Group>,

let Visibility = [CC1Option, CC1AsOption] in {

def tune_cpu : Separate<["-"], "tune-cpu">,
HelpText<"Tune for a specific cpu type">,
MarshallingInfoString<TargetOpts<"TuneCPU">>;
def target_abi : Separate<["-"], "target-abi">,
HelpText<"Target a particular ABI type">,
MarshallingInfoString<TargetOpts<"ABI">>;
Expand All @@ -6769,6 +6764,9 @@ def darwin_target_variant_triple : Separate<["-"], "darwin-target-variant-triple

let Visibility = [CC1Option, CC1AsOption, FC1Option] in {

def tune_cpu : Separate<["-"], "tune-cpu">,
HelpText<"Tune for a specific cpu type">,
MarshallingInfoString<TargetOpts<"TuneCPU">>;
def target_cpu : Separate<["-"], "target-cpu">,
HelpText<"Target a specific cpu type">,
MarshallingInfoString<TargetOpts<"CPU">>;
Expand Down Expand Up @@ -8597,6 +8595,8 @@ def : Separate<["-"], "Xmicrosoft-windows-sdk-root">,
Alias<_SLASH_winsdkdir>;
def : Separate<["-"], "Xmicrosoft-windows-sdk-version">,
Alias<_SLASH_winsdkversion>;
def : Separate<["-"], "Xmicrosoft-windows-sys-root">,
Alias<_SLASH_winsysroot>;

// Ignored:

Expand Down Expand Up @@ -8768,3 +8768,11 @@ def spirv : DXCFlag<"spirv">,
def fspv_target_env_EQ : Joined<["-"], "fspv-target-env=">, Group<dxc_Group>,
HelpText<"Specify the target environment">,
Values<"vulkan1.2, vulkan1.3">;
def no_wasm_opt : Flag<["--"], "no-wasm-opt">,
Group<m_Group>,
HelpText<"Disable the wasm-opt optimizer">,
MarshallingInfoFlag<LangOpts<"NoWasmOpt">>;
def wasm_opt : Flag<["--"], "wasm-opt">,
Group<m_Group>,
HelpText<"Enable the wasm-opt optimizer (default)">,
MarshallingInfoNegativeFlag<LangOpts<"NoWasmOpt">>;
9 changes: 9 additions & 0 deletions clang/include/clang/Frontend/CompilerInvocation.h
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,15 @@ class CompilerInvocation : public CompilerInvocationBase {
/// executable), for finding the builtin compiler path.
static std::string GetResourcesPath(const char *Argv0, void *MainAddr);

/// Populate \p Opts with the default set of pointer authentication-related
/// options given \p LangOpts and \p Triple.
///
/// Note: This is intended to be used by tools which must be aware of
/// pointer authentication-related code generation, e.g. lldb.
static void setDefaultPointerAuthOptions(PointerAuthOptions &Opts,
const LangOptions &LangOpts,
const llvm::Triple &Triple);

/// Retrieve a module hash string that is suitable for uniquely
/// identifying the conditions under which the module was built.
std::string getModuleHash() const;
Expand Down
10 changes: 0 additions & 10 deletions clang/include/clang/Lex/HeaderSearchOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

#include "clang/Basic/LLVM.h"
#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/HashBuilder.h"
Expand Down Expand Up @@ -310,21 +309,12 @@ class HeaderSearchOptions {
}
};

inline llvm::hash_code hash_value(const HeaderSearchOptions::Entry &E) {
return llvm::hash_combine(E.Path, E.Group, E.IsFramework, E.IgnoreSysRoot);
}

template <typename HasherT, llvm::endianness Endianness>
inline void addHash(llvm::HashBuilder<HasherT, Endianness> &HBuilder,
const HeaderSearchOptions::Entry &E) {
HBuilder.add(E.Path, E.Group, E.IsFramework, E.IgnoreSysRoot);
}

inline llvm::hash_code
hash_value(const HeaderSearchOptions::SystemHeaderPrefix &SHP) {
return llvm::hash_combine(SHP.Prefix, SHP.IsSystemHeader);
}

template <typename HasherT, llvm::endianness Endianness>
inline void addHash(llvm::HashBuilder<HasherT, Endianness> &HBuilder,
const HeaderSearchOptions::SystemHeaderPrefix &SHP) {
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Sema/ParsedAttr.h
Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,7 @@ enum AttributeArgumentNType {
AANT_ArgumentIdentifier,
AANT_ArgumentConstantExpr,
AANT_ArgumentBuiltinFunction,
AANT_ArgumentNullptrOrBoolIntOrEnumLiteral,
};

/// These constants match the enumerated choices of
Expand All @@ -1101,6 +1102,7 @@ enum AttributeDeclKind {
ExpectedFunctionVariableOrClass,
ExpectedKernelFunction,
ExpectedFunctionWithProtoType,
ExpectedFunctionReturningPointerBoolIntOrEnum,
};

inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB,
Expand Down
85 changes: 82 additions & 3 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,63 @@ enum class TagUseKind {
Friend // Friend declaration: 'friend struct foo;'
};

/// Used with attributes/effects with a boolean condition, e.g. `nonblocking`.
enum class FunctionEffectMode : uint8_t {
None, // effect is not present.
False, // effect(false).
True, // effect(true).
Dependent // effect(expr) where expr is dependent.
};

struct FunctionEffectDiff {
enum class Kind { Added, Removed, ConditionMismatch };

FunctionEffect::Kind EffectKind;
Kind DiffKind;
FunctionEffectWithCondition Old; // invalid when Added.
FunctionEffectWithCondition New; // invalid when Removed.

StringRef effectName() const {
if (Old.Effect.kind() != FunctionEffect::Kind::None)
return Old.Effect.name();
return New.Effect.name();
}

/// Describes the result of effects differing between a base class's virtual
/// method and an overriding method in a subclass.
enum class OverrideResult {
NoAction,
Warn,
Merge // Merge missing effect from base to derived.
};

/// Return true if adding or removing the effect as part of a type conversion
/// should generate a diagnostic.
bool shouldDiagnoseConversion(QualType SrcType,
const FunctionEffectsRef &SrcFX,
QualType DstType,
const FunctionEffectsRef &DstFX) const;

/// Return true if adding or removing the effect in a redeclaration should
/// generate a diagnostic.
bool shouldDiagnoseRedeclaration(const FunctionDecl &OldFunction,
const FunctionEffectsRef &OldFX,
const FunctionDecl &NewFunction,
const FunctionEffectsRef &NewFX) const;

/// Return true if adding or removing the effect in a C++ virtual method
/// override should generate a diagnostic.
OverrideResult shouldDiagnoseMethodOverride(
const CXXMethodDecl &OldMethod, const FunctionEffectsRef &OldFX,
const CXXMethodDecl &NewMethod, const FunctionEffectsRef &NewFX) const;
};

struct FunctionEffectDifferences : public SmallVector<FunctionEffectDiff> {
/// Caller should short-circuit by checking for equality first.
FunctionEffectDifferences(const FunctionEffectsRef &Old,
const FunctionEffectsRef &New);
};

/// Sema - This implements semantic analysis and AST building for C.
/// \nosubgrouping
class Sema final : public SemaBase {
Expand Down Expand Up @@ -783,6 +840,28 @@ class Sema final : public SemaBase {
/// Warn when implicitly casting 0 to nullptr.
void diagnoseZeroToNullptrConversion(CastKind Kind, const Expr *E);

// ----- function effects ---

/// Warn when implicitly changing function effects.
void diagnoseFunctionEffectConversion(QualType DstType, QualType SrcType,
SourceLocation Loc);

/// Warn and return true if adding an effect to a set would create a conflict.
bool diagnoseConflictingFunctionEffect(const FunctionEffectsRef &FX,
const FunctionEffectWithCondition &EC,
SourceLocation NewAttrLoc);

void
diagnoseFunctionEffectMergeConflicts(const FunctionEffectSet::Conflicts &Errs,
SourceLocation NewLoc,
SourceLocation OldLoc);

/// Try to parse the conditional expression attached to an effect attribute
/// (e.g. 'nonblocking'). (c.f. Sema::ActOnNoexceptSpec). Return an empty
/// optional on error.
std::optional<FunctionEffectMode>
ActOnEffectExpression(Expr *CondExpr, StringRef AttributeName);

bool makeUnavailableInSystemHeader(SourceLocation loc,
UnavailableAttr::ImplicitReason reason);

Expand Down Expand Up @@ -4031,8 +4110,8 @@ class Sema final : public SemaBase {
const ParsedAttributesView &AttrList);
Decl *ActOnUsingEnumDeclaration(Scope *CurScope, AccessSpecifier AS,
SourceLocation UsingLoc,
SourceLocation EnumLoc,
SourceLocation IdentLoc, IdentifierInfo &II,
SourceLocation EnumLoc, SourceRange TyLoc,
const IdentifierInfo &II, ParsedType Ty,
CXXScopeSpec *SS = nullptr);
Decl *ActOnAliasDeclaration(Scope *CurScope, AccessSpecifier AS,
MultiTemplateParamsArg TemplateParams,
Expand Down Expand Up @@ -4612,7 +4691,7 @@ class Sema final : public SemaBase {

std::string getAmbiguousPathsDisplayString(CXXBasePaths &Paths);

bool CheckOverridingFunctionAttributes(const CXXMethodDecl *New,
bool CheckOverridingFunctionAttributes(CXXMethodDecl *New,
const CXXMethodDecl *Old);

/// CheckOverridingFunctionReturnType - Checks whether the return types are
Expand Down
48 changes: 36 additions & 12 deletions clang/include/clang/Serialization/ASTBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "clang/Serialization/SourceLocationEncoding.h"
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/Bitstream/BitCodes.h"
#include "llvm/Support/MathExtras.h"
#include <cassert>
#include <cstdint>

Expand Down Expand Up @@ -70,41 +71,64 @@ using DeclID = DeclIDBase::DeclID;

/// An ID number that refers to a type in an AST file.
///
/// The ID of a type is partitioned into two parts: the lower
/// three bits are used to store the const/volatile/restrict
/// qualifiers (as with QualType) and the upper bits provide a
/// type index. The type index values are partitioned into two
/// The ID of a type is partitioned into three parts:
/// - the lower three bits are used to store the const/volatile/restrict
/// qualifiers (as with QualType).
/// - the next 29 bits provide a type index in the corresponding
/// module file.
/// - the upper 32 bits provide a module file index.
///
/// The type index values are partitioned into two
/// sets. The values below NUM_PREDEF_TYPE_IDs are predefined type
/// IDs (based on the PREDEF_TYPE_*_ID constants), with 0 as a
/// placeholder for "no type". Values from NUM_PREDEF_TYPE_IDs are
/// other types that have serialized representations.
using TypeID = uint32_t;
/// placeholder for "no type". The module file index for predefined
/// types are always 0 since they don't belong to any modules.
/// Values from NUM_PREDEF_TYPE_IDs are other types that have
/// serialized representations.
using TypeID = uint64_t;
/// Same with TypeID except that the LocalTypeID is only meaningful
/// with the corresponding ModuleFile.
///
/// FIXME: Make TypeID and LocalTypeID a class to improve the type
/// safety.
using LocalTypeID = TypeID;

/// A type index; the type ID with the qualifier bits removed.
/// Keep structure alignment 32-bit since the blob is assumed as 32-bit
/// aligned.
class TypeIdx {
uint32_t ModuleFileIndex = 0;
uint32_t Idx = 0;

public:
TypeIdx() = default;
explicit TypeIdx(uint32_t index) : Idx(index) {}

uint32_t getIndex() const { return Idx; }
explicit TypeIdx(uint32_t ModuleFileIdx, uint32_t Idx)
: ModuleFileIndex(ModuleFileIdx), Idx(Idx) {}

uint32_t getModuleFileIndex() const { return ModuleFileIndex; }

uint64_t getValue() const { return ((uint64_t)ModuleFileIndex << 32) | Idx; }

TypeID asTypeID(unsigned FastQuals) const {
if (Idx == uint32_t(-1))
return TypeID(-1);

return (Idx << Qualifiers::FastWidth) | FastQuals;
unsigned Index = (Idx << Qualifiers::FastWidth) | FastQuals;
return ((uint64_t)ModuleFileIndex << 32) | Index;
}

static TypeIdx fromTypeID(TypeID ID) {
if (ID == TypeID(-1))
return TypeIdx(-1);
return TypeIdx(0, -1);

return TypeIdx(ID >> Qualifiers::FastWidth);
return TypeIdx(ID >> 32, (ID & llvm::maskTrailingOnes<TypeID>(32)) >>
Qualifiers::FastWidth);
}
};

static_assert(alignof(TypeIdx) == 4);

/// A structure for putting "fast"-unqualified QualTypes into a
/// DenseMap. This uses the standard pointer hash function.
struct UnsafeQualTypeDenseMapInfo {
Expand Down
22 changes: 10 additions & 12 deletions clang/include/clang/Serialization/ASTReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -491,14 +491,6 @@ class ASTReader
/// ID = (I + 1) << FastQual::Width has already been loaded
llvm::PagedVector<QualType> TypesLoaded;

using GlobalTypeMapType =
ContinuousRangeMap<serialization::TypeID, ModuleFile *, 4>;

/// Mapping from global type IDs to the module in which the
/// type resides along with the offset that should be added to the
/// global type ID to produce a local ID.
GlobalTypeMapType GlobalTypeMap;

/// Declarations that have already been loaded from the chain.
///
/// When the pointer at index I is non-NULL, the declaration with ID
Expand Down Expand Up @@ -1429,8 +1421,8 @@ class ASTReader
RecordLocation(ModuleFile *M, uint64_t O) : F(M), Offset(O) {}
};

QualType readTypeRecord(unsigned Index);
RecordLocation TypeCursorForIndex(unsigned Index);
QualType readTypeRecord(serialization::TypeID ID);
RecordLocation TypeCursorForIndex(serialization::TypeID ID);
void LoadedDecl(unsigned Index, Decl *D);
Decl *ReadDeclRecord(GlobalDeclID ID);
void markIncompleteDeclChain(Decl *D);
Expand Down Expand Up @@ -1544,6 +1536,11 @@ class ASTReader
std::pair<ModuleFile *, unsigned>
translateIdentifierIDToIndex(serialization::IdentifierID ID) const;

/// Translate an \param TypeID ID to the index of TypesLoaded
/// array and the corresponding module file.
std::pair<ModuleFile *, unsigned>
translateTypeIDToIndex(serialization::TypeID ID) const;

public:
/// Load the AST file and validate its contents against the given
/// Preprocessor.
Expand Down Expand Up @@ -1892,10 +1889,11 @@ class ASTReader
QualType GetType(serialization::TypeID ID);

/// Resolve a local type ID within a given AST file into a type.
QualType getLocalType(ModuleFile &F, unsigned LocalID);
QualType getLocalType(ModuleFile &F, serialization::LocalTypeID LocalID);

/// Map a local type ID within a given AST file into a global type ID.
serialization::TypeID getGlobalTypeID(ModuleFile &F, unsigned LocalID) const;
serialization::TypeID
getGlobalTypeID(ModuleFile &F, serialization::LocalTypeID LocalID) const;

/// Read a type from the current position in the given record, which
/// was read from the given AST file.
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Serialization/ASTRecordReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ class ASTRecordReader
void readTypeLoc(TypeLoc TL, LocSeq *Seq = nullptr);

/// Map a local type ID within a given AST file to a global type ID.
serialization::TypeID getGlobalTypeID(unsigned LocalID) const {
serialization::TypeID getGlobalTypeID(serialization::TypeID LocalID) const {
return Reader->getGlobalTypeID(*F, LocalID);
}

Expand Down
13 changes: 11 additions & 2 deletions clang/include/clang/Serialization/ASTWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,11 @@ class ASTWriter : public ASTDeserializationListener,
/// unit, while 0 is reserved for NULL.
llvm::DenseMap<const Decl *, LocalDeclID> DeclIDs;

/// Set of predefined decls. This is a helper data to determine if a decl
/// is predefined. It should be more clear and safer to query the set
/// instead of comparing the result of `getDeclID()` or `GetDeclRef()`.
llvm::SmallPtrSet<const Decl *, 32> PredefinedDecls;

/// Offset of each declaration in the bitstream, indexed by
/// the declaration's ID.
std::vector<serialization::DeclOffset> DeclOffsets;
Expand Down Expand Up @@ -563,8 +568,6 @@ class ASTWriter : public ASTDeserializationListener,
void WriteType(QualType T);

bool isLookupResultExternal(StoredDeclsList &Result, DeclContext *DC);
bool isLookupResultEntirelyExternalOrUnreachable(StoredDeclsList &Result,
DeclContext *DC);

void GenerateNameLookupTable(const DeclContext *DC,
llvm::SmallVectorImpl<char> &LookupTable);
Expand Down Expand Up @@ -844,6 +847,8 @@ class ASTWriter : public ASTDeserializationListener,
bool hasChain() const { return Chain; }
ASTReader *getChain() const { return Chain; }

bool isWritingModule() const { return WritingModule; }

bool isWritingStdCXXNamedModules() const {
return WritingModule && WritingModule->isNamedModule();
}
Expand All @@ -852,6 +857,10 @@ class ASTWriter : public ASTDeserializationListener,

bool getDoneWritingDeclsAndTypes() const { return DoneWritingDeclsAndTypes; }

bool isDeclPredefined(const Decl *D) const {
return PredefinedDecls.count(D);
}

void handleVTable(CXXRecordDecl *RD);

private:
Expand Down
3 changes: 0 additions & 3 deletions clang/include/clang/Serialization/ModuleFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -482,9 +482,6 @@ class ModuleFile {
/// the global type ID space.
serialization::TypeID BaseTypeIndex = 0;

/// Remapping table for type IDs in this module.
ContinuousRangeMap<uint32_t, int, 2> TypeRemap;

// === Miscellaneous ===

/// Diagnostic IDs and their mappings that the user changed.
Expand Down
56 changes: 54 additions & 2 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1110,6 +1110,31 @@ void ASTContext::setCurrentNamedModule(Module *M) {
CurrentCXXNamedModule = M;
}

bool ASTContext::isInSameModule(const Module *M1, const Module *M2) {
if (!M1 != !M2)
return false;

/// Get the representative module for M. The representative module is the
/// first module unit for a specific primary module name. So that the module
/// units have the same representative module belongs to the same module.
///
/// The process is helpful to reduce the expensive string operations.
auto GetRepresentativeModule = [this](const Module *M) {
auto Iter = SameModuleLookupSet.find(M);
if (Iter != SameModuleLookupSet.end())
return Iter->second;

const Module *RepresentativeModule =
PrimaryModuleNameMap.try_emplace(M->getPrimaryModuleInterfaceName(), M)
.first->second;
SameModuleLookupSet[M] = RepresentativeModule;
return RepresentativeModule;
};

assert(M1 && "Shouldn't call `isInSameModule` if both M1 and M2 are none.");
return GetRepresentativeModule(M1) == GetRepresentativeModule(M2);
}

ExternCContextDecl *ASTContext::getExternCContextDecl() const {
if (!ExternCContext)
ExternCContext = ExternCContextDecl::Create(*this, getTranslationUnitDecl());
Expand Down Expand Up @@ -4576,11 +4601,13 @@ QualType ASTContext::getFunctionTypeInternal(
size_t Size = FunctionProtoType::totalSizeToAlloc<
QualType, SourceLocation, FunctionType::FunctionTypeExtraBitfields,
FunctionType::FunctionTypeArmAttributes, FunctionType::ExceptionType,
Expr *, FunctionDecl *, FunctionProtoType::ExtParameterInfo, Qualifiers>(
Expr *, FunctionDecl *, FunctionProtoType::ExtParameterInfo,
FunctionEffect, EffectConditionExpr, Qualifiers>(
NumArgs, EPI.Variadic, EPI.requiresFunctionProtoTypeExtraBitfields(),
EPI.requiresFunctionProtoTypeArmAttributes(), ESH.NumExceptionType,
ESH.NumExprPtr, ESH.NumFunctionDeclPtr,
EPI.ExtParameterInfos ? NumArgs : 0,
EPI.ExtParameterInfos ? NumArgs : 0, EPI.FunctionEffects.size(),
EPI.FunctionEffects.conditions().size(),
EPI.TypeQuals.hasNonFastQualifiers() ? 1 : 0);

auto *FTP = (FunctionProtoType *)Allocate(Size, alignof(FunctionProtoType));
Expand Down Expand Up @@ -10525,6 +10552,8 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs,

FunctionType::ExtInfo einfo = lbaseInfo.withNoReturn(NoReturn);

std::optional<FunctionEffectSet> MergedFX;

if (lproto && rproto) { // two C99 style function prototypes
assert((AllowCXX ||
(!lproto->hasExceptionSpec() && !rproto->hasExceptionSpec())) &&
Expand All @@ -10540,6 +10569,25 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs,
if (lproto->getMethodQuals() != rproto->getMethodQuals())
return {};

// Function effects are handled similarly to noreturn, see above.
FunctionEffectsRef LHSFX = lproto->getFunctionEffects();
FunctionEffectsRef RHSFX = rproto->getFunctionEffects();
if (LHSFX != RHSFX) {
if (IsConditionalOperator)
MergedFX = FunctionEffectSet::getIntersection(LHSFX, RHSFX);
else {
FunctionEffectSet::Conflicts Errs;
MergedFX = FunctionEffectSet::getUnion(LHSFX, RHSFX, Errs);
// Here we're discarding a possible error due to conflicts in the effect
// sets. But we're not in a context where we can report it. The
// operation does however guarantee maintenance of invariants.
}
if (*MergedFX != LHSFX)
allLTypes = false;
if (*MergedFX != RHSFX)
allRTypes = false;
}

SmallVector<FunctionProtoType::ExtParameterInfo, 4> newParamInfos;
bool canUseLeft, canUseRight;
if (!mergeExtParameterInfo(lproto, rproto, canUseLeft, canUseRight,
Expand Down Expand Up @@ -10583,6 +10631,8 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs,
EPI.ExtInfo = einfo;
EPI.ExtParameterInfos =
newParamInfos.empty() ? nullptr : newParamInfos.data();
if (MergedFX)
EPI.FunctionEffects = *MergedFX;
return getFunctionType(retType, types, EPI);
}

Expand Down Expand Up @@ -10620,6 +10670,8 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs,

FunctionProtoType::ExtProtoInfo EPI = proto->getExtProtoInfo();
EPI.ExtInfo = einfo;
if (MergedFX)
EPI.FunctionEffects = *MergedFX;
return getFunctionType(retType, proto->getParamTypes(), EPI);
}

Expand Down
7 changes: 5 additions & 2 deletions clang/lib/AST/ASTImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6565,6 +6565,11 @@ ExpectedDecl ASTNodeImporter::VisitVarTemplateSpecializationDecl(
return D2;
}

// Update InsertPos, because preceding import calls may have invalidated
// it by adding new specializations.
if (!VarTemplate->findSpecialization(TemplateArgs, InsertPos))
VarTemplate->AddSpecialization(D2, InsertPos);

QualType T;
if (Error Err = importInto(T, D->getType()))
return std::move(Err);
Expand Down Expand Up @@ -6603,8 +6608,6 @@ ExpectedDecl ASTNodeImporter::VisitVarTemplateSpecializationDecl(
if (FoundSpecialization)
D2->setPreviousDecl(FoundSpecialization->getMostRecentDecl());

VarTemplate->AddSpecialization(D2, InsertPos);

addDeclToContexts(D, D2);

// Import the rest of the chain. I.e. import all subsequent declarations.
Expand Down
21 changes: 18 additions & 3 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1885,7 +1885,8 @@ static bool EvaluateAtomic(const Expr *E, const LValue *This, APValue &Result,
EvalInfo &Info);
static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result);
static bool EvaluateBuiltinStrLen(const Expr *E, uint64_t &Result,
EvalInfo &Info);
EvalInfo &Info,
std::string *StringResult = nullptr);

/// Evaluate an integer or fixed point expression into an APResult.
static bool EvaluateFixedPointOrInteger(const Expr *E, APFixedPoint &Result,
Expand Down Expand Up @@ -17009,7 +17010,7 @@ bool Expr::tryEvaluateObjectSize(uint64_t &Result, ASTContext &Ctx,
}

static bool EvaluateBuiltinStrLen(const Expr *E, uint64_t &Result,
EvalInfo &Info) {
EvalInfo &Info, std::string *StringResult) {
if (!E->getType()->hasPointerRepresentation() || !E->isPRValue())
return false;

Expand All @@ -17036,6 +17037,8 @@ static bool EvaluateBuiltinStrLen(const Expr *E, uint64_t &Result,
Str = Str.substr(0, Pos);

Result = Str.size();
if (StringResult)
*StringResult = Str;
return true;
}

Expand All @@ -17051,12 +17054,24 @@ static bool EvaluateBuiltinStrLen(const Expr *E, uint64_t &Result,
if (!Char.getInt()) {
Result = Strlen;
return true;
}
} else if (StringResult)
StringResult->push_back(Char.getInt().getExtValue());
if (!HandleLValueArrayAdjustment(Info, E, String, CharTy, 1))
return false;
}
}

std::optional<std::string> Expr::tryEvaluateString(ASTContext &Ctx) const {
Expr::EvalStatus Status;
EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantFold);
uint64_t Result;
std::string StringResult;

if (EvaluateBuiltinStrLen(this, Result, Info, &StringResult))
return StringResult;
return {};
}

bool Expr::EvaluateCharRangeAsString(std::string &Result,
const Expr *SizeExpression,
const Expr *PtrExpression, ASTContext &Ctx,
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/Interp/ByteCodeEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ class ByteCodeEmitter {
bool jump(const LabelTy &Label);
bool fallthrough(const LabelTy &Label);

/// We're always emitting bytecode.
bool isActive() const { return true; }

/// Callback for local registration.
Local createLocal(Descriptor *D);

Expand Down
59 changes: 37 additions & 22 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1234,7 +1234,7 @@ bool ByteCodeExprGen<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
const Record *R = getRecord(E->getType());

if (Inits.size() == 1 && E->getType() == Inits[0]->getType())
return this->visitInitializer(Inits[0]);
return this->delegate(Inits[0]);

auto initPrimitiveField = [=](const Record::Field *FieldToInit,
const Expr *Init, PrimType T) -> bool {
Expand Down Expand Up @@ -1329,22 +1329,8 @@ bool ByteCodeExprGen<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
}

if (T->isArrayType()) {
// Prepare composite return value.
if (!Initializing) {
if (GlobalDecl) {
std::optional<unsigned> GlobalIndex = P.createGlobal(E);
if (!GlobalIndex)
return false;
if (!this->emitGetPtrGlobal(*GlobalIndex, E))
return false;
} else {
std::optional<unsigned> LocalIndex = allocateLocal(E);
if (!LocalIndex)
return false;
if (!this->emitGetPtrLocal(*LocalIndex, E))
return false;
}
}
if (Inits.size() == 1 && E->getType() == Inits[0]->getType())
return this->delegate(Inits[0]);

unsigned ElementIndex = 0;
for (const Expr *Init : Inits) {
Expand Down Expand Up @@ -2150,7 +2136,7 @@ bool ByteCodeExprGen<Emitter>::VisitMaterializeTemporaryExpr(

if (Initializing) {
// We already have a value, just initialize that.
return this->visitInitializer(SubExpr);
return this->delegate(SubExpr);
}
// If we don't end up using the materialized temporary anyway, don't
// bother creating it.
Expand Down Expand Up @@ -3404,11 +3390,16 @@ bool ByteCodeExprGen<Emitter>::visitDecl(const VarDecl *VD,
}

template <class Emitter>
bool ByteCodeExprGen<Emitter>::visitVarDecl(const VarDecl *VD) {
VarCreationState ByteCodeExprGen<Emitter>::visitVarDecl(const VarDecl *VD) {
// We don't know what to do with these, so just return false.
if (VD->getType().isNull())
return false;

// This case is EvalEmitter-only. If we won't create any instructions for the
// initializer anyway, don't bother creating the variable in the first place.
if (!this->isActive())
return VarCreationState::NotCreated();

const Expr *Init = VD->getInit();
std::optional<PrimType> VarT = classify(VD->getType());

Expand Down Expand Up @@ -3562,6 +3553,24 @@ bool ByteCodeExprGen<Emitter>::VisitBuiltinCallExpr(const CallExpr *E) {
if (!Func)
return false;

// For these, we're expected to ultimately return an APValue pointing
// to the CallExpr. This is needed to get the correct codegen.
unsigned Builtin = E->getBuiltinCallee();
if (Builtin == Builtin::BI__builtin___CFStringMakeConstantString ||
Builtin == Builtin::BI__builtin___NSStringMakeConstantString ||
Builtin == Builtin::BI__builtin_ptrauth_sign_constant ||
Builtin == Builtin::BI__builtin_function_start) {
if (std::optional<unsigned> GlobalOffset = P.createGlobal(E)) {
if (!this->emitGetPtrGlobal(*GlobalOffset, E))
return false;

if (PrimType PT = classifyPrim(E); PT != PT_Ptr && isPtrType(PT))
return this->emitDecayPtr(PT_Ptr, PT, E);
return true;
}
return false;
}

QualType ReturnType = E->getType();
std::optional<PrimType> ReturnT = classify(E);

Expand Down Expand Up @@ -4226,7 +4235,10 @@ bool ByteCodeExprGen<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
if ((VD->hasGlobalStorage() || VD->isLocalVarDecl() ||
VD->isStaticDataMember()) &&
typeShouldBeVisited(VD->getType())) {
if (!this->visitVarDecl(VD))
auto VarState = this->visitVarDecl(VD);
if (VarState.notCreated())
return true;
if (!VarState)
return false;
// Retry.
return this->visitDeclRef(VD, E);
Expand All @@ -4236,7 +4248,10 @@ bool ByteCodeExprGen<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
if (const auto *VD = dyn_cast<VarDecl>(D);
VD && VD->getAnyInitializer() &&
VD->getType().isConstant(Ctx.getASTContext()) && !VD->isWeak()) {
if (!this->visitVarDecl(VD))
auto VarState = this->visitVarDecl(VD);
if (VarState.notCreated())
return true;
if (!VarState)
return false;
// Retry.
return this->visitDeclRef(VD, E);
Expand All @@ -4250,7 +4265,7 @@ bool ByteCodeExprGen<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
if (E->getType()->isVoidType())
return true;
// Convert the dummy pointer to another pointer type if we have to.
if (PrimType PT = classifyPrim(E); PT != PT_Ptr) {
if (PrimType PT = classifyPrim(E); PT != PT_Ptr && isPtrType(PT)) {
if (!this->emitDecayPtr(PT_Ptr, PT, E))
return false;
}
Expand Down
15 changes: 13 additions & 2 deletions clang/lib/AST/Interp/ByteCodeExprGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ struct InitLink {
};
};

/// State encapsulating if a the variable creation has been successful,
/// unsuccessful, or no variable has been created at all.
struct VarCreationState {
std::optional<bool> S = std::nullopt;
VarCreationState() = default;
VarCreationState(bool b) : S(b) {}
static VarCreationState NotCreated() { return VarCreationState(); }

operator bool() const { return S && *S; }
bool notCreated() const { return !S; }
};

/// Compilation context for expressions.
template <class Emitter>
class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
Expand Down Expand Up @@ -220,9 +232,8 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
/// Just pass evaluation on to \p E. This leaves all the parsing flags
/// intact.
bool delegate(const Expr *E);

/// Creates and initializes a variable from the given decl.
bool visitVarDecl(const VarDecl *VD);
VarCreationState visitVarDecl(const VarDecl *VD);
/// Visit an APValue.
bool visitAPValue(const APValue &Val, PrimType ValType, const Expr *E);
bool visitAPValueInitializer(const APValue &Val, const Expr *E);
Expand Down
19 changes: 16 additions & 3 deletions clang/lib/AST/Interp/Descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,14 @@ static void dtorArrayTy(Block *, std::byte *Ptr, const Descriptor *D) {
template <typename T>
static void moveArrayTy(Block *, const std::byte *Src, std::byte *Dst,
const Descriptor *D) {
// FIXME: Need to copy the InitMap?
// FIXME: Get rid of the const_cast.
InitMapPtr &SrcIMP =
*reinterpret_cast<InitMapPtr *>(const_cast<std::byte *>(Src));
if (SrcIMP) {
// We only ever invoke the moveFunc when moving block contents to a
// DeadBlock. DeadBlocks don't need InitMaps, so we destroy them here.
SrcIMP = std::nullopt;
}
Src += sizeof(InitMapPtr);
Dst += sizeof(InitMapPtr);
for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) {
Expand Down Expand Up @@ -359,8 +366,14 @@ QualType Descriptor::getType() const {

QualType Descriptor::getElemQualType() const {
assert(isArray());
const auto *AT = cast<ArrayType>(getType());
return AT->getElementType();
QualType T = getType();
if (const auto *AT = T->getAsArrayTypeUnsafe())
return AT->getElementType();
if (const auto *CT = T->getAs<ComplexType>())
return CT->getElementType();
if (const auto *CT = T->getAs<VectorType>())
return CT->getElementType();
llvm_unreachable("Array that's not an array/complex/vector type?");
}

SourceLocation Descriptor::getLocation() const {
Expand Down
8 changes: 4 additions & 4 deletions clang/lib/AST/Interp/EvalEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ class EvalEmitter : public SourceMapper {
bool jump(const LabelTy &Label);
bool fallthrough(const LabelTy &Label);

/// Since expressions can only jump forward, predicated execution is
/// used to deal with if-else statements.
bool isActive() const { return CurrentLabel == ActiveLabel; }

/// Callback for registering a local.
Local createLocal(Descriptor *D);

Expand Down Expand Up @@ -117,10 +121,6 @@ class EvalEmitter : public SourceMapper {
/// Active block which should be executed.
LabelTy ActiveLabel = 0;

/// Since expressions can only jump forward, predicated execution is
/// used to deal with if-else statements.
bool isActive() const { return CurrentLabel == ActiveLabel; }

protected:
#define GET_EVAL_PROTO
#include "Opcodes.inc"
Expand Down
Loading