195 changes: 195 additions & 0 deletions clang-tools-extra/clang-tidy/utils/DesignatedInitializers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
//===--- DesignatedInitializers.cpp - clang-tidy --------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file provides utilities for designated initializers.
///
//===----------------------------------------------------------------------===//

#include "DesignatedInitializers.h"
#include "clang/AST/DeclCXX.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/ScopeExit.h"

namespace clang::tidy::utils {

namespace {

/// Returns true if Name is reserved, like _Foo or __Vector_base.
static inline bool isReservedName(llvm::StringRef Name) {
// This doesn't catch all cases, but the most common.
return Name.size() >= 2 && Name[0] == '_' &&
(isUppercase(Name[1]) || Name[1] == '_');
}

// Helper class to iterate over the designator names of an aggregate type.
//
// For an array type, yields [0], [1], [2]...
// For aggregate classes, yields null for each base, then .field1, .field2,
// ...
class AggregateDesignatorNames {
public:
AggregateDesignatorNames(QualType T) {
if (!T.isNull()) {
T = T.getCanonicalType();
if (T->isArrayType()) {
IsArray = true;
Valid = true;
return;
}
if (const RecordDecl *RD = T->getAsRecordDecl()) {
Valid = true;
FieldsIt = RD->field_begin();
FieldsEnd = RD->field_end();
if (const auto *CRD = llvm::dyn_cast<CXXRecordDecl>(RD)) {
BasesIt = CRD->bases_begin();
BasesEnd = CRD->bases_end();
Valid = CRD->isAggregate();
}
OneField = Valid && BasesIt == BasesEnd && FieldsIt != FieldsEnd &&
std::next(FieldsIt) == FieldsEnd;
}
}
}
// Returns false if the type was not an aggregate.
operator bool() { return Valid; }
// Advance to the next element in the aggregate.
void next() {
if (IsArray)
++Index;
else if (BasesIt != BasesEnd)
++BasesIt;
else if (FieldsIt != FieldsEnd)
++FieldsIt;
}
// Print the designator to Out.
// Returns false if we could not produce a designator for this element.
bool append(std::string &Out, bool ForSubobject) {
if (IsArray) {
Out.push_back('[');
Out.append(std::to_string(Index));
Out.push_back(']');
return true;
}
if (BasesIt != BasesEnd)
return false; // Bases can't be designated. Should we make one up?
if (FieldsIt != FieldsEnd) {
llvm::StringRef FieldName;
if (const IdentifierInfo *II = FieldsIt->getIdentifier())
FieldName = II->getName();

// For certain objects, their subobjects may be named directly.
if (ForSubobject &&
(FieldsIt->isAnonymousStructOrUnion() ||
// std::array<int,3> x = {1,2,3}. Designators not strictly valid!
(OneField && isReservedName(FieldName))))
return true;

if (!FieldName.empty() && !isReservedName(FieldName)) {
Out.push_back('.');
Out.append(FieldName.begin(), FieldName.end());
return true;
}
return false;
}
return false;
}

private:
bool Valid = false;
bool IsArray = false;
bool OneField = false; // e.g. std::array { T __elements[N]; }
unsigned Index = 0;
CXXRecordDecl::base_class_const_iterator BasesIt;
CXXRecordDecl::base_class_const_iterator BasesEnd;
RecordDecl::field_iterator FieldsIt;
RecordDecl::field_iterator FieldsEnd;
};

// Collect designator labels describing the elements of an init list.
//
// This function contributes the designators of some (sub)object, which is
// represented by the semantic InitListExpr Sem.
// This includes any nested subobjects, but *only* if they are part of the
// same original syntactic init list (due to brace elision). In other words,
// it may descend into subobjects but not written init-lists.
//
// For example: struct Outer { Inner a,b; }; struct Inner { int x, y; }
// Outer o{{1, 2}, 3};
// This function will be called with Sem = { {1, 2}, {3, ImplicitValue} }
// It should generate designators '.a:' and '.b.x:'.
// '.a:' is produced directly without recursing into the written sublist.
// (The written sublist will have a separate collectDesignators() call later).
// Recursion with Prefix='.b' and Sem = {3, ImplicitValue} produces '.b.x:'.
void collectDesignators(const InitListExpr *Sem,
llvm::DenseMap<SourceLocation, std::string> &Out,
const llvm::DenseSet<SourceLocation> &NestedBraces,
std::string &Prefix) {
if (!Sem || Sem->isTransparent())
return;
assert(Sem->isSemanticForm());

// The elements of the semantic form all correspond to direct subobjects of
// the aggregate type. `Fields` iterates over these subobject names.
AggregateDesignatorNames Fields(Sem->getType());
if (!Fields)
return;
for (const Expr *Init : Sem->inits()) {
auto Next = llvm::make_scope_exit([&, Size(Prefix.size())] {
Fields.next(); // Always advance to the next subobject name.
Prefix.resize(Size); // Erase any designator we appended.
});
// Skip for a broken initializer or if it is a "hole" in a subobject that
// was not explicitly initialized.
if (!Init || llvm::isa<ImplicitValueInitExpr>(Init))
continue;

const auto *BraceElidedSubobject = llvm::dyn_cast<InitListExpr>(Init);
if (BraceElidedSubobject &&
NestedBraces.contains(BraceElidedSubobject->getLBraceLoc()))
BraceElidedSubobject = nullptr; // there were braces!

if (!Fields.append(Prefix, BraceElidedSubobject != nullptr))
continue; // no designator available for this subobject
if (BraceElidedSubobject) {
// If the braces were elided, this aggregate subobject is initialized
// inline in the same syntactic list.
// Descend into the semantic list describing the subobject.
// (NestedBraces are still correct, they're from the same syntactic
// list).
collectDesignators(BraceElidedSubobject, Out, NestedBraces, Prefix);
continue;
}
Out.try_emplace(Init->getBeginLoc(), Prefix);
}
}

} // namespace

llvm::DenseMap<SourceLocation, std::string>
getUnwrittenDesignators(const InitListExpr *Syn) {
assert(Syn->isSyntacticForm());

// collectDesignators needs to know which InitListExprs in the semantic tree
// were actually written, but InitListExpr::isExplicit() lies.
// Instead, record where braces of sub-init-lists occur in the syntactic form.
llvm::DenseSet<SourceLocation> NestedBraces;
for (const Expr *Init : Syn->inits())
if (auto *Nested = llvm::dyn_cast<InitListExpr>(Init))
NestedBraces.insert(Nested->getLBraceLoc());

// Traverse the semantic form to find the designators.
// We use their SourceLocation to correlate with the syntactic form later.
llvm::DenseMap<SourceLocation, std::string> Designators;
std::string EmptyPrefix;
collectDesignators(Syn->isSemanticForm() ? Syn : Syn->getSemanticForm(),
Designators, NestedBraces, EmptyPrefix);
return Designators;
}

} // namespace clang::tidy::utils
42 changes: 42 additions & 0 deletions clang-tools-extra/clang-tidy/utils/DesignatedInitializers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//===--- DesignatedInitializers.h - clang-tidy ------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file provides utilities for designated initializers.
///
//===----------------------------------------------------------------------===//

#include "clang/AST/Expr.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/DenseMap.h"

