119 changes: 115 additions & 4 deletions clang/lib/Sema/SemaInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,13 +190,35 @@ static void updateGNUCompoundLiteralRValue(Expr *E) {
}
}

static bool initializingConstexprVariable(const InitializedEntity &Entity) {
Decl *D = Entity.getDecl();
const InitializedEntity *Parent = &Entity;

while (Parent) {
D = Parent->getDecl();
Parent = Parent->getParent();
}

if (const auto *VD = dyn_cast_if_present<VarDecl>(D); VD && VD->isConstexpr())
return true;

return false;
}

static void CheckC23ConstexprInitStringLiteral(const StringLiteral *SE,
Sema &SemaRef, QualType &TT);

static void CheckStringInit(Expr *Str, QualType &DeclT, const ArrayType *AT,
Sema &S) {
Sema &S, bool CheckC23ConstexprInit = false) {
// Get the length of the string as parsed.
auto *ConstantArrayTy =
cast<ConstantArrayType>(Str->getType()->getAsArrayTypeUnsafe());
uint64_t StrLength = ConstantArrayTy->getSize().getZExtValue();

if (CheckC23ConstexprInit)
if (const StringLiteral *SL = dyn_cast<StringLiteral>(Str->IgnoreParens()))
CheckC23ConstexprInitStringLiteral(SL, S, DeclT);

if (const IncompleteArrayType *IAT = dyn_cast<IncompleteArrayType>(AT)) {
// C99 6.7.8p14. We have an array of character type with unknown size
// being initialized to a string literal.
Expand Down Expand Up @@ -1476,7 +1498,9 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity,
if (IsStringInit(expr, arrayType, SemaRef.Context) == SIF_None) {
// FIXME: Should we do this checking in verify-only mode?
if (!VerifyOnly)
CheckStringInit(expr, ElemType, arrayType, SemaRef);
CheckStringInit(expr, ElemType, arrayType, SemaRef,
SemaRef.getLangOpts().C23 &&
initializingConstexprVariable(Entity));
if (StructuredList)
UpdateStructuredListElement(StructuredList, StructuredIndex, expr);
++Index;
Expand Down Expand Up @@ -1941,7 +1965,9 @@ void InitListChecker::CheckArrayType(const InitializedEntity &Entity,
// constant for each string.
// FIXME: Should we do these checks in verify-only mode too?
if (!VerifyOnly)
CheckStringInit(IList->getInit(Index), DeclType, arrayType, SemaRef);
CheckStringInit(IList->getInit(Index), DeclType, arrayType, SemaRef,
SemaRef.getLangOpts().C23 &&
initializingConstexprVariable(Entity));
if (StructuredList) {
UpdateStructuredListElement(StructuredList, StructuredIndex,
IList->getInit(Index));
Expand Down Expand Up @@ -8377,6 +8403,9 @@ static void DiagnoseNarrowingInInitList(Sema &S,
QualType EntityType,
const Expr *PostInit);

static void CheckC23ConstexprInitConversion(Sema &S, QualType FromType,
QualType ToType, Expr *Init);

/// Provide warnings when std::move is used on construction.
static void CheckMoveOnConstruction(Sema &S, const Expr *InitExpr,
bool IsReturnStmt) {
Expand Down Expand Up @@ -9203,6 +9232,23 @@ ExprResult InitializationSequence::Perform(Sema &S,
return ExprError();
CurInit = CurInitExprRes;

if (S.getLangOpts().C23 && initializingConstexprVariable(Entity)) {
CheckC23ConstexprInitConversion(S, SourceType, Entity.getType(),
CurInit.get());

// C23 6.7.1p6: If an object or subobject declared with storage-class
// specifier constexpr has pointer, integer, or arithmetic type, any
// explicit initializer value for it shall be null, an integer
// constant expression, or an arithmetic constant expression,
// respectively.
Expr::EvalResult ER;
if (Entity.getType()->getAs<PointerType>() &&
CurInit.get()->EvaluateAsRValue(ER, S.Context) &&
!ER.Val.isNullPointer()) {
S.Diag(Kind.getLocation(), diag::err_c23_constexpr_pointer_not_null);
}
}

bool Complained;
if (S.DiagnoseAssignmentResult(ConvTy, Kind.getLocation(),
Step->Type, SourceType,
Expand All @@ -9220,7 +9266,9 @@ ExprResult InitializationSequence::Perform(Sema &S,
QualType Ty = Step->Type;
bool UpdateType = ResultType && Entity.getType()->isIncompleteArrayType();
CheckStringInit(CurInit.get(), UpdateType ? *ResultType : Ty,
S.Context.getAsArrayType(Ty), S);
S.Context.getAsArrayType(Ty), S,
S.getLangOpts().C23 &&
initializingConstexprVariable(Entity));
break;
}

Expand Down Expand Up @@ -10509,6 +10557,69 @@ static void DiagnoseNarrowingInInitList(Sema &S,
S.getLocForEndOfToken(PostInit->getEndLoc()), ")");
}

static void CheckC23ConstexprInitConversion(Sema &S, QualType FromType,
QualType ToType, Expr *Init) {
assert(S.getLangOpts().C23);
ImplicitConversionSequence ICS = S.TryImplicitConversion(
Init->IgnoreParenImpCasts(), ToType, /*SuppressUserConversions*/ false,
Sema::AllowedExplicit::None,
/*InOverloadResolution*/ false,
/*CStyle*/ false,
/*AllowObjCWritebackConversion=*/false);

if (!ICS.isStandard())
return;

APValue Value;
QualType PreNarrowingType;
// Reuse C++ narrowing check.
switch (ICS.Standard.getNarrowingKind(
S.Context, Init, Value, PreNarrowingType,
/*IgnoreFloatToIntegralConversion*/ false)) {
// The value doesn't fit.
case NK_Constant_Narrowing:
S.Diag(Init->getBeginLoc(), diag::err_c23_constexpr_init_not_representable)
<< Value.getAsString(S.Context, PreNarrowingType) << ToType;
return;

// Conversion to a narrower type.
case NK_Type_Narrowing:
S.Diag(Init->getBeginLoc(), diag::err_c23_constexpr_init_type_mismatch)
<< ToType << FromType;
return;

// Since we only reuse narrowing check for C23 constexpr variables here, we're
// not really interested in these cases.
case NK_Dependent_Narrowing:
case NK_Variable_Narrowing:
case NK_Not_Narrowing:
return;
}
llvm_unreachable("unhandled case in switch");
}

static void CheckC23ConstexprInitStringLiteral(const StringLiteral *SE,
Sema &SemaRef, QualType &TT) {
assert(SemaRef.getLangOpts().C23);
// character that string literal contains fits into TT - target type.
const ArrayType *AT = SemaRef.Context.getAsArrayType(TT);
QualType CharType = AT->getElementType();
uint32_t BitWidth = SemaRef.Context.getTypeSize(CharType);
bool isUnsigned = CharType->isUnsignedIntegerType();
llvm::APSInt Value(BitWidth, isUnsigned);
for (unsigned I = 0, N = SE->getLength(); I != N; ++I) {
int64_t C = SE->getCodeUnitS(I, SemaRef.Context.getCharWidth());
Value = C;
if (Value != C) {
SemaRef.Diag(SemaRef.getLocationOfStringLiteralByte(SE, I),
diag::err_c23_constexpr_init_not_representable)
<< C << CharType;
return;
}
}
return;
}

//===----------------------------------------------------------------------===//
// Initialization helper functions
//===----------------------------------------------------------------------===//
Expand Down
94 changes: 91 additions & 3 deletions clang/lib/Sema/SemaModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,90 @@ static std::string stringFromPath(ModuleIdPath Path) {
return Name;
}

/// Helper function for makeTransitiveImportsVisible to decide whether
/// the \param Imported module unit is in the same module with the \param
/// CurrentModule.
/// \param FoundPrimaryModuleInterface is a helper parameter to record the
/// primary module interface unit corresponding to the module \param
/// CurrentModule. Since currently it is expensive to decide whether two module
/// units come from the same module by comparing the module name.
static bool
isImportingModuleUnitFromSameModule(Module *Imported, Module *CurrentModule,
Module *&FoundPrimaryModuleInterface) {
if (!Imported->isNamedModule())
return false;

// The a partition unit we're importing must be in the same module of the
// current module.
if (Imported->isModulePartition())
return true;

// If we found the primary module interface during the search process, we can
// return quickly to avoid expensive string comparison.
if (FoundPrimaryModuleInterface)
return Imported == FoundPrimaryModuleInterface;

if (!CurrentModule)
return false;

// Then the imported module must be a primary module interface unit. It
// is only allowed to import the primary module interface unit from the same
// module in the implementation unit and the implementation partition unit.

// Since we'll handle implementation unit above. We can only care
// about the implementation partition unit here.
if (!CurrentModule->isModulePartitionImplementation())
return false;

if (Imported->getPrimaryModuleInterfaceName() ==
CurrentModule->getPrimaryModuleInterfaceName()) {
assert(!FoundPrimaryModuleInterface ||
FoundPrimaryModuleInterface == Imported);
FoundPrimaryModuleInterface = Imported;
return true;
}

return false;
}

/// [module.import]p7:
/// Additionally, when a module-import-declaration in a module unit of some
/// module M imports another module unit U of M, it also imports all
/// translation units imported by non-exported module-import-declarations in
/// the module unit purview of U. These rules can in turn lead to the
/// importation of yet more translation units.
static void
makeTransitiveImportsVisible(VisibleModuleSet &VisibleModules, Module *Imported,
Module *CurrentModule, SourceLocation ImportLoc,
bool IsImportingPrimaryModuleInterface = false) {
assert(Imported->isNamedModule() &&
"'makeTransitiveImportsVisible()' is intended for standard C++ named "
"modules only.");

llvm::SmallVector<Module *, 4> Worklist;
Worklist.push_back(Imported);

Module *FoundPrimaryModuleInterface =
IsImportingPrimaryModuleInterface ? Imported : nullptr;

while (!Worklist.empty()) {
Module *Importing = Worklist.pop_back_val();

if (VisibleModules.isVisible(Importing))
continue;

// FIXME: The ImportLoc here is not meaningful. It may be problematic if we
// use the sourcelocation loaded from the visible modules.
VisibleModules.setVisible(Importing, ImportLoc);

if (isImportingModuleUnitFromSameModule(Importing, CurrentModule,
FoundPrimaryModuleInterface))
for (Module *TransImported : Importing->Imports)
if (!VisibleModules.isVisible(TransImported))
Worklist.push_back(TransImported);
}
}

Sema::DeclGroupPtrTy
Sema::ActOnGlobalModuleFragmentDecl(SourceLocation ModuleLoc) {
// We start in the global module;
Expand Down Expand Up @@ -396,8 +480,8 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
// and return the import decl to be added to the current TU.
if (Interface) {

VisibleModules.setVisible(Interface, ModuleLoc);
VisibleModules.makeTransitiveImportsVisible(Interface, ModuleLoc);
makeTransitiveImportsVisible(VisibleModules, Interface, Mod, ModuleLoc,
/*IsImportingPrimaryModuleInterface=*/true);

// Make the import decl for the interface in the impl module.
ImportDecl *Import = ImportDecl::Create(Context, CurContext, ModuleLoc,
Expand Down Expand Up @@ -554,7 +638,11 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
if (Mod->isHeaderUnit())
Diag(ImportLoc, diag::warn_experimental_header_unit);

VisibleModules.setVisible(Mod, ImportLoc);
if (Mod->isNamedModule())
makeTransitiveImportsVisible(VisibleModules, Mod, getCurrentModule(),
ImportLoc);
else
VisibleModules.setVisible(Mod, ImportLoc);

checkModuleImportContext(*this, Mod, ImportLoc, CurContext);

Expand Down
65 changes: 55 additions & 10 deletions clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,8 @@ static const Expr *IgnoreNarrowingConversion(ASTContext &Ctx,
NarrowingKind StandardConversionSequence::getNarrowingKind(
ASTContext &Ctx, const Expr *Converted, APValue &ConstantValue,
QualType &ConstantType, bool IgnoreFloatToIntegralConversion) const {
assert(Ctx.getLangOpts().CPlusPlus && "narrowing check outside C++");
assert((Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23) &&
"narrowing check outside C++");

// C++11 [dcl.init.list]p7:
// A narrowing conversion is an implicit conversion ...
Expand Down Expand Up @@ -414,20 +415,41 @@ NarrowingKind StandardConversionSequence::getNarrowingKind(
if (Initializer->isValueDependent())
return NK_Dependent_Narrowing;

if (Initializer->isCXX11ConstantExpr(Ctx, &ConstantValue)) {
Expr::EvalResult R;
if ((Ctx.getLangOpts().C23 && Initializer->EvaluateAsRValue(R, Ctx)) ||
Initializer->isCXX11ConstantExpr(Ctx, &ConstantValue)) {
// Constant!
if (Ctx.getLangOpts().C23)
ConstantValue = R.Val;
assert(ConstantValue.isFloat());
llvm::APFloat FloatVal = ConstantValue.getFloat();
// Convert the source value into the target type.
bool ignored;
llvm::APFloat::opStatus ConvertStatus = FloatVal.convert(
Ctx.getFloatTypeSemantics(ToType),
llvm::APFloat::rmNearestTiesToEven, &ignored);
// If there was no overflow, the source value is within the range of
// values that can be represented.
if (ConvertStatus & llvm::APFloat::opOverflow) {
ConstantType = Initializer->getType();
return NK_Constant_Narrowing;
llvm::APFloat Converted = FloatVal;
llvm::APFloat::opStatus ConvertStatus =
Converted.convert(Ctx.getFloatTypeSemantics(ToType),
llvm::APFloat::rmNearestTiesToEven, &ignored);
Converted.convert(Ctx.getFloatTypeSemantics(FromType),
llvm::APFloat::rmNearestTiesToEven, &ignored);
if (Ctx.getLangOpts().C23) {
if (FloatVal.isNaN() && Converted.isNaN() &&
!FloatVal.isSignaling() && !Converted.isSignaling()) {
// Quiet NaNs are considered the same value, regardless of
// payloads.
return NK_Not_Narrowing;
}
// For normal values, check exact equality.
if (!Converted.bitwiseIsEqual(FloatVal)) {
ConstantType = Initializer->getType();
return NK_Constant_Narrowing;
}
} else {
// If there was no overflow, the source value is within the range of
// values that can be represented.
if (ConvertStatus & llvm::APFloat::opOverflow) {
ConstantType = Initializer->getType();
return NK_Constant_Narrowing;
}
}
} else {
return NK_Variable_Narrowing;
Expand Down Expand Up @@ -494,7 +516,30 @@ NarrowingKind StandardConversionSequence::getNarrowingKind(
}
return NK_Not_Narrowing;
}
case ICK_Complex_Real:
if (FromType->isComplexType() && !ToType->isComplexType())
return NK_Type_Narrowing;
return NK_Not_Narrowing;

case ICK_Floating_Promotion:
if (Ctx.getLangOpts().C23) {
const Expr *Initializer = IgnoreNarrowingConversion(Ctx, Converted);
Expr::EvalResult R;
if (Initializer->EvaluateAsRValue(R, Ctx)) {
ConstantValue = R.Val;
assert(ConstantValue.isFloat());
llvm::APFloat FloatVal = ConstantValue.getFloat();
// C23 6.7.3p6 If the initializer has real type and a signaling NaN
// value, the unqualified versions of the type of the initializer and
// the corresponding real type of the object declared shall be
// compatible.
if (FloatVal.isNaN() && FloatVal.isSignaling()) {
ConstantType = Initializer->getType();
return NK_Constant_Narrowing;
}
}
}
return NK_Not_Narrowing;
default:
// Other kinds of conversions are not narrowings.
return NK_Not_Narrowing;
Expand Down
87 changes: 87 additions & 0 deletions clang/test/C/C2x/n3018.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// RUN: %clang_cc1 -std=c23 -verify -triple x86_64 -pedantic -Wno-conversion -Wno-constant-conversion %s

/* WG14 N3018: Full
* The constexpr specifier for object definitions
*/

#define ULLONG_MAX (__LONG_LONG_MAX__*2ULL+1ULL)
#define UINT_MAX (__INT_MAX__ *2U +1U)

void Example0() {
constexpr unsigned int minusOne = -1;
// expected-error@-1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned int'}}
constexpr unsigned int uint_max = -1U;
constexpr double onethird = 1.0/3.0;
constexpr double onethirdtrunc = (double)(1.0/3.0);

constexpr char string[] = { "\xFF", };
constexpr unsigned char ucstring[] = { "\xFF", };
// expected-error@-1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned char'}}
constexpr char string1[] = { -1, 0, };
constexpr unsigned char ucstring1[] = { -1, 0, };
// expected-error@-1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned char'}}

// TODO: Make sure these work correctly once char8_t and _Decimal are supported
// constexpr char8_t u8string[] = { 255, 0, }; // ok
// constexpr char8_t u8string[] = { u8"\xFF", }; // ok
// constexpr _Decimal32 small = DEC64_TRUE_MIN * 0;// constraint violation
}

void Example1() {
constexpr int K = 47;
enum {
A = K,
};
constexpr int L = K;
static int b = K + 1;
int array[K];
_Static_assert(K == 47);
}

constexpr int K = 47;
static const int b = K + 1;

void Example2() {
constexpr int A = 42LL;
constexpr signed short B = ULLONG_MAX;
// expected-error@-1 {{constexpr initializer evaluates to 18446744073709551615 which is not exactly representable in type 'const short'}}
constexpr float C = 47u;

constexpr float D = 432000000;
constexpr float E = 1.0 / 3.0;
// expected-error@-1 {{constexpr initializer evaluates to 3.333333e-01 which is not exactly representable in type 'const float'}}
constexpr float F = 1.0f / 3.0f;
}


void Example3() {
constexpr static unsigned short array[] = {
3000,
300000,
// expected-error@-1 {{constexpr initializer evaluates to 300000 which is not exactly representable in type 'const unsigned short'}}
-1
// expected-error@-1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned short'}}
};

constexpr static unsigned short array1[] = {
3000,
3000,
-1
// expected-error@-1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned short'}}
};

struct S {
int x, y;
};
constexpr struct S s = {
.x = __INT_MAX__,
.y = UINT_MAX,
// expected-error@-1 {{constexpr initializer evaluates to 4294967295 which is not exactly representable in type 'int'}}
};
}

void Example4() {
struct s { void *p; };
constexpr struct s A = { nullptr };
constexpr struct s B = A;
}
25 changes: 25 additions & 0 deletions clang/test/CXX/drs/dr438.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// RUN: %clang_cc1 -std=c++98 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++11 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++14 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++17 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++20 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++23 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++2c %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK

namespace dr438 { // dr438: 2.7

void f() {
long A[2];
A[0] = 0;
A[A[0]] = 1;
}

} // namespace dr438

// CHECK-LABEL: define {{.*}} void @dr438::f()()
// CHECK: [[A:%.+]] = alloca [2 x i64]
// CHECK: {{.+}} = getelementptr inbounds [2 x i64], ptr [[A]], i64 0, i64 0
// CHECK: [[ARRAYIDX1:%.+]] = getelementptr inbounds [2 x i64], ptr [[A]], i64 0, i64 0
// CHECK-NEXT: [[TEMPIDX:%.+]] = load i64, ptr [[ARRAYIDX1]]
// CHECK-NEXT: [[ARRAYIDX2:%.+]] = getelementptr inbounds [2 x i64], ptr [[A]], i64 0, i64 [[TEMPIDX]]
// CHECK-LABEL: }
30 changes: 30 additions & 0 deletions clang/test/CXX/drs/dr439.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// RUN: %clang_cc1 -std=c++98 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++11 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++14 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++17 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++20 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++23 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++2c %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK

namespace dr439 { // dr439: 2.7

void f() {
int* p1 = new int;
const int* p2 = static_cast<const int*>(static_cast<void *>(p1));
bool b = p1 == p2; // b will have the value true.
}

} // namespace dr439

// We're checking that p2 was copied from p1, and then was carried over
// to the comparison without change.

// CHECK-LABEL: define {{.*}} void @dr439::f()()
// CHECK: [[P1:%.+]] = alloca ptr, align 8
// CHECK-NEXT: [[P2:%.+]] = alloca ptr, align 8
// CHECK: [[TEMP0:%.+]] = load ptr, ptr [[P1]]
// CHECK-NEXT: store ptr [[TEMP0:%.+]], ptr [[P2]]
// CHECK-NEXT: [[TEMP1:%.+]] = load ptr, ptr [[P1]]
// CHECK-NEXT: [[TEMP2:%.+]] = load ptr, ptr [[P2]]
// CHECK-NEXT: {{.*}} = icmp eq ptr [[TEMP1]], [[TEMP2]]
// CHECK-LABEL: }
38 changes: 38 additions & 0 deletions clang/test/CXX/drs/dr441.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// RUN: %clang_cc1 -std=c++98 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++11 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++14 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++17 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++20 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++23 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++2c %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK

namespace dr441 { // dr441: 2.7

struct A {
A() {}
};

A dynamic_init;
int i;
int& ir = i;
int* ip = &i;

} // namespace dr441

// CHECK-DAG: @dr441::dynamic_init = global %"struct.dr441::A" zeroinitializer
// CHECK-DAG: @dr441::i = global i32 0
// CHECK-DAG: @dr441::ir = constant ptr @dr441::i
// CHECK-DAG: @dr441::ip = global ptr @dr441::i
// CHECK-DAG: @llvm.global_ctors = appending global [{{.+}}] [{ {{.+}} } { {{.+}}, ptr @_GLOBAL__sub_I_dr441.cpp, {{.+}} }]

// CHECK-LABEL: define {{.*}} void @__cxx_global_var_init()
// CHECK-NEXT: entry:
// CHECK-NEXT: call void @dr441::A::A()({{.*}} @dr441::dynamic_init)
// CHECK-NEXT: ret void
// CHECK-NEXT: }

// CHECK-LABEL: define {{.*}} void @_GLOBAL__sub_I_dr441.cpp()
// CHECK-NEXT: entry:
// CHECK-NEXT: call void @__cxx_global_var_init()
// CHECK-NEXT: ret void
// CHECK-NEXT: }
33 changes: 33 additions & 0 deletions clang/test/CXX/drs/dr462.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// RUN: %clang_cc1 -std=c++98 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++11 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++14 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++17 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++20 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++23 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++2c %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK

#if __cplusplus == 199711L
#define NOTHROW throw()
#else
#define NOTHROW noexcept(true)
#endif

namespace dr462 { // dr462: 2.7

struct A {
~A() NOTHROW {}
};

extern void full_expr_fence() NOTHROW;

void f() {
const A& r = (3, A());
full_expr_fence();
}

} // namespace dr462

// CHECK-LABEL: define {{.*}} void @dr462::f()()
// CHECK: call void @dr462::full_expr_fence()()
// CHECK: call void @dr462::A::~A()
// CHECK-LABEL: }
37 changes: 37 additions & 0 deletions clang/test/CXX/drs/dr492.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// RUN: %clang_cc1 -std=c++98 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++11 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++14 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++17 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++20 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++23 %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK
// RUN: %clang_cc1 -std=c++2c %s -triple x86_64-linux-gnu -emit-llvm -o - -fexceptions -fcxx-exceptions -pedantic-errors | llvm-cxxfilt -n | FileCheck %s --check-prefixes CHECK

#if __cplusplus == 199711L
#define NOTHROW throw()
#else
#define NOTHROW noexcept(true)
#endif

namespace std {
struct type_info {
const char* name() const NOTHROW;
};
}

namespace dr492 { // dr492: 2.7

void f() {
typeid(int).name();
typeid(const int).name();
typeid(volatile int).name();
typeid(const volatile int).name();
}

} // namespace dr492

// CHECK-LABEL: define {{.*}} void @dr492::f()()
// CHECK: {{.*}} = call {{.*}} @std::type_info::name() const({{.*}} @typeinfo for int)
// CHECK-NEXT: {{.*}} = call {{.*}} @std::type_info::name() const({{.*}} @typeinfo for int)
// CHECK-NEXT: {{.*}} = call {{.*}} @std::type_info::name() const({{.*}} @typeinfo for int)
// CHECK-NEXT: {{.*}} = call {{.*}} @std::type_info::name() const({{.*}} @typeinfo for int)
// CHECK-LABEL: }
12 changes: 6 additions & 6 deletions clang/test/CXX/drs/dr4xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -698,9 +698,9 @@ namespace dr437 { // dr437: sup 1308
};
}

// dr438 FIXME write a codegen test
// dr439 FIXME write a codegen test
// dr441 FIXME write a codegen test
// dr438 is in dr438.cpp
// dr439 is in dr439.cpp
// dr441 is in dr441.cpp
// dr442: sup 348
// dr443: na

Expand Down Expand Up @@ -943,7 +943,7 @@ namespace dr460 { // dr460: yes
}

// dr461: na
// dr462 FIXME write a codegen test
// dr462 is in dr462.cpp
// dr463: na
// dr464: na
// dr465: na
Expand Down Expand Up @@ -1089,7 +1089,7 @@ namespace dr474 { // dr474: 3.4
}
}

// dr475 FIXME write a codegen test
// dr475 FIXME write a libc++abi test

namespace dr477 { // dr477: 3.5
struct A {
Expand Down Expand Up @@ -1437,7 +1437,7 @@ namespace dr491 { // dr491: dup 413
// expected-error@-1 {{excess elements in array initializer}}
}

// dr492 FIXME write a codegen test
// dr492 is in dr492.cpp

namespace dr493 { // dr493: dup 976
struct X {
Expand Down
109 changes: 109 additions & 0 deletions clang/test/Modules/transitive-import.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// RUN: rm -rf %t
// RUN: mkdir -p %t
// RUN: split-file %s %t
//
// RUN: %clang_cc1 -std=c++20 %t/Invisible.cppm -emit-module-interface -o %t/Invisible.pcm
// RUN: %clang_cc1 -std=c++20 %t/Other.cppm -emit-module-interface -fprebuilt-module-path=%t \
// RUN: -o %t/Other.pcm
// RUN: %clang_cc1 -std=c++20 %t/Another.cppm -emit-module-interface -o %t/Another.pcm
// RUN: %clang_cc1 -std=c++20 %t/A-interface.cppm -emit-module-interface \
// RUN: -fprebuilt-module-path=%t -o %t/A-interface.pcm
// RUN: %clang_cc1 -std=c++20 %t/A-interface2.cppm -emit-module-interface \
// RUN: -fprebuilt-module-path=%t -o %t/A-interface2.pcm
// RUN: %clang_cc1 -std=c++20 %t/A-interface3.cppm -emit-module-interface \
// RUN: -fprebuilt-module-path=%t -o %t/A-interface3.pcm
// RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-module-interface \
// RUN: -fprebuilt-module-path=%t -o %t/A.pcm

// RUN: %clang_cc1 -std=c++20 %t/A.cpp -fprebuilt-module-path=%t -fsyntax-only -verify
// RUN: %clang_cc1 -std=c++20 %t/A-impl.cppm -fprebuilt-module-path=%t -fsyntax-only -verify

// RUN: %clang_cc1 -std=c++20 %t/A-impl2.cppm -fprebuilt-module-path=%t -fsyntax-only -verify

//--- Invisible.cppm
export module Invisible;
export void invisible() {}

//--- Other.cppm
export module Other;
import Invisible;
export void other() {}

//--- Another.cppm
export module Another;
export void another() {}

//--- A-interface.cppm
export module A:interface;
import Other;
export void a_interface() {}

//--- A-interface2.cppm
export module A:interface2;
import Another;
export void a_interface2() {}

//--- A-interface3.cppm
export module A:interface3;
import :interface;
import :interface2;
export void a_interface3() {}

//--- A.cppm
export module A;
import Another;
import :interface;
import :interface2;
import :interface3;

export void a() {}
export void impl();

//--- A.cpp
module A;
void impl() {
a_interface();
a_interface2();
a_interface3();

other();
another();

invisible(); // expected-error {{declaration of 'invisible' must be imported from module 'Invisible' before it is required}}
// expected-note@* {{declaration here is not visible}}
}

//--- A-impl.cppm
module A:impl;
import :interface3;

void impl_part() {
a_interface();
a_interface2();
a_interface3();

other();
another();

invisible(); // expected-error {{declaration of 'invisible' must be imported from module 'Invisible' before it is required}}
// expected-note@* {{declaration here is not visible}}
}

//--- A-impl2.cppm
module A:impl2;
import A;

void impl_part2() {
a();
impl();

a_interface();
a_interface2();
a_interface3();

other();
another();

invisible(); // expected-error {{declaration of 'invisible' must be imported from module 'Invisible' before it is required}}
// expected-note@* {{declaration here is not visible}}
}
15 changes: 15 additions & 0 deletions clang/test/Parser/c23-constexpr.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// RUN: %clang_cc1 -fsyntax-only -verify=c23 -std=c23 %s -Wpre-c2x-compat
// RUN: %clang_cc1 -fsyntax-only -verify=c17 -std=c17 %s

constexpr int a = 0; // c17-error {{unknown type name 'constexpr'}} \
c23-warning {{'constexpr' is incompatible with C standards before C23}}

void func(int array[constexpr]); // c23-error {{expected expression}} \
// c17-error {{use of undeclared}}

_Atomic constexpr int b = 0; // c23-error {{constexpr variable cannot have type 'const _Atomic(int)'}} \
// c23-warning {{'constexpr' is incompatible with C standards before C23}} \
// c17-error {{unknown type name 'constexpr'}}

int static constexpr c = 1; // c17-error {{expected ';' after top level declarator}} \
// c23-warning {{'constexpr' is incompatible with C standards before C23}}
359 changes: 359 additions & 0 deletions clang/test/Sema/constexpr.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,359 @@
// RUN: %clang_cc1 -std=c23 -verify -triple x86_64 -pedantic -Wno-conversion -Wno-constant-conversion -Wno-div-by-zero %s

// Check that constexpr only applies to variables.
constexpr void f0() {} // expected-error {{'constexpr' can only be used in variable declarations}}
constexpr const int f1() { return 0; } // expected-error {{'constexpr' can only be used in variable declarations}}

constexpr struct S1 { int f; }; //expected-error {{struct cannot be marked constexpr}}
constexpr struct S2 ; // expected-error {{struct cannot be marked constexpr}}
constexpr union U1; // expected-error {{union cannot be marked constexpr}}
constexpr union U2 {int a; float b;}; // expected-error {{union cannot be marked constexpr}}
constexpr enum E1 {A = 1, B = 2} ; // expected-error {{enum cannot be marked constexpr}}
struct S3 {
static constexpr int f = 0; // expected-error {{type name does not allow storage class}}
// expected-error@-1 {{type name does not allow constexpr}}
// expected-error@-2 {{expected ';' at end}}
constexpr int f1 = 0;
// expected-error@-1 {{type name does not allow constexpr}}
// expected-error@-2 {{expected ';' at end}}
};

constexpr; // expected-error {{'constexpr' can only be used in variable declarations}}
constexpr int V1 = 3;
constexpr float V2 = 7.0;
int V3 = (constexpr)3; // expected-error {{expected expression}}

void f2() {
constexpr int a = 0;
constexpr float b = 1.7f;
}

// Check how constexpr works with other storage-class specifiers.
constexpr auto V4 = 1;
constexpr static auto V5 = 1;
constexpr static const auto V6 = 1;
constexpr static const int V7 = 1;
constexpr static int V8 = 1;
constexpr auto Ulong = 1L;
constexpr auto CompoundLiteral = (int){13};
constexpr auto DoubleCast = (double)(1 / 3);
constexpr auto String = "this is a string"; // expected-error {{constexpr pointer initializer is not null}}
constexpr signed auto Long = 1L; // expected-error {{'auto' cannot be signed or unsigned}}
_Static_assert(_Generic(Ulong, long : 1));
_Static_assert(_Generic(CompoundLiteral, int : 1));
_Static_assert(_Generic(DoubleCast, double : 1));
_Static_assert(_Generic(String, char* : 1));

typedef constexpr int Foo; // expected-error {{typedef cannot be constexpr}}
constexpr typedef int Bar; // expected-error {{typedef cannot be constexpr}}

void f3(constexpr register int P1) { // expected-error {{function parameter cannot be constexpr}}
constexpr register int V9 = 0;
constexpr register auto V10 = 0.0;
}

constexpr thread_local int V11 = 38; // expected-error {{cannot combine with previous '_Thread_local' declaration specifier}}
constexpr static thread_local double V12 = 38; // expected-error {{cannot combine with previous '_Thread_local' declaration specifier}}
constexpr extern thread_local char V13; // expected-error {{cannot combine with previous '_Thread_local' declaration specifier}}
// expected-error@-1 {{cannot combine with previous 'extern' declaration specifier}}
// expected-error@-2 {{constexpr variable declaration must be a definition}}
constexpr thread_local short V14 = 38; // expected-error {{cannot combine with previous '_Thread_local' declaration specifier}}

// Check how constexpr works with qualifiers.
constexpr _Atomic int V15 = 0; // expected-error {{constexpr variable cannot have type 'const _Atomic(int)'}}
constexpr _Atomic(int) V16 = 0; // expected-error {{constexpr variable cannot have type 'const _Atomic(int)'}}

constexpr volatile int V17 = 0; // expected-error {{constexpr variable cannot have type 'const volatile int'}}

constexpr int * restrict V18 = 0; // expected-error {{constexpr variable cannot have type 'int *const restrict'}}

constexpr extern char Oops = 1; // expected-error {{cannot combine with previous 'extern' declaration specifier}} \
// expected-warning {{'extern' variable has an initializer}}

constexpr int * restrict * Oops1 = 0;

typedef _Atomic(int) TheA;
typedef volatile short TheV;
typedef float * restrict TheR;

constexpr TheA V19[3] = {};
// expected-error@-1 {{constexpr variable cannot have type 'const TheA[3]' (aka 'const _Atomic(int)[3]')}}
constexpr TheV V20[3] = {};
// expected-error@-1 {{constexpr variable cannot have type 'const TheV[3]' (aka 'const volatile short[3]')}}
constexpr TheR V21[3] = {};
// expected-error@-1 {{constexpr variable cannot have type 'const TheR[3]' (aka 'float *restrict const[3]')}}

struct HasA {
TheA f;
int b;
};

struct HasV {
float b;
TheV f;
};

struct HasR {
short b;
int a;
TheR f;
};

constexpr struct HasA V22[2] = {};
// expected-error@-1 {{constexpr variable cannot have type 'TheA' (aka '_Atomic(int)')}}
constexpr struct HasV V23[2] = {};
// expected-error@-1 {{constexpr variable cannot have type 'TheV' (aka 'volatile short')}}
constexpr struct HasR V24[2] = {};
// expected-error@-1 {{constexpr variable cannot have type 'TheR' (aka 'float *restrict')}}

union U3 {
float a;
union {
struct HasA f;
struct HasR f1;
};
};

constexpr union U3 V25 = {};
// expected-error@-1 {{constexpr variable cannot have type 'TheA' (aka '_Atomic(int)')}}
constexpr union U3 V26[8] = {};
// expected-error@-1 {{constexpr variable cannot have type 'TheA' (aka '_Atomic(int)')}}

struct S4 {
union U3 f[3];
};

constexpr struct S4 V27 = {};
// expected-error@-1 {{constexpr variable cannot have type 'TheA' (aka '_Atomic(int)')}}
constexpr const int V28 = 28;

struct S {
union {
volatile int i;
};
int j;
};

constexpr struct S s = {}; // expected-error {{constexpr variable cannot have type 'volatile int'}}

// Check that constexpr variable must have a valid initializer which is a
// constant expression.
constexpr int V29;
// expected-error@-1 {{constexpr variable 'V29' must be initialized by a constant expression}}

struct S5 {
int f;
};

constexpr struct S5 V30;
// expected-error@-1 {{constexpr variable 'V30' must be initialized by a constant expression}}
constexpr struct S5 V31 = {};

int randomFoo() { return 7; }

constexpr float V32 = randomFoo();
// expected-error@-1 {{constexpr variable 'V32' must be initialized by a constant expression}}

const int V33 = 4;
const int V34 = 0;
const int V35 = 2;

constexpr int V36 = V33 / V34;
// expected-error@-1 {{constexpr variable 'V36' must be initialized by a constant expression}}
constexpr int V37 = V33 / V35;
// expected-error@-1 {{constexpr variable 'V37' must be initialized by a constant expression}}
constexpr int V38 = 3;
constexpr int V39 = V38 / V38;
constexpr int V40 = V38 / 2;
constexpr int V41 = V38 / 0;
// expected-error@-1 {{constexpr variable 'V41' must be initialized by a constant expression}}
// expected-note@-2 {{division by zero}}
constexpr int V42 = V38 & 0;

constexpr struct S5 V43 = { randomFoo() };
// expected-error@-1 {{constexpr variable 'V43' must be initialized by a constant expression}}
constexpr struct S5 V44 = { 0 };
constexpr struct S5 V45 = { V38 / 0 };
// expected-error@-1 {{constexpr variable 'V45' must be initialized by a constant expression}}
// expected-note@-2 {{division by zero}}

constexpr float V46[3] = {randomFoo() };
// expected-error@-1 {{constexpr variable 'V46' must be initialized by a constant expression}}
constexpr struct S5 V47[3] = {randomFoo() };
// expected-error@-1 {{constexpr variable 'V47' must be initialized by a constant expression}}

const static int V48 = V38;
constexpr static int V49 = V48;
// expected-error@-1 {{constexpr variable 'V49' must be initialized by a constant expression}}

void f4(const int P1) {
constexpr int V = P1;
// expected-error@-1 {{constexpr variable 'V' must be initialized by a constant expression}}

constexpr int V1 = 12;
constexpr const int *V2 = &V1;
// expected-error@-1 {{constexpr variable 'V2' must be initialized by a constant expression}}
}

// Check that initializer for constexpr variable should match the type of the
// variable and is exactly representable int the variable's type.

struct S6 {
unsigned char a;
};

struct S7 {
union {
float a;
};
unsigned int b;
};

struct S8 {
unsigned char a[3];
unsigned int b[3];
};

constexpr struct S8 DesigInit = {.b = {299, 7, 8}, .a = {-1, 7, 8}};
// expected-error@-1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'unsigned char'}}

void f5() {
constexpr char V50 = 300;
// expected-error@-1 {{constexpr initializer evaluates to 300 which is not exactly representable in type 'const char'}}
constexpr float V51 = 1.0 / 3.0;
// expected-error@-1 {{constexpr initializer evaluates to 3.333333e-01 which is not exactly representable in type 'const float'}}
constexpr float V52 = 0.7;
// expected-error@-1 {{constexpr initializer evaluates to 7.000000e-01 which is not exactly representable in type 'const float'}}
constexpr float V53 = 1.0f / 3.0f;
constexpr float V54 = 432000000000;
// expected-error@-1 {{constexpr initializer evaluates to 432000000000 which is not exactly representable in type 'const float'}}
constexpr unsigned char V55[] = {
"\xAF",
// expected-error@-1 {{constexpr initializer evaluates to -81 which is not exactly representable in type 'const unsigned char'}}
};

constexpr unsigned char V56[] = {
u8"\xAF",
};
constexpr struct S6 V57 = {299};
// expected-error@-1 {{constexpr initializer evaluates to 299 which is not exactly representable in type 'unsigned char'}}
constexpr struct S6 V58 = {-299};
// expected-error@-1 {{constexpr initializer evaluates to -299 which is not exactly representable in type 'unsigned char'}}
constexpr double V59 = 0.5;
constexpr double V60 = 1.0;
constexpr float V61 = V59 / V60;
constexpr double V62 = 1.7;
constexpr float V63 = V59 / V62;
// expected-error@-1 {{constexpr initializer evaluates to 2.941176e-01 which is not exactly representable in type 'const float'}}

constexpr unsigned char V64 = '\xAF';
// expected-error@-1 {{constexpr initializer evaluates to -81 which is not exactly representable in type 'const unsigned char'}}
constexpr unsigned char V65 = u8'\xAF';

constexpr char V66[3] = {300};
// expected-error@-1 {{constexpr initializer evaluates to 300 which is not exactly representable in type 'const char'}}
constexpr struct S6 V67[3] = {300};
// expected-error@-1 {{constexpr initializer evaluates to 300 which is not exactly representable in type 'unsigned char'}}

constexpr struct S7 V68 = {0.3, -1 };
// expected-error@-1 {{constexpr initializer evaluates to 3.000000e-01 which is not exactly representable in type 'float'}}
// expected-error@-2 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'unsigned int'}}
constexpr struct S7 V69 = {0.5, -1 };
// expected-error@-1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'unsigned int'}}
constexpr struct S7 V70[3] = {{123456789}};
// expected-error@-1 {{constexpr initializer evaluates to 123456789 which is not exactly representable in type 'float'}}

constexpr int V71 = 0.3;
// expected-error@-1 {{constexpr initializer for type 'const int' is of type 'double'}}
constexpr int V72 = V59;
// expected-error@-1 {{constexpr initializer for type 'const int' is of type 'const double'}}
constexpr struct S6 V73 = {V59};
// expected-error@-1 {{constexpr initializer for type 'unsigned char' is of type 'const double'}}

constexpr float V74 = 1;
constexpr float V75 = V59;
constexpr unsigned int V76[3] = {0.5};
// expected-error@-1 {{constexpr initializer for type 'const unsigned int' is of type 'double'}}

constexpr _Complex float V77 = 0;
constexpr float V78 = V77;
// expected-error@-1 {{constexpr initializer for type 'const float' is of type 'const _Complex float'}}
constexpr int V79 = V77;
// expected-error@-1 {{constexpr initializer for type 'const int' is of type 'const _Complex float'}}

}

constexpr char string[] = "test""ing this out\xFF";
constexpr unsigned char ustring[] = "test""ing this out\xFF";
// expected-error@-1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned char'}}
constexpr char u8string[] = u8"test"u8"ing this out\xFF";
// expected-error@-1 {{constexpr initializer evaluates to 255 which is not exactly representable in type 'const char'}}
constexpr unsigned char u8ustring[] = u8"test"u8"ing this out\xFF";
constexpr unsigned short uustring[] = u"test"u"ing this out\xFF";
constexpr unsigned int Ustring[] = U"test"U"ing this out\xFF";
constexpr unsigned char Arr2[6][6] = {
{"ek\xFF"}, {"ek\xFF"}
// expected-error@-1 2{{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned char'}}
};

constexpr int i = (12);
constexpr int j = (i);
constexpr unsigned jneg = (-i);
// expected-error@-1 {{constexpr initializer evaluates to -12 which is not exactly representable in type 'const unsigned int'}}

// Check that initializer for pointer constexpr variable should be null.
constexpr int V80 = 3;
constexpr const int *V81 = &V80;
// expected-error@-1 {{constexpr pointer initializer is not null}}
constexpr int *V82 = 0;
constexpr int *V83 = V82;
constexpr int *V84 = 42;
// expected-error@-1 {{constexpr variable 'V84' must be initialized by a constant expression}}
// expected-note@-2 {{this conversion is not allowed in a constant expression}}
// expected-error@-3 {{constexpr pointer initializer is not null}}
constexpr int *V85 = nullptr;

// Check that constexpr variables should not be VLAs.
void f6(const int P1) {
constexpr int V86[P1] = {};
// expected-error@-1 {{constexpr variable cannot have type 'const int[P1]'}}
const int V87 = 3;
constexpr int V88[V87] = {};
// expected-warning@-1 {{variable length array folded to constant array as an extension}}
int V89 = 7;
constexpr int V90[V89] = {};
// expected-error@-1 {{constexpr variable cannot have type 'const int[V89]'}}
}

void f7(int n, int array[n]) {
constexpr typeof(array) foo = 0; // Accepted because array is a pointer type, not a VLA type
int (*(*fp)(int n))[n];
constexpr typeof(fp) bar = 0; // expected-error {{constexpr variable cannot have type 'const typeof (fp)' (aka 'int (*(*const)(int))[n]')}}
}

// Check how constexpr works with NaNs and infinities.
#define FLT_NAN __builtin_nanf("1")
#define DBL_NAN __builtin_nan("1")
#define LD_NAN __builtin_nanf("1")
#define FLT_SNAN __builtin_nansf("1")
#define DBL_SNAN __builtin_nans("1")
#define LD_SNAN __builtin_nansl("1")
#define INF __builtin_inf()
void infsNaNs() {
// Inf and quiet NaN is always fine, signaling NaN must have the same type.
constexpr float fl0 = INF;
constexpr float fl1 = (long double)INF;
constexpr float fl2 = (long double)FLT_NAN;
constexpr float fl3 = FLT_NAN;
constexpr float fl5 = DBL_NAN;
constexpr float fl6 = LD_NAN;
constexpr float fl7 = DBL_SNAN; // expected-error {{constexpr initializer evaluates to nan which is not exactly representable in type 'const float'}}
constexpr float fl8 = LD_SNAN; // expected-error {{constexpr initializer evaluates to nan which is not exactly representable in type 'const float'}}

constexpr double db0 = FLT_NAN;
constexpr double db2 = DBL_NAN;
constexpr double db3 = DBL_SNAN;
constexpr double db4 = FLT_SNAN; // expected-error {{constexpr initializer evaluates to nan which is not exactly representable in type 'const double'}}
constexpr double db5 = LD_SNAN; // expected-error {{constexpr initializer evaluates to nan which is not exactly representable in type 'const double'}}
constexpr double db6 = INF;
}
2 changes: 1 addition & 1 deletion clang/www/c_status.html
Original file line number Diff line number Diff line change
Expand Up @@ -1201,7 +1201,7 @@ <h2 id="c2x">C23 implementation status</h2>
<tr>
<td>constexpr for object definitions</td>
<td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3018.htm">N3018</a></td>
<td class="none" align="center">No</td>
<td class="unreleased" align="center">Clang 19</td>
</tr>
<tr>
<td>Introduce storage class specifiers for compound literals</td>
Expand Down
10 changes: 5 additions & 5 deletions clang/www/cxx_dr_status.html
Original file line number Diff line number Diff line change
Expand Up @@ -2668,13 +2668,13 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td><a href="https://cplusplus.github.io/CWG/issues/438.html">438</a></td>
<td>CD2</td>
<td>Possible flaw in wording for multiple accesses to object between sequence points</td>
<td class="unknown" align="center">Unknown</td>
<td class="full" align="center">Clang 2.7</td>
</tr>
<tr id="439">
<td><a href="https://cplusplus.github.io/CWG/issues/439.html">439</a></td>
<td>CD1</td>
<td>Guarantees on casting pointer back to cv-qualified version of original type</td>
<td class="unknown" align="center">Unknown</td>
<td class="full" align="center">Clang 2.7</td>
</tr>
<tr id="440">
<td><a href="https://cplusplus.github.io/CWG/issues/440.html">440</a></td>
Expand All @@ -2686,7 +2686,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td><a href="https://cplusplus.github.io/CWG/issues/441.html">441</a></td>
<td>CD1</td>
<td>Ordering of static reference initialization</td>
<td class="unknown" align="center">Unknown</td>
<td class="full" align="center">Clang 2.7</td>
</tr>
<tr id="442">
<td><a href="https://cplusplus.github.io/CWG/issues/442.html">442</a></td>
Expand Down Expand Up @@ -2812,7 +2812,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td><a href="https://cplusplus.github.io/CWG/issues/462.html">462</a></td>
<td>CD3</td>
<td>Lifetime of temporaries bound to comma expressions</td>
<td class="unknown" align="center">Unknown</td>
<td class="full" align="center">Clang 2.7</td>
</tr>
<tr id="463">
<td><a href="https://cplusplus.github.io/CWG/issues/463.html">463</a></td>
Expand Down Expand Up @@ -2992,7 +2992,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td><a href="https://cplusplus.github.io/CWG/issues/492.html">492</a></td>
<td>CD1</td>
<td><TT>typeid</TT> constness inconsistent with example</td>
<td class="unknown" align="center">Unknown</td>
<td class="full" align="center">Clang 2.7</td>
</tr>
<tr id="493">
<td><a href="https://cplusplus.github.io/CWG/issues/493.html">493</a></td>
Expand Down
62 changes: 46 additions & 16 deletions lld/test/ELF/linkerscript/wildcards.s
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# REQUIRES: x86
# RUN: split-file %s %t
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %t/asm -o %t.o
# RUN: rm -rf %t && split-file %s %t && cd %t
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux asm -o a.o

## Default case: abc and abx included in text.
# RUN: echo "SECTIONS { \
# RUN: .text : { *(.abc .abx) } }" > %t.script
# RUN: ld.lld -o %t.out --script %t.script %t.o
# RUN: llvm-objdump --section-headers %t.out | \
# RUN: .text : { *(.abc .abx) } }" > a.t
# RUN: ld.lld -o out --script a.t a.o
# RUN: llvm-objdump --section-headers out | \
# RUN: FileCheck -check-prefix=SEC-DEFAULT %s
# SEC-DEFAULT: Sections:
# SEC-DEFAULT-NEXT: Idx Name Size
Expand All @@ -22,16 +22,16 @@

## Now replace the symbol with '?' and check that results are the same.
# RUN: echo "SECTIONS { \
# RUN: .text : { *(.abc .ab?) } }" > %t.script
# RUN: ld.lld -o %t.out --script %t.script %t.o
# RUN: llvm-objdump --section-headers %t.out | \
# RUN: .text : { *(.abc .ab?) } }" > b.t
# RUN: ld.lld -o out -T b.t a.o
# RUN: llvm-objdump --section-headers out | \
# RUN: FileCheck -check-prefix=SEC-DEFAULT %s

## Now see how replacing '?' with '*' will consume whole abcd.
# RUN: echo "SECTIONS { \
# RUN: .text : { *(.abc .ab*) } }" > %t.script
# RUN: ld.lld -o %t.out --script %t.script %t.o
# RUN: llvm-objdump --section-headers %t.out | \
# RUN: .text : { *(.abc .ab*) } }" > c.t
# RUN: ld.lld -o out --script c.t a.o
# RUN: llvm-objdump --section-headers out | \
# RUN: FileCheck -check-prefix=SEC-ALL %s
# SEC-ALL: Sections:
# SEC-ALL-NEXT: Idx Name Size
Expand All @@ -46,9 +46,9 @@

## All sections started with .a are merged.
# RUN: echo "SECTIONS { \
# RUN: .text : { *(.a*) } }" > %t.script
# RUN: ld.lld -o %t.out --script %t.script %t.o
# RUN: llvm-objdump --section-headers %t.out | \
# RUN: .text : { *(.a*) } }" > d.t
# RUN: ld.lld -o out --script d.t a.o
# RUN: llvm-objdump --section-headers out | \
# RUN: FileCheck -check-prefix=SEC-NO %s
# SEC-NO: Sections:
# SEC-NO-NEXT: Idx Name Size
Expand Down Expand Up @@ -84,10 +84,40 @@
.globl _start
_start:

#--- bracket.lds
# RUN: ld.lld -T bracket.lds a.o -o out
# RUN: llvm-objdump --section-headers out | FileCheck %s --check-prefix=SEC-DEFAULT
SECTIONS {
.text : { *([.]abc .ab[v-y] ) }
}

## Test a few non-wildcard meta characters rejected by GNU ld.

#--- lbrace.lds
# RUN: ld.lld -T lbrace.lds a.o -o out
SECTIONS {
.text : { *(.a* { ) }
}

#--- lparen.lds
## ( is recognized as a section name pattern. Note, ( is rejected by GNU ld.
# RUN: ld.lld -T %t/lparen.lds %t.o -o %t.out
# RUN: llvm-objdump --section-headers %t.out | FileCheck --check-prefix=SEC-NO %s
# RUN: ld.lld -T lparen.lds a.o -o out
# RUN: llvm-objdump --section-headers out | FileCheck --check-prefix=SEC-NO %s
SECTIONS {
.text : { *(.a* ( ) }
}

#--- rbrace.lds
# RUN: ld.lld -T rbrace.lds a.o -o out
SECTIONS {
.text : { *(.a* } ) }
}

#--- rparen.lds
# RUN: not ld.lld -T rparen.lds a.o 2>&1 | FileCheck %s --check-prefix=ERR-RPAREN --match-full-lines --strict-whitespace
# ERR-RPAREN:{{.*}}: expected filename pattern
# ERR-RPAREN-NEXT:>>> .text : { *(.a* ) ) }
# ERR-RPAREN-NEXT:>>> ^
SECTIONS {
.text : { *(.a* ) ) }
}
7 changes: 7 additions & 0 deletions llvm/include/llvm/Transforms/Utils/Local.h
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,13 @@ Value *salvageDebugInfoImpl(Instruction &I, uint64_t CurrentLocOps,
bool replaceAllDbgUsesWith(Instruction &From, Value &To, Instruction &DomPoint,
DominatorTree &DT);

/// If a terminator in an unreachable basic block has an operand of type
/// Instruction, transform it into poison. Return true if any operands
/// are changed to poison. Original Values prior to being changed to poison
/// are returned in \p PoisonedValues.
bool handleUnreachableTerminator(Instruction *I,
SmallVectorImpl<Value *> &PoisonedValues);

/// Remove all instructions from a basic block other than its terminator
/// and any present EH pad instructions. Returns a pair where the first element
/// is the number of instructions (excluding debug info intrinsics) that have
Expand Down
9 changes: 7 additions & 2 deletions llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1285,7 +1285,11 @@ Instruction *InstCombinerImpl::foldSelectValueEquivalence(SelectInst &Sel,
isGuaranteedNotToBeUndefOrPoison(CmpRHS, SQ.AC, &Sel, &DT)) {
if (Value *V = simplifyWithOpReplaced(TrueVal, CmpLHS, CmpRHS, SQ,
/* AllowRefinement */ true))
return replaceOperand(Sel, Swapped ? 2 : 1, V);
// Require either the replacement or the simplification result to be a
// constant to avoid infinite loops.
// FIXME: Make this check more precise.
if (isa<Constant>(CmpRHS) || isa<Constant>(V))
return replaceOperand(Sel, Swapped ? 2 : 1, V);

// Even if TrueVal does not simplify, we can directly replace a use of
// CmpLHS with CmpRHS, as long as the instruction is not used anywhere
Expand All @@ -1303,7 +1307,8 @@ Instruction *InstCombinerImpl::foldSelectValueEquivalence(SelectInst &Sel,
isGuaranteedNotToBeUndefOrPoison(CmpLHS, SQ.AC, &Sel, &DT))
if (Value *V = simplifyWithOpReplaced(TrueVal, CmpRHS, CmpLHS, SQ,
/* AllowRefinement */ true))
return replaceOperand(Sel, Swapped ? 2 : 1, V);
if (isa<Constant>(CmpLHS) || isa<Constant>(V))
return replaceOperand(Sel, Swapped ? 2 : 1, V);

auto *FalseInst = dyn_cast<Instruction>(FalseVal);
if (!FalseInst)
Expand Down
9 changes: 6 additions & 3 deletions llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3241,9 +3241,12 @@ void InstCombinerImpl::handleUnreachableFrom(
MadeIRChange = true;
}

// RemoveDIs: to match behaviour in dbg.value mode, drop debug-info on
// terminator too.
BB->getTerminator()->dropDbgValues();
SmallVector<Value *> Changed;
if (handleUnreachableTerminator(BB->getTerminator(), Changed)) {
MadeIRChange = true;
for (Value *V : Changed)
addToWorklist(cast<Instruction>(V));
}

// Handle potentially dead successors.
for (BasicBlock *Succ : successors(BB))
Expand Down
22 changes: 20 additions & 2 deletions llvm/lib/Transforms/Utils/Local.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2762,15 +2762,33 @@ bool llvm::replaceAllDbgUsesWith(Instruction &From, Value &To,
return false;
}

bool llvm::handleUnreachableTerminator(
Instruction *I, SmallVectorImpl<Value *> &PoisonedValues) {
bool Changed = false;
// RemoveDIs: erase debug-info on this instruction manually.
I->dropDbgValues();
for (Use &U : I->operands()) {
Value *Op = U.get();
if (isa<Instruction>(Op) && !Op->getType()->isTokenTy()) {
U.set(PoisonValue::get(Op->getType()));
PoisonedValues.push_back(Op);
Changed = true;
}
}

return Changed;
}

std::pair<unsigned, unsigned>
llvm::removeAllNonTerminatorAndEHPadInstructions(BasicBlock *BB) {
unsigned NumDeadInst = 0;
unsigned NumDeadDbgInst = 0;
// Delete the instructions backwards, as it has a reduced likelihood of
// having to update as many def-use and use-def chains.
Instruction *EndInst = BB->getTerminator(); // Last not to be deleted.
// RemoveDIs: erasing debug-info must be done manually.
EndInst->dropDbgValues();
SmallVector<Value *> Uses;
handleUnreachableTerminator(EndInst, Uses);

while (EndInst != &BB->front()) {
// Delete the next to last instruction.
Instruction *Inst = &*--EndInst->getIterator();
Expand Down
1,088 changes: 1,024 additions & 64 deletions llvm/test/CodeGen/RISCV/rvv/vwadd-sdnode.ll

Large diffs are not rendered by default.

864 changes: 816 additions & 48 deletions llvm/test/CodeGen/RISCV/rvv/vwmul-sdnode.ll

Large diffs are not rendered by default.

1,072 changes: 1,008 additions & 64 deletions llvm/test/CodeGen/RISCV/rvv/vwsub-sdnode.ll

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions llvm/test/Transforms/InstCombine/phi-select-constant.ll
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,11 @@ end:
define i16 @sink_to_unreachable_crash(i1 %a) {
; CHECK-LABEL: @sink_to_unreachable_crash(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[S:%.*]] = select i1 [[A:%.*]], i16 0, i16 5
; CHECK-NEXT: br label [[INF_LOOP:%.*]]
; CHECK: inf_loop:
; CHECK-NEXT: br label [[INF_LOOP]]
; CHECK: unreachable:
; CHECK-NEXT: ret i16 [[S]]
; CHECK-NEXT: ret i16 poison
;
entry:
%s = select i1 %a, i16 0, i16 5
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/Transforms/InstCombine/pr63791.ll
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ define void @y() {
; CHECK: for.cond5.preheader.i:
; CHECK-NEXT: br i1 false, label [[FOR_INC19_I:%.*]], label [[FOR_COND1_LOOPEXIT_I:%.*]]
; CHECK: for.inc19.i:
; CHECK-NEXT: br i1 false, label [[FOR_INC19_I]], label [[FOR_COND1_LOOPEXIT_I]]
; CHECK-NEXT: br i1 poison, label [[FOR_INC19_I]], label [[FOR_COND1_LOOPEXIT_I]]
;
entry:
br label %for.cond.i
Expand Down
38 changes: 37 additions & 1 deletion llvm/test/Transforms/InstCombine/select.ll
Original file line number Diff line number Diff line change
Expand Up @@ -2849,12 +2849,14 @@ define i8 @select_replacement_sub(i8 %x, i8 %y, i8 %z) {
ret i8 %sel
}

; FIXME: This is safe to fold.
define i8 @select_replacement_shift_noundef(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @select_replacement_shift_noundef(
; CHECK-NEXT: [[SHR:%.*]] = lshr exact i8 [[X:%.*]], 1
; CHECK-NEXT: call void @use_i8(i8 noundef [[SHR]])
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[SHR]], [[Y:%.*]]
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i8 [[X]], i8 [[Z:%.*]]
; CHECK-NEXT: [[SHL:%.*]] = shl i8 [[Y]], 1
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i8 [[SHL]], i8 [[Z:%.*]]
; CHECK-NEXT: ret i8 [[SEL]]
;
%shr = lshr exact i8 %x, 1
Expand Down Expand Up @@ -2904,6 +2906,40 @@ define i32 @select_replacement_loop2(i32 %arg, i32 %arg2) {
ret i32 %sel
}

define i8 @select_replacement_loop3(i32 noundef %x) {
; CHECK-LABEL: @select_replacement_loop3(
; CHECK-NEXT: [[TRUNC:%.*]] = trunc i32 [[X:%.*]] to i8
; CHECK-NEXT: [[REV:%.*]] = call i8 @llvm.bitreverse.i8(i8 [[TRUNC]])
; CHECK-NEXT: [[EXT:%.*]] = zext i8 [[REV]] to i32
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[EXT]], [[X]]
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i8 [[TRUNC]], i8 0
; CHECK-NEXT: ret i8 [[SEL]]
;
%trunc = trunc i32 %x to i8
%rev = call i8 @llvm.bitreverse.i8(i8 %trunc)
%ext = zext i8 %rev to i32
%cmp = icmp eq i32 %ext, %x
%sel = select i1 %cmp, i8 %trunc, i8 0
ret i8 %sel
}

define i16 @select_replacement_loop4(i16 noundef %p_12) {
; CHECK-LABEL: @select_replacement_loop4(
; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i16 [[P_12:%.*]], 2
; CHECK-NEXT: [[AND1:%.*]] = and i16 [[P_12]], 1
; CHECK-NEXT: [[AND2:%.*]] = select i1 [[CMP1]], i16 [[AND1]], i16 0
; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i16 [[AND2]], [[P_12]]
; CHECK-NEXT: [[AND3:%.*]] = select i1 [[CMP2]], i16 [[AND1]], i16 0
; CHECK-NEXT: ret i16 [[AND3]]
;
%cmp1 = icmp ult i16 %p_12, 2
%and1 = and i16 %p_12, 1
%and2 = select i1 %cmp1, i16 %and1, i16 0
%cmp2 = icmp eq i16 %and2, %p_12
%and3 = select i1 %cmp2, i16 %and1, i16 0
ret i16 %and3
}

define ptr @select_replacement_gep_inbounds(ptr %base, i64 %offset) {
; CHECK-LABEL: @select_replacement_gep_inbounds(
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, ptr [[BASE:%.*]], i64 [[OFFSET:%.*]]
Expand Down
154 changes: 154 additions & 0 deletions llvm/test/Transforms/InstCombine/sink_to_unreachable.ll
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,157 @@ bb3:
%p = phi i32 [0, %bb1], [%a, %bb2]
ret i32 %p
}

define i1 @sink_to_unreachable_ret(i16 %X) {
; CHECK-LABEL: @sink_to_unreachable_ret(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: br i1 true, label [[LOOP]], label [[UNREACH:%.*]]
; CHECK: unreach:
; CHECK-NEXT: ret i1 poison
;
entry:
br label %loop

loop:
%p = icmp sgt i16 %X, 16
br i1 true, label %loop, label %unreach

unreach:
ret i1 %p
}

define void @sink_to_unreachable_condbr(i16 %X) {
; CHECK-LABEL: @sink_to_unreachable_condbr(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: br i1 true, label [[LOOP]], label [[UNREACH:%.*]]
; CHECK: unreach:
; CHECK-NEXT: br i1 poison, label [[DUMMY:%.*]], label [[LOOP]]
; CHECK: dummy:
; CHECK-NEXT: unreachable
;
entry:
br label %loop

loop:
%p = icmp sgt i16 %X, 16
br i1 true, label %loop, label %unreach

unreach:
br i1 %p, label %dummy, label %loop

dummy:
unreachable
}

define void @sink_to_unreachable_switch(i16 %X) {
; CHECK-LABEL: @sink_to_unreachable_switch(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: br i1 true, label [[LOOP]], label [[UNREACH:%.*]]
; CHECK: unreach:
; CHECK-NEXT: switch i16 poison, label [[UNREACH_RET:%.*]] [
; CHECK-NEXT: ]
; CHECK: unreach.ret:
; CHECK-NEXT: unreachable
;
entry:
br label %loop

loop:
%quantum = srem i16 %X, 32
br i1 true, label %loop, label %unreach

unreach:
switch i16 %quantum, label %unreach.ret []

unreach.ret:
unreachable
}

define void @sink_to_unreachable_indirectbr(ptr %Ptr) {
; CHECK-LABEL: @sink_to_unreachable_indirectbr(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: br i1 true, label [[LOOP]], label [[UNREACH:%.*]]
; CHECK: unreach:
; CHECK-NEXT: indirectbr ptr poison, [label %loop]
;
entry:
br label %loop

loop:
%gep = getelementptr inbounds ptr, ptr %Ptr, i16 1
br i1 true, label %loop, label %unreach

unreach:
indirectbr ptr %gep, [label %loop]
}

define void @sink_to_unreachable_invoke(ptr %Ptr) personality ptr @__CxxFrameHandler3 {
; CHECK-LABEL: @sink_to_unreachable_invoke(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: br i1 true, label [[LOOP]], label [[UNREACH:%.*]]
; CHECK: unreach:
; CHECK-NEXT: invoke void poison(i1 false)
; CHECK-NEXT: to label [[DUMMY:%.*]] unwind label [[ICATCH_DISPATCH:%.*]]
; CHECK: unreach2:
; CHECK-NEXT: invoke void @__CxxFrameHandler3(ptr poison)
; CHECK-NEXT: to label [[DUMMY]] unwind label [[ICATCH_DISPATCH]]
; CHECK: unreach3:
; CHECK-NEXT: [[CLEAN:%.*]] = cleanuppad within none []
; CHECK-NEXT: invoke void @__CxxFrameHandler3(ptr poison) [ "funclet"(token [[CLEAN]]) ]
; CHECK-NEXT: to label [[DUMMY]] unwind label [[ICATCH_DISPATCH]]
; CHECK: icatch.dispatch:
; CHECK-NEXT: [[TMP1:%.*]] = catchswitch within none [label %icatch] unwind to caller
; CHECK: icatch:
; CHECK-NEXT: [[TMP2:%.*]] = catchpad within [[TMP1]] [ptr null, i32 64, ptr null]
; CHECK-NEXT: catchret from [[TMP2]] to label [[DUMMY2:%.*]]
; CHECK: dummy:
; CHECK-NEXT: ret void
; CHECK: dummy2:
; CHECK-NEXT: ret void
;
entry:
br label %loop

loop:
%gep = getelementptr inbounds ptr, ptr %Ptr, i16 1
br i1 true, label %loop, label %unreach

unreach:
invoke void %gep(i1 false)
to label %dummy unwind label %icatch.dispatch

unreach2:
invoke void @__CxxFrameHandler3(ptr %gep)
to label %dummy unwind label %icatch.dispatch

unreach3:
%clean = cleanuppad within none []
invoke void @__CxxFrameHandler3(ptr %gep) [ "funclet"(token %clean) ]
to label %dummy unwind label %icatch.dispatch

icatch.dispatch:
%tmp1 = catchswitch within none [label %icatch] unwind to caller

icatch:
%tmp2 = catchpad within %tmp1 [ptr null, i32 64, ptr null]
catchret from %tmp2 to label %dummy2

dummy:
ret void

dummy2:
ret void
}

declare void @may_throw()
declare i32 @__CxxFrameHandler3(...)
57 changes: 57 additions & 0 deletions llvm/test/Transforms/InstCombine/unreachable-code.ll
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,63 @@ bb2:
br label %bb
}

declare void @invoke(ptr)
declare i32 @__gxx_personality_v0(...)
define void @test(i1 %x) personality ptr @__gxx_personality_v0 {
; CHECK-LABEL: define void @test
; CHECK-SAME: (i1 [[X:%.*]]) personality ptr @__gxx_personality_v0 {
; CHECK-NEXT: entry:
; CHECK-NEXT: br i1 [[X]], label [[IF_ELSE:%.*]], label [[CLEAN1:%.*]]
; CHECK: if.else:
; CHECK-NEXT: store i32 1, ptr undef, align 4
; CHECK-NEXT: invoke void @invoke(ptr poison)
; CHECK-NEXT: to label [[CONT:%.*]] unwind label [[LPAD5:%.*]]
; CHECK: cont:
; CHECK-NEXT: invoke void @invoke(ptr poison)
; CHECK-NEXT: to label [[CLEAN1]] unwind label [[LPAD6:%.*]]
; CHECK: lpad5:
; CHECK-NEXT: [[TMP0:%.*]] = landingpad { ptr, i32 }
; CHECK-NEXT: cleanup
; CHECK-NEXT: br label [[CLEAN1]]
; CHECK: lpad6:
; CHECK-NEXT: [[TMP1:%.*]] = landingpad { ptr, i32 }
; CHECK-NEXT: cleanup
; CHECK-NEXT: br label [[CLEAN2:%.*]]
; CHECK: clean1:
; CHECK-NEXT: ret void
; CHECK: clean2:
; CHECK-NEXT: ret void
;
entry:
%ref = alloca ptr
br i1 %x, label %if.else, label %clean1

if.else:
store i32 1, ptr undef
invoke void @invoke(ptr %ref)
to label %cont unwind label %lpad5

cont:
invoke void @invoke(ptr %ref)
to label %clean1 unwind label %lpad6

lpad5:
%13 = landingpad { ptr, i32 }
cleanup
br label %clean1

lpad6:
%14 = landingpad { ptr, i32 }
cleanup
br label %clean2

clean1:
ret void

clean2:
ret void
}

;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
; DEFAULT_ITER: {{.*}}
; MAX1: {{.*}}
1 change: 1 addition & 0 deletions llvm/utils/gn/secondary/llvm/lib/CodeGen/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ static_library("CodeGen") {
"MachineCSE.cpp",
"MachineCheckDebugify.cpp",
"MachineCombiner.cpp",
"MachineConvergenceVerifier.cpp",
"MachineCopyPropagation.cpp",
"MachineCycleAnalysis.cpp",
"MachineDebugify.cpp",
Expand Down
1 change: 0 additions & 1 deletion mlir/lib/Dialect/GPU/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ if(MLIR_ENABLE_ROCM_CONVERSIONS)
target_compile_definitions(obj.MLIRGPUTransforms
PRIVATE
__DEFAULT_ROCM_PATH__="${DEFAULT_ROCM_PATH}"
MLIR_GPU_TO_HSACO_PASS_ENABLE=1
)

target_link_libraries(MLIRGPUTransforms
Expand Down
7 changes: 4 additions & 3 deletions mlir/lib/Dialect/GPU/Transforms/SerializeToHsaco.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@
//
//===----------------------------------------------------------------------===//

#include "mlir/Config/mlir-config.h"
#include "mlir/Dialect/GPU/Transforms/Passes.h"
#include "mlir/IR/Location.h"
#include "mlir/IR/MLIRContext.h"

#if MLIR_GPU_TO_HSACO_PASS_ENABLE
#if MLIR_ENABLE_ROCM_CONVERSIONS
#include "mlir/ExecutionEngine/OptUtils.h"
#include "mlir/Pass/Pass.h"
#include "mlir/Support/FileUtilities.h"
Expand Down Expand Up @@ -454,6 +455,6 @@ std::unique_ptr<Pass> mlir::createGpuSerializeToHsacoPass(StringRef triple,
optLevel);
}

#else // MLIR_GPU_TO_HSACO_PASS_ENABLE
#else // MLIR_ENABLE_ROCM_CONVERSIONS
void mlir::registerGpuSerializeToHsacoPass() {}
#endif // MLIR_GPU_TO_HSACO_PASS_ENABLE
#endif // MLIR_ENABLE_ROCM_CONVERSIONS