Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
// RUN: %check_clang_tidy %s bugprone-sizeof-expression %t -- -config="{CheckOptions: {bugprone-sizeof-expression.WarnOnSizeOfIntegerExpression: true, bugprone-sizeof-expression.WarnOnSizeOfPointer: true}}" --

class C {
int size() { return sizeof(this); }
// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of 'sizeof(this)'
};

#define LEN 8

int X;
extern int A[10];
extern short B[10];

#pragma pack(1)
struct S { char a, b, c; };

enum E { E_VALUE = 0 };
enum class EC { VALUE = 0 };

bool AsBool() { return false; }
int AsInt() { return 0; }
E AsEnum() { return E_VALUE; }
EC AsEnumClass() { return EC::VALUE; }
S AsStruct() { return {}; }

struct M {
int AsInt() { return 0; }
E AsEnum() { return E_VALUE; }
S AsStruct() { return {}; }
};

int Test1(const char* ptr) {
int sum = 0;
sum += sizeof(LEN);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(K)'
sum += sizeof(LEN + 1);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(K)'
sum += sizeof(sum, LEN);
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: suspicious usage of 'sizeof(..., ...)'
sum += sizeof(AsBool());
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer
sum += sizeof(AsInt());
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer
sum += sizeof(AsEnum());
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer
sum += sizeof(AsEnumClass());
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer
sum += sizeof(M{}.AsInt());
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer
sum += sizeof(M{}.AsEnum());
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer
sum += sizeof(sizeof(X));
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))'
sum += sizeof(LEN + sizeof(X));
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))'
sum += sizeof(LEN + LEN + sizeof(X));
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))'
sum += sizeof(LEN + (LEN + sizeof(X)));
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))'
sum += sizeof(LEN + -sizeof(X));
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))'
sum += sizeof(LEN + - + -sizeof(X));
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))'
sum += sizeof(char) / sizeof(char);
// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'
sum += sizeof(A) / sizeof(S);
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator
sum += sizeof(char) / sizeof(int);
// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator
sum += sizeof(char) / sizeof(A);
// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator
sum += sizeof(B[0]) / sizeof(A);
// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator
sum += sizeof(ptr) / sizeof(char);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(ptr) / sizeof(ptr[0]);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(ptr) / sizeof(char*);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(ptr) / sizeof(void*);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(ptr) / sizeof(const void volatile*);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(ptr) / sizeof(char);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(int) * sizeof(char);
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious 'sizeof' by 'sizeof' multiplication
sum += sizeof(ptr) * sizeof(ptr[0]);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
// CHECK-MESSAGES: :[[@LINE-2]]:22: warning: suspicious 'sizeof' by 'sizeof' multiplication
sum += sizeof(int) * (2 * sizeof(char));
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious 'sizeof' by 'sizeof' multiplication
sum += (2 * sizeof(char)) * sizeof(int);
// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: suspicious 'sizeof' by 'sizeof' multiplication
if (sizeof(A) < 0x100000) sum += 42;
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: suspicious comparison of 'sizeof(expr)' to a constant
if (sizeof(A) <= 0xFFFFFFFEU) sum += 42;
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: suspicious comparison of 'sizeof(expr)' to a constant
return sum;
}

int Test5() {
typedef int Array10[10];
typedef C ArrayC[10];

struct MyStruct {
Array10 arr;
Array10* ptr;
};
typedef const MyStruct TMyStruct;
typedef const MyStruct *PMyStruct;
typedef TMyStruct *PMyStruct2;

static TMyStruct kGlocalMyStruct = {};
static TMyStruct volatile * kGlocalMyStructPtr = &kGlocalMyStruct;

MyStruct S;
PMyStruct PS;
PMyStruct2 PS2;
Array10 A10;
C *PtrArray[10];
C *PC;

char *PChar;
int *PInt, **PPInt;
MyStruct **PPMyStruct;

int sum = 0;
sum += sizeof(&S.arr);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(&kGlocalMyStruct.arr);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(&kGlocalMyStructPtr->arr);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(S.arr + 0);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(+ S.arr);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof((int*)S.arr);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer

sum += sizeof(S.ptr);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(kGlocalMyStruct.ptr);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(kGlocalMyStructPtr->ptr);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer

sum += sizeof(&kGlocalMyStruct);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(&S);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(MyStruct*);
sum += sizeof(PMyStruct);
sum += sizeof(PS);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(PS2);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(&A10);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(PtrArray) / sizeof(PtrArray[1]);
// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(A10) / sizeof(PtrArray[0]);
sum += sizeof(PC) / sizeof(PtrArray[0]);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
// CHECK-MESSAGES: :[[@LINE-2]]:21: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'
sum += sizeof(ArrayC) / sizeof(PtrArray[0]);
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator

sum += sizeof(PChar);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(PInt);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(PPInt);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(PPMyStruct);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer

return sum;
}

void some_generic_function(const void *arg, int argsize);
int *IntP, **IntPP;
C *ClassP, **ClassPP;

void GenericFunctionTest() {
// The `sizeof(pointer)` checks ignore situations where the pointer is
// produced by dereferencing a pointer-to-pointer, because this is unlikely
// to be an accident and can appear in legitimate code that tries to call
// a generic function which emulates dynamic typing within C.
some_generic_function(IntPP, sizeof(*IntPP));
some_generic_function(ClassPP, sizeof(*ClassPP));
// Using `...[0]` instead of the dereference operator is another common
// variant, which is also widespread in the idiomatic array-size calculation:
// `sizeof(array) / sizeof(array[0])`.
some_generic_function(IntPP, sizeof(IntPP[0]));
some_generic_function(ClassPP, sizeof(ClassPP[0]));
// FIXME: There is a third common pattern where the generic function is
// called with `&Variable` and `sizeof(Variable)`. Right now these are
// reported by the `sizeof(pointer)` checks, but this causes some false
// positives, so it would be good to create an exception for them.
some_generic_function(&IntPP, sizeof(IntP));
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
some_generic_function(&ClassPP, sizeof(ClassP));
// CHECK-MESSAGES: :[[@LINE-1]]:35: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
}

int ValidExpressions() {
int A[] = {1, 2, 3, 4};
static const char str[] = "hello";
static const char* ptr[] { "aaa", "bbb", "ccc" };
typedef C *CA10[10];
C *PtrArray[10];
CA10 PtrArray1;

int sum = 0;
if (sizeof(A) < 10)
sum += sizeof(A);
sum += sizeof(int);
sum += sizeof(AsStruct());
sum += sizeof(M{}.AsStruct());
sum += sizeof(A[sizeof(A) / sizeof(int)]);
// Here the outer sizeof is reported, but the inner ones are accepted:
sum += sizeof(&A[sizeof(A) / sizeof(int)]);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(sizeof(0)); // Special case: sizeof size_t.
sum += sizeof(void*);
sum += sizeof(void const *);
sum += sizeof(void const *) / 4;
sum += sizeof(str);
sum += sizeof(str) / sizeof(char);
sum += sizeof(str) / sizeof(str[0]);
sum += sizeof(ptr) / sizeof(ptr[0]);
sum += sizeof(ptr) / sizeof(*(ptr));
sum += sizeof(PtrArray) / sizeof(PtrArray[0]);
// Canonical type of PtrArray1 is same as PtrArray.
sum = sizeof(PtrArray) / sizeof(PtrArray1[0]);
// There is no warning for 'sizeof(T*)/sizeof(Q)' case.
sum += sizeof(PtrArray) / sizeof(A[0]);
return sum;
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,6 @@ int Test1(const char* ptr) {
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)'
sum += sizeof(ptr) / sizeof(char);
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)'
sum += sizeof(ptr) / sizeof(ptr[0]);
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)'
sum += sizeof(int) * sizeof(char);
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious 'sizeof' by 'sizeof' multiplication
sum += sizeof(ptr) * sizeof(ptr[0]);
Expand Down Expand Up @@ -207,50 +205,57 @@ int Test5() {
C *PtrArray[10];
C *PC;

char *PChar;
int *PInt, **PPInt;
MyStruct **PPMyStruct;

int sum = 0;
sum += sizeof(&S.arr);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(&kGlocalMyStruct.arr);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(&kGlocalMyStructPtr->arr);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(S.arr + 0);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(+ S.arr);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof((int*)S.arr);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer

sum += sizeof(S.ptr);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(kGlocalMyStruct.ptr);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(kGlocalMyStructPtr->ptr);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer

sum += sizeof(&kGlocalMyStruct);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(&S);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(MyStruct*);
sum += sizeof(PMyStruct);
sum += sizeof(PS);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(PS2);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(&A10);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(PtrArray) / sizeof(PtrArray[1]);
// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(A10) / sizeof(PtrArray[0]);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
sum += sizeof(PC) / sizeof(PtrArray[0]);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
// CHECK-MESSAGES: :[[@LINE-2]]:21: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'
// CHECK-MESSAGES: :[[@LINE-3]]:23: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
sum += sizeof(ArrayC) / sizeof(PtrArray[0]);
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator
// CHECK-MESSAGES: :[[@LINE-2]]:27: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate

// These pointers do not point to aggregate types, so they are not reported in this mode:
sum += sizeof(PChar);
sum += sizeof(PInt);
sum += sizeof(PPInt);
sum += sizeof(PPMyStruct);

return sum;
}
Expand Down Expand Up @@ -293,6 +298,32 @@ bool Baz() { return sizeof(A) < N; }
// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: suspicious comparison of 'sizeof(expr)' to a constant
bool Test7() { return Baz<-1>(); }

void some_generic_function(const void *arg, int argsize);
int *IntP, **IntPP;
C *ClassP, **ClassPP;

void GenericFunctionTest() {
// The `sizeof(pointer)` checks ignore situations where the pointer is
// produced by dereferencing a pointer-to-pointer, because this is unlikely
// to be an accident and can appear in legitimate code that tries to call
// a generic function which emulates dynamic typing within C.
some_generic_function(IntPP, sizeof(*IntPP));
some_generic_function(ClassPP, sizeof(*ClassPP));
// Using `...[0]` instead of the dereference operator is another common
// variant, which is also widespread in the idiomatic array-size calculation:
// `sizeof(array) / sizeof(array[0])`.
some_generic_function(IntPP, sizeof(IntPP[0]));
some_generic_function(ClassPP, sizeof(ClassPP[0]));
// FIXME: There is a third common pattern where the generic function is
// called with `&Variable` and `sizeof(Variable)`. Right now these are
// reported by the `sizeof(pointer)` checks, but this causes some false
// positives, so it would be good to create an exception for them.
// NOTE: `sizeof(IntP)` is only reported with `WarnOnSizeOfPointer=true`.
some_generic_function(&IntPP, sizeof(IntP));
some_generic_function(&ClassPP, sizeof(ClassP));
// CHECK-MESSAGES: :[[@LINE-1]]:35: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
}