namespace clang::tidy::utils {

/// Get designators describing the elements of a (syntactic) init list.
///
/// Given for example the type
/// \code
/// struct S { int i, j; };
/// \endcode
/// and the definition
/// \code
/// S s{1, 2};
/// \endcode
/// calling `getUnwrittenDesignators` for the initializer list expression
/// `{1, 2}` would produce the map `{loc(1): ".i", loc(2): ".j"}`.
///
/// It does not produce designators for any explicitly-written nested lists,
/// e.g. `{1, .j=2}` would only return `{loc(1): ".i"}`.
///
/// It also considers structs with fields of record types like
/// `struct T { S s; };`. In this case, there would be designators of the
/// form `.s.i` and `.s.j` in the returned map.
llvm::DenseMap<clang::SourceLocation, std::string>
getUnwrittenDesignators(const clang::InitListExpr *Syn);

} // namespace clang::tidy::utils
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ target_link_libraries(clangDaemon
clangIncludeCleaner
clangPseudo
clangTidy
clangTidyUtils

clangdSupport
)
Expand Down
170 changes: 4 additions & 166 deletions clang-tools-extra/clangd/InlayHints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
#include "InlayHints.h"
#include "../clang-tidy/utils/DesignatedInitializers.h"
#include "AST.h"
#include "Config.h"
#include "HeuristicResolver.h"
Expand All @@ -24,7 +25,6 @@
#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
Expand All @@ -42,169 +42,6 @@ namespace {
// For now, inlay hints are always anchored at the left or right of their range.
enum class HintSide { Left, Right };

// Helper class to iterate over the designator names of an aggregate type.
//
// For an array type, yields [0], [1], [2]...
// For aggregate classes, yields null for each base, then .field1, .field2, ...
class AggregateDesignatorNames {
public:
AggregateDesignatorNames(QualType T) {
if (!T.isNull()) {
T = T.getCanonicalType();
if (T->isArrayType()) {
IsArray = true;
Valid = true;
return;
}
if (const RecordDecl *RD = T->getAsRecordDecl()) {
Valid = true;
FieldsIt = RD->field_begin();
FieldsEnd = RD->field_end();
if (const auto *CRD = llvm::dyn_cast<CXXRecordDecl>(RD)) {
BasesIt = CRD->bases_begin();
BasesEnd = CRD->bases_end();
Valid = CRD->isAggregate();
}
OneField = Valid && BasesIt == BasesEnd && FieldsIt != FieldsEnd &&
std::next(FieldsIt) == FieldsEnd;
}
}
}
// Returns false if the type was not an aggregate.
operator bool() { return Valid; }
// Advance to the next element in the aggregate.
void next() {
if (IsArray)
++Index;
else if (BasesIt != BasesEnd)
++BasesIt;
else if (FieldsIt != FieldsEnd)
++FieldsIt;
}
// Print the designator to Out.
// Returns false if we could not produce a designator for this element.
bool append(std::string &Out, bool ForSubobject) {
if (IsArray) {
Out.push_back('[');
Out.append(std::to_string(Index));
Out.push_back(']');
return true;
}
if (BasesIt != BasesEnd)
return false; // Bases can't be designated. Should we make one up?
if (FieldsIt != FieldsEnd) {
llvm::StringRef FieldName;
if (const IdentifierInfo *II = FieldsIt->getIdentifier())
FieldName = II->getName();

// For certain objects, their subobjects may be named directly.
if (ForSubobject &&
(FieldsIt->isAnonymousStructOrUnion() ||
// std::array<int,3> x = {1,2,3}. Designators not strictly valid!
(OneField && isReservedName(FieldName))))
return true;

if (!FieldName.empty() && !isReservedName(FieldName)) {
Out.push_back('.');
Out.append(FieldName.begin(), FieldName.end());
return true;
}
return false;
}
return false;
}

private:
bool Valid = false;
bool IsArray = false;
bool OneField = false; // e.g. std::array { T __elements[N]; }
unsigned Index = 0;
CXXRecordDecl::base_class_const_iterator BasesIt;
CXXRecordDecl::base_class_const_iterator BasesEnd;
RecordDecl::field_iterator FieldsIt;
RecordDecl::field_iterator FieldsEnd;
};

// Collect designator labels describing the elements of an init list.
//
// This function contributes the designators of some (sub)object, which is
// represented by the semantic InitListExpr Sem.
// This includes any nested subobjects, but *only* if they are part of the same
// original syntactic init list (due to brace elision).
// In other words, it may descend into subobjects but not written init-lists.
//
// For example: struct Outer { Inner a,b; }; struct Inner { int x, y; }
// Outer o{{1, 2}, 3};
// This function will be called with Sem = { {1, 2}, {3, ImplicitValue} }
// It should generate designators '.a:' and '.b.x:'.
// '.a:' is produced directly without recursing into the written sublist.
// (The written sublist will have a separate collectDesignators() call later).
// Recursion with Prefix='.b' and Sem = {3, ImplicitValue} produces '.b.x:'.
void collectDesignators(const InitListExpr *Sem,
llvm::DenseMap<SourceLocation, std::string> &Out,
const llvm::DenseSet<SourceLocation> &NestedBraces,
std::string &Prefix) {
if (!Sem || Sem->isTransparent())
return;
assert(Sem->isSemanticForm());

// The elements of the semantic form all correspond to direct subobjects of
// the aggregate type. `Fields` iterates over these subobject names.
AggregateDesignatorNames Fields(Sem->getType());
if (!Fields)
return;
for (const Expr *Init : Sem->inits()) {
auto Next = llvm::make_scope_exit([&, Size(Prefix.size())] {
Fields.next(); // Always advance to the next subobject name.
Prefix.resize(Size); // Erase any designator we appended.
});
// Skip for a broken initializer or if it is a "hole" in a subobject that
// was not explicitly initialized.
if (!Init || llvm::isa<ImplicitValueInitExpr>(Init))
continue;

const auto *BraceElidedSubobject = llvm::dyn_cast<InitListExpr>(Init);
if (BraceElidedSubobject &&
NestedBraces.contains(BraceElidedSubobject->getLBraceLoc()))
BraceElidedSubobject = nullptr; // there were braces!

if (!Fields.append(Prefix, BraceElidedSubobject != nullptr))
continue; // no designator available for this subobject
if (BraceElidedSubobject) {
// If the braces were elided, this aggregate subobject is initialized
// inline in the same syntactic list.
// Descend into the semantic list describing the subobject.
// (NestedBraces are still correct, they're from the same syntactic list).
collectDesignators(BraceElidedSubobject, Out, NestedBraces, Prefix);
continue;
}
Out.try_emplace(Init->getBeginLoc(), Prefix);
}
}

// Get designators describing the elements of a (syntactic) init list.
// This does not produce designators for any explicitly-written nested lists.
llvm::DenseMap<SourceLocation, std::string>
getDesignators(const InitListExpr *Syn) {
assert(Syn->isSyntacticForm());

// collectDesignators needs to know which InitListExprs in the semantic tree
// were actually written, but InitListExpr::isExplicit() lies.
// Instead, record where braces of sub-init-lists occur in the syntactic form.
llvm::DenseSet<SourceLocation> NestedBraces;
for (const Expr *Init : Syn->inits())
if (auto *Nested = llvm::dyn_cast<InitListExpr>(Init))
NestedBraces.insert(Nested->getLBraceLoc());

// Traverse the semantic form to find the designators.
// We use their SourceLocation to correlate with the syntactic form later.
llvm::DenseMap<SourceLocation, std::string> Designators;
std::string EmptyPrefix;
collectDesignators(Syn->isSemanticForm() ? Syn : Syn->getSemanticForm(),
Designators, NestedBraces, EmptyPrefix);
return Designators;
}

void stripLeadingUnderscores(StringRef &Name) { Name = Name.ltrim('_'); }

// getDeclForType() returns the decl responsible for Type's spelling.
Expand Down Expand Up @@ -847,14 +684,15 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
// This is the one we will ultimately attach designators to.
// It may have subobject initializers inlined without braces. The *semantic*
// form of the init-list has nested init-lists for these.
// getDesignators will look at the semantic form to determine the labels.
// getUnwrittenDesignators will look at the semantic form to determine the
// labels.
assert(Syn->isSyntacticForm() && "RAV should not visit implicit code!");
if (!Cfg.InlayHints.Designators)
return true;
if (Syn->isIdiomaticZeroInitializer(AST.getLangOpts()))
return true;
llvm::DenseMap<SourceLocation, std::string> Designators =
getDesignators(Syn);
tidy::utils::getUnwrittenDesignators(Syn);
for (const Expr *Init : Syn->inits()) {
if (llvm::isa<DesignatedInitExpr>(Init))
continue;
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/tool/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ clang_target_link_libraries(clangdMain
target_link_libraries(clangdMain
PRIVATE
clangTidy
clangTidyUtils

clangDaemon
clangdRemoteIndex
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ target_link_libraries(ClangdTests
clangIncludeCleaner
clangTesting
clangTidy
clangTidyUtils
clangdSupport
)

Expand Down
6 changes: 6 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ Improvements to clang-tidy
New checks
^^^^^^^^^^

- New :doc:`modernize-use-designated-initializers
<clang-tidy/checks/modernize/use-designated-initializers>` check.

Finds initializer lists for aggregate types that could be
written as designated initializers instead.

- New :doc:`readability-use-std-min-max
<clang-tidy/checks/readability/use-std-min-max>` check.

Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ Clang-Tidy Checks
:doc:`modernize-use-bool-literals <modernize/use-bool-literals>`, "Yes"
:doc:`modernize-use-constraints <modernize/use-constraints>`, "Yes"
:doc:`modernize-use-default-member-init <modernize/use-default-member-init>`, "Yes"
:doc:`modernize-use-designated-initializers <modernize/use-designated-initializers>`, "Yes"
:doc:`modernize-use-emplace <modernize/use-emplace>`, "Yes"
:doc:`modernize-use-equals-default <modernize/use-equals-default>`, "Yes"
:doc:`modernize-use-equals-delete <modernize/use-equals-delete>`, "Yes"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
.. title:: clang-tidy - modernize-use-designated-initializers

modernize-use-designated-initializers
=====================================

Finds initializer lists for aggregate types which could be written as designated
initializers instead.

With plain initializer lists, it is very easy to introduce bugs when adding new
fields in the middle of a struct or class type. The same confusion might arise
when changing the order of fields.

C++20 supports the designated initializer syntax for aggregate types. By
applying it, we can always be sure that aggregates are constructed correctly,
because every variable being initialized is referenced by its name.

Example:

.. code-block::
struct S { int i, j; };
is an aggregate type that should be initialized as

.. code-block::
S s{.i = 1, .j = 2};
instead of

.. code-block::
S s{1, 2};
which could easily become an issue when ``i`` and ``j`` are swapped in the
declaration of ``S``.

Even when compiling in a language version older than C++20, depending on your
compiler, designated initializers are potentially supported. Therefore, the
check is not restricted to C++20 and newer versions. Check out the options
``-Wc99-designator`` to get support for mixed designators in initializer list in
C and ``-Wc++20-designator`` for support of designated initializers in older C++
language modes.

Options
-------

.. option:: IgnoreMacros

The value `false` specifies that components of initializer lists expanded from
macros are not checked. The default value is `true`.

.. option:: IgnoreSingleElementAggregates

The value `false` specifies that even initializers for aggregate types with
only a single element should be checked. The default value is `true`.

.. option:: RestrictToPODTypes

The value `true` specifies that only Plain Old Data (POD) types shall be
checked. This makes the check applicable to even older C++ standards. The
default value is `false`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
// RUN: %check_clang_tidy -std=c++17 %s modernize-use-designated-initializers %t \
// RUN: -- \
// RUN: -- -fno-delayed-template-parsing
// RUN: %check_clang_tidy -check-suffixes=,SINGLE-ELEMENT -std=c++17 %s modernize-use-designated-initializers %t \
// RUN: -- -config="{CheckOptions: {modernize-use-designated-initializers.IgnoreSingleElementAggregates: false}}" \
// RUN: -- -fno-delayed-template-parsing
// RUN: %check_clang_tidy -check-suffixes=POD -std=c++17 %s modernize-use-designated-initializers %t \
// RUN: -- -config="{CheckOptions: {modernize-use-designated-initializers.RestrictToPODTypes: true}}" \
// RUN: -- -fno-delayed-template-parsing
// RUN: %check_clang_tidy -check-suffixes=,MACROS -std=c++17 %s modernize-use-designated-initializers %t \
// RUN: -- -config="{CheckOptions: {modernize-use-designated-initializers.IgnoreMacros: false}}" \
// RUN: -- -fno-delayed-template-parsing

struct S1 {};

S1 s11{};
S1 s12 = {};
S1 s13();
S1 s14;

struct S2 { int i, j; };

S2 s21{.i=1, .j =2};

S2 s22 = {1, 2};
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use designated initializer list to initialize 'S2' [modernize-use-designated-initializers]
// CHECK-MESSAGES: :[[@LINE-6]]:1: note: aggregate type is defined here
// CHECK-MESSAGES-POD: :[[@LINE-3]]:10: warning: use designated initializer list to initialize 'S2' [modernize-use-designated-initializers]
// CHECK-MESSAGES-POD: :[[@LINE-8]]:1: note: aggregate type is defined here
// CHECK-FIXES: S2 s22 = {.i=1, .j=2};

S2 s23{1};
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use designated initializer list to initialize 'S2' [modernize-use-designated-initializers]
// CHECK-MESSAGES: :[[@LINE-13]]:1: note: aggregate type is defined here
// CHECK-MESSAGES-POD: :[[@LINE-3]]:7: warning: use designated initializer list to initialize 'S2' [modernize-use-designated-initializers]
// CHECK-MESSAGES-POD: :[[@LINE-15]]:1: note: aggregate type is defined here
// CHECK-FIXES: S2 s23{.i=1};

S2 s24{.i = 1};

S2 s25 = {.i=1, 2};
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use designated init expression to initialize field 'j' [modernize-use-designated-initializers]
// CHECK-MESSAGES-POD: :[[@LINE-2]]:17: warning: use designated init expression to initialize field 'j' [modernize-use-designated-initializers]
// CHECK-FIXES: S2 s25 = {.i=1, .j=2};

class S3 {
public:
S2 s2;
double d;
};

S3 s31 = {.s2 = 1, 2, 3.1};
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use designated init expression to initialize field 's2.j' [modernize-use-designated-initializers]
// CHECK-MESSAGES: :[[@LINE-2]]:23: warning: use designated init expression to initialize field 'd' [modernize-use-designated-initializers]
// CHECK-MESSAGES-POD: :[[@LINE-3]]:20: warning: use designated init expression to initialize field 's2.j' [modernize-use-designated-initializers]
// CHECK-MESSAGES-POD: :[[@LINE-4]]:23: warning: use designated init expression to initialize field 'd' [modernize-use-designated-initializers]
// CHECK-FIXES: S3 s31 = {.s2 = 1, .s2.j=2, .d=3.1};

S3 s32 = {{.i = 1, 2}};
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use designated initializer list to initialize 'S3' [modernize-use-designated-initializers]
// CHECK-MESSAGES: :[[@LINE-15]]:1: note: aggregate type is defined here
// CHECK-MESSAGES: :[[@LINE-3]]:20: warning: use designated init expression to initialize field 'j' [modernize-use-designated-initializers]
// CHECK-MESSAGES-POD: :[[@LINE-4]]:10: warning: use designated initializer list to initialize 'S3' [modernize-use-designated-initializers]
// CHECK-MESSAGES-POD: :[[@LINE-18]]:1: note: aggregate type is defined here
// CHECK-MESSAGES-POD: :[[@LINE-6]]:20: warning: use designated init expression to initialize field 'j' [modernize-use-designated-initializers]
// CHECK-FIXES: S3 s32 = {.s2={.i = 1, .j=2}};

S3 s33 = {{2}, .d=3.1};
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use designated init expression to initialize field 's2' [modernize-use-designated-initializers]
// CHECK-MESSAGES: :[[@LINE-2]]:11: warning: use designated initializer list to initialize 'S2' [modernize-use-designated-initializers]
// CHECK-MESSAGES: :[[@LINE-50]]:1: note: aggregate type is defined here
// CHECK-MESSAGES-POD: :[[@LINE-4]]:11: warning: use designated init expression to initialize field 's2' [modernize-use-designated-initializers]
// CHECK-MESSAGES-POD: :[[@LINE-5]]:11: warning: use designated initializer list to initialize 'S2' [modernize-use-designated-initializers]
// CHECK-MESSAGES-POD: :[[@LINE-53]]:1: note: aggregate type is defined here
// CHECK-FIXES: S3 s33 = {.s2={.i=2}, .d=3.1};

struct S4 {
double d;
private: static int i;
};

S4 s41 {2.2};
// CHECK-MESSAGES-SINGLE-ELEMENT: :[[@LINE-1]]:8: warning: use designated initializer list to initialize 'S4' [modernize-use-designated-initializers]
// CHECK-MESSAGES-SINGLE-ELEMENT: :[[@LINE-7]]:1: note: aggregate type is defined here
// CHECK-FIXES-SINGLE-ELEMENT: S4 s41 {.d=2.2};

S4 s42 = {{}};
// CHECK-MESSAGES-SINGLE-ELEMENT: :[[@LINE-1]]:10: warning: use designated initializer list to initialize 'S4' [modernize-use-designated-initializers]
// CHECK-MESSAGES-SINGLE-ELEMENT: :[[@LINE-12]]:1: note: aggregate type is defined here
// CHECK-FIXES-SINGLE-ELEMENT: S4 s42 = {.d={}};

template<typename S> S template1() { return {10, 11}; }

S2 s26 = template1<S2>();

template<typename S> S template2() { return {}; }

S2 s27 = template2<S2>();

struct S5: S2 { int x, y; };

S5 s51 {1, 2, .x = 3, .y = 4};

struct S6 {
int i;
struct { int j; } s;
};

S6 s61 {1, 2};
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use designated initializer list to initialize 'S6' [modernize-use-designated-initializers]
// CHECK-MESSAGES: :[[@LINE-7]]:1: note: aggregate type is defined here
// CHECK-MESSAGES-POD: :[[@LINE-3]]:8: warning: use designated initializer list to initialize 'S6' [modernize-use-designated-initializers]
// CHECK-MESSAGES-POD: :[[@LINE-9]]:1: note: aggregate type is defined here
// CHECK-FIXES: S6 s61 {.i=1, .s.j=2};

struct S7 {
union {
int k;
double d;
} u;
};

S7 s71 {1};
// CHECK-MESSAGES-SINGLE-ELEMENT: :[[@LINE-1]]:8: warning: use designated initializer list to initialize 'S7' [modernize-use-designated-initializers]
// CHECK-MESSAGES-SINGLE-ELEMENT: :[[@LINE-9]]:1: note: aggregate type is defined here
// CHECK-FIXES-SINGLE-ELEMENT: S7 s71 {.u.k=1};

struct S8: S7 { int i; };

S8 s81{1, 2};

struct S9 {
int i, j;
S9 &operator=(S9);
};

S9 s91{1, 2};
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use designated initializer list to initialize 'S9' [modernize-use-designated-initializers]
// CHECK-MESSAGES: :[[@LINE-7]]:1: note: aggregate type is defined here
// CHECK-FIXES: S9 s91{.i=1, .j=2};

struct S10 { int i = 1, j = 2; };

S10 s101 {1, .j=2};
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use designated init expression to initialize field 'i' [modernize-use-designated-initializers]
// CHECK-FIXES: S10 s101 {.i=1, .j=2};

struct S11 { int i; S10 s10; };

S11 s111 { .i = 1 };
S11 s112 { 1 };
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use designated initializer list to initialize 'S11' [modernize-use-designated-initializers]
// CHECK-MESSAGES: :[[@LINE-5]]:1: note: aggregate type is defined here
// CHECK-FIXES: S11 s112 { .i=1 };

S11 s113 { .i=1, {}};
// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use designated init expression to initialize field 's10' [modernize-use-designated-initializers]
// CHECK-FIXES: S11 s113 { .i=1, .s10={}};

S11 s114 { .i=1, .s10={1, .j=2}};
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use designated init expression to initialize field 'i' [modernize-use-designated-initializers]
// CHECK-FIXES: S11 s114 { .i=1, .s10={.i=1, .j=2}};

struct S12 {
int i;
struct { int j; };
};

S12 s121 {1, 2};
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use designated initializer list to initialize 'S12' [modernize-use-designated-initializers]
// CHECK-MESSAGES: :[[@LINE-7]]:1: note: aggregate type is defined here
// CHECK-MESSAGES-POD: :[[@LINE-3]]:10: warning: use designated initializer list to initialize 'S12' [modernize-use-designated-initializers]
// CHECK-MESSAGES-POD: :[[@LINE-9]]:1: note: aggregate type is defined here
// CHECK-FIXES: S12 s121 {.i=1, .j=2};

struct S13 {
union {
int k;
double d;
};
int i;
};

S13 s131 {1, 2};
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use designated initializer list to initialize 'S13' [modernize-use-designated-initializers]
// CHECK-MESSAGES: :[[@LINE-10]]:1: note: aggregate type is defined here
// CHECK-MESSAGES-POD: :[[@LINE-3]]:10: warning: use designated initializer list to initialize 'S13' [modernize-use-designated-initializers]
// CHECK-MESSAGES-POD: :[[@LINE-12]]:1: note: aggregate type is defined here
// CHECK-FIXES: S13 s131 {.k=1, .i=2};

#define A (3+2)
#define B .j=1

S9 s92 {A, B};
// CHECK-MESSAGES-MACROS: :[[@LINE-1]]:9: warning: use designated init expression to initialize field 'i' [modernize-use-designated-initializers]
// CHECK-MESSAGES-MACROS: :[[@LINE-5]]:11: note: expanded from macro 'A'

#define DECLARE_S93 S9 s93 {1, 2}

DECLARE_S93;
// CHECK-MESSAGES-MACROS: :[[@LINE-1]]:1: warning: use designated initializer list to initialize 'S9' [modernize-use-designated-initializers]
// CHECK-MESSAGES-MACROS: :[[@LINE-4]]:28: note: expanded from macro 'DECLARE_S93'
// CHECK-MESSAGES-MACROS: :[[@LINE-71]]:1: note: aggregate type is defined here
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,9 @@ Bug Fixes to C++ Support
Fixes (`#82941 <https://github.com/llvm/llvm-project/issues/82941>`_),
(`#42411 <https://github.com/llvm/llvm-project/issues/42411>`_), and
(`#18121 <https://github.com/llvm/llvm-project/issues/18121>`_).
- Clang now properly reports supported C++11 attributes when using
``__has_cpp_attribute`` and parses attributes with arguments in C++03
(`#82995 <https://github.com/llvm/llvm-project/issues/82995>`_)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -723,9 +723,12 @@ RecordStorageLocation *getImplicitObjectLocation(const CXXMemberCallExpr &MCE,
RecordStorageLocation *getBaseObjectLocation(const MemberExpr &ME,
const Environment &Env);

/// Returns the fields of `RD` that are initialized by an `InitListExpr`, in the
/// order in which they appear in `InitListExpr::inits()`.
std::vector<FieldDecl *> getFieldsForInitListExpr(const RecordDecl *RD);
/// Returns the fields of a `RecordDecl` that are initialized by an
/// `InitListExpr`, in the order in which they appear in
/// `InitListExpr::inits()`.
/// `Init->getType()` must be a record type.
std::vector<const FieldDecl *>
getFieldsForInitListExpr(const InitListExpr *InitList);

/// Associates a new `RecordValue` with `Loc` and returns the new value.
RecordValue &refreshRecordValue(RecordStorageLocation &Loc, Environment &Env);
Expand Down
1 change: 0 additions & 1 deletion clang/include/clang/Basic/DiagnosticDriverKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -693,7 +693,6 @@ def err_drv_cannot_mix_options : Error<"cannot specify '%1' along with '%0'">;
def err_drv_invalid_object_mode : Error<
"OBJECT_MODE setting %0 is not recognized and is not a valid setting">;

def err_aix_unsupported_tls_model : Error<"TLS model '%0' is not yet supported on AIX">;
def err_roptr_requires_data_sections: Error<"-mxcoff-roptr is supported only with -fdata-sections">;
def err_roptr_cannot_build_shared: Error<"-mxcoff-roptr is not supported with -shared">;

Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -765,7 +765,7 @@ def err_builtin_redeclare : Error<"cannot redeclare builtin function %0">;
def err_arm_invalid_specialreg : Error<"invalid special register for builtin">;
def err_arm_invalid_coproc : Error<"coprocessor %0 must be configured as "
"%select{GCP|CDE}1">;
def err_invalid_cpu_supports : Error<"invalid cpu feature string for builtin">;
def warn_invalid_cpu_supports : Warning<"invalid cpu feature string for builtin">;
def err_invalid_cpu_is : Error<"invalid cpu name for builtin">;
def err_invalid_cpu_specific_dispatch_value : Error<
"invalid option '%0' for %select{cpu_specific|cpu_dispatch}1">;
Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/Basic/TargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -1386,7 +1386,7 @@ class TargetInfo : public TransferrableTargetInfo,
case LangOptions::SignReturnAddressScopeKind::All:
return "all";
}
assert(false && "Unexpected SignReturnAddressScopeKind");
llvm_unreachable("Unexpected SignReturnAddressScopeKind");
}

const char *getSignKeyStr() const {
Expand All @@ -1396,7 +1396,7 @@ class TargetInfo : public TransferrableTargetInfo,
case LangOptions::SignReturnAddressKeyKind::BKey:
return "b_key";
}
assert(false && "Unexpected SignReturnAddressKeyKind");
llvm_unreachable("Unexpected SignReturnAddressKeyKind");
}
};

Expand Down
10 changes: 7 additions & 3 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1220,14 +1220,18 @@ bool ByteCodeExprGen<Emitter>::VisitArrayInitLoopExpr(

template <class Emitter>
bool ByteCodeExprGen<Emitter>::VisitOpaqueValueExpr(const OpaqueValueExpr *E) {
const Expr *SourceExpr = E->getSourceExpr();
if (!SourceExpr)
return false;

if (Initializing)
return this->visitInitializer(E->getSourceExpr());
return this->visitInitializer(SourceExpr);

PrimType SubExprT = classify(E->getSourceExpr()).value_or(PT_Ptr);
PrimType SubExprT = classify(SourceExpr).value_or(PT_Ptr);
if (auto It = OpaqueExprs.find(E); It != OpaqueExprs.end())
return this->emitGetLocal(SubExprT, It->second, E);

if (!this->visit(E->getSourceExpr()))
if (!this->visit(SourceExpr))
return false;

// At this point we either have the evaluated source expression or a pointer
Expand Down
43 changes: 25 additions & 18 deletions clang/lib/AST/Interp/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) {
}

bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) {
assert(Stk.empty());
ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk, Result);
bool Recursing = !Stk.empty();
ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk);

auto Res = C.interpretExpr(E, /*ConvertResultToRValue=*/E->isGLValue());

Expand All @@ -51,42 +51,47 @@ bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) {
return false;
}

assert(Stk.empty());
if (!Recursing) {
assert(Stk.empty());
#ifndef NDEBUG
// Make sure we don't rely on some value being still alive in
// InterpStack memory.
Stk.clear();
// Make sure we don't rely on some value being still alive in
// InterpStack memory.
Stk.clear();
#endif
}

Result = Res.toAPValue();

return true;
}

bool Context::evaluate(State &Parent, const Expr *E, APValue &Result) {
assert(Stk.empty());
ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk, Result);
bool Recursing = !Stk.empty();
ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk);

auto Res = C.interpretExpr(E);
if (Res.isInvalid()) {
Stk.clear();
return false;
}

assert(Stk.empty());
if (!Recursing) {
assert(Stk.empty());
#ifndef NDEBUG
// Make sure we don't rely on some value being still alive in
// InterpStack memory.
Stk.clear();
// Make sure we don't rely on some value being still alive in
// InterpStack memory.
Stk.clear();
#endif
}

Result = Res.toAPValue();
return true;
}

bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD,
APValue &Result) {
assert(Stk.empty());
ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk, Result);
bool Recursing = !Stk.empty();
ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk);

bool CheckGlobalInitialized =
shouldBeGloballyIndexed(VD) &&
Expand All @@ -97,12 +102,14 @@ bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD,
return false;
}

assert(Stk.empty());
if (!Recursing) {
assert(Stk.empty());
#ifndef NDEBUG
// Make sure we don't rely on some value being still alive in
// InterpStack memory.
Stk.clear();
// Make sure we don't rely on some value being still alive in
// InterpStack memory.
Stk.clear();
#endif
}