int ValidExpressions() {
int A[] = {1, 2, 3, 4};
static const char str[] = "hello";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,11 @@ std::string HelloString;
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: no header providing "std::string" is directly included [misc-include-cleaner]
int FooBarResult = foobar();
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: no header providing "foobar" is directly included [misc-include-cleaner]

namespace valid {

namespace gh93335 {
void log2() {}
} // namespace gh93335

} // namespace valid
4 changes: 3 additions & 1 deletion clang/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,9 @@ if (LLVM_COMPILER_IS_GCC_COMPATIBLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wno-long-long")
endif ()

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-nested-anon-types" )
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-nested-anon-types" )
endif ()
endif ()

# Determine HOST_LINK_VERSION on Darwin.
Expand Down
1 change: 1 addition & 0 deletions clang/docs/tools/clang-formatted-files.txt
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,7 @@ clang/tools/libclang/CXCursor.h
clang/tools/scan-build-py/tests/functional/src/include/clean-one.h
clang/unittests/Analysis/CFGBuildResult.h
clang/unittests/Analysis/MacroExpansionContextTest.cpp
clang/unittests/Analysis/FlowSensitive/ASTOpsTest.cpp
clang/unittests/Analysis/FlowSensitive/CNFFormula.cpp
clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp
Expand Down
25 changes: 21 additions & 4 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,17 +318,23 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
if (DiscardResult)
return this->discard(SubExpr);

std::optional<PrimType> FromT = classify(SubExpr->getType());
QualType SubExprTy = SubExpr->getType();
std::optional<PrimType> FromT = classify(SubExprTy);
std::optional<PrimType> ToT = classify(CE->getType());
if (!FromT || !ToT)
return false;

assert(isPtrType(*FromT));
assert(isPtrType(*ToT));
if (FromT == ToT) {
if (SubExpr->getType()->isVoidPointerType())
return this->visit(SubExpr) && this->emitVoidPtrCast(CE);
return this->delegate(SubExpr);
if (CE->getType()->isVoidPointerType())
return this->delegate(SubExpr);

if (!this->visit(SubExpr))
return false;
if (FromT == PT_Ptr)
return this->emitPtrPtrCast(SubExprTy->isVoidPointerType(), CE);
return true;
}

if (!this->visit(SubExpr))
Expand Down Expand Up @@ -1682,6 +1688,17 @@ bool ByteCodeExprGen<Emitter>::VisitObjCStringLiteral(
return this->delegate(E->getString());
}

template <class Emitter>
bool ByteCodeExprGen<Emitter>::VisitObjCEncodeExpr(const ObjCEncodeExpr *E) {
auto &A = Ctx.getASTContext();
std::string Str;
A.getObjCEncodingForType(E->getEncodedType(), Str);
StringLiteral *SL =
StringLiteral::Create(A, Str, StringLiteralKind::Ordinary,
/*Pascal=*/false, E->getType(), E->getAtLoc());
return this->delegate(SL);
}

template <class Emitter>
bool ByteCodeExprGen<Emitter>::VisitSYCLUniqueStableNameExpr(
const SYCLUniqueStableNameExpr *E) {
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/ByteCodeExprGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
bool VisitAbstractConditionalOperator(const AbstractConditionalOperator *E);
bool VisitStringLiteral(const StringLiteral *E);
bool VisitObjCStringLiteral(const ObjCStringLiteral *E);
bool VisitObjCEncodeExpr(const ObjCEncodeExpr *E);
bool VisitSYCLUniqueStableNameExpr(const SYCLUniqueStableNameExpr *E);
bool VisitCharacterLiteral(const CharacterLiteral *E);
bool VisitCompoundAssignOperator(const CompoundAssignOperator *E);
Expand Down
23 changes: 19 additions & 4 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -1980,10 +1980,25 @@ static inline bool CastPointerIntegralAPS(InterpState &S, CodePtr OpPC,
return true;
}

static inline bool VoidPtrCast(InterpState &S, CodePtr OpPC) {
const SourceInfo &E = S.Current->getSource(OpPC);
S.CCEDiag(E, diag::note_constexpr_invalid_cast)
<< 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC);
static inline bool PtrPtrCast(InterpState &S, CodePtr OpPC, bool SrcIsVoidPtr) {
const auto &Ptr = S.Stk.peek<Pointer>();

if (SrcIsVoidPtr && S.getLangOpts().CPlusPlus) {
bool HasValidResult = !Ptr.isZero();

if (HasValidResult) {
// FIXME: note_constexpr_invalid_void_star_cast
} else if (!S.getLangOpts().CPlusPlus26) {
const SourceInfo &E = S.Current->getSource(OpPC);
S.CCEDiag(E, diag::note_constexpr_invalid_cast)
<< 3 << "'void *'" << S.Current->getRange(OpPC);
}
} else {
const SourceInfo &E = S.Current->getSource(OpPC);
S.CCEDiag(E, diag::note_constexpr_invalid_cast)
<< 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC);
}

return true;
}

Expand Down
27 changes: 6 additions & 21 deletions clang/lib/AST/Interp/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ class AluOpcode : Opcode {
}

class FloatOpcode : Opcode {
let Types = [];
let Args = [ArgRoundingMode];
}

Expand Down Expand Up @@ -195,17 +194,14 @@ def NoRet : Opcode {}

def Call : Opcode {
let Args = [ArgFunction, ArgUint32];
let Types = [];
}

def CallVirt : Opcode {
let Args = [ArgFunction, ArgUint32];
let Types = [];
}

def CallBI : Opcode {
let Args = [ArgFunction, ArgCallExpr];
let Types = [];
}

def CallPtr : Opcode {
Expand All @@ -214,7 +210,6 @@ def CallPtr : Opcode {

def CallVar : Opcode {
let Args = [ArgFunction, ArgUint32];
let Types = [];
}

def OffsetOf : Opcode {
Expand Down Expand Up @@ -399,8 +394,6 @@ def InitGlobalTemp : AccessOpcode {
// [Pointer] -> [Pointer]
def InitGlobalTempComp : Opcode {
let Args = [ArgLETD];
let Types = [];
let HasGroup = 0;
}
// [Value] -> []
def SetGlobal : AccessOpcode;
Expand Down Expand Up @@ -505,13 +498,9 @@ def SubPtr : Opcode {
}

// [Pointer] -> [Pointer]
def IncPtr : Opcode {
let HasGroup = 0;
}
def IncPtr : Opcode;
// [Pointer] -> [Pointer]
def DecPtr : Opcode {
let HasGroup = 0;
}
def DecPtr : Opcode;

//===----------------------------------------------------------------------===//
// Function pointers.
Expand Down Expand Up @@ -607,7 +596,6 @@ def Cast: Opcode {
}

def CastFP : Opcode {
let Types = [];
let Args = [ArgFltSemantics, ArgRoundingMode];
}

Expand Down Expand Up @@ -642,12 +630,10 @@ def CastFloatingIntegral : Opcode {
}

def CastFloatingIntegralAP : Opcode {
let Types = [];
let Args = [ArgUint32];
}

def CastFloatingIntegralAPS : Opcode {
let Types = [];
let Args = [ArgUint32];
}

Expand All @@ -656,16 +642,15 @@ def CastPointerIntegral : Opcode {
let HasGroup = 1;
}
def CastPointerIntegralAP : Opcode {
let Types = [];
let HasGroup = 0;
let Args = [ArgUint32];
}
def CastPointerIntegralAPS : Opcode {
let Types = [];
let HasGroup = 0;
let Args = [ArgUint32];
}
def VoidPtrCast : Opcode;
def PtrPtrCast : Opcode {
let Args = [ArgBool];

}

def DecayPtr : Opcode {
let Types = [PtrTypeClass, PtrTypeClass];
Expand Down
5 changes: 2 additions & 3 deletions clang/lib/AST/Mangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,9 +301,8 @@ void MangleContext::mangleBlock(const DeclContext *DC, const BlockDecl *BD,
} else {
assert((isa<NamedDecl>(DC) || isa<BlockDecl>(DC)) &&
"expected a NamedDecl or BlockDecl");
if (isa<BlockDecl>(DC))
for (; isa_and_nonnull<BlockDecl>(DC); DC = DC->getParent())
(void) getBlockId(cast<BlockDecl>(DC), true);
for (; isa_and_nonnull<BlockDecl>(DC); DC = DC->getParent())
(void)getBlockId(cast<BlockDecl>(DC), true);
assert((isa<TranslationUnitDecl>(DC) || isa<NamedDecl>(DC)) &&
"expected a TranslationUnitDecl or a NamedDecl");
if (const auto *CD = dyn_cast<CXXConstructorDecl>(DC))
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/MicrosoftMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -899,6 +899,8 @@ void MicrosoftCXXNameMangler::mangleFloat(llvm::APFloat Number) {
case APFloat::S_Float8E4M3FNUZ:
case APFloat::S_Float8E4M3B11FNUZ:
case APFloat::S_FloatTF32:
case APFloat::S_Float6E3M2FN:
case APFloat::S_Float6E2M3FN:
llvm_unreachable("Tried to mangle unexpected APFloat semantics");
}

Expand Down
11 changes: 7 additions & 4 deletions clang/lib/Analysis/FlowSensitive/ASTOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ getFieldsForInitListExpr(const InitListT *InitList) {
std::vector<const FieldDecl *> Fields;

if (InitList->getType()->isUnionType()) {
Fields.push_back(InitList->getInitializedFieldInUnion());
if (const FieldDecl *Field = InitList->getInitializedFieldInUnion())
Fields.push_back(Field);
return Fields;
}

Expand Down Expand Up @@ -137,9 +138,11 @@ RecordInitListHelper::RecordInitListHelper(
// it doesn't do this -- so we create an `ImplicitValueInitExpr` ourselves.
SmallVector<Expr *> InitsForUnion;
if (Ty->isUnionType() && Inits.empty()) {
assert(Fields.size() == 1);
ImplicitValueInitForUnion.emplace(Fields.front()->getType());
InitsForUnion.push_back(&*ImplicitValueInitForUnion);
assert(Fields.size() <= 1);
if (!Fields.empty()) {
ImplicitValueInitForUnion.emplace(Fields.front()->getType());
InitsForUnion.push_back(&*ImplicitValueInitForUnion);
}
Inits = InitsForUnion;
}

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ class ResultObjectVisitor : public AnalysisASTVisitor<ResultObjectVisitor> {
// below them can initialize the same object (or part of it).
if (isa<CXXConstructExpr>(E) || isa<CallExpr>(E) || isa<LambdaExpr>(E) ||
isa<CXXDefaultArgExpr>(E) || isa<CXXDefaultInitExpr>(E) ||
isa<CXXStdInitializerListExpr>(E) ||
isa<CXXStdInitializerListExpr>(E) || isa<AtomicExpr>(E) ||
// We treat `BuiltinBitCastExpr` as an "original initializer" too as
// it may not even be casting from a record type -- and even if it is,
// the two objects are in general of unrelated type.
Expand Down
16 changes: 14 additions & 2 deletions clang/test/AST/Interp/cxx23.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// UNSUPPORTED: target={{.*}}-zos{{.*}}
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=ref20,all,all20 %s
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -verify=ref23,all %s
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=ref,ref20,all,all20 %s
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -verify=ref,ref23,all %s
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=expected20,all,all20 %s -fexperimental-new-constant-interpreter
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -verify=expected23,all %s -fexperimental-new-constant-interpreter

Expand Down Expand Up @@ -200,3 +200,15 @@ namespace UndefinedThreeWay {
static_assert(!(*test_a_threeway)(A(), A())); // all-error {{static assertion expression is not an integral constant expression}} \
// all-note {{undefined function 'operator<=>' cannot be used in a constant expression}}
}

/// FIXME: The new interpreter is missing the "initializer of q is not a constant expression" diagnostics.a
/// That's because the cast from void* to int* is considered fine, but diagnosed. So we don't consider
/// q to be uninitialized.
namespace VoidCast {
constexpr void* p = nullptr;
constexpr int* q = static_cast<int*>(p); // all-error {{must be initialized by a constant expression}} \
// all-note {{cast from 'void *' is not allowed in a constant expression}} \
// ref-note {{declared here}}
static_assert(q == nullptr); // ref-error {{not an integral constant expression}} \
// ref-note {{initializer of 'q' is not a constant expression}}
}
10 changes: 10 additions & 0 deletions clang/test/AST/Interp/cxx26.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// RUN: %clang_cc1 -std=c++26 -fsyntax-only -fcxx-exceptions -verify=ref,both %s
// RUN: %clang_cc1 -std=c++26 -fsyntax-only -fcxx-exceptions -verify=expected,both %s -fexperimental-new-constant-interpreter

// both-no-diagnostics

namespace VoidCast {
constexpr void* p = nullptr;
constexpr int* q = static_cast<int*>(p);
static_assert(q == nullptr);
}
8 changes: 4 additions & 4 deletions clang/test/CodeGen/instrument-objc-method.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ @implementation ObjCClass
+ (void)initialize {
}

// BARE: @"\01+[ObjCClass load]"{{\(.*\)}} #1
// PREINLINE: declare void @llvm.dbg.declare(metadata, metadata, metadata) #1
// BARE: @"\01+[ObjCClass load]"{{\(.*\)}} #2
+ (void)load __attribute__((no_instrument_function)) {
}

// PREINLINE: @"\01-[ObjCClass dealloc]"{{\(.*\)}} #1
// BARE: @"\01-[ObjCClass dealloc]"{{\(.*\)}} #1
// PREINLINE: @"\01-[ObjCClass dealloc]"{{\(.*\)}} #2
// BARE: @"\01-[ObjCClass dealloc]"{{\(.*\)}} #2
- (void)dealloc __attribute__((no_instrument_function)) {
}

// PREINLINE: declare void @llvm.dbg.declare(metadata, metadata, metadata) #2
// PREINLINE: attributes #0 = { {{.*}}"instrument-function-entry"="__cyg_profile_func_enter"
// PREINLINE-NOT: attributes #0 = { {{.*}}"instrument-function-entry"="__cyg_profile_func_enter_bare"
// PREINLINE-NOT: attributes #2 = { {{.*}}"__cyg_profile_func_enter"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,5 +221,5 @@ void tu2(int &i) {
}
}

// CHECK: [[BW_LIKELY]] = !{!"branch_weights", !"expected", i32 2000, i32 1}
// CHECK: [[BW_UNLIKELY]] = !{!"branch_weights", !"expected", i32 1, i32 2000}
// CHECK: [[BW_LIKELY]] = !{!"branch_weights", i32 2000, i32 1}
// CHECK: [[BW_UNLIKELY]] = !{!"branch_weights", i32 1, i32 2000}
1 change: 1 addition & 0 deletions clang/test/CodeGenObjC/encode-test-3.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -triple=i686-apple-darwin9 -emit-llvm -o - %s | FileCheck %s
// RUN: %clang_cc1 -triple=i686-apple-darwin9 -emit-llvm -o - %s -fexperimental-new-constant-interpreter | FileCheck %s

int main(void) {
int n;
Expand Down
31 changes: 31 additions & 0 deletions clang/test/Driver/apple-os-triples.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Test triple manipulations.

// RUN: %clang -### -c %s \
// RUN: --target=i386-apple-darwin10 -mappletvsimulator-version-min=9.0 -arch x86_64 2>&1 | \
// RUN: FileCheck %s -DARCH=x86_64 -DOS=tvos9.0.0-simulator
// RUN: %clang -### -c %s \
// RUN: --target=armv7s-apple-darwin10 -mappletvos-version-min=9.0 -arch arm64 2>&1 | \
// RUN: FileCheck %s -DARCH=arm64 -DOS=tvos9.0.0
// RUN: env TVOS_DEPLOYMENT_TARGET=9.0 %clang -### -c %s \
// RUN: -isysroot SDKs/MacOSX10.9.sdk -target i386-apple-darwin10 -arch x86_64 2>&1 | \
// RUN: FileCheck %s -DARCH=x86_64 -DOS=tvos9.0.0

// RUN: %clang -### -c %s \
// RUN: --target=x86_64-apple-driverkit19.0 2>&1 | \
// RUN: FileCheck %s -DARCH=x86_64 -DOS=driverkit19.0.0

// RUN: %clang -### -c %s \
// RUN: --target=i386-apple-darwin10 -miphonesimulator-version-min=7.0 -arch i386 2>&1 | \
// RUN: FileCheck %s -DARCH=i386 -DOS=ios7.0.0-simulator
// RUN: %clang -### -c %s \
// RUN: --target=armv7s-apple-darwin10 -miphoneos-version-min=7.0 -arch armv7s 2>&1 | \
// RUN: FileCheck %s -DARCH=thumbv7s -DOS=ios7.0.0

// RUN: %clang -### -c %s \
// RUN: --target=i386-apple-darwin10 -mwatchsimulator-version-min=2.0 -arch i386 2>&1 | \
// RUN: FileCheck %s -DARCH=i386 -DOS=watchos2.0.0-simulator
// RUN: %clang -### -c %s \
// RUN: --target=armv7s-apple-darwin10 -mwatchos-version-min=2.0 -arch armv7k 2>&1 | \
// RUN: FileCheck %s -DARCH=thumbv7k -DOS=watchos2.0.0

// CHECK: "-cc1" "-triple" "[[ARCH]]-apple-[[OS]]"
8 changes: 0 additions & 8 deletions clang/test/Driver/appletvos-version-min.c

This file was deleted.

5 changes: 0 additions & 5 deletions clang/test/Driver/driverkit-version-min.c

This file was deleted.

7 changes: 0 additions & 7 deletions clang/test/Driver/ios-version-min.c

This file was deleted.

5 changes: 0 additions & 5 deletions clang/test/Driver/riscv-arch.c
Original file line number Diff line number Diff line change
Expand Up @@ -231,11 +231,6 @@
// RV32-STD: error: invalid arch name 'rv32imqc',
// RV32-STD: unsupported standard user-level extension 'q'

// RUN: not %clang --target=riscv32-unknown-elf -march=rv32ib -### %s \
// RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV32-B %s
// RV32-B: error: invalid arch name 'rv32ib',
// RV32-B: unsupported standard user-level extension 'b'

// RUN: not %clang --target=riscv32-unknown-elf -march=rv32xabc -### %s \
// RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV32X %s
// RV32X: error: invalid arch name 'rv32xabc',
Expand Down
7 changes: 0 additions & 7 deletions clang/test/Driver/watchos-version-min.c

This file was deleted.

12 changes: 12 additions & 0 deletions clang/test/Preprocessor/riscv-target-features.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
// CHECK-NOT: __riscv_64e {{.*$}}
// CHECK-NOT: __riscv_a {{.*$}}
// CHECK-NOT: __riscv_atomic
// CHECK-NOT: __riscv_b {{.*$}}
// CHECK-NOT: __riscv_c {{.*$}}
// CHECK-NOT: __riscv_compressed {{.*$}}
// CHECK-NOT: __riscv_d {{.*$}}
Expand Down Expand Up @@ -194,6 +195,17 @@
// CHECK-A-EXT: __riscv_a 2001000{{$}}
// CHECK-A-EXT: __riscv_atomic 1

// RUN: %clang --target=riscv32-unknown-linux-gnu \
// RUN: -march=rv32ib -x c -E -dM %s \
// RUN: -o - | FileCheck --check-prefix=CHECK-B-EXT %s
// RUN: %clang --target=riscv64-unknown-linux-gnu \
// RUN: -march=rv64ib -x c -E -dM %s \
// RUN: -o - | FileCheck --check-prefix=CHECK-B-EXT %s
// CHECK-B-EXT: __riscv_b 1000000{{$}}
// CHECK-B-EXT: __riscv_zba 1000000{{$}}
// CHECK-B-EXT: __riscv_zbb 1000000{{$}}
// CHECK-B-EXT: __riscv_zbs 1000000{{$}}

// RUN: %clang --target=riscv32-unknown-linux-gnu \
// RUN: -march=rv32ic -E -dM %s \
// RUN: -o - | FileCheck --check-prefix=CHECK-C-EXT %s
Expand Down
88 changes: 88 additions & 0 deletions clang/unittests/Analysis/FlowSensitive/ASTOpsTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//===- unittests/Analysis/FlowSensitive/ASTOpsTest.cpp --------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "clang/Analysis/FlowSensitive/ASTOps.h"
#include "TestingSupport.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <memory>

namespace {

using namespace clang;
using namespace dataflow;

using ast_matchers::cxxRecordDecl;
using ast_matchers::hasName;
using ast_matchers::hasType;
using ast_matchers::initListExpr;
using ast_matchers::match;
using ast_matchers::selectFirst;
using test::findValueDecl;
using testing::IsEmpty;
using testing::UnorderedElementsAre;

TEST(ASTOpsTest, RecordInitListHelperOnEmptyUnionInitList) {
// This is a regression test: The `RecordInitListHelper` used to assert-fail
// when called for the `InitListExpr` of an empty union.
std::string Code = R"cc(
struct S {
S() : UField{} {};
union U {} UField;
};
)cc";
std::unique_ptr<ASTUnit> Unit =
tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"});
auto &ASTCtx = Unit->getASTContext();

ASSERT_EQ(ASTCtx.getDiagnostics().getClient()->getNumErrors(), 0U);

auto *InitList = selectFirst<InitListExpr>(
"init",
match(initListExpr(hasType(cxxRecordDecl(hasName("U")))).bind("init"),
ASTCtx));
ASSERT_NE(InitList, nullptr);

RecordInitListHelper Helper(InitList);
EXPECT_THAT(Helper.base_inits(), IsEmpty());
EXPECT_THAT(Helper.field_inits(), IsEmpty());
}

TEST(ASTOpsTest, ReferencedDeclsOnUnionInitList) {
// This is a regression test: `getReferencedDecls()` used to return a null
// `FieldDecl` in this case (in addition to the correct non-null `FieldDecl`)
// because `getInitializedFieldInUnion()` returns null for the syntactic form
// of the `InitListExpr`.
std::string Code = R"cc(
struct S {
S() : UField{0} {};
union U {
int I;
} UField;
};
)cc";
std::unique_ptr<ASTUnit> Unit =
tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"});
auto &ASTCtx = Unit->getASTContext();

ASSERT_EQ(ASTCtx.getDiagnostics().getClient()->getNumErrors(), 0U);

auto *InitList = selectFirst<InitListExpr>(
"init",
match(initListExpr(hasType(cxxRecordDecl(hasName("U")))).bind("init"),
ASTCtx));
ASSERT_NE(InitList, nullptr);
auto *IDecl = cast<FieldDecl>(findValueDecl(ASTCtx, "I"));

EXPECT_THAT(getReferencedDecls(*InitList).Fields,
UnorderedElementsAre(IDecl));
}

} // namespace
1 change: 1 addition & 0 deletions clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS

add_clang_unittest(ClangAnalysisFlowSensitiveTests
ArenaTest.cpp
ASTOpsTest.cpp
CFGMatchSwitchTest.cpp
ChromiumCheckModelTest.cpp
DataflowAnalysisContextTest.cpp
Expand Down
26 changes: 26 additions & 0 deletions clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3345,6 +3345,32 @@ TEST(TransferTest, ResultObjectLocationForBuiltinBitCastExpr) {
});
}

TEST(TransferTest, ResultObjectLocationForAtomicExpr) {
std::string Code = R"(
struct S {};
void target(_Atomic(S) *ptr) {
S s = __c11_atomic_load(ptr, __ATOMIC_SEQ_CST);
// [[p]]
}
)";
using ast_matchers::atomicExpr;
using ast_matchers::match;
using ast_matchers::selectFirst;
using ast_matchers::traverse;
runDataflow(
Code,
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
ASTContext &ASTCtx) {
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");

auto *Atomic = selectFirst<AtomicExpr>(
"atomic", match(atomicExpr().bind("atomic"), ASTCtx));

EXPECT_EQ(&Env.getResultObjectLocation(*Atomic),
&getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s"));
});
}

TEST(TransferTest, ResultObjectLocationPropagatesThroughConditionalOperator) {
std::string Code = R"(
struct A {
Expand Down
8 changes: 5 additions & 3 deletions flang/include/flang/Optimizer/Builder/Runtime/Inquiry.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ class FirOpBuilder;

namespace fir::runtime {

/// Generate call to general `LboundDim` runtime routine. Calls to LBOUND
/// without a DIM argument get transformed into descriptor inquiries so they're
/// not handled in the runtime.
/// Generate call to `LboundDim` runtime routine.
mlir::Value genLboundDim(fir::FirOpBuilder &builder, mlir::Location loc,
mlir::Value array, mlir::Value dim);

/// Generate call to Lbound` runtime routine.
void genLbound(fir::FirOpBuilder &builder, mlir::Location loc,
mlir::Value resultAddr, mlir::Value arrayt, mlir::Value kind);

/// Generate call to general `Ubound` runtime routine. Calls to UBOUND
/// with a DIM argument get transformed into an expression equivalent to
/// SIZE() + LBOUND() - 1, so they don't have an intrinsic in the runtime.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,73 +35,73 @@ using TypeBuilderFunc = mlir::Type (*)(mlir::MLIRContext *);

/// Get the LLVM IR dialect model for building a particular C++ type, `T`.
template <typename T>
TypeBuilderFunc getModel();
static TypeBuilderFunc getModel();

template <>
TypeBuilderFunc getModel<void *>() {
constexpr TypeBuilderFunc getModel<void *>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return mlir::LLVM::LLVMPointerType::get(context);
};
}
template <>
TypeBuilderFunc getModel<unsigned>() {
constexpr TypeBuilderFunc getModel<unsigned>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return mlir::IntegerType::get(context, sizeof(unsigned) * 8);
};
}
template <>
TypeBuilderFunc getModel<int>() {
constexpr TypeBuilderFunc getModel<int>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return mlir::IntegerType::get(context, sizeof(int) * 8);
};
}
template <>
TypeBuilderFunc getModel<unsigned long>() {
constexpr TypeBuilderFunc getModel<unsigned long>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return mlir::IntegerType::get(context, sizeof(unsigned long) * 8);
};
}
template <>
TypeBuilderFunc getModel<unsigned long long>() {
constexpr TypeBuilderFunc getModel<unsigned long long>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return mlir::IntegerType::get(context, sizeof(unsigned long long) * 8);
};
}
template <>
TypeBuilderFunc getModel<long long>() {
constexpr TypeBuilderFunc getModel<long long>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return mlir::IntegerType::get(context, sizeof(long long) * 8);
};
}
template <>
TypeBuilderFunc getModel<Fortran::ISO::CFI_rank_t>() {
constexpr TypeBuilderFunc getModel<Fortran::ISO::CFI_rank_t>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return mlir::IntegerType::get(context,
sizeof(Fortran::ISO::CFI_rank_t) * 8);
};
}
template <>
TypeBuilderFunc getModel<Fortran::ISO::CFI_type_t>() {
constexpr TypeBuilderFunc getModel<Fortran::ISO::CFI_type_t>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return mlir::IntegerType::get(context,
sizeof(Fortran::ISO::CFI_type_t) * 8);
};
}
template <>
TypeBuilderFunc getModel<long>() {
constexpr TypeBuilderFunc getModel<long>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return mlir::IntegerType::get(context, sizeof(long) * 8);
};
}
template <>
TypeBuilderFunc getModel<Fortran::ISO::CFI_dim_t>() {
constexpr TypeBuilderFunc getModel<Fortran::ISO::CFI_dim_t>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
auto indexTy = getModel<Fortran::ISO::CFI_index_t>()(context);
return mlir::LLVM::LLVMArrayType::get(indexTy, 3);
};
}
template <>
TypeBuilderFunc
constexpr TypeBuilderFunc
getModel<Fortran::ISO::cfi_internal::FlexibleArray<Fortran::ISO::CFI_dim_t>>() {
return getModel<Fortran::ISO::CFI_dim_t>();
}
Expand Down
32 changes: 18 additions & 14 deletions flang/lib/Optimizer/Builder/IntrinsicCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6362,16 +6362,17 @@ IntrinsicLibrary::genLbound(mlir::Type resultType,
llvm::ArrayRef<fir::ExtendedValue> args) {
assert(args.size() == 2 || args.size() == 3);
const fir::ExtendedValue &array = args[0];
if (const auto *boxValue = array.getBoxOf<fir::BoxValue>())
if (boxValue->hasAssumedRank())
TODO(loc, "intrinsic: lbound with assumed rank argument");
// Semantics builds signatures for LBOUND calls as either
// LBOUND(array, dim, [kind]) or LBOUND(array, [kind]).
const bool dimIsAbsent = args.size() == 2 || isStaticallyAbsent(args, 1);
if (array.hasAssumedRank() && dimIsAbsent)
return genAssumedRankBoundInquiry(builder, loc, resultType, args,
/*kindPos=*/1, fir::runtime::genLbound);

mlir::Type indexType = builder.getIndexType();

// Semantics builds signatures for LBOUND calls as either
// LBOUND(array, dim, [kind]) or LBOUND(array, [kind]).
if (args.size() == 2 || isStaticallyAbsent(args, 1)) {
// DIM is absent.
if (dimIsAbsent) {
// DIM is absent and the rank of array is a compile time constant.
mlir::Type lbType = fir::unwrapSequenceType(resultType);
unsigned rank = array.rank();
mlir::Type lbArrayType = fir::SequenceType::get(
Expand All @@ -6396,13 +6397,16 @@ IntrinsicLibrary::genLbound(mlir::Type resultType,
// DIM is present.
mlir::Value dim = fir::getBase(args[1]);

// If it is a compile time constant, skip the runtime call.
if (std::optional<std::int64_t> cstDim = fir::getIntIfConstant(dim)) {
mlir::Value one = builder.createIntegerConstant(loc, resultType, 1);
mlir::Value zero = builder.createIntegerConstant(loc, indexType, 0);
mlir::Value lb = computeLBOUND(builder, loc, array, *cstDim - 1, zero, one);
return builder.createConvert(loc, resultType, lb);
}
// If it is a compile time constant and the rank is known, skip the runtime
// call.
if (!array.hasAssumedRank())
if (std::optional<std::int64_t> cstDim = fir::getIntIfConstant(dim)) {
mlir::Value one = builder.createIntegerConstant(loc, resultType, 1);
mlir::Value zero = builder.createIntegerConstant(loc, indexType, 0);
mlir::Value lb =
computeLBOUND(builder, loc, array, *cstDim - 1, zero, one);
return builder.createConvert(loc, resultType, lb);
}

fir::ExtendedValue box = createBoxForRuntimeBoundInquiry(loc, builder, array);
return builder.createConvert(
Expand Down
14 changes: 14 additions & 0 deletions flang/lib/Optimizer/Builder/Runtime/Inquiry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,20 @@ mlir::Value fir::runtime::genLboundDim(fir::FirOpBuilder &builder,
return builder.create<fir::CallOp>(loc, lboundFunc, args).getResult(0);
}

void fir::runtime::genLbound(fir::FirOpBuilder &builder, mlir::Location loc,
mlir::Value resultAddr, mlir::Value array,
mlir::Value kind) {
mlir::func::FuncOp func =
fir::runtime::getRuntimeFunc<mkRTKey(Lbound)>(loc, builder);
auto fTy = func.getFunctionType();
auto sourceFile = fir::factory::locationToFilename(builder, loc);
auto sourceLine =
fir::factory::locationToLineNo(builder, loc, fTy.getInput(4));
auto args = fir::runtime::createArguments(
builder, loc, fTy, resultAddr, array, kind, sourceFile, sourceLine);
builder.create<fir::CallOp>(loc, func, args).getResult(0);
}

/// Generate call to `Ubound` runtime routine. Calls to UBOUND with a DIM
/// argument get transformed into an expression equivalent to
/// SIZE() + LBOUND() - 1, so they don't have an intrinsic in the runtime.
Expand Down
2 changes: 1 addition & 1 deletion flang/lib/Optimizer/CodeGen/TypeConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
#define DEBUG_TYPE "flang-type-conversion"

#include "flang/Optimizer/CodeGen/TypeConverter.h"
#include "DescriptorModel.h"
#include "flang/Common/Fortran.h"
#include "flang/Optimizer/Builder/Todo.h" // remove when TODO's are done
#include "flang/Optimizer/CodeGen/DescriptorModel.h"
#include "flang/Optimizer/CodeGen/TBAABuilder.h"
#include "flang/Optimizer/CodeGen/Target.h"
#include "flang/Optimizer/Dialect/FIRType.h"
Expand Down
120 changes: 119 additions & 1 deletion flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,55 @@
#define DEBUG_TYPE "flang-debug-type-generator"

#include "DebugTypeGenerator.h"
#include "flang/Optimizer/CodeGen/DescriptorModel.h"
#include "flang/Optimizer/CodeGen/TypeConverter.h"
#include "flang/Optimizer/Support/DataLayout.h"
#include "mlir/Pass/Pass.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/Support/Debug.h"

namespace fir {

/// Calculate offset of any field in the descriptor.
template <int DescriptorField>
std::uint64_t getComponentOffset(const mlir::DataLayout &dl,
mlir::MLIRContext *context,
mlir::Type llvmFieldType) {
static_assert(DescriptorField > 0 && DescriptorField < 10);
mlir::Type previousFieldType =
getDescFieldTypeModel<DescriptorField - 1>()(context);
std::uint64_t previousOffset =
getComponentOffset<DescriptorField - 1>(dl, context, previousFieldType);
std::uint64_t offset = previousOffset + dl.getTypeSize(previousFieldType);
std::uint64_t fieldAlignment = dl.getTypeABIAlignment(llvmFieldType);
return llvm::alignTo(offset, fieldAlignment);
}
template <>
std::uint64_t getComponentOffset<0>(const mlir::DataLayout &dl,
mlir::MLIRContext *context,
mlir::Type llvmFieldType) {
return 0;
}

DebugTypeGenerator::DebugTypeGenerator(mlir::ModuleOp m)
: module(m), kindMapping(getKindMapping(m)) {
LLVM_DEBUG(llvm::dbgs() << "DITypeAttr generator\n");

std::optional<mlir::DataLayout> dl =
fir::support::getOrSetDataLayout(module, /*allowDefaultLayout=*/true);
if (!dl) {
mlir::emitError(module.getLoc(), "Missing data layout attribute in module");
return;
}

mlir::MLIRContext *context = module.getContext();

// The debug information requires the offset of certain fields in the
// descriptors like lower_bound and extent for each dimension.
mlir::Type llvmDimsType = getDescFieldTypeModel<kDimsPosInBox>()(context);
dimsOffset = getComponentOffset<kDimsPosInBox>(*dl, context, llvmDimsType);
dimsSize = dl->getTypeSize(llvmDimsType);
}

static mlir::LLVM::DITypeAttr genBasicType(mlir::MLIRContext *context,
Expand All @@ -37,10 +77,82 @@ static mlir::LLVM::DITypeAttr genPlaceholderType(mlir::MLIRContext *context) {
llvm::dwarf::DW_ATE_signed);
}

mlir::LLVM::DITypeAttr DebugTypeGenerator::convertBoxedSequenceType(
fir::SequenceType seqTy, mlir::LLVM::DIFileAttr fileAttr,
mlir::LLVM::DIScopeAttr scope, mlir::Location loc, bool genAllocated,
bool genAssociated) {

mlir::MLIRContext *context = module.getContext();
// FIXME: Assumed rank arrays not supported yet
if (seqTy.hasUnknownShape())
return genPlaceholderType(context);

llvm::SmallVector<mlir::LLVM::DIExpressionElemAttr> ops;
auto addOp = [&](unsigned opc, llvm::ArrayRef<uint64_t> vals) {
ops.push_back(mlir::LLVM::DIExpressionElemAttr::get(context, opc, vals));
};

addOp(llvm::dwarf::DW_OP_push_object_address, {});
addOp(llvm::dwarf::DW_OP_deref, {});

// dataLocation = *base_addr
mlir::LLVM::DIExpressionAttr dataLocation =
mlir::LLVM::DIExpressionAttr::get(context, ops);
addOp(llvm::dwarf::DW_OP_lit0, {});
addOp(llvm::dwarf::DW_OP_ne, {});

// allocated = associated = (*base_addr != 0)
mlir::LLVM::DIExpressionAttr valid =
mlir::LLVM::DIExpressionAttr::get(context, ops);
mlir::LLVM::DIExpressionAttr associated = genAllocated ? valid : nullptr;
mlir::LLVM::DIExpressionAttr allocated = genAssociated ? valid : nullptr;
ops.clear();

llvm::SmallVector<mlir::LLVM::DINodeAttr> elements;
mlir::LLVM::DITypeAttr elemTy =
convertType(seqTy.getEleTy(), fileAttr, scope, loc);
unsigned offset = dimsOffset;
const unsigned indexSize = dimsSize / 3;
for ([[maybe_unused]] auto _ : seqTy.getShape()) {
// For each dimension, find the offset of count and lower bound in the
// descriptor and generate the dwarf expression to extract it.
// FIXME: If `indexSize` happens to be bigger than address size on the
// system then we may have to change 'DW_OP_deref' here.
addOp(llvm::dwarf::DW_OP_push_object_address, {});
addOp(llvm::dwarf::DW_OP_plus_uconst,
{offset + (indexSize * kDimExtentPos)});
addOp(llvm::dwarf::DW_OP_deref, {});
// count[i] = *(base_addr + offset + (indexSize * kDimExtentPos))
// where 'offset' is dimsOffset + (i * dimsSize)
mlir::LLVM::DIExpressionAttr countAttr =
mlir::LLVM::DIExpressionAttr::get(context, ops);
ops.clear();

addOp(llvm::dwarf::DW_OP_push_object_address, {});
addOp(llvm::dwarf::DW_OP_plus_uconst,
{offset + (indexSize * kDimLowerBoundPos)});
addOp(llvm::dwarf::DW_OP_deref, {});
// lower_bound[i] = *(base_addr + offset + (indexSize * kDimLowerBoundPos))
mlir::LLVM::DIExpressionAttr lowerAttr =
mlir::LLVM::DIExpressionAttr::get(context, ops);
ops.clear();

offset += dimsSize;
mlir::LLVM::DISubrangeAttr subrangeTy = mlir::LLVM::DISubrangeAttr::get(
context, nullptr, lowerAttr, countAttr, nullptr);
elements.push_back(subrangeTy);
}
return mlir::LLVM::DICompositeTypeAttr::get(
context, llvm::dwarf::DW_TAG_array_type, /*recursive id*/ {},
/* name */ nullptr, /* file */ nullptr, /* line */ 0,
/* scope */ nullptr, elemTy, mlir::LLVM::DIFlags::Zero,
/* sizeInBits */ 0, /*alignInBits*/ 0, elements, dataLocation,
/* rank */ nullptr, allocated, associated);
}

mlir::LLVM::DITypeAttr DebugTypeGenerator::convertSequenceType(
fir::SequenceType seqTy, mlir::LLVM::DIFileAttr fileAttr,
mlir::LLVM::DIScopeAttr scope, mlir::Location loc) {

mlir::MLIRContext *context = module.getContext();
// FIXME: Only fixed sizes arrays handled at the moment.
if (seqTy.hasDynamicExtents())
Expand Down Expand Up @@ -112,6 +224,12 @@ DebugTypeGenerator::convertType(mlir::Type Ty, mlir::LLVM::DIFileAttr fileAttr,
bitWidth * 2, llvm::dwarf::DW_ATE_complex_float);
} else if (auto seqTy = mlir::dyn_cast_or_null<fir::SequenceType>(Ty)) {
return convertSequenceType(seqTy, fileAttr, scope, loc);
} else if (auto boxTy = mlir::dyn_cast_or_null<fir::BoxType>(Ty)) {
auto elTy = boxTy.getElementType();
if (auto seqTy = mlir::dyn_cast_or_null<fir::SequenceType>(elTy))
return convertBoxedSequenceType(seqTy, fileAttr, scope, loc, false,
false);
return genPlaceholderType(context);
} else {
// FIXME: These types are currently unhandled. We are generating a
// placeholder type to allow us to test supported bits.
Expand Down
12 changes: 12 additions & 0 deletions flang/lib/Optimizer/Transforms/DebugTypeGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,20 @@ class DebugTypeGenerator {
mlir::LLVM::DIFileAttr fileAttr,
mlir::LLVM::DIScopeAttr scope,
mlir::Location loc);

/// The 'genAllocated' is true when we want to generate 'allocated' field
/// in the DICompositeType. It is needed for the allocatable arrays.
/// Similarly, 'genAssociated' is used with 'pointer' type to generate
/// 'associated' field.
mlir::LLVM::DITypeAttr
convertBoxedSequenceType(fir::SequenceType seqTy,
mlir::LLVM::DIFileAttr fileAttr,
mlir::LLVM::DIScopeAttr scope, mlir::Location loc,
bool genAllocated, bool genAssociated);
mlir::ModuleOp module;
KindMapping kindMapping;
std::uint64_t dimsSize;
std::uint64_t dimsOffset;
};

} // namespace fir
Expand Down
13 changes: 13 additions & 0 deletions flang/test/Integration/debug-assumed-shape-array.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
! RUN: %flang_fc1 -emit-llvm -debug-info-kind=standalone %s -o - | FileCheck %s

subroutine ff(arr)
implicit none
integer :: arr(:, :)
return arr(1,1)
end subroutine ff

! CHECK-DAG: !DICompositeType(tag: DW_TAG_array_type{{.*}}elements: ![[ELEMS:[0-9]+]], dataLocation: !DIExpression(DW_OP_push_object_address, DW_OP_deref))
! CHECK-DAG: ![[ELEMS]] = !{![[ELEM1:[0-9]+]], ![[ELEM2:[0-9]+]]}
! CHECK-DAG: ![[ELEM1]] = !DISubrange(lowerBound: !DIExpression(DW_OP_push_object_address, DW_OP_plus_uconst, 24, DW_OP_deref), upperBound: !DIExpression(DW_OP_push_object_address, DW_OP_plus_uconst, 32, DW_OP_deref))
! CHECK-DAG: ![[ELEM2]] = !DISubrange(lowerBound: !DIExpression(DW_OP_push_object_address, DW_OP_plus_uconst, 48, DW_OP_deref), upperBound: !DIExpression(DW_OP_push_object_address, DW_OP_plus_uconst, 56, DW_OP_deref))

55 changes: 55 additions & 0 deletions flang/test/Lower/HLFIR/assumed-rank-inquiries-3.f90
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,58 @@ subroutine test_shape_2(x)
! CHECK: %[[VAL_13:.*]] = fir.box_rank %[[VAL_4]] : (!fir.box<!fir.ptr<!fir.array<*:f32>>>) -> index
! CHECK: %[[VAL_14:.*]] = fir.shape %[[VAL_13]] : (index) -> !fir.shape<1>
! CHECK: %[[VAL_15:.*]]:2 = hlfir.declare %[[VAL_12]](%[[VAL_14]]) {uniq_name = ".tmp.intrinsic_result"} : (!fir.ref<!fir.array<?xi32>>, !fir.shape<1>) -> (!fir.box<!fir.array<?xi32>>, !fir.ref<!fir.array<?xi32>>)


subroutine test_lbound(x)
real :: x(..)
call takes_integer_array(lbound(x))
end subroutine
! CHECK-LABEL: func.func @_QPtest_lbound(
! CHECK: %[[VAL_1:.*]] = fir.alloca !fir.array<15xi32>
! CHECK: %[[VAL_4:.*]] = arith.constant 4 : i32
! CHECK: %[[VAL_7:.*]] = fir.convert %[[VAL_1]] : (!fir.ref<!fir.array<15xi32>>) -> !fir.llvm_ptr<i8>
! CHECK: %[[VAL_8:.*]] = fir.convert %[[VAL_3:.*]] : (!fir.box<!fir.array<*:f32>>) -> !fir.box<none>
! CHECK: %[[VAL_10:.*]] = fir.call @_FortranALbound(%[[VAL_7]], %[[VAL_8]], %[[VAL_4]], %{{.*}}, %{{.*}})
! CHECK: %[[VAL_11:.*]] = fir.convert %[[VAL_1]] : (!fir.ref<!fir.array<15xi32>>) -> !fir.ref<!fir.array<?xi32>>
! CHECK: %[[VAL_12:.*]] = fir.box_rank %[[VAL_3]] : (!fir.box<!fir.array<*:f32>>) -> index
! CHECK: %[[VAL_13:.*]] = fir.shape %[[VAL_12]] : (index) -> !fir.shape<1>
! CHECK: %[[VAL_14:.*]]:2 = hlfir.declare %[[VAL_11]](%[[VAL_13]]) {uniq_name = ".tmp.intrinsic_result"} : (!fir.ref<!fir.array<?xi32>>, !fir.shape<1>) -> (!fir.box<!fir.array<?xi32>>, !fir.ref<!fir.array<?xi32>>)
! CHECK: %[[VAL_15:.*]] = arith.constant false
! CHECK: %[[VAL_16:.*]] = hlfir.as_expr %[[VAL_14]]#0 move %[[VAL_15]] : (!fir.box<!fir.array<?xi32>>, i1) -> !hlfir.expr<?xi32>
! CHECK: %[[VAL_17:.*]]:3 = hlfir.associate %[[VAL_16]](%[[VAL_13]]) {adapt.valuebyref} : (!hlfir.expr<?xi32>, !fir.shape<1>) -> (!fir.box<!fir.array<?xi32>>, !fir.ref<!fir.array<?xi32>>, i1)
! CHECK: fir.call @_QPtakes_integer_array(%[[VAL_17]]#1) fastmath<contract> : (!fir.ref<!fir.array<?xi32>>) -> ()
! CHECK: hlfir.end_associate %[[VAL_17]]#1, %[[VAL_17]]#2 : !fir.ref<!fir.array<?xi32>>, i1
! CHECK: hlfir.destroy %[[VAL_16]] : !hlfir.expr<?xi32>
! CHECK: return
! CHECK: }

subroutine test_lbound_kind(x)
real :: x(..)
call takes_integer8_array(lbound(x, kind=8))
end subroutine
! CHECK-LABEL: func.func @_QPtest_lbound_kind(
! CHECK: %[[VAL_1:.*]] = fir.alloca !fir.array<15xi64>
! CHECK: %[[VAL_4:.*]] = arith.constant 8 : i32
! CHECK: %[[VAL_7:.*]] = fir.convert %[[VAL_1]] : (!fir.ref<!fir.array<15xi64>>) -> !fir.llvm_ptr<i8>
! CHECK: %[[VAL_8:.*]] = fir.convert %[[VAL_3:.*]] : (!fir.box<!fir.array<*:f32>>) -> !fir.box<none>
! CHECK: %[[VAL_10:.*]] = fir.call @_FortranALbound(%[[VAL_7]], %[[VAL_8]], %[[VAL_4]], %{{.*}}, %{{.*}})
! CHECK: %[[VAL_11:.*]] = fir.convert %[[VAL_1]] : (!fir.ref<!fir.array<15xi64>>) -> !fir.ref<!fir.array<?xi64>>
! CHECK: %[[VAL_12:.*]] = fir.box_rank %[[VAL_3]] : (!fir.box<!fir.array<*:f32>>) -> index
! CHECK: %[[VAL_13:.*]] = fir.shape %[[VAL_12]] : (index) -> !fir.shape<1>
! CHECK: %[[VAL_14:.*]]:2 = hlfir.declare %[[VAL_11]](%[[VAL_13]]) {uniq_name = ".tmp.intrinsic_result"} : (!fir.ref<!fir.array<?xi64>>, !fir.shape<1>) -> (!fir.box<!fir.array<?xi64>>, !fir.ref<!fir.array<?xi64>>)

subroutine test_lbound_2(x)
real, pointer :: x(..)
call takes_integer_array(lbound(x))
end subroutine
! CHECK-LABEL: func.func @_QPtest_lbound_2(
! CHECK: %[[VAL_1:.*]] = fir.alloca !fir.array<15xi32>
! CHECK: %[[VAL_4:.*]] = fir.load %[[VAL_3:.*]] : !fir.ref<!fir.box<!fir.ptr<!fir.array<*:f32>>>>
! CHECK: %[[VAL_5:.*]] = arith.constant 4 : i32
! CHECK: %[[VAL_8:.*]] = fir.convert %[[VAL_1]] : (!fir.ref<!fir.array<15xi32>>) -> !fir.llvm_ptr<i8>
! CHECK: %[[VAL_9:.*]] = fir.convert %[[VAL_4]] : (!fir.box<!fir.ptr<!fir.array<*:f32>>>) -> !fir.box<none>
! CHECK: %[[VAL_11:.*]] = fir.call @_FortranALbound(%[[VAL_8]], %[[VAL_9]], %[[VAL_5]], %{{.*}}, %{{.*}})
! CHECK: %[[VAL_12:.*]] = fir.convert %[[VAL_1]] : (!fir.ref<!fir.array<15xi32>>) -> !fir.ref<!fir.array<?xi32>>
! CHECK: %[[VAL_13:.*]] = fir.box_rank %[[VAL_4]] : (!fir.box<!fir.ptr<!fir.array<*:f32>>>) -> index
! CHECK: %[[VAL_14:.*]] = fir.shape %[[VAL_13]] : (index) -> !fir.shape<1>
! CHECK: %[[VAL_15:.*]]:2 = hlfir.declare %[[VAL_12]](%[[VAL_14]]) {uniq_name = ".tmp.intrinsic_result"} : (!fir.ref<!fir.array<?xi32>>, !fir.shape<1>) -> (!fir.box<!fir.array<?xi32>>, !fir.ref<!fir.array<?xi32>>)
2 changes: 1 addition & 1 deletion flang/test/Transforms/debug-90683.fir
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

// This test checks that debug information for fir.real type works ok.

module attributes {} {
module attributes {dlti.dl_spec = #dlti.dl_spec<>} {
func.func @_QPfn1(%arg0: !fir.ref<!fir.complex<8>> {fir.bindc_name = "a"} ) {
%0 = fir.declare %arg0 {uniq_name = "_QFfn1Ea"} : (!fir.ref<!fir.complex<8>>) -> !fir.ref<!fir.complex<8>>
%1 = fir.alloca f32 {bindc_name = "abserror", uniq_name = "_QFfn1Eabserror"}
Expand Down
16 changes: 16 additions & 0 deletions flang/test/Transforms/debug-assumed-shape-array.fir
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// RUN: fir-opt --add-debug-info --mlir-print-debuginfo %s | FileCheck %s

module attributes {dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<i64, dense<64> : vector<2xi64>>, #dlti.dl_entry<!llvm.ptr<272>, dense<64> : vector<4xi64>>, #dlti.dl_entry<!llvm.ptr<271>, dense<32> : vector<4xi64>>, #dlti.dl_entry<!llvm.ptr<270>, dense<32> : vector<4xi64>>, #dlti.dl_entry<f128, dense<128> : vector<2xi64>>, #dlti.dl_entry<f80, dense<128> : vector<2xi64>>, #dlti.dl_entry<i128, dense<128> : vector<2xi64>>, #dlti.dl_entry<i8, dense<8> : vector<2xi64>>, #dlti.dl_entry<!llvm.ptr, dense<64> : vector<4xi64>>, #dlti.dl_entry<i1, dense<8> : vector<2xi64>>, #dlti.dl_entry<f16, dense<16> : vector<2xi64>>, #dlti.dl_entry<f64, dense<64> : vector<2xi64>>, #dlti.dl_entry<i32, dense<32> : vector<2xi64>>, #dlti.dl_entry<i16, dense<16> : vector<2xi64>>, #dlti.dl_entry<"dlti.stack_alignment", 128 : i64>, #dlti.dl_entry<"dlti.endianness", "little">>, fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"} {
func.func @ff_(%arg0: !fir.box<!fir.array<?x?xi32>> {fir.bindc_name = "arr"} ) {
%0 = fir.undefined !fir.dscope
%1 = fircg.ext_declare %arg0 dummy_scope %0 {uniq_name = "_QFffEarr"} : (!fir.box<!fir.array<?x?xi32>>, !fir.dscope) -> !fir.box<!fir.array<?x?xi32>> loc(#loc1)
return
} loc(#loc2)
}
#loc1 = loc("test1.f90":1:1)
#loc2 = loc("test1.f90":3:16)

// CHECK: #llvm.di_composite_type<tag = DW_TAG_array_type
// CHECK-SAME: elements = #llvm.di_subrange<lowerBound = #llvm.di_expression<[DW_OP_push_object_address, DW_OP_plus_uconst(24), DW_OP_deref]>, upperBound = #llvm.di_expression<[DW_OP_push_object_address, DW_OP_plus_uconst(32), DW_OP_deref]>>
// CHECK-SAME: #llvm.di_subrange<lowerBound = #llvm.di_expression<[DW_OP_push_object_address, DW_OP_plus_uconst(48), DW_OP_deref]>, upperBound = #llvm.di_expression<[DW_OP_push_object_address, DW_OP_plus_uconst(56), DW_OP_deref]>>
// CHECK-SAME: dataLocation = <[DW_OP_push_object_address, DW_OP_deref]>>
2 changes: 1 addition & 1 deletion flang/test/Transforms/debug-complex-1.fir
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// check conversion of complex type of different size. Both fir and mlir
// variants are checked.

module attributes {fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.target_triple = "native"} {
module attributes {dlti.dl_spec = #dlti.dl_spec<>} {
func.func @test1(%x : !fir.complex<4>) -> !fir.complex<8> {
%1 = fir.convert %x : (!fir.complex<4>) -> !fir.complex<8>
return %1 : !fir.complex<8>
Expand Down
2 changes: 1 addition & 1 deletion flang/test/Transforms/debug-fixed-array-type.fir
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// RUN: fir-opt --add-debug-info --mlir-print-debuginfo %s | FileCheck %s

module attributes {} {
module attributes {dlti.dl_spec = #dlti.dl_spec<>} {
func.func @_QQmain() attributes {fir.bindc_name = "mn"} {
%c7 = arith.constant 7 : index
%c8 = arith.constant 8 : index
Expand Down
2 changes: 1 addition & 1 deletion flang/test/Transforms/debug-line-table-existing.fir
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// REQUIRES: system-linux

// Test that there are no changes to a function with existed fused loc debug
module attributes {} {
module attributes {dlti.dl_spec = #dlti.dl_spec<>} {
func.func @_QPs1() {
return loc(#loc1)
} loc(#loc2)
Expand Down
4 changes: 2 additions & 2 deletions flang/test/Transforms/debug-line-table-inc-file.fir
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// REQUIRES: system-linux

// Test for included functions that have a different debug location than the current file
module attributes {} {
module attributes {dlti.dl_spec = #dlti.dl_spec<>} {
func.func @_QPsinc() {
return loc(#loc2)
} loc(#loc1)
Expand All @@ -19,7 +19,7 @@ module attributes {} {
#loc4 = loc("/home/user01/llvm-project/build_release/simple.f90":4:3)
#loc5 = loc("/home/user01/llvm-project/build_release/simple.f90":5:1)
// CHECK: module {
// CHECK: module
// CHECK: func.func @_QPsinc() {
// CHECK: } loc(#[[FUSED_LOC_INC_FILE:.*]])
// CHECK: func.func @_QQmain() {
Expand Down
2 changes: 1 addition & 1 deletion flang/test/Transforms/debug-line-table-inc-same-file.fir
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

// Test that there is only one FileAttribute generated for multiple functions
// in the same file.
module attributes {} {
module attributes {dlti.dl_spec = #dlti.dl_spec<>} {
func.func @_QPs1() {
return loc(#loc2)
} loc(#loc1)
Expand Down
2 changes: 1 addition & 1 deletion flang/test/Transforms/debug-line-table.fir
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// RUN: fir-opt --add-debug-info="debug-level=LineTablesOnly" --mlir-print-debuginfo %s | FileCheck %s --check-prefix=LINETABLE
// RUN: fir-opt --add-debug-info="is-optimized=true" --mlir-print-debuginfo %s | FileCheck %s --check-prefix=OPT

module attributes { fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.data_layout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", llvm.target_triple = "aarch64-unknown-linux-gnu"} {
module attributes {dlti.dl_spec = #dlti.dl_spec<>} {
func.func @_QPsb() {
return loc(#loc_sb)
} loc(#loc_sb)
Expand Down
16 changes: 8 additions & 8 deletions flang/test/Transforms/debug-local-var-2.f90
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,20 @@

! CHECK-LABEL: define {{.*}}i64 @_QFPfn1
! CHECK-SAME: (ptr %[[ARG1:.*]], ptr %[[ARG2:.*]], ptr %[[ARG3:.*]])
! CHECK-DAG: call void @llvm.dbg.declare(metadata ptr %[[ARG1]], metadata ![[A1:.*]], metadata !DIExpression())
! CHECK-DAG: call void @llvm.dbg.declare(metadata ptr %[[ARG2]], metadata ![[B1:.*]], metadata !DIExpression())
! CHECK-DAG: call void @llvm.dbg.declare(metadata ptr %[[ARG3]], metadata ![[C1:.*]], metadata !DIExpression())
! CHECK-DAG: tail call void @llvm.dbg.declare(metadata ptr %[[ARG1]], metadata ![[A1:.*]], metadata !DIExpression())
! CHECK-DAG: tail call void @llvm.dbg.declare(metadata ptr %[[ARG2]], metadata ![[B1:.*]], metadata !DIExpression())
! CHECK-DAG: tail call void @llvm.dbg.declare(metadata ptr %[[ARG3]], metadata ![[C1:.*]], metadata !DIExpression())
! CHECK-DAG: %[[AL2:.*]] = alloca i64
! CHECK-DAG: call void @llvm.dbg.declare(metadata ptr %[[AL2]], metadata ![[RES1:.*]], metadata !DIExpression())
! CHECK-DAG: tail call void @llvm.dbg.declare(metadata ptr %[[AL2]], metadata ![[RES1:.*]], metadata !DIExpression())
! CHECK-LABEL: }

! CHECK-LABEL: define {{.*}}i32 @_QFPfn2
! CHECK-SAME: (ptr %[[FN2ARG1:.*]], ptr %[[FN2ARG2:.*]], ptr %[[FN2ARG3:.*]])
! CHECK-DAG: call void @llvm.dbg.declare(metadata ptr %[[FN2ARG1]], metadata ![[A2:.*]], metadata !DIExpression())
! CHECK-DAG: call void @llvm.dbg.declare(metadata ptr %[[FN2ARG2]], metadata ![[B2:.*]], metadata !DIExpression())
! CHECK-DAG: call void @llvm.dbg.declare(metadata ptr %[[FN2ARG3]], metadata ![[C2:.*]], metadata !DIExpression())
! CHECK-DAG: tail call void @llvm.dbg.declare(metadata ptr %[[FN2ARG1]], metadata ![[A2:.*]], metadata !DIExpression())
! CHECK-DAG: tail call void @llvm.dbg.declare(metadata ptr %[[FN2ARG2]], metadata ![[B2:.*]], metadata !DIExpression())
! CHECK-DAG: tail call void @llvm.dbg.declare(metadata ptr %[[FN2ARG3]], metadata ![[C2:.*]], metadata !DIExpression())
! CHECK-DAG: %[[AL3:.*]] = alloca i32
! CHECK-DAG: call void @llvm.dbg.declare(metadata ptr %[[AL3]], metadata ![[RES2:.*]], metadata !DIExpression())
! CHECK-DAG: tail call void @llvm.dbg.declare(metadata ptr %[[AL3]], metadata ![[RES2:.*]], metadata !DIExpression())
! CHECK-LABEL: }

program mn
Expand Down
2 changes: 1 addition & 1 deletion flang/test/Transforms/debug-module-1.fir
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// RUN: fir-opt --add-debug-info --mlir-print-debuginfo %s | FileCheck %s


module attributes {} {
module attributes {dlti.dl_spec = #dlti.dl_spec<>} {
fir.global @_QMhelperEgli : i32 {
%0 = fir.zero_bits i32
fir.has_value %0 : i32
Expand Down
2 changes: 2 additions & 0 deletions libc/config/linux/aarch64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,8 @@ if(LIBC_TYPES_HAS_FLOAT16)
libc.src.math.rintf16
libc.src.math.roundf16
libc.src.math.roundevenf16
libc.src.math.totalorderf16
libc.src.math.totalordermagf16
libc.src.math.truncf16
libc.src.math.ufromfpf16
libc.src.math.ufromfpxf16
Expand Down
2 changes: 2 additions & 0 deletions libc/config/linux/x86_64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,8 @@ if(LIBC_TYPES_HAS_FLOAT16)
libc.src.math.rintf16
libc.src.math.roundf16
libc.src.math.roundevenf16
libc.src.math.totalorderf16
libc.src.math.totalordermagf16
libc.src.math.truncf16
libc.src.math.ufromfpf16
libc.src.math.ufromfpxf16
Expand Down
4 changes: 2 additions & 2 deletions libc/docs/c23.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ Additions:
* rsqrt*
* __STDC_IEC_60559_DFP__ functions (_Decimal32, _Decimal64, _Decimal128)
* compoundn*
* totalorder*
* totalordermag*
* totalorder* |check|
* totalordermag* |check|
* getpayload*
* setpayload*
* iscannonical
Expand Down
4 changes: 4 additions & 0 deletions libc/docs/math/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,10 @@ Basic Operations
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
| scalbn | |check| | |check| | |check| | | |check| | 7.12.6.19 | F.10.3.19 |
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
| totalorder | | | | |check| | | F.10.12.1 | N/A |
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
| totalordermag | | | | |check| | | F.10.12.2 | N/A |
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
| trunc | |check| | |check| | |check| | |check| | |check| | 7.12.9.9 | F.10.6.9 |
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
| ufromfp | |check| | |check| | |check| | |check| | |check| | 7.12.9.10 | F.10.6.10 |
Expand Down
4 changes: 4 additions & 0 deletions libc/spec/stdc.td
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,10 @@ def StdC : StandardSpec<"stdc"> {
FunctionSpec<"canonicalizel", RetValSpec<IntType>, [ArgSpec<LongDoubleType>, ArgSpec<LongDoubleType>]>,
GuardedFunctionSpec<"canonicalizef16", RetValSpec<IntType>, [ArgSpec<Float16Type>, ArgSpec<Float16Type>], "LIBC_TYPES_HAS_FLOAT16">,
GuardedFunctionSpec<"canonicalizef128", RetValSpec<IntType>, [ArgSpec<Float128Type>, ArgSpec<Float128Type>], "LIBC_TYPES_HAS_FLOAT128">,

GuardedFunctionSpec<"totalorderf16", RetValSpec<IntType>, [ArgSpec<Float16Ptr>, ArgSpec<Float16Ptr>], "LIBC_TYPES_HAS_FLOAT16">,

GuardedFunctionSpec<"totalordermagf16", RetValSpec<IntType>, [ArgSpec<Float16Ptr>, ArgSpec<Float16Ptr>], "LIBC_TYPES_HAS_FLOAT16">,
]
>;

Expand Down
25 changes: 25 additions & 0 deletions libc/src/__support/FPUtil/BasicOperations.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,31 @@ LIBC_INLINE int canonicalize(T &cx, const T &x) {
return 0;
}

template <typename T>
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>
totalorder(T x, T y) {
using FPBits = FPBits<T>;
FPBits x_bits(x);
FPBits y_bits(y);

using StorageType = typename FPBits::StorageType;
StorageType x_u = x_bits.uintval();
StorageType y_u = y_bits.uintval();

using signed_t = cpp::make_signed_t<StorageType>;
signed_t x_signed = static_cast<signed_t>(x_u);
signed_t y_signed = static_cast<signed_t>(y_u);

bool both_neg = (x_u & y_u & FPBits::SIGN_MASK) != 0;
return x_signed == y_signed || ((x_signed <= y_signed) != both_neg);
}

template <typename T>
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>
totalordermag(T x, T y) {
return FPBits<T>(x).abs().uintval() <= FPBits<T>(y).abs().uintval();
}

} // namespace fputil
} // namespace LIBC_NAMESPACE

Expand Down
4 changes: 4 additions & 0 deletions libc/src/math/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,10 @@ add_math_entrypoint_object(tanhf)
add_math_entrypoint_object(tgamma)
add_math_entrypoint_object(tgammaf)

add_math_entrypoint_object(totalorderf16)

add_math_entrypoint_object(totalordermagf16)

add_math_entrypoint_object(trunc)
add_math_entrypoint_object(truncf)
add_math_entrypoint_object(truncl)
Expand Down
24 changes: 24 additions & 0 deletions libc/src/math/generic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3577,3 +3577,27 @@ add_entrypoint_object(
COMPILE_OPTIONS
-O3
)

add_entrypoint_object(
totalorderf16
SRCS
totalorderf16.cpp
HDRS
../totalorderf16.h
DEPENDS
libc.src.__support.FPUtil.basic_operations
COMPILE_OPTIONS
-O3
)

add_entrypoint_object(
totalordermagf16
SRCS
totalordermagf16.cpp
HDRS
../totalordermagf16.h
DEPENDS
libc.src.__support.FPUtil.basic_operations
COMPILE_OPTIONS
-O3
)
19 changes: 19 additions & 0 deletions libc/src/math/generic/totalorderf16.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//===-- Implementation of totalorderf16 function --------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/math/totalorderf16.h"
#include "src/__support/FPUtil/BasicOperations.h"
#include "src/__support/common.h"

namespace LIBC_NAMESPACE {

LLVM_LIBC_FUNCTION(int, totalorderf16, (const float16 *x, const float16 *y)) {
return static_cast<int>(fputil::totalorder(*x, *y));
}

} // namespace LIBC_NAMESPACE
20 changes: 20 additions & 0 deletions libc/src/math/generic/totalordermagf16.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation of totalordermagf16 function -----------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/math/totalordermagf16.h"
#include "src/__support/FPUtil/BasicOperations.h"
#include "src/__support/common.h"

namespace LIBC_NAMESPACE {

LLVM_LIBC_FUNCTION(int, totalordermagf16,
(const float16 *x, const float16 *y)) {
return static_cast<int>(fputil::totalordermag(*x, *y));
}

} // namespace LIBC_NAMESPACE
20 changes: 20 additions & 0 deletions libc/src/math/totalorderf16.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation header for totalorderf16 -----------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_MATH_TOTALORDERF16_H
#define LLVM_LIBC_SRC_MATH_TOTALORDERF16_H

#include "src/__support/macros/properties/types.h"

namespace LIBC_NAMESPACE {

int totalorderf16(const float16 *x, const float16 *y);

} // namespace LIBC_NAMESPACE

#endif // LLVM_LIBC_SRC_MATH_TOTALORDERF16_H
20 changes: 20 additions & 0 deletions libc/src/math/totalordermagf16.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation header for totalordermagf16 --------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_MATH_TOTALORDERMAGF16_H
#define LLVM_LIBC_SRC_MATH_TOTALORDERMAGF16_H

#include "src/__support/macros/properties/types.h"

namespace LIBC_NAMESPACE {

int totalordermagf16(const float16 *x, const float16 *y);

} // namespace LIBC_NAMESPACE

#endif // LLVM_LIBC_SRC_MATH_TOTALORDERMAGF16_H
6 changes: 4 additions & 2 deletions libc/test/UnitTest/FPMatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,10 @@ template <typename T> struct FPTest : public Test {
LIBC_NAMESPACE::cpp::numeric_limits<StorageType>::max(); \
const T zero = FPBits::zero(Sign::POS).get_val(); \
const T neg_zero = FPBits::zero(Sign::NEG).get_val(); \
const T aNaN = FPBits::quiet_nan().get_val(); \
const T sNaN = FPBits::signaling_nan().get_val(); \
const T aNaN = FPBits::quiet_nan(Sign::POS).get_val(); \
const T neg_aNaN = FPBits::quiet_nan(Sign::NEG).get_val(); \
const T sNaN = FPBits::signaling_nan(Sign::POS).get_val(); \
const T neg_sNaN = FPBits::signaling_nan(Sign::NEG).get_val(); \
const T inf = FPBits::inf(Sign::POS).get_val(); \
const T neg_inf = FPBits::inf(Sign::NEG).get_val(); \
const T min_normal = FPBits::min_normal().get_val(); \
Expand Down
24 changes: 24 additions & 0 deletions libc/test/src/math/smoke/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3519,3 +3519,27 @@ add_fp_unittest(
libc.src.math.powf
libc.src.__support.FPUtil.fp_bits
)

add_fp_unittest(
totalorderf16_test
SUITE
libc-math-smoke-tests
SRCS
totalorderf16_test.cpp
HDRS
TotalOrderTest.h
DEPENDS
libc.src.math.totalorderf16
)

add_fp_unittest(
totalordermagf16_test
SUITE
libc-math-smoke-tests
SRCS
totalordermagf16_test.cpp
HDRS
TotalOrderMagTest.h
DEPENDS
libc.src.math.totalordermagf16
)
142 changes: 142 additions & 0 deletions libc/test/src/math/smoke/TotalOrderMagTest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
//===-- Utility class to test flavors of totalordermag ----------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LIBC_TEST_SRC_MATH_SMOKE_TOTALORDERMAGTEST_H
#define LIBC_TEST_SRC_MATH_SMOKE_TOTALORDERMAGTEST_H

#include "test/UnitTest/FEnvSafeTest.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"

template <typename T>
class TotalOrderMagTestTemplate : public LIBC_NAMESPACE::testing::FEnvSafeTest {

DECLARE_SPECIAL_CONSTANTS(T)

public:
typedef int (*TotalOrderMagFunc)(const T *, const T *);

bool funcWrapper(TotalOrderMagFunc func, T x, T y) {
return func(&x, &y) != 0;
}

void testXLesserThanY(TotalOrderMagFunc func) {
EXPECT_TRUE(funcWrapper(func, neg_inf, inf));

EXPECT_TRUE(funcWrapper(func, T(0.0), T(0.1)));
EXPECT_TRUE(funcWrapper(func, T(0.0), T(123.38)));

EXPECT_FALSE(funcWrapper(func, T(-0.1), T(0.0)));
EXPECT_FALSE(funcWrapper(func, T(-123.38), T(0.0)));

EXPECT_TRUE(funcWrapper(func, T(-0.1), T(0.1)));
EXPECT_TRUE(funcWrapper(func, T(-123.38), T(123.38)));
}

void testXGreaterThanY(TotalOrderMagFunc func) {
EXPECT_TRUE(funcWrapper(func, inf, neg_inf));

EXPECT_TRUE(funcWrapper(func, T(0.0), T(-0.1)));
EXPECT_TRUE(funcWrapper(func, T(0.0), T(-123.38)));

EXPECT_FALSE(funcWrapper(func, T(0.1), T(0.0)));
EXPECT_FALSE(funcWrapper(func, T(123.38), T(0.0)));

EXPECT_TRUE(funcWrapper(func, T(0.1), T(-0.1)));
EXPECT_TRUE(funcWrapper(func, T(123.38), T(-123.38)));
}

void testXEqualToY(TotalOrderMagFunc func) {
EXPECT_TRUE(funcWrapper(func, inf, inf));
EXPECT_TRUE(funcWrapper(func, neg_inf, neg_inf));

EXPECT_TRUE(funcWrapper(func, T(-0.0), T(0.0)));
EXPECT_TRUE(funcWrapper(func, T(0.0), T(-0.0)));

EXPECT_TRUE(funcWrapper(func, T(0.0), T(0.0)));
EXPECT_TRUE(funcWrapper(func, T(-0.0), T(-0.0)));
EXPECT_TRUE(funcWrapper(func, T(0.1), T(0.1)));
EXPECT_TRUE(funcWrapper(func, T(-0.1), T(-0.1)));
EXPECT_TRUE(funcWrapper(func, T(123.38), T(123.38)));
EXPECT_TRUE(funcWrapper(func, T(-123.38), T(-123.38)));
}

void testSingleNaN(TotalOrderMagFunc func) {
EXPECT_FALSE(funcWrapper(func, neg_aNaN, T(0.0)));
EXPECT_FALSE(funcWrapper(func, neg_aNaN, T(0.1)));
EXPECT_FALSE(funcWrapper(func, neg_aNaN, T(123.38)));

EXPECT_TRUE(funcWrapper(func, T(0.0), neg_aNaN));
EXPECT_TRUE(funcWrapper(func, T(0.1), neg_aNaN));
EXPECT_TRUE(funcWrapper(func, T(123.38), neg_aNaN));

EXPECT_TRUE(funcWrapper(func, T(0.0), aNaN));
EXPECT_TRUE(funcWrapper(func, T(0.1), aNaN));
EXPECT_TRUE(funcWrapper(func, T(123.38), aNaN));

EXPECT_FALSE(funcWrapper(func, aNaN, T(0.0)));
EXPECT_FALSE(funcWrapper(func, aNaN, T(0.1)));
EXPECT_FALSE(funcWrapper(func, aNaN, T(123.38)));
}

void testNaNSigns(TotalOrderMagFunc func) {
EXPECT_TRUE(funcWrapper(func, neg_aNaN, aNaN));
EXPECT_FALSE(funcWrapper(func, neg_aNaN, sNaN));
EXPECT_TRUE(funcWrapper(func, neg_sNaN, aNaN));
EXPECT_TRUE(funcWrapper(func, neg_sNaN, sNaN));

EXPECT_TRUE(funcWrapper(func, aNaN, neg_aNaN));
EXPECT_FALSE(funcWrapper(func, aNaN, neg_sNaN));
EXPECT_TRUE(funcWrapper(func, sNaN, neg_aNaN));
EXPECT_TRUE(funcWrapper(func, sNaN, neg_sNaN));
}

void testQuietVsSignalingNaN(TotalOrderMagFunc func) {
EXPECT_FALSE(funcWrapper(func, neg_aNaN, neg_sNaN));
EXPECT_TRUE(funcWrapper(func, neg_sNaN, neg_aNaN));
EXPECT_TRUE(funcWrapper(func, sNaN, aNaN));
EXPECT_FALSE(funcWrapper(func, aNaN, sNaN));
}

void testNaNPayloads(TotalOrderMagFunc func) {
T qnan_123 = FPBits::quiet_nan(Sign::POS, 0x123).get_val();
T neg_qnan_123 = FPBits::quiet_nan(Sign::NEG, 0x123).get_val();
T snan_123 = FPBits::signaling_nan(Sign::POS, 0x123).get_val();
T neg_snan_123 = FPBits::signaling_nan(Sign::NEG, 0x123).get_val();

EXPECT_TRUE(funcWrapper(func, aNaN, aNaN));
EXPECT_TRUE(funcWrapper(func, sNaN, sNaN));
EXPECT_TRUE(funcWrapper(func, aNaN, qnan_123));
EXPECT_TRUE(funcWrapper(func, sNaN, snan_123));
EXPECT_FALSE(funcWrapper(func, qnan_123, aNaN));
EXPECT_FALSE(funcWrapper(func, snan_123, sNaN));

EXPECT_TRUE(funcWrapper(func, neg_aNaN, neg_aNaN));
EXPECT_TRUE(funcWrapper(func, neg_sNaN, neg_sNaN));
EXPECT_TRUE(funcWrapper(func, neg_aNaN, neg_qnan_123));
EXPECT_TRUE(funcWrapper(func, neg_sNaN, neg_snan_123));
EXPECT_FALSE(funcWrapper(func, neg_qnan_123, neg_aNaN));
EXPECT_FALSE(funcWrapper(func, neg_snan_123, neg_sNaN));
}
};

#define LIST_TOTALORDERMAG_TESTS(T, func) \
using LlvmLibcTotalOrderMagTest = TotalOrderMagTestTemplate<T>; \
TEST_F(LlvmLibcTotalOrderMagTest, XLesserThanY) { testXLesserThanY(&func); } \
TEST_F(LlvmLibcTotalOrderMagTest, XGreaterThanY) { \
testXGreaterThanY(&func); \
} \
TEST_F(LlvmLibcTotalOrderMagTest, XEqualToY) { testXEqualToY(&func); } \
TEST_F(LlvmLibcTotalOrderMagTest, SingleNaN) { testSingleNaN(&func); } \
TEST_F(LlvmLibcTotalOrderMagTest, NaNSigns) { testNaNSigns(&func); } \
TEST_F(LlvmLibcTotalOrderMagTest, QuietVsSignalingNaN) { \
testQuietVsSignalingNaN(&func); \
} \
TEST_F(LlvmLibcTotalOrderMagTest, NaNPayloads) { testNaNPayloads(&func); }

#endif // LIBC_TEST_SRC_MATH_SMOKE_TOTALORDERMAGTEST_H
138 changes: 138 additions & 0 deletions libc/test/src/math/smoke/TotalOrderTest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
//===-- Utility class to test different flavors of totalorder ---*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LIBC_TEST_SRC_MATH_SMOKE_TOTALORDERTEST_H
#define LIBC_TEST_SRC_MATH_SMOKE_TOTALORDERTEST_H

#include "test/UnitTest/FEnvSafeTest.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"

template <typename T>
class TotalOrderTestTemplate : public LIBC_NAMESPACE::testing::FEnvSafeTest {

DECLARE_SPECIAL_CONSTANTS(T)

public:
typedef int (*TotalOrderFunc)(const T *, const T *);

bool funcWrapper(TotalOrderFunc func, T x, T y) { return func(&x, &y) != 0; }

void testXLesserThanY(TotalOrderFunc func) {
EXPECT_TRUE(funcWrapper(func, neg_inf, inf));

EXPECT_TRUE(funcWrapper(func, T(0.0), T(0.1)));
EXPECT_TRUE(funcWrapper(func, T(0.0), T(123.38)));

EXPECT_TRUE(funcWrapper(func, T(-0.1), T(0.0)));
EXPECT_TRUE(funcWrapper(func, T(-123.38), T(0.0)));

EXPECT_TRUE(funcWrapper(func, T(-0.1), T(0.1)));
EXPECT_TRUE(funcWrapper(func, T(-123.38), T(123.38)));
}

void testXGreaterThanY(TotalOrderFunc func) {
EXPECT_FALSE(funcWrapper(func, inf, neg_inf));

EXPECT_FALSE(funcWrapper(func, T(0.0), T(-0.1)));
EXPECT_FALSE(funcWrapper(func, T(0.0), T(-123.38)));

EXPECT_FALSE(funcWrapper(func, T(0.1), T(0.0)));
EXPECT_FALSE(funcWrapper(func, T(123.38), T(0.0)));

EXPECT_FALSE(funcWrapper(func, T(0.1), T(-0.1)));
EXPECT_FALSE(funcWrapper(func, T(123.38), T(-123.38)));
}

void testXEqualToY(TotalOrderFunc func) {
EXPECT_TRUE(funcWrapper(func, inf, inf));
EXPECT_TRUE(funcWrapper(func, neg_inf, neg_inf));

EXPECT_TRUE(funcWrapper(func, T(-0.0), T(0.0)));
EXPECT_FALSE(funcWrapper(func, T(0.0), T(-0.0)));

EXPECT_TRUE(funcWrapper(func, T(0.0), T(0.0)));
EXPECT_TRUE(funcWrapper(func, T(-0.0), T(-0.0)));
EXPECT_TRUE(funcWrapper(func, T(0.1), T(0.1)));
EXPECT_TRUE(funcWrapper(func, T(-0.1), T(-0.1)));
EXPECT_TRUE(funcWrapper(func, T(123.38), T(123.38)));
EXPECT_TRUE(funcWrapper(func, T(-123.38), T(-123.38)));
}

void testSingleNaN(TotalOrderFunc func) {
EXPECT_TRUE(funcWrapper(func, neg_aNaN, T(0.0)));
EXPECT_TRUE(funcWrapper(func, neg_aNaN, T(0.1)));
EXPECT_TRUE(funcWrapper(func, neg_aNaN, T(123.38)));

EXPECT_FALSE(funcWrapper(func, T(0.0), neg_aNaN));
EXPECT_FALSE(funcWrapper(func, T(0.1), neg_aNaN));
EXPECT_FALSE(funcWrapper(func, T(123.38), neg_aNaN));

EXPECT_TRUE(funcWrapper(func, T(0.0), aNaN));
EXPECT_TRUE(funcWrapper(func, T(0.1), aNaN));
EXPECT_TRUE(funcWrapper(func, T(123.38), aNaN));

EXPECT_FALSE(funcWrapper(func, aNaN, T(0.0)));
EXPECT_FALSE(funcWrapper(func, aNaN, T(0.1)));
EXPECT_FALSE(funcWrapper(func, aNaN, T(123.38)));
}

void testNaNSigns(TotalOrderFunc func) {
EXPECT_TRUE(funcWrapper(func, neg_aNaN, aNaN));
EXPECT_TRUE(funcWrapper(func, neg_aNaN, sNaN));
EXPECT_TRUE(funcWrapper(func, neg_sNaN, aNaN));
EXPECT_TRUE(funcWrapper(func, neg_sNaN, sNaN));

EXPECT_FALSE(funcWrapper(func, aNaN, neg_aNaN));
EXPECT_FALSE(funcWrapper(func, aNaN, neg_sNaN));
EXPECT_FALSE(funcWrapper(func, sNaN, neg_aNaN));
EXPECT_FALSE(funcWrapper(func, sNaN, neg_sNaN));
}

void testQuietVsSignalingNaN(TotalOrderFunc func) {
EXPECT_TRUE(funcWrapper(func, neg_aNaN, neg_sNaN));
EXPECT_FALSE(funcWrapper(func, neg_sNaN, neg_aNaN));
EXPECT_TRUE(funcWrapper(func, sNaN, aNaN));
EXPECT_FALSE(funcWrapper(func, aNaN, sNaN));
}

void testNaNPayloads(TotalOrderFunc func) {
T qnan_123 = FPBits::quiet_nan(Sign::POS, 0x123).get_val();
T neg_qnan_123 = FPBits::quiet_nan(Sign::NEG, 0x123).get_val();
T snan_123 = FPBits::signaling_nan(Sign::POS, 0x123).get_val();
T neg_snan_123 = FPBits::signaling_nan(Sign::NEG, 0x123).get_val();

EXPECT_TRUE(funcWrapper(func, aNaN, aNaN));
EXPECT_TRUE(funcWrapper(func, sNaN, sNaN));
EXPECT_TRUE(funcWrapper(func, aNaN, qnan_123));
EXPECT_TRUE(funcWrapper(func, sNaN, snan_123));
EXPECT_FALSE(funcWrapper(func, qnan_123, aNaN));
EXPECT_FALSE(funcWrapper(func, snan_123, sNaN));

EXPECT_TRUE(funcWrapper(func, neg_aNaN, neg_aNaN));
EXPECT_TRUE(funcWrapper(func, neg_sNaN, neg_sNaN));
EXPECT_FALSE(funcWrapper(func, neg_aNaN, neg_qnan_123));
EXPECT_FALSE(funcWrapper(func, neg_sNaN, neg_snan_123));
EXPECT_TRUE(funcWrapper(func, neg_qnan_123, neg_aNaN));
EXPECT_TRUE(funcWrapper(func, neg_snan_123, neg_sNaN));
}
};

#define LIST_TOTALORDER_TESTS(T, func) \
using LlvmLibcTotalOrderTest = TotalOrderTestTemplate<T>; \
TEST_F(LlvmLibcTotalOrderTest, XLesserThanY) { testXLesserThanY(&func); } \
TEST_F(LlvmLibcTotalOrderTest, XGreaterThanY) { testXGreaterThanY(&func); } \
TEST_F(LlvmLibcTotalOrderTest, XEqualToY) { testXEqualToY(&func); } \
TEST_F(LlvmLibcTotalOrderTest, SingleNaN) { testSingleNaN(&func); } \
TEST_F(LlvmLibcTotalOrderTest, NaNSigns) { testNaNSigns(&func); } \
TEST_F(LlvmLibcTotalOrderTest, QuietVsSignalingNaN) { \
testQuietVsSignalingNaN(&func); \
} \
TEST_F(LlvmLibcTotalOrderTest, NaNPayloads) { testNaNPayloads(&func); }

#endif // LIBC_TEST_SRC_MATH_SMOKE_TOTALORDERTEST_H
13 changes: 13 additions & 0 deletions libc/test/src/math/smoke/totalorderf16_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//===-- Unittests for totalorderf16 ---------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "TotalOrderTest.h"

#include "src/math/totalorderf16.h"

LIST_TOTALORDER_TESTS(float16, LIBC_NAMESPACE::totalorderf16)
13 changes: 13 additions & 0 deletions libc/test/src/math/smoke/totalordermagf16_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//===-- Unittests for totalordermagf16 ------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "TotalOrderMagTest.h"

#include "src/math/totalordermagf16.h"

LIST_TOTALORDERMAG_TESTS(float16, LIBC_NAMESPACE::totalordermagf16)
6 changes: 6 additions & 0 deletions libcxx/include/__algorithm/simd_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include <__algorithm/min.h>
#include <__bit/bit_cast.h>
#include <__bit/countl.h>
#include <__bit/countr.h>
#include <__config>
#include <__type_traits/is_arithmetic.h>
Expand Down Expand Up @@ -126,8 +127,13 @@ _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI size_t __find_first_set(__simd_vector<_T

// This has MSan disabled du to https://github.com/llvm/llvm-project/issues/85876
auto __impl = [&]<class _MaskT>(_MaskT) _LIBCPP_NO_SANITIZE("memory") noexcept {
# if defined(_LIBCPP_BIG_ENDIAN)
return std::min<size_t>(
_Np, std::__countl_zero(__builtin_bit_cast(_MaskT, __builtin_convertvector(__vec, __mask_vec))));
# else
return std::min<size_t>(
_Np, std::__countr_zero(__builtin_bit_cast(_MaskT, __builtin_convertvector(__vec, __mask_vec))));
# endif
};

if constexpr (sizeof(__mask_vec) == sizeof(uint8_t)) {
Expand Down
5 changes: 5 additions & 0 deletions lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ bool DebugNamesDWARFIndex::ProcessEntry(
DWARFDIE die = GetDIE(entry);
if (!die)
return true;
// Clang used to erroneously emit index entries for declaration DIEs in case
// when the definition is in a type unit (llvm.org/pr77696).
if (die.IsStructUnionOrClass() &&
die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0))
return true;
return callback(die);
}

Expand Down
Loading