Result = Res.toAPValue();
return true;
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class Context final {

/// Classifies an expression.
std::optional<PrimType> classify(const Expr *E) const {
assert(E);
if (E->isGLValue()) {
if (E->getType()->isFunctionType())
return PT_FnPtr;
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/Interp/EvalEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ using namespace clang;
using namespace clang::interp;

EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent,
InterpStack &Stk, APValue &Result)
InterpStack &Stk)
: Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), EvalResult(&Ctx) {
// Create a dummy frame for the interpreter which does not have locals.
S.Current =
Expand Down
3 changes: 1 addition & 2 deletions clang/lib/AST/Interp/EvalEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ class EvalEmitter : public SourceMapper {
InterpState &getState() { return S; }

protected:
EvalEmitter(Context &Ctx, Program &P, State &Parent, InterpStack &Stk,
APValue &Result);
EvalEmitter(Context &Ctx, Program &P, State &Parent, InterpStack &Stk);

virtual ~EvalEmitter();

Expand Down
25 changes: 20 additions & 5 deletions clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -361,8 +361,8 @@ getFieldsGlobalsAndFuncs(const Stmt &S, FieldSet &Fields,
if (const auto *FD = dyn_cast<FieldDecl>(VD))
Fields.insert(FD);
} else if (auto *InitList = dyn_cast<InitListExpr>(&S)) {
if (RecordDecl *RD = InitList->getType()->getAsRecordDecl())
for (const auto *FD : getFieldsForInitListExpr(RD))
if (InitList->getType()->isRecordType())
for (const auto *FD : getFieldsForInitListExpr(InitList))
Fields.insert(FD);
}
}
Expand Down Expand Up @@ -983,7 +983,7 @@ StorageLocation &Environment::createObjectInternal(const ValueDecl *D,
}

Value *Val = nullptr;
if (InitExpr)
if (InitExpr) {
// In the (few) cases where an expression is intentionally
// "uninterpreted", `InitExpr` is not associated with a value. There are
// two ways to handle this situation: propagate the status, so that
Expand All @@ -998,6 +998,11 @@ StorageLocation &Environment::createObjectInternal(const ValueDecl *D,
// default value (assuming we don't update the environment API to return
// references).
Val = getValue(*InitExpr);

if (!Val && isa<ImplicitValueInitExpr>(InitExpr) &&
InitExpr->getType()->isPointerType())
Val = &getOrCreateNullPointerValue(InitExpr->getType()->getPointeeType());
}
if (!Val)
Val = createValue(Ty);

Expand Down Expand Up @@ -1104,12 +1109,22 @@ RecordStorageLocation *getBaseObjectLocation(const MemberExpr &ME,
return Env.get<RecordStorageLocation>(*Base);
}

std::vector<FieldDecl *> getFieldsForInitListExpr(const RecordDecl *RD) {
std::vector<const FieldDecl *>
getFieldsForInitListExpr(const InitListExpr *InitList) {
const RecordDecl *RD = InitList->getType()->getAsRecordDecl();
assert(RD != nullptr);

std::vector<const FieldDecl *> Fields;

if (InitList->getType()->isUnionType()) {
Fields.push_back(InitList->getInitializedFieldInUnion());
return Fields;
}

// Unnamed bitfields are only used for padding and do not appear in
// `InitListExpr`'s inits. However, those fields do appear in `RecordDecl`'s
// field list, and we thus need to remove them before mapping inits to
// fields to avoid mapping inits to the wrongs fields.
std::vector<FieldDecl *> Fields;
llvm::copy_if(
RD->fields(), std::back_inserter(Fields),
[](const FieldDecl *Field) { return !Field->isUnnamedBitfield(); });
Expand Down
40 changes: 28 additions & 12 deletions clang/lib/Analysis/FlowSensitive/Transfer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -663,14 +663,7 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
void VisitInitListExpr(const InitListExpr *S) {
QualType Type = S->getType();

if (Type->isUnionType()) {
// FIXME: Initialize unions properly.
if (auto *Val = Env.createValue(Type))
Env.setValue(*S, *Val);
return;
}

if (!Type->isStructureOrClassType()) {
if (!Type->isRecordType()) {
// Until array initialization is implemented, we skip arrays and don't
// need to care about cases where `getNumInits() > 1`.
if (!Type->isArrayType() && S->getNumInits() == 1)
Expand All @@ -688,14 +681,26 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
llvm::DenseMap<const ValueDecl *, StorageLocation *> FieldLocs;

// This only contains the direct fields for the given type.
std::vector<FieldDecl *> FieldsForInit =
getFieldsForInitListExpr(Type->getAsRecordDecl());
std::vector<const FieldDecl *> FieldsForInit = getFieldsForInitListExpr(S);

// `S->inits()` contains all the initializer epressions, including the
// `S->inits()` contains all the initializer expressions, including the
// ones for direct base classes.
auto Inits = S->inits();
ArrayRef<Expr *> Inits = S->inits();
size_t InitIdx = 0;

// Unions initialized with an empty initializer list need special treatment.
// For structs/classes initialized with an empty initializer list, Clang
// puts `ImplicitValueInitExpr`s in `InitListExpr::inits()`, but for unions,
// it doesn't do this -- so we create an `ImplicitValueInitExpr` ourselves.
std::optional<ImplicitValueInitExpr> ImplicitValueInitForUnion;
SmallVector<Expr *> InitsForUnion;
if (S->getType()->isUnionType() && Inits.empty()) {
assert(FieldsForInit.size() == 1);
ImplicitValueInitForUnion.emplace(FieldsForInit.front()->getType());
InitsForUnion.push_back(&*ImplicitValueInitForUnion);
Inits = InitsForUnion;
}

// Initialize base classes.
if (auto* R = S->getType()->getAsCXXRecordDecl()) {
assert(FieldsForInit.size() + R->getNumBases() == Inits.size());
Expand Down Expand Up @@ -731,6 +736,17 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
FieldLocs.insert({Field, &Loc});
}

// In the case of a union, we don't in general have initializers for all
// of the fields. Create storage locations for the remaining fields (but
// don't associate them with values).
if (Type->isUnionType()) {
for (const FieldDecl *Field :
Env.getDataflowAnalysisContext().getModeledFields(Type)) {
if (auto [it, inserted] = FieldLocs.insert({Field, nullptr}); inserted)
it->second = &Env.createStorageLocation(Field->getType());
}
}

// Check that we satisfy the invariant that a `RecordStorageLoation`
// contains exactly the set of modeled fields for that type.
// `ModeledFields` includes fields from all the bases, but only the
Expand Down
7 changes: 6 additions & 1 deletion clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13952,6 +13952,8 @@ Value *CodeGenFunction::EmitX86CpuIs(StringRef CPUStr) {
Value *CodeGenFunction::EmitX86CpuSupports(const CallExpr *E) {
const Expr *FeatureExpr = E->getArg(0)->IgnoreParenCasts();
StringRef FeatureStr = cast<StringLiteral>(FeatureExpr)->getString();
if (!getContext().getTargetInfo().validateCpuSupports(FeatureStr))
return Builder.getFalse();
return EmitX86CpuSupports(FeatureStr);
}

Expand Down Expand Up @@ -14041,6 +14043,8 @@ Value *CodeGenFunction::EmitAArch64CpuSupports(const CallExpr *E) {
ArgStr.split(Features, "+");
for (auto &Feature : Features) {
Feature = Feature.trim();
if (!llvm::AArch64::parseArchExtension(Feature))
return Builder.getFalse();
if (Feature != "default")
Features.push_back(Feature);
}
Expand Down Expand Up @@ -16639,7 +16643,8 @@ Value *CodeGenFunction::EmitPPCBuiltinExpr(unsigned BuiltinID,
.Case(Name, {FA_WORD, Bitmask})
#include "llvm/TargetParser/PPCTargetParser.def"
.Default({0, 0});
assert(BitMask && "Invalid target feature string. Missed by SemaChecking?");
if (!BitMask)
return Builder.getFalse();
Value *Op0 = llvm::ConstantInt::get(Int32Ty, FeatureWord);
llvm::Function *F = CGM.getIntrinsic(Intrinsic::ppc_fixed_addr_ld);
Value *TheCall = Builder.CreateCall(F, {Op0}, "cpu_supports");
Expand Down
9 changes: 2 additions & 7 deletions clang/lib/CodeGen/Targets/AArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -882,13 +882,8 @@ void AArch64ABIInfo::appendAttributeMangling(StringRef AttrStr,
for (auto &Feat : Features)
Feat = Feat.trim();

// FIXME: It was brought up in #79316 that sorting the features which are
// used for mangling based on their multiversion priority is not a good
// practice. Changing the feature priorities will break the ABI. Perhaps
// it would be preferable to perform a lexicographical sort instead.
const TargetInfo &TI = CGT.getTarget();
llvm::sort(Features, [&TI](const StringRef LHS, const StringRef RHS) {
return TI.multiVersionSortPriority(LHS) < TI.multiVersionSortPriority(RHS);
llvm::sort(Features, [](const StringRef LHS, const StringRef RHS) {
return LHS.compare(RHS) < 0;
});

for (auto &Feat : Features)
Expand Down
8 changes: 0 additions & 8 deletions clang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1975,14 +1975,6 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
Opts.LinkBitcodeFiles.push_back(F);
}

if (Arg *A = Args.getLastArg(OPT_ftlsmodel_EQ)) {
if (T.isOSAIX()) {
StringRef Name = A->getValue();
if (Name == "local-dynamic")
Diags.Report(diag::err_aix_unsupported_tls_model) << Name;
}
}

if (Arg *A = Args.getLastArg(OPT_fdenormal_fp_math_EQ)) {
StringRef Val = A->getValue();
Opts.FPDenormalMode = llvm::parseDenormalFPAttribute(Val);
Expand Down
6 changes: 4 additions & 2 deletions clang/lib/InstallAPI/Frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,10 @@ std::unique_ptr<MemoryBuffer> createInputBuffer(const InstallAPIContext &Ctx) {
if (Contents.empty())
return nullptr;

return llvm::MemoryBuffer::getMemBufferCopy(
Contents, "installapi-includes" + getFileExtension(Ctx.LangMode));
SmallString<64> BufferName(
{"installapi-includes-", Ctx.Slice->getTriple().str(), "-",
getName(Ctx.Type), getFileExtension(Ctx.LangMode)});
return llvm::MemoryBuffer::getMemBufferCopy(Contents, BufferName);
}

} // namespace clang::installapi
8 changes: 5 additions & 3 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2180,9 +2180,11 @@ static bool SemaBuiltinCpu(Sema &S, const TargetInfo &TI, CallExpr *TheCall,

// Check the contents of the string.
StringRef Feature = cast<StringLiteral>(Arg)->getString();
if (IsCPUSupports && !TheTI->validateCpuSupports(Feature))
return S.Diag(TheCall->getBeginLoc(), diag::err_invalid_cpu_supports)
<< Arg->getSourceRange();
if (IsCPUSupports && !TheTI->validateCpuSupports(Feature)) {
S.Diag(TheCall->getBeginLoc(), diag::warn_invalid_cpu_supports)
<< Arg->getSourceRange();
return false;
}
if (!IsCPUSupports && !TheTI->validateCpuIs(Feature))
return S.Diag(TheCall->getBeginLoc(), diag::err_invalid_cpu_is)
<< Arg->getSourceRange();
Expand Down
6 changes: 0 additions & 6 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2053,12 +2053,6 @@ static void handleTLSModelAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
return;
}

if (S.Context.getTargetInfo().getTriple().isOSAIX() &&
Model == "local-dynamic") {
S.Diag(LiteralLoc, diag::err_aix_attr_unsupported_tls_model) << Model;
return;
}

D->addAttr(::new (S.Context) TLSModelAttr(S.Context, AL, Model));
}

Expand Down
297 changes: 171 additions & 126 deletions clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp

Large diffs are not rendered by default.

67 changes: 67 additions & 0 deletions clang/test/Analysis/stream-note.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,3 +166,70 @@ void check_eof_notes_feof_or_no_error(void) {
}
fclose(F);
}

void check_indeterminate_notes(void) {
FILE *F;
F = fopen("foo1.c", "r");
if (F == NULL) // expected-note {{Taking false branch}} \
// expected-note {{'F' is not equal to NULL}}
return;
int R = fgetc(F); // no note
if (R >= 0) { // expected-note {{Taking true branch}} \
// expected-note {{'R' is >= 0}}
fgetc(F); // expected-note {{Assuming this stream operation fails}}
if (ferror(F)) // expected-note {{Taking true branch}}
fgetc(F); // expected-warning {{File position of the stream might be 'indeterminate' after a failed operation. Can cause undefined behavior}} \
// expected-note {{File position of the stream might be 'indeterminate' after a failed operation. Can cause undefined behavior}}
}
fclose(F);
}

void check_indeterminate_after_clearerr(void) {
FILE *F;
char Buf[10];
F = fopen("foo1.c", "r");
if (F == NULL) // expected-note {{Taking false branch}} \
// expected-note {{'F' is not equal to NULL}}
return;
fread(Buf, 1, 1, F); // expected-note {{Assuming this stream operation fails}}
if (ferror(F)) { // expected-note {{Taking true branch}}
clearerr(F);
fread(Buf, 1, 1, F); // expected-warning {{might be 'indeterminate' after a failed operation}} \
// expected-note {{might be 'indeterminate' after a failed operation}}
}
fclose(F);
}

void check_indeterminate_eof(void) {
FILE *F;
char Buf[2];
F = fopen("foo1.c", "r");
if (F == NULL) // expected-note {{Taking false branch}} \
// expected-note {{'F' is not equal to NULL}} \
// expected-note {{Taking false branch}} \
// expected-note {{'F' is not equal to NULL}}
return;
fgets(Buf, sizeof(Buf), F); // expected-note {{Assuming this stream operation fails}} \
// expected-note {{Assuming stream reaches end-of-file here}}

fgets(Buf, sizeof(Buf), F); // expected-warning {{might be 'indeterminate'}} \
// expected-note {{might be 'indeterminate'}} \
// expected-warning {{stream is in EOF state}} \
// expected-note {{stream is in EOF state}}
fclose(F);
}

void check_indeterminate_fseek(void) {
FILE *F = fopen("file", "r");
if (!F) // expected-note {{Taking false branch}} \
// expected-note {{'F' is non-null}}
return;
int Ret = fseek(F, 1, SEEK_SET); // expected-note {{Assuming this stream operation fails}}
if (Ret) { // expected-note {{Taking true branch}} \
// expected-note {{'Ret' is not equal to 0}}
char Buf[2];
fwrite(Buf, 1, 2, F); // expected-warning {{might be 'indeterminate'}} \
// expected-note {{might be 'indeterminate'}}
}
fclose(F);
}
9 changes: 6 additions & 3 deletions clang/test/CodeGen/PowerPC/aix-tls-model.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// RUN: %clang_cc1 %s -triple powerpc-unknown-aix -target-cpu pwr8 -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-GD
// RUN: %clang_cc1 %s -triple powerpc-unknown-aix -target-cpu pwr8 -ftls-model=global-dynamic -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-GD
// RUN: not %clang_cc1 %s -triple powerpc-unknown-aix -target-cpu pwr8 -ftls-model=local-dynamic -emit-llvm 2>&1 | FileCheck %s -check-prefix=CHECK-LD-ERROR
// RUN: %clang_cc1 %s -triple powerpc-unknown-aix -target-cpu pwr8 -ftls-model=local-dynamic -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-LD
// RUN: %clang_cc1 %s -triple powerpc-unknown-aix -target-cpu pwr8 -ftls-model=initial-exec -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-IE
// RUN: %clang_cc1 %s -triple powerpc-unknown-aix -target-cpu pwr8 -ftls-model=local-exec -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-LE
// RUN: %clang_cc1 %s -triple powerpc64-unknown-aix -target-cpu pwr8 -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-GD
// RUN: %clang_cc1 %s -triple powerpc64-unknown-aix -target-cpu pwr8 -ftls-model=global-dynamic -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-GD
// RUN: not %clang_cc1 %s -triple powerpc64-unknown-aix -target-cpu pwr8 -ftls-model=local-dynamic -emit-llvm 2>&1 | FileCheck %s -check-prefix=CHECK-LD-ERROR
// RUN: %clang_cc1 %s -triple powerpc64-unknown-aix -target-cpu pwr8 -ftls-model=local-dynamic -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-LD
// RUN: %clang_cc1 %s -triple powerpc64-unknown-aix -target-cpu pwr8 -ftls-model=initial-exec -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-IE
// RUN: %clang_cc1 %s -triple powerpc64-unknown-aix -target-cpu pwr8 -ftls-model=local-exec -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-LE

Expand All @@ -21,7 +21,10 @@ int f() {
// CHECK-GD: @z2 ={{.*}} global i32 0
// CHECK-GD: @x ={{.*}} thread_local global i32 0
// CHECK-GD: @_ZZ1fvE1y = internal thread_local global i32 0
// CHECK-LD-ERROR: error: TLS model 'local-dynamic' is not yet supported on AIX
// CHECK-LD: @z1 ={{.*}} global i32 0
// CHECK-LD: @z2 ={{.*}} global i32 0
// CHECK-LD: @x ={{.*}} thread_local(localdynamic) global i32 0
// CHECK-LD: @_ZZ1fvE1y = internal thread_local(localdynamic) global i32 0
// CHECK-IE: @z1 ={{.*}} global i32 0
// CHECK-IE: @z2 ={{.*}} global i32 0
// CHECK-IE: @x ={{.*}} thread_local(initialexec) global i32 0
Expand Down
8 changes: 8 additions & 0 deletions clang/test/CodeGen/aarch64-cpu-supports.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
// CHECK-NEXT: store i32 3, ptr [[RETVAL]], align 4
// CHECK-NEXT: br label [[RETURN]]
// CHECK: if.end4:
// CHECK-NEXT: br i1 false, label [[IF_THEN5:%.*]], label [[IF_END6:%.*]]
// CHECK: if.then5:
// CHECK-NEXT: store i32 4, ptr [[RETVAL]], align 4
// CHECK-NEXT: br label [[RETURN]]
// CHECK: if.end6:
// CHECK-NEXT: store i32 0, ptr [[RETVAL]], align 4
// CHECK-NEXT: br label [[RETURN]]
// CHECK: return:
Expand All @@ -50,5 +55,8 @@ int main(void) {
if (__builtin_cpu_supports("sme2+ls64_v+wfxt"))
return 3;

if (__builtin_cpu_supports("avx2"))
return 4;

return 0;
}
20 changes: 10 additions & 10 deletions clang/test/CodeGen/attr-target-clones-aarch64.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ inline int __attribute__((target_clones("fp16", "sve2-bitperm+fcma", "default"))
// CHECK: @ftc_inline3 = weak_odr ifunc i32 (), ptr @ftc_inline3.resolver
//.
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: @ftc._MlseMaes(
// CHECK-LABEL: @ftc._MaesMlse(
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 0
//
Expand All @@ -69,7 +69,7 @@ inline int __attribute__((target_clones("fp16", "sve2-bitperm+fcma", "default"))
// CHECK-NEXT: [[TMP3:%.*]] = and i1 true, [[TMP2]]
// CHECK-NEXT: br i1 [[TMP3]], label [[RESOLVER_RETURN:%.*]], label [[RESOLVER_ELSE:%.*]]
// CHECK: resolver_return:
// CHECK-NEXT: ret ptr @ftc._MlseMaes
// CHECK-NEXT: ret ptr @ftc._MaesMlse
// CHECK: resolver_else:
// CHECK-NEXT: [[TMP4:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
// CHECK-NEXT: [[TMP5:%.*]] = and i64 [[TMP4]], 68719476736
Expand All @@ -89,7 +89,7 @@ inline int __attribute__((target_clones("fp16", "sve2-bitperm+fcma", "default"))
//
//
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: @ftc_def._Msha2Mmemtag2(
// CHECK-LABEL: @ftc_def._Mmemtag2Msha2(
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 1
//
Expand All @@ -109,7 +109,7 @@ inline int __attribute__((target_clones("fp16", "sve2-bitperm+fcma", "default"))
// CHECK-NEXT: [[TMP3:%.*]] = and i1 true, [[TMP2]]
// CHECK-NEXT: br i1 [[TMP3]], label [[RESOLVER_RETURN:%.*]], label [[RESOLVER_ELSE:%.*]]
// CHECK: resolver_return:
// CHECK-NEXT: ret ptr @ftc_def._Msha2Mmemtag2
// CHECK-NEXT: ret ptr @ftc_def._Mmemtag2Msha2
// CHECK: resolver_else:
// CHECK-NEXT: [[TMP4:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
// CHECK-NEXT: [[TMP5:%.*]] = and i64 [[TMP4]], 4096
Expand Down Expand Up @@ -155,7 +155,7 @@ inline int __attribute__((target_clones("fp16", "sve2-bitperm+fcma", "default"))
//
//
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: @ftc_dup2._MdotprodMcrc(
// CHECK-LABEL: @ftc_dup2._McrcMdotprod(
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 3
//
Expand All @@ -175,7 +175,7 @@ inline int __attribute__((target_clones("fp16", "sve2-bitperm+fcma", "default"))
// CHECK-NEXT: [[TMP3:%.*]] = and i1 true, [[TMP2]]
// CHECK-NEXT: br i1 [[TMP3]], label [[RESOLVER_RETURN:%.*]], label [[RESOLVER_ELSE:%.*]]
// CHECK: resolver_return:
// CHECK-NEXT: ret ptr @ftc_dup2._MdotprodMcrc
// CHECK-NEXT: ret ptr @ftc_dup2._McrcMdotprod
// CHECK: resolver_else:
// CHECK-NEXT: [[TMP4:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
// CHECK-NEXT: [[TMP5:%.*]] = and i64 [[TMP4]], 256
Expand Down Expand Up @@ -239,7 +239,7 @@ inline int __attribute__((target_clones("fp16", "sve2-bitperm+fcma", "default"))
// CHECK-NEXT: [[TMP7:%.*]] = and i1 true, [[TMP6]]
// CHECK-NEXT: br i1 [[TMP7]], label [[RESOLVER_RETURN1:%.*]], label [[RESOLVER_ELSE2:%.*]]
// CHECK: resolver_return1:
// CHECK-NEXT: ret ptr @ftc_inline1._MrcpcMpredres
// CHECK-NEXT: ret ptr @ftc_inline1._MpredresMrcpc
// CHECK: resolver_else2:
// CHECK-NEXT: [[TMP8:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
// CHECK-NEXT: [[TMP9:%.*]] = and i64 [[TMP8]], 513
Expand Down Expand Up @@ -283,7 +283,7 @@ inline int __attribute__((target_clones("fp16", "sve2-bitperm+fcma", "default"))
// CHECK-NEXT: [[TMP3:%.*]] = and i1 true, [[TMP2]]
// CHECK-NEXT: br i1 [[TMP3]], label [[RESOLVER_RETURN:%.*]], label [[RESOLVER_ELSE:%.*]]
// CHECK: resolver_return:
// CHECK-NEXT: ret ptr @ftc_inline3._MsveMsb
// CHECK-NEXT: ret ptr @ftc_inline3._MsbMsve
// CHECK: resolver_else:
// CHECK-NEXT: [[TMP4:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
// CHECK-NEXT: [[TMP5:%.*]] = and i64 [[TMP4]], 1125899906842624
Expand All @@ -303,7 +303,7 @@ inline int __attribute__((target_clones("fp16", "sve2-bitperm+fcma", "default"))
//
//
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: @ftc_inline1._MrcpcMpredres(
// CHECK-LABEL: @ftc_inline1._MpredresMrcpc(
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 1
//
Expand Down Expand Up @@ -345,7 +345,7 @@ inline int __attribute__((target_clones("fp16", "sve2-bitperm+fcma", "default"))
//
//
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: @ftc_inline3._MsveMsb(
// CHECK-LABEL: @ftc_inline3._MsbMsve(
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 3
//
Expand Down
52 changes: 26 additions & 26 deletions clang/test/CodeGen/attr-target-version.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,14 +106,14 @@ int hoo(void) {
// CHECK: @fmv_c = weak_odr ifunc void (), ptr @fmv_c.resolver
//.
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: define {{[^@]+}}@fmv._MrngMflagmMfp16fml
// CHECK-LABEL: define {{[^@]+}}@fmv._MflagmMfp16fmlMrng
// CHECK-SAME: () #[[ATTR0:[0-9]+]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 1
//
//
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: define {{[^@]+}}@fmv_one._MsimdMls64
// CHECK-LABEL: define {{[^@]+}}@fmv_one._Mls64Msimd
// CHECK-SAME: () #[[ATTR1:[0-9]+]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 1
Expand Down Expand Up @@ -147,7 +147,7 @@ int hoo(void) {
// CHECK-NEXT: [[TMP3:%.*]] = and i1 true, [[TMP2]]
// CHECK-NEXT: br i1 [[TMP3]], label [[RESOLVER_RETURN:%.*]], label [[RESOLVER_ELSE:%.*]]
// CHECK: resolver_return:
// CHECK-NEXT: ret ptr @fmv._MrngMflagmMfp16fml
// CHECK-NEXT: ret ptr @fmv._MflagmMfp16fmlMrng
// CHECK: resolver_else:
// CHECK-NEXT: [[TMP4:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
// CHECK-NEXT: [[TMP5:%.*]] = and i64 [[TMP4]], 72057594037927940
Expand Down Expand Up @@ -187,7 +187,7 @@ int hoo(void) {
// CHECK-NEXT: [[TMP23:%.*]] = and i1 true, [[TMP22]]
// CHECK-NEXT: br i1 [[TMP23]], label [[RESOLVER_RETURN9:%.*]], label [[RESOLVER_ELSE10:%.*]]
// CHECK: resolver_return9:
// CHECK-NEXT: ret ptr @fmv._MfpMaes
// CHECK-NEXT: ret ptr @fmv._MaesMfp
// CHECK: resolver_else10:
// CHECK-NEXT: [[TMP24:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
// CHECK-NEXT: [[TMP25:%.*]] = and i64 [[TMP24]], 4224
Expand Down Expand Up @@ -218,12 +218,12 @@ int hoo(void) {
//
// CHECK-LABEL: define {{[^@]+}}@fmv_one.resolver() comdat {
// CHECK-NEXT: resolver_entry:
// CHECK-NEXT: ret ptr @fmv_one._MsimdMls64
// CHECK-NEXT: ret ptr @fmv_one._Mls64Msimd
//
//
// CHECK-LABEL: define {{[^@]+}}@fmv_two.resolver() comdat {
// CHECK-NEXT: resolver_entry:
// CHECK-NEXT: ret ptr @fmv_two._MsimdMfp16
// CHECK-NEXT: ret ptr @fmv_two._Mfp16Msimd
//
//
// CHECK-LABEL: define {{[^@]+}}@fmv_e.resolver() comdat {
Expand Down Expand Up @@ -266,47 +266,47 @@ int hoo(void) {
// CHECK-NEXT: [[TMP3:%.*]] = and i1 true, [[TMP2]]
// CHECK-NEXT: br i1 [[TMP3]], label [[RESOLVER_RETURN:%.*]], label [[RESOLVER_ELSE:%.*]]
// CHECK: resolver_return:
// CHECK-NEXT: ret ptr @fmv_inline._Mfp16Mfp16MfcmaMsme
// CHECK-NEXT: ret ptr @fmv_inline._MfcmaMfp16Mfp16Msme
// CHECK: resolver_else:
// CHECK-NEXT: [[TMP4:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
// CHECK-NEXT: [[TMP5:%.*]] = and i64 [[TMP4]], 864726312827224064
// CHECK-NEXT: [[TMP6:%.*]] = icmp eq i64 [[TMP5]], 864726312827224064
// CHECK-NEXT: [[TMP7:%.*]] = and i1 true, [[TMP6]]
// CHECK-NEXT: br i1 [[TMP7]], label [[RESOLVER_RETURN1:%.*]], label [[RESOLVER_ELSE2:%.*]]
// CHECK: resolver_return1:
// CHECK-NEXT: ret ptr @fmv_inline._Mrcpc3Mmemtag3Mmops
// CHECK-NEXT: ret ptr @fmv_inline._Mmemtag3MmopsMrcpc3
// CHECK: resolver_else2:
// CHECK-NEXT: [[TMP8:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
// CHECK-NEXT: [[TMP9:%.*]] = and i64 [[TMP8]], 893353197568
// CHECK-NEXT: [[TMP10:%.*]] = icmp eq i64 [[TMP9]], 893353197568
// CHECK-NEXT: [[TMP11:%.*]] = and i1 true, [[TMP10]]
// CHECK-NEXT: br i1 [[TMP11]], label [[RESOLVER_RETURN3:%.*]], label [[RESOLVER_ELSE4:%.*]]
// CHECK: resolver_return3:
// CHECK-NEXT: ret ptr @fmv_inline._Msve2Msve2-pmull128Msve2-bitperm
// CHECK-NEXT: ret ptr @fmv_inline._Msve2Msve2-bitpermMsve2-pmull128
// CHECK: resolver_else4:
// CHECK-NEXT: [[TMP12:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
// CHECK-NEXT: [[TMP13:%.*]] = and i64 [[TMP12]], 34359773184
// CHECK-NEXT: [[TMP14:%.*]] = icmp eq i64 [[TMP13]], 34359773184
// CHECK-NEXT: [[TMP15:%.*]] = and i1 true, [[TMP14]]
// CHECK-NEXT: br i1 [[TMP15]], label [[RESOLVER_RETURN5:%.*]], label [[RESOLVER_ELSE6:%.*]]
// CHECK: resolver_return5:
// CHECK-NEXT: ret ptr @fmv_inline._Msha1MpmullMf64mm
// CHECK-NEXT: ret ptr @fmv_inline._Mf64mmMpmullMsha1
// CHECK: resolver_else6:
// CHECK-NEXT: [[TMP16:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
// CHECK-NEXT: [[TMP17:%.*]] = and i64 [[TMP16]], 17246986240
// CHECK-NEXT: [[TMP18:%.*]] = icmp eq i64 [[TMP17]], 17246986240
// CHECK-NEXT: [[TMP19:%.*]] = and i1 true, [[TMP18]]
// CHECK-NEXT: br i1 [[TMP19]], label [[RESOLVER_RETURN7:%.*]], label [[RESOLVER_ELSE8:%.*]]
// CHECK: resolver_return7:
// CHECK-NEXT: ret ptr @fmv_inline._Msha3Mi8mmMf32mm
// CHECK-NEXT: ret ptr @fmv_inline._Mf32mmMi8mmMsha3
// CHECK: resolver_else8:
// CHECK-NEXT: [[TMP20:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
// CHECK-NEXT: [[TMP21:%.*]] = and i64 [[TMP20]], 19791209299968
// CHECK-NEXT: [[TMP22:%.*]] = icmp eq i64 [[TMP21]], 19791209299968
// CHECK-NEXT: [[TMP23:%.*]] = and i1 true, [[TMP22]]
// CHECK-NEXT: br i1 [[TMP23]], label [[RESOLVER_RETURN9:%.*]], label [[RESOLVER_ELSE10:%.*]]
// CHECK: resolver_return9:
// CHECK-NEXT: ret ptr @fmv_inline._Msve2-sm4Mmemtag2
// CHECK-NEXT: ret ptr @fmv_inline._Mmemtag2Msve2-sm4
// CHECK: resolver_else10:
// CHECK-NEXT: [[TMP24:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
// CHECK-NEXT: [[TMP25:%.*]] = and i64 [[TMP24]], 1236950581248
Expand Down Expand Up @@ -338,7 +338,7 @@ int hoo(void) {
// CHECK-NEXT: [[TMP39:%.*]] = and i1 true, [[TMP38]]
// CHECK-NEXT: br i1 [[TMP39]], label [[RESOLVER_RETURN17:%.*]], label [[RESOLVER_ELSE18:%.*]]
// CHECK: resolver_return17:
// CHECK-NEXT: ret ptr @fmv_inline._MrcpcMfrintts
// CHECK-NEXT: ret ptr @fmv_inline._MfrinttsMrcpc
// CHECK: resolver_else18:
// CHECK-NEXT: [[TMP40:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
// CHECK-NEXT: [[TMP41:%.*]] = and i64 [[TMP40]], 8650752
Expand All @@ -362,15 +362,15 @@ int hoo(void) {
// CHECK-NEXT: [[TMP51:%.*]] = and i1 true, [[TMP50]]
// CHECK-NEXT: br i1 [[TMP51]], label [[RESOLVER_RETURN23:%.*]], label [[RESOLVER_ELSE24:%.*]]
// CHECK: resolver_return23:
// CHECK-NEXT: ret ptr @fmv_inline._MsimdMfp16fml
// CHECK-NEXT: ret ptr @fmv_inline._Mfp16fmlMsimd
// CHECK: resolver_else24:
// CHECK-NEXT: [[TMP52:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
// CHECK-NEXT: [[TMP53:%.*]] = and i64 [[TMP52]], 16400
// CHECK-NEXT: [[TMP54:%.*]] = icmp eq i64 [[TMP53]], 16400
// CHECK-NEXT: [[TMP55:%.*]] = and i1 true, [[TMP54]]
// CHECK-NEXT: br i1 [[TMP55]], label [[RESOLVER_RETURN25:%.*]], label [[RESOLVER_ELSE26:%.*]]
// CHECK: resolver_return25:
// CHECK-NEXT: ret ptr @fmv_inline._MdotprodMaes
// CHECK-NEXT: ret ptr @fmv_inline._MaesMdotprod
// CHECK: resolver_else26:
// CHECK-NEXT: [[TMP56:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
// CHECK-NEXT: [[TMP57:%.*]] = and i64 [[TMP56]], 192
Expand Down Expand Up @@ -484,7 +484,7 @@ int hoo(void) {
//
//
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: define {{[^@]+}}@fmv._MfpMaes
// CHECK-LABEL: define {{[^@]+}}@fmv._MaesMfp
// CHECK-SAME: () #[[ATTR1]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 6
Expand Down Expand Up @@ -547,7 +547,7 @@ int hoo(void) {
//
//
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: define {{[^@]+}}@fmv_two._MsimdMfp16
// CHECK-LABEL: define {{[^@]+}}@fmv_two._Mfp16Msimd
// CHECK-SAME: () #[[ATTR1]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 4
Expand All @@ -568,21 +568,21 @@ int hoo(void) {
//
//
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: define {{[^@]+}}@fmv_inline._Msha1MpmullMf64mm
// CHECK-LABEL: define {{[^@]+}}@fmv_inline._Mf64mmMpmullMsha1
// CHECK-SAME: () #[[ATTR12:[0-9]+]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 1
//
//
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: define {{[^@]+}}@fmv_inline._Mfp16Mfp16MfcmaMsme
// CHECK-LABEL: define {{[^@]+}}@fmv_inline._MfcmaMfp16Mfp16Msme
// CHECK-SAME: () #[[ATTR13:[0-9]+]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 2
//
//
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: define {{[^@]+}}@fmv_inline._Msha3Mi8mmMf32mm
// CHECK-LABEL: define {{[^@]+}}@fmv_inline._Mf32mmMi8mmMsha3
// CHECK-SAME: () #[[ATTR14:[0-9]+]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 12
Expand Down Expand Up @@ -610,7 +610,7 @@ int hoo(void) {
//
//
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: define {{[^@]+}}@fmv_inline._MrcpcMfrintts
// CHECK-LABEL: define {{[^@]+}}@fmv_inline._MfrinttsMrcpc
// CHECK-SAME: () #[[ATTR18:[0-9]+]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 3
Expand All @@ -631,35 +631,35 @@ int hoo(void) {
//
//
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: define {{[^@]+}}@fmv_inline._Msve2Msve2-pmull128Msve2-bitperm
// CHECK-LABEL: define {{[^@]+}}@fmv_inline._Msve2Msve2-bitpermMsve2-pmull128
// CHECK-SAME: () #[[ATTR21:[0-9]+]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 9
//
//
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: define {{[^@]+}}@fmv_inline._Msve2-sm4Mmemtag2
// CHECK-LABEL: define {{[^@]+}}@fmv_inline._Mmemtag2Msve2-sm4
// CHECK-SAME: () #[[ATTR22:[0-9]+]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 10
//
//
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: define {{[^@]+}}@fmv_inline._Mrcpc3Mmemtag3Mmops
// CHECK-LABEL: define {{[^@]+}}@fmv_inline._Mmemtag3MmopsMrcpc3
// CHECK-SAME: () #[[ATTR23:[0-9]+]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 11
//
//
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: define {{[^@]+}}@fmv_inline._MdotprodMaes
// CHECK-LABEL: define {{[^@]+}}@fmv_inline._MaesMdotprod
// CHECK-SAME: () #[[ATTR6]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 13
//
//
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: define {{[^@]+}}@fmv_inline._MsimdMfp16fml
// CHECK-LABEL: define {{[^@]+}}@fmv_inline._Mfp16fmlMsimd
// CHECK-SAME: () #[[ATTR7]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 14
Expand Down
2 changes: 1 addition & 1 deletion clang/test/CodeGen/builtins-hexagon.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// REQUIRES: hexagon-registered-target
// RUN: %clang_cc1 -triple hexagon-unknown-elf -target-cpu hexagonv65 -target-feature +hvxv65 -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -triple hexagon-unknown-elf -target-cpu hexagonv65 -target-feature +hvxv65 -target-feature +hvx-length128b -emit-llvm %s -o - | FileCheck %s

void test() {
int v64 __attribute__((__vector_size__(64)));
Expand Down
74 changes: 60 additions & 14 deletions clang/test/CodeGenCXX/attr-target-clones-aarch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,33 @@ void run_foo_tml() {
}




//.
// CHECK: @__aarch64_cpu_features = external dso_local global { i64 }
// CHECK: @_Z7foo_ovli.ifunc = weak_odr alias i32 (i32), ptr @_Z7foo_ovli
// CHECK: @_Z7foo_ovlv.ifunc = weak_odr alias i32 (), ptr @_Z7foo_ovlv
// CHECK: @_ZN7MyClassIssE7foo_tmlEv.ifunc = weak_odr alias i32 (ptr), ptr @_ZN7MyClassIssE7foo_tmlEv
// CHECK: @_ZN7MyClassIisE7foo_tmlEv.ifunc = weak_odr alias i32 (ptr), ptr @_ZN7MyClassIisE7foo_tmlEv
// CHECK: @_Z7foo_ovli = weak_odr ifunc i32 (i32), ptr @_Z7foo_ovli.resolver
// CHECK: @_Z7foo_ovlv = weak_odr ifunc i32 (), ptr @_Z7foo_ovlv.resolver
// CHECK: @_ZN7MyClassIssE7foo_tmlEv = weak_odr ifunc i32 (ptr), ptr @_ZN7MyClassIssE7foo_tmlEv.resolver
// CHECK: @_ZN7MyClassIisE7foo_tmlEv = weak_odr ifunc i32 (ptr), ptr @_ZN7MyClassIisE7foo_tmlEv.resolver

//.
// CHECK-LABEL: @_Z7foo_ovli._Mfp16Mls64_v(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[DOTADDR:%.*]] = alloca i32, align 4
// CHECK-NEXT: store i32 [[TMP0:%.*]], ptr [[DOTADDR]], align 4
// CHECK-NEXT: ret i32 1
//
//
// CHECK-LABEL: @_Z7foo_ovli.default(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[DOTADDR:%.*]] = alloca i32, align 4
// CHECK-NEXT: store i32 [[TMP0:%.*]], ptr [[DOTADDR]], align 4
// CHECK-NEXT: ret i32 1
//
//
// CHECK-LABEL: @_Z7foo_ovli.resolver(
// CHECK-NEXT: resolver_entry:
// CHECK-NEXT: call void @__init_cpu_features_resolver()
Expand All @@ -63,13 +74,19 @@ void run_foo_tml() {
// CHECK: resolver_return:
// CHECK-NEXT: ret ptr @_Z7foo_ovli._Mfp16Mls64_v
// CHECK: resolver_else:
// CHECK-NEXT: ret ptr @_Z7foo_ovli
// CHECK-NEXT: ret ptr @_Z7foo_ovli.default
//
//
// CHECK-LABEL: @_Z7foo_ovlv._Mls64Mls64_accdata(
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 2
//
//
// CHECK-LABEL: @_Z7foo_ovlv.default(
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 2
//
//
// CHECK-LABEL: @_Z7foo_ovlv.resolver(
// CHECK-NEXT: resolver_entry:
// CHECK-NEXT: call void @__init_cpu_features_resolver()
Expand All @@ -82,12 +99,16 @@ void run_foo_tml() {
// CHECK-NEXT: ret ptr @_Z7foo_ovlv._Mls64Mls64_accdata
// CHECK: resolver_else:
// CHECK-NEXT: ret ptr @_Z7foo_ovlv.default
//
//
// CHECK-LABEL: @_Z3barv(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[CALL:%.*]] = call noundef i32 @_Z7foo_ovli(i32 noundef 1)
// CHECK-NEXT: [[CALL1:%.*]] = call noundef i32 @_Z7foo_ovlv()
// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[CALL]], [[CALL1]]
// CHECK-NEXT: ret i32 [[ADD]]
//
//
// CHECK-LABEL: @_Z11run_foo_tmlv(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[MC1:%.*]] = alloca [[STRUCT_MYCLASS:%.*]], align 1
Expand All @@ -99,6 +120,8 @@ void run_foo_tml() {
// CHECK-NEXT: [[CALL2:%.*]] = call noundef i32 @_ZN7MyClassIfsE7foo_tmlEv(ptr noundef nonnull align 1 dereferenceable(1) [[MC3]])
// CHECK-NEXT: [[CALL3:%.*]] = call noundef i32 @_ZN7MyClassIdfE7foo_tmlEv(ptr noundef nonnull align 1 dereferenceable(1) [[MC4]])
// CHECK-NEXT: ret void
//
//
// CHECK-LABEL: @_ZN7MyClassIssE7foo_tmlEv.resolver(
// CHECK-NEXT: resolver_entry:
// CHECK-NEXT: call void @__init_cpu_features_resolver()
Expand All @@ -108,7 +131,7 @@ void run_foo_tml() {
// CHECK-NEXT: [[TMP3:%.*]] = and i1 true, [[TMP2]]
// CHECK-NEXT: br i1 [[TMP3]], label [[RESOLVER_RETURN:%.*]], label [[RESOLVER_ELSE:%.*]]
// CHECK: resolver_return:
// CHECK-NEXT: ret ptr @_ZN7MyClassIssE7foo_tmlEv._MssbsMsme-f64f64
// CHECK-NEXT: ret ptr @_ZN7MyClassIssE7foo_tmlEv._Msme-f64f64Mssbs
// CHECK: resolver_else:
// CHECK-NEXT: [[TMP4:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
// CHECK-NEXT: [[TMP5:%.*]] = and i64 [[TMP4]], 16777216
Expand All @@ -118,7 +141,9 @@ void run_foo_tml() {
// CHECK: resolver_return1:
// CHECK-NEXT: ret ptr @_ZN7MyClassIssE7foo_tmlEv._Mfrintts
// CHECK: resolver_else2:
// CHECK-NEXT: ret ptr @_ZN7MyClassIssE7foo_tmlEv
// CHECK-NEXT: ret ptr @_ZN7MyClassIssE7foo_tmlEv.default
//
//
// CHECK-LABEL: @_ZN7MyClassIisE7foo_tmlEv.resolver(
// CHECK-NEXT: resolver_entry:
// CHECK-NEXT: call void @__init_cpu_features_resolver()
Expand All @@ -128,7 +153,7 @@ void run_foo_tml() {
// CHECK-NEXT: [[TMP3:%.*]] = and i1 true, [[TMP2]]
// CHECK-NEXT: br i1 [[TMP3]], label [[RESOLVER_RETURN:%.*]], label [[RESOLVER_ELSE:%.*]]
// CHECK: resolver_return:
// CHECK-NEXT: ret ptr @_ZN7MyClassIisE7foo_tmlEv._MssbsMsme-f64f64
// CHECK-NEXT: ret ptr @_ZN7MyClassIisE7foo_tmlEv._Msme-f64f64Mssbs
// CHECK: resolver_else:
// CHECK-NEXT: [[TMP4:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
// CHECK-NEXT: [[TMP5:%.*]] = and i64 [[TMP4]], 16777216
Expand All @@ -138,58 +163,79 @@ void run_foo_tml() {
// CHECK: resolver_return1:
// CHECK-NEXT: ret ptr @_ZN7MyClassIisE7foo_tmlEv._Mfrintts
// CHECK: resolver_else2:
// CHECK-NEXT: ret ptr @_ZN7MyClassIisE7foo_tmlEv
// CHECK-NEXT: ret ptr @_ZN7MyClassIisE7foo_tmlEv.default
//
//
// CHECK-LABEL: @_ZN7MyClassIfsE7foo_tmlEv(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8
// CHECK-NEXT: store ptr [[THIS:%.*]], ptr [[THIS_ADDR]], align 8
// CHECK-NEXT: [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
// CHECK-NEXT: ret i32 3
//
//
// CHECK-LABEL: @_ZN7MyClassIdfE7foo_tmlEv(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8
// CHECK-NEXT: store ptr [[THIS:%.*]], ptr [[THIS_ADDR]], align 8
// CHECK-NEXT: [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
// CHECK-NEXT: ret i32 4
//
//
// CHECK-LABEL: @_ZN7MyClassIssE7foo_tmlEv._Mfrintts(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8
// CHECK-NEXT: store ptr [[THIS:%.*]], ptr [[THIS_ADDR]], align 8
// CHECK-NEXT: [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
// CHECK-NEXT: ret i32 1
// CHECK-LABEL: @_ZN7MyClassIssE7foo_tmlEv._MssbsMsme-f64f64(
//
//
// CHECK-LABEL: @_ZN7MyClassIssE7foo_tmlEv._Msme-f64f64Mssbs(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8
// CHECK-NEXT: store ptr [[THIS:%.*]], ptr [[THIS_ADDR]], align 8
// CHECK-NEXT: [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
// CHECK-NEXT: ret i32 1
//
//
// CHECK-LABEL: @_ZN7MyClassIssE7foo_tmlEv.default(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8
// CHECK-NEXT: store ptr [[THIS:%.*]], ptr [[THIS_ADDR]], align 8
// CHECK-NEXT: [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
// CHECK-NEXT: ret i32 1
//
//
// CHECK-LABEL: @_ZN7MyClassIisE7foo_tmlEv._Mfrintts(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8
// CHECK-NEXT: store ptr [[THIS:%.*]], ptr [[THIS_ADDR]], align 8
// CHECK-NEXT: [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
// CHECK-NEXT: ret i32 2
// CHECK-LABEL: @_ZN7MyClassIisE7foo_tmlEv._MssbsMsme-f64f64(
//
//
// CHECK-LABEL: @_ZN7MyClassIisE7foo_tmlEv._Msme-f64f64Mssbs(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8
// CHECK-NEXT: store ptr [[THIS:%.*]], ptr [[THIS_ADDR]], align 8
// CHECK-NEXT: [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
// CHECK-NEXT: ret i32 2
//
//
// CHECK-LABEL: @_ZN7MyClassIisE7foo_tmlEv.default(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8
// CHECK-NEXT: store ptr [[THIS:%.*]], ptr [[THIS_ADDR]], align 8
// CHECK-NEXT: [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
// CHECK-NEXT: ret i32 2

// CHECK: attributes #0 = { mustprogress noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+fp-armv8,+fullfp16,+neon" }
// CHECK: attributes #1 = { mustprogress noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
// CHECK: attributes #2 = { mustprogress noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+ls64" }
// CHECK: attributes #3 = { mustprogress noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+fptoint" }
// CHECK: attributes #4 = { mustprogress noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme,+sme-f64f64" }
//
//.
// CHECK: attributes #[[ATTR0:[0-9]+]] = { mustprogress noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+fp-armv8,+fullfp16,+neon" }
// CHECK: attributes #[[ATTR1:[0-9]+]] = { mustprogress noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
// CHECK: attributes #[[ATTR2:[0-9]+]] = { mustprogress noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+ls64" }
// CHECK: attributes #[[ATTR3:[0-9]+]] = { mustprogress noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+fptoint" }
// CHECK: attributes #[[ATTR4:[0-9]+]] = { mustprogress noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme,+sme-f64f64" }
//.
// CHECK: [[META0:![0-9]+]] = !{i32 1, !"wchar_size", i32 4}
// CHECK: [[META1:![0-9]+]] = !{!"{{.*}}clang version {{.*}}"}
//.
4 changes: 2 additions & 2 deletions clang/test/CodeGenCXX/attr-target-version.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ int bar() {
// CHECK-NEXT: ret i32 1
//
//
// CHECK-LABEL: @_Z3foov._Msm4Mebf16(
// CHECK-LABEL: @_Z3foov._Mebf16Msm4(
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 3
//
Expand Down Expand Up @@ -101,7 +101,7 @@ int bar() {
// CHECK-NEXT: [[TMP3:%.*]] = and i1 true, [[TMP2]]
// CHECK-NEXT: br i1 [[TMP3]], label [[RESOLVER_RETURN:%.*]], label [[RESOLVER_ELSE:%.*]]
// CHECK: resolver_return:
// CHECK-NEXT: ret ptr @_Z3foov._Msm4Mebf16
// CHECK-NEXT: ret ptr @_Z3foov._Mebf16Msm4
// CHECK: resolver_else:
// CHECK-NEXT: ret ptr @_Z3foov.default
//
Expand Down
3 changes: 2 additions & 1 deletion clang/test/Misc/warning-flags.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ This test serves two purposes:

The list of warnings below should NEVER grow. It should gradually shrink to 0.

CHECK: Warnings without flags (66):
CHECK: Warnings without flags (67):

CHECK-NEXT: ext_expected_semi_decl_list
CHECK-NEXT: ext_explicit_specialization_storage_class
Expand Down Expand Up @@ -58,6 +58,7 @@ CHECK-NEXT: warn_ignoring_ftabstop_value
CHECK-NEXT: warn_implements_nscopying
CHECK-NEXT: warn_incompatible_qualified_id
CHECK-NEXT: warn_invalid_asm_cast_lvalue
CHECK-NEXT: warn_invalid_cpu_supports
CHECK-NEXT: warn_maynot_respond
CHECK-NEXT: warn_method_param_redefinition
CHECK-NEXT: warn_missing_case_for_condition
Expand Down
4 changes: 3 additions & 1 deletion clang/test/Preprocessor/has_attribute.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// RUN: %clang_cc1 -triple i386-unknown-unknown -fms-compatibility -std=c++03 -E -P %s -o - | FileCheck %s --check-prefixes=CHECK,ITANIUM --implicit-check-not=:
// RUN: %clang_cc1 -triple i386-unknown-unknown -fms-compatibility -std=c++11 -E -P %s -o - | FileCheck %s --check-prefixes=CHECK,ITANIUM --implicit-check-not=:
// RUN: %clang_cc1 -triple i386-windows -fms-compatibility -std=c++03 -E -P %s -o - | FileCheck %s --check-prefixes=CHECK,WINDOWS --implicit-check-not=:
// RUN: %clang_cc1 -triple i386-windows -fms-compatibility -std=c++11 -E -P %s -o - | FileCheck %s --check-prefixes=CHECK,WINDOWS --implicit-check-not=:

#define CXX11(x) x: __has_cpp_attribute(x)
Expand Down Expand Up @@ -65,7 +67,7 @@ CXX11(unlikely)
// CHECK: likely: 201803L
// CHECK: maybe_unused: 201603L
// ITANIUM: no_unique_address: 201803L
// WINDOWS: no_unique_address: 0
// WINDOWS: no_unique_address: 0
// ITANIUM: msvc::no_unique_address: 0
// WINDOWS: msvc::no_unique_address: 201803L
// CHECK: nodiscard: 201907L
Expand Down
10 changes: 5 additions & 5 deletions clang/test/Sema/aarch64-cpu-supports.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ int test_aarch64_features(void) {
// expected-error@+1 {{expression is not a string literal}}
if (__builtin_cpu_supports(ssbs2))
return 1;
// expected-error@+1 {{invalid cpu feature string}}
// expected-warning@+1 {{invalid cpu feature string}}
if (__builtin_cpu_supports(""))
return 2;
// expected-error@+1 {{invalid cpu feature string}}
// expected-warning@+1 {{invalid cpu feature string}}
if (__builtin_cpu_supports("pmull128"))
return 3;
// expected-error@+1 {{invalid cpu feature string}}
// expected-warning@+1 {{invalid cpu feature string}}
if (__builtin_cpu_supports("sve2,rpres"))
return 4;
// expected-error@+1 {{invalid cpu feature string}}
// expected-warning@+1 {{invalid cpu feature string}}
if (__builtin_cpu_supports("dgh+sve2-pmull"))
return 5;
// expected-error@+1 {{invalid cpu feature string}}
// expected-warning@+1 {{invalid cpu feature string}}
if (__builtin_cpu_supports("default"))
return 6;
if (__builtin_cpu_supports(" ssbs + bti "))
Expand Down
2 changes: 1 addition & 1 deletion clang/test/Sema/aix-attr-tls_model.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
#endif

static __thread int y __attribute((tls_model("global-dynamic"))); // no-warning
static __thread int y __attribute((tls_model("local-dynamic"))); // expected-error {{TLS model 'local-dynamic' is not yet supported on AIX}}
static __thread int y __attribute((tls_model("local-dynamic"))); // expected-no-diagnostics
static __thread int y __attribute((tls_model("initial-exec"))); // no-warning
static __thread int y __attribute((tls_model("local-exec"))); // no-warning
6 changes: 3 additions & 3 deletions clang/test/Sema/builtin-cpu-supports.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ extern const char *str;

int main(void) {
#ifdef __x86_64__
if (__builtin_cpu_supports("ss")) // expected-error {{invalid cpu feature string}}
if (__builtin_cpu_supports("ss")) // expected-warning {{invalid cpu feature string}}
a("sse4.2");

if (__builtin_cpu_supports(str)) // expected-error {{expression is not a string literal}}
Expand All @@ -25,9 +25,9 @@ int main(void) {
(void)__builtin_cpu_supports("x86-64-v2");
(void)__builtin_cpu_supports("x86-64-v3");
(void)__builtin_cpu_supports("x86-64-v4");
(void)__builtin_cpu_supports("x86-64-v5"); // expected-error {{invalid cpu feature string for builtin}}
(void)__builtin_cpu_supports("x86-64-v5"); // expected-warning {{invalid cpu feature string for builtin}}
#else
if (__builtin_cpu_supports("neon")) // expected-error {{invalid cpu feature string for builtin}}
if (__builtin_cpu_supports("neon")) // expected-warning {{invalid cpu feature string for builtin}}
a("vsx");

if (__builtin_cpu_is("cortex-x3")) // expected-error {{builtin is not supported on this target}}
Expand Down
3 changes: 2 additions & 1 deletion clang/test/SemaCXX/attr-declspec-ignored.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 %s -verify -fsyntax-only
// RUN: %clang_cc1 %s -std=c++03 -Wno-c++11-extensions -verify -fsyntax-only

namespace test1 {
__attribute__((visibility("hidden"))) __attribute__((aligned)) class A; // expected-warning{{attribute 'visibility' is ignored, place it after "class" to apply attribute to type declaration}} \
Expand Down Expand Up @@ -28,7 +29,7 @@ namespace test1 {
// expected-warning{{attribute 'aligned' is ignored, place it after "enum class" to apply attribute to type declaration}}
__attribute__((visibility("hidden"))) __attribute__((aligned)) enum struct ES {}; // expected-warning{{attribute 'visibility' is ignored, place it after "enum struct" to apply attribute to type declaration}} \
// expected-warning{{attribute 'aligned' is ignored, place it after "enum struct" to apply attribute to type declaration}}

// Also test [[]] attribute syntax. (On a non-nested declaration, these
// generate a hard "misplaced attributes" error, which we test for
// elsewhere.)
Expand Down
29 changes: 15 additions & 14 deletions clang/test/SemaCXX/attr-gnu.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// RUN: %clang_cc1 -std=gnu++17 -fsyntax-only -fms-compatibility -verify %s

void f() {
// GNU-style attributes are prohibited in this position.
// RUN: %clang_cc1 -std=gnu++03 -fsyntax-only -fms-compatibility -Wno-c++11-extensions -Wno-c++17-extensions -verify %s
// RUN: %clang_cc1 -std=gnu++17 -fsyntax-only -fms-compatibility -verify %s

void f() {
// GNU-style attributes are prohibited in this position.
auto P = new int * __attribute__((vector_size(8))); // expected-error {{an attribute list cannot appear here}} \
// expected-error {{invalid vector element type 'int *'}}

Expand Down Expand Up @@ -47,13 +48,13 @@ void tuTest1(Tu<int> u); // expected-note {{candidate function not viable: no kn
void tuTest2(Tu3 u); // expected-note {{candidate function not viable: no known conversion from 'int' to 'Tu3' for 1st argument}}
void tu() {
int x = 2;
tuTest1(x); // expected-error {{no matching function for call to 'tuTest1'}}
tuTest2(x); // expected-error {{no matching function for call to 'tuTest2'}}
}
[[gnu::__const__]] int f2() { return 12; }
[[__gnu__::__const__]] int f3() { return 12; }
[[using __gnu__ : __const__]] int f4() { return 12; }
static_assert(__has_cpp_attribute(gnu::__const__));
static_assert(__has_cpp_attribute(__gnu__::__const__));
tuTest1(x); // expected-error {{no matching function for call to 'tuTest1'}}
tuTest2(x); // expected-error {{no matching function for call to 'tuTest2'}}
}

[[gnu::__const__]] int f2() { return 12; }
[[__gnu__::__const__]] int f3() { return 12; }
[[using __gnu__ : __const__]] int f4() { return 12; }

static_assert(__has_cpp_attribute(gnu::__const__));
static_assert(__has_cpp_attribute(__gnu__::__const__));
9 changes: 9 additions & 0 deletions clang/test/SemaCXX/cxx03-cxx11-attr.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// RUN: %clang_cc1 -std=c++03 -fsyntax-only %s

// Ensure that __has_cpp_attribute and argument parsing work in C++03

#if !__has_cpp_attribute(nodiscard)
# error
#endif

[[gnu::assume_aligned(4)]] void* g() { return __nullptr; }
19 changes: 19 additions & 0 deletions clang/unittests/Analysis/FlowSensitive/TestingSupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,8 @@ llvm::Error checkDataflowWithNoopAnalysis(
{});

/// Returns the `ValueDecl` for the given identifier.
/// The returned pointer is guaranteed to be non-null; the function asserts if
/// no `ValueDecl` with the given name is found.
///
/// Requirements:
///
Expand Down Expand Up @@ -475,6 +477,15 @@ ValueT &getValueForDecl(ASTContext &ASTCtx, const Environment &Env,
return *cast<ValueT>(Env.getValue(*VD));
}

/// Returns the storage location for the field called `Name` of `Loc`.
/// Optionally casts the field storage location to `T`.
template <typename T = StorageLocation>
std::enable_if_t<std::is_base_of_v<StorageLocation, T>, T &>
getFieldLoc(const RecordStorageLocation &Loc, llvm::StringRef Name,
ASTContext &ASTCtx) {
return *cast<T>(Loc.getChild(*findValueDecl(ASTCtx, Name)));
}

/// Returns the value of a `Field` on the record referenced by `Loc.`
/// Returns null if `Loc` is null.
inline Value *getFieldValue(const RecordStorageLocation *Loc,
Expand All @@ -487,6 +498,14 @@ inline Value *getFieldValue(const RecordStorageLocation *Loc,
return Env.getValue(*FieldLoc);
}

/// Returns the value of a `Field` on the record referenced by `Loc.`
/// Returns null if `Loc` is null.
inline Value *getFieldValue(const RecordStorageLocation *Loc,
llvm::StringRef Name, ASTContext &ASTCtx,
const Environment &Env) {
return getFieldValue(Loc, *findValueDecl(ASTCtx, Name), Env);
}

/// Creates and owns constraints which are boolean values.
class ConstraintContext {
unsigned NextAtom = 0;
Expand Down
82 changes: 80 additions & 2 deletions clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2392,14 +2392,92 @@ TEST(TransferTest, InitListExprAsUnion) {
} F;

public:
constexpr target() : F{nullptr} {}
constexpr target() : F{nullptr} {
int *null = nullptr;
F.b; // Make sure we reference 'b' so it is modeled.
// [[p]]
}
};
)cc";
runDataflow(
Code,
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
ASTContext &ASTCtx) {
// Just verify that it doesn't crash.
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");

auto &FLoc = getFieldLoc<RecordStorageLocation>(
*Env.getThisPointeeStorageLocation(), "F", ASTCtx);
auto *AVal = cast<PointerValue>(getFieldValue(&FLoc, "a", ASTCtx, Env));
EXPECT_EQ(AVal, &getValueForDecl<PointerValue>(ASTCtx, Env, "null"));
EXPECT_EQ(getFieldValue(&FLoc, "b", ASTCtx, Env), nullptr);
});
}

TEST(TransferTest, EmptyInitListExprForUnion) {
// This is a crash repro.
std::string Code = R"cc(
class target {
union {
int *a;
bool *b;
} F;

public:
// Empty initializer list means that `F` is aggregate-initialized.
// For a union, this has the effect that the first member of the union
// is copy-initialized from an empty initializer list; in this specific
// case, this has the effect of initializing `a` with null.
constexpr target() : F{} {
int *null = nullptr;
F.b; // Make sure we reference 'b' so it is modeled.
// [[p]]
}
};
)cc";
runDataflow(
Code,
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
ASTContext &ASTCtx) {
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");

auto &FLoc = getFieldLoc<RecordStorageLocation>(
*Env.getThisPointeeStorageLocation(), "F", ASTCtx);
auto *AVal = cast<PointerValue>(getFieldValue(&FLoc, "a", ASTCtx, Env));
EXPECT_EQ(AVal, &getValueForDecl<PointerValue>(ASTCtx, Env, "null"));
EXPECT_EQ(getFieldValue(&FLoc, "b", ASTCtx, Env), nullptr);
});
}

TEST(TransferTest, EmptyInitListExprForStruct) {
std::string Code = R"cc(
class target {
struct {
int *a;
bool *b;
} F;

public:
constexpr target() : F{} {
int *NullIntPtr = nullptr;
bool *NullBoolPtr = nullptr;
// [[p]]
}
};
)cc";
runDataflow(
Code,
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
ASTContext &ASTCtx) {
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");

auto &FLoc = getFieldLoc<RecordStorageLocation>(
*Env.getThisPointeeStorageLocation(), "F", ASTCtx);
auto *AVal = cast<PointerValue>(getFieldValue(&FLoc, "a", ASTCtx, Env));
EXPECT_EQ(AVal,
&getValueForDecl<PointerValue>(ASTCtx, Env, "NullIntPtr"));
auto *BVal = cast<PointerValue>(getFieldValue(&FLoc, "b", ASTCtx, Env));
EXPECT_EQ(BVal,
&getValueForDecl<PointerValue>(ASTCtx, Env, "NullBoolPtr"));
});
}

Expand Down
12 changes: 1 addition & 11 deletions clang/utils/TableGen/ClangAttrEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3576,10 +3576,6 @@ static void GenerateHasAttrSpellingStringSwitch(
const Record *R = Attr->getValueAsDef("Target");
std::vector<StringRef> Arches = R->getValueAsListOfStrings("Arches");
GenerateTargetSpecificAttrChecks(R, Arches, Test, nullptr);

// If this is the C++11 variety, also add in the LangOpts test.
if (Variety == "CXX11")
Test += " && LangOpts.CPlusPlus11";
} else if (!Attr->getValueAsListOfDefs("TargetSpecificSpellings").empty()) {
// Add target checks if this spelling is target-specific.
const std::vector<Record *> TargetSpellings =
Expand All @@ -3597,13 +3593,7 @@ static void GenerateHasAttrSpellingStringSwitch(
}
}
}

if (Variety == "CXX11")
Test += " && LangOpts.CPlusPlus11";
} else if (Variety == "CXX11")
// C++11 mode should be checked against LangOpts, which is presumed to be
// present in the caller.
Test = "LangOpts.CPlusPlus11";
}

std::string TestStr = !Test.empty()
? Test + " ? " + llvm::itostr(Version) + " : 0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,8 @@ CHECK_TYPE_SIZE(nfds_t);
CHECK_TYPE_SIZE(sigset_t);

COMPILER_CHECK(sizeof(__sanitizer_sigaction) == sizeof(struct sigaction));
COMPILER_CHECK(sizeof(__sanitizer_siginfo) == sizeof(siginfo_t));
CHECK_SIZE_AND_OFFSET(siginfo_t, si_value);
// Can't write checks for sa_handler and sa_sigaction due to them being
// preprocessor macros.
CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -301,11 +301,29 @@ struct __sanitizer_sigset_t {

typedef __sanitizer_sigset_t __sanitizer_kernel_sigset_t;

union __sanitizer_sigval {
int sival_int;
void *sival_ptr;
};

struct __sanitizer_siginfo {
// The size is determined by looking at sizeof of real siginfo_t on linux.
u64 opaque[128 / sizeof(u64)];
int si_signo;
int si_errno;
int si_code;
pid_t si_pid;
u32 si_uid;
int si_status;
void *si_addr;
union __sanitizer_sigval si_value;
# if SANITIZER_WORDSIZE == 64
char data[40];
# else
char data[32];
# endif
};

typedef __sanitizer_siginfo __sanitizer_siginfo_t;

using __sanitizer_sighandler_ptr = void (*)(int sig);
using __sanitizer_sigactionhandler_ptr = void (*)(int sig,
__sanitizer_siginfo *siginfo,
Expand Down
22 changes: 11 additions & 11 deletions compiler-rt/lib/sanitizer_common/sanitizer_procmaps_bsd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@ namespace __sanitizer {

#if SANITIZER_FREEBSD
void GetMemoryProfile(fill_profile_f cb, uptr *stats) {
const int Mib[] = {
CTL_KERN,
KERN_PROC,
KERN_PROC_PID,
getpid()
};

struct kinfo_proc InfoProc;
uptr Len = sizeof(InfoProc);
CHECK_EQ(internal_sysctl(Mib, ARRAY_SIZE(Mib), nullptr, (uptr *)&InfoProc, &Len, 0), 0);
cb(0, InfoProc.ki_rssize * GetPageSizeCached(), false, stats);
const int Mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()};

struct kinfo_proc *InfoProc;
uptr Len = sizeof(*InfoProc);
uptr Size = Len;
InfoProc = (struct kinfo_proc *)MmapOrDie(Size, "GetMemoryProfile()");
CHECK_EQ(
internal_sysctl(Mib, ARRAY_SIZE(Mib), nullptr, (uptr *)InfoProc, &Len, 0),
0);
cb(0, InfoProc->ki_rssize * GetPageSizeCached(), false, stats);
UnmapOrDie(InfoProc, Size, true);
}
#endif

Expand Down
24 changes: 23 additions & 1 deletion compiler-rt/lib/scudo/standalone/secondary.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,29 @@ bool mapSecondary(const Options &Options, uptr CommitBase, uptr CommitSize,
Flags |= MAP_RESIZABLE;
Flags |= MAP_ALLOWNOMEM;

const uptr MaxUnusedCacheBytes = MaxUnusedCachePages * getPageSizeCached();
const uptr PageSize = getPageSizeCached();
if (SCUDO_TRUSTY) {
/*
* On Trusty we need AllocPos to be usable for shared memory, which cannot
* cross multiple mappings. This means we need to split around AllocPos
* and not over it. We can only do this if the address is page-aligned.
*/
const uptr TaggedSize = AllocPos - CommitBase;
if (useMemoryTagging<Config>(Options) && isAligned(TaggedSize, PageSize)) {
DCHECK_GT(TaggedSize, 0);
return MemMap.remap(CommitBase, TaggedSize, "scudo:secondary",
MAP_MEMTAG | Flags) &&
MemMap.remap(AllocPos, CommitSize - TaggedSize, "scudo:secondary",
Flags);
} else {
const uptr RemapFlags =
(useMemoryTagging<Config>(Options) ? MAP_MEMTAG : 0) | Flags;
return MemMap.remap(CommitBase, CommitSize, "scudo:secondary",
RemapFlags);
}
}

const uptr MaxUnusedCacheBytes = MaxUnusedCachePages * PageSize;
if (useMemoryTagging<Config>(Options) && CommitSize > MaxUnusedCacheBytes) {
const uptr UntaggedPos = Max(AllocPos, CommitBase + MaxUnusedCacheBytes);
return MemMap.remap(CommitBase, UntaggedPos - CommitBase, "scudo:secondary",
Expand Down
20 changes: 20 additions & 0 deletions compiler-rt/test/hwasan/TestCases/longjmp-out-of-range.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// RUN: %clang_hwasan -O0 %s -o %t && %run %t 2>&1 | FileCheck %s

// REQUIRES: pointer-tagging
#include <assert.h>
#include <sanitizer/hwasan_interface.h>
#include <stdlib.h>

__attribute__((noinline)) int f(void *caller_frame) {
int z = 0;
int *volatile p = &z;
// Tag of local is never zero.
assert(__hwasan_tag_pointer(p, 0) != p);
__hwasan_handle_longjmp(NULL);
return p[0];
}

int main() {
return f(__builtin_frame_address(0));
// CHECK: HWASan is ignoring requested __hwasan_handle_longjmp:
}
3 changes: 2 additions & 1 deletion flang/lib/Lower/OpenMP/OpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -840,7 +840,8 @@ genEnterExitUpdateDataOp(Fortran::lower::AbstractConverter &converter,
llvm::SmallVector<mlir::Attribute> dependTypeOperands;

Fortran::parser::OmpIfClause::DirectiveNameModifier directiveName;
llvm::omp::Directive directive;
// GCC 9.3.0 emits a (probably) bogus warning about an unused variable.
[[maybe_unused]] llvm::omp::Directive directive;
if constexpr (std::is_same_v<OpTy, mlir::omp::EnterDataOp>) {
directiveName =
Fortran::parser::OmpIfClause::DirectiveNameModifier::TargetEnterData;
Expand Down
39 changes: 16 additions & 23 deletions flang/lib/Optimizer/HLFIR/Transforms/LowerHLFIRIntrinsics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@
#include "flang/Optimizer/HLFIR/HLFIROps.h"
#include "flang/Optimizer/HLFIR/Passes.h"
#include "mlir/IR/BuiltinDialect.h"
#include "mlir/IR/MLIRContext.h"
#include "mlir/IR/PatternMatch.h"
#include "mlir/Pass/Pass.h"
#include "mlir/Pass/PassManager.h"
#include "mlir/Support/LogicalResult.h"
#include "mlir/Transforms/DialectConversion.h"
#include <mlir/IR/MLIRContext.h>
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
#include <optional>

namespace hlfir {
Expand Down Expand Up @@ -176,14 +176,7 @@ class HlfirIntrinsicConversion : public mlir::OpRewritePattern<OP> {
rewriter.eraseOp(use);
}
}
// TODO: This entire pass should be a greedy pattern rewrite or a manual
// IR traversal. A dialect conversion cannot be used here because
// `replaceAllUsesWith` is not supported. Similarly, `replaceOp` is not
// suitable because "op->getResult(0)" and "base" can have different types.
// In such a case, the dialect conversion will attempt to convert the type,
// but no type converter is specified in this pass. Also note that all
// patterns in this pass are actually rewrite patterns.
op->getResult(0).replaceAllUsesWith(base);

rewriter.replaceOp(op, base);
}
};
Expand Down Expand Up @@ -491,19 +484,19 @@ class LowerHLFIRIntrinsics
ProductOpConversion, TransposeOpConversion, CountOpConversion,
DotProductOpConversion, MaxvalOpConversion, MinvalOpConversion,
MinlocOpConversion, MaxlocOpConversion>(context);
mlir::ConversionTarget target(*context);
target.addLegalDialect<mlir::BuiltinDialect, mlir::arith::ArithDialect,
mlir::func::FuncDialect, fir::FIROpsDialect,
hlfir::hlfirDialect>();
target.addIllegalOp<hlfir::MatmulOp, hlfir::MatmulTransposeOp, hlfir::SumOp,
hlfir::ProductOp, hlfir::TransposeOp, hlfir::AnyOp,
hlfir::AllOp, hlfir::DotProductOp, hlfir::CountOp,
hlfir::MaxvalOp, hlfir::MinvalOp, hlfir::MinlocOp,
hlfir::MaxlocOp>();
target.markUnknownOpDynamicallyLegal(
[](mlir::Operation *) { return true; });
if (mlir::failed(
mlir::applyFullConversion(module, target, std::move(patterns)))) {

// While conceptually this pass is performing dialect conversion, we use
// pattern rewrites here instead of dialect conversion because this pass
// looses array bounds from some of the expressions e.g.
// !hlfir.expr<2xi32> -> !hlfir.expr<?xi32>
// MLIR thinks this is a different type so dialect conversion fails.
// Pattern rewriting only requires that the resulting IR is still valid
mlir::GreedyRewriteConfig config;
// Prevent the pattern driver from merging blocks
config.enableRegionSimplification = false;

if (mlir::failed(mlir::applyPatternsAndFoldGreedily(
module, std::move(patterns), config))) {
mlir::emitError(mlir::UnknownLoc::get(context),
"failure in HLFIR intrinsic lowering");
signalPassFailure();
Expand Down
2 changes: 1 addition & 1 deletion flang/runtime/Float128Math/exponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
namespace Fortran::runtime {
extern "C" {

#if LDBL_MANT_DIG == 113 || HAS_FLOAT128
#if LDBL_MANT_DIG != 113 && HAS_FLOAT128
// EXPONENT (16.9.75)
CppTypeFor<TypeCategory::Integer, 4> RTDEF(Exponent16_4)(F128Type x) {
return Exponent<CppTypeFor<TypeCategory::Integer, 4>>(x);
Expand Down
2 changes: 1 addition & 1 deletion flang/runtime/Float128Math/fraction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
namespace Fortran::runtime {
extern "C" {

#if LDBL_MANT_DIG == 113 || HAS_FLOAT128
#if LDBL_MANT_DIG != 113 && HAS_FLOAT128
// FRACTION (16.9.80)
F128Type RTDEF(Fraction16)(F128Type x) { return Fraction(x); }
#endif
Expand Down
2 changes: 1 addition & 1 deletion flang/runtime/Float128Math/mod-real.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
namespace Fortran::runtime {
extern "C" {

#if LDBL_MANT_DIG == 113 || HAS_FLOAT128
#if LDBL_MANT_DIG != 113 && HAS_FLOAT128
// MOD (16.9.135)
F128Type RTDEF(ModReal16)(
F128Type x, F128Type p, const char *sourceFile, int sourceLine) {
Expand Down
2 changes: 1 addition & 1 deletion flang/runtime/Float128Math/modulo-real.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
namespace Fortran::runtime {
extern "C" {

#if LDBL_MANT_DIG == 113 || HAS_FLOAT128
#if LDBL_MANT_DIG != 113 && HAS_FLOAT128
// MODULO (16.9.136)
F128Type RTDEF(ModuloReal16)(
F128Type x, F128Type p, const char *sourceFile, int sourceLine) {
Expand Down
2 changes: 1 addition & 1 deletion flang/runtime/Float128Math/nearest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
namespace Fortran::runtime {
extern "C" {

#if LDBL_MANT_DIG == 113 || HAS_FLOAT128
#if LDBL_MANT_DIG != 113 && HAS_FLOAT128
CppTypeFor<TypeCategory::Real, 16> RTDEF(Nearest16)(
CppTypeFor<TypeCategory::Real, 16> x, bool positive) {
return Nextafter<true>::invoke(
Expand Down
2 changes: 1 addition & 1 deletion flang/runtime/Float128Math/rrspacing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
namespace Fortran::runtime {
extern "C" {

#if LDBL_MANT_DIG == 113 || HAS_FLOAT128
#if LDBL_MANT_DIG != 113 && HAS_FLOAT128
// FRACTION (16.9.80)
F128Type RTDEF(RRSpacing16)(F128Type x) { return RRSpacing<113>(x); }
#endif
Expand Down
Loading