aboutsummaryrefslogtreecommitdiff
path: root/clang/lib
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/AST/ASTStructuralEquivalence.cpp70
-rw-r--r--clang/lib/AST/ByteCode/Compiler.cpp10
-rw-r--r--clang/lib/AST/ByteCode/Compiler.h1
-rw-r--r--clang/lib/AST/ByteCode/Disasm.cpp2
-rw-r--r--clang/lib/AST/ByteCode/DynamicAllocator.cpp2
-rw-r--r--clang/lib/AST/ByteCode/Interp.h40
-rw-r--r--clang/lib/AST/ByteCode/InterpBlock.cpp43
-rw-r--r--clang/lib/AST/ByteCode/InterpBuiltin.cpp14
-rw-r--r--clang/lib/AST/ByteCode/InterpState.cpp2
-rw-r--r--clang/lib/AST/ByteCode/Opcodes.td4
-rw-r--r--clang/lib/AST/ByteCode/Pointer.cpp17
-rw-r--r--clang/lib/AST/ByteCode/Pointer.h15
-rw-r--r--clang/lib/AST/Decl.cpp9
-rw-r--r--clang/lib/AST/Expr.cpp14
-rw-r--r--clang/lib/AST/ExprConstant.cpp17
-rw-r--r--clang/lib/AST/RecordLayoutBuilder.cpp2
-rw-r--r--clang/lib/Analysis/UnsafeBufferUsage.cpp89
-rw-r--r--clang/lib/Basic/FileManager.cpp5
-rw-r--r--clang/lib/Basic/Targets/AMDGPU.cpp7
-rw-r--r--clang/lib/Basic/Targets/WebAssembly.cpp23
-rw-r--r--clang/lib/Basic/Targets/WebAssembly.h2
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp5
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenCall.cpp8
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenCall.h5
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenDecl.cpp62
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenExpr.cpp43
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp57
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenFunction.h4
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenModule.cpp94
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenModule.h3
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp4
-rw-r--r--clang/lib/CIR/Dialect/IR/CIRDialect.cpp16
-rw-r--r--clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp2
-rw-r--r--clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp71
-rw-r--r--clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp100
-rw-r--r--clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h15
-rw-r--r--clang/lib/CodeGen/CGBuiltin.cpp3
-rw-r--r--clang/lib/CodeGen/CGCoroutine.cpp2
-rw-r--r--clang/lib/CodeGen/CGDebugInfo.cpp2
-rw-r--r--clang/lib/CodeGen/CGExpr.cpp1
-rw-r--r--clang/lib/Driver/Driver.cpp19
-rw-r--r--clang/lib/Driver/ToolChain.cpp9
-rw-r--r--clang/lib/Driver/ToolChains/AMDGPU.h1
-rw-r--r--clang/lib/Driver/ToolChains/BareMetal.cpp3
-rw-r--r--clang/lib/Driver/ToolChains/Clang.cpp3
-rw-r--r--clang/lib/Driver/ToolChains/CommonArgs.cpp30
-rw-r--r--clang/lib/Driver/ToolChains/Cuda.h56
-rw-r--r--clang/lib/Driver/ToolChains/Darwin.h8
-rw-r--r--clang/lib/Driver/ToolChains/Gnu.h8
-rw-r--r--clang/lib/Driver/ToolChains/HIPAMD.h1
-rw-r--r--clang/lib/Driver/ToolChains/LazyDetector.h45
-rw-r--r--clang/lib/Driver/ToolChains/MSVC.h8
-rw-r--r--clang/lib/Driver/ToolChains/MinGW.cpp11
-rw-r--r--clang/lib/Driver/ToolChains/MinGW.h5
-rw-r--r--clang/lib/Driver/ToolChains/ROCm.h314
-rw-r--r--clang/lib/Driver/ToolChains/SYCL.h11
-rw-r--r--clang/lib/Format/IntegerLiteralSeparatorFixer.cpp11
-rw-r--r--clang/lib/Frontend/InitPreprocessor.cpp4
-rw-r--r--clang/lib/Headers/opencl-c.h16
-rw-r--r--clang/lib/Sema/AnalysisBasedWarnings.cpp4
-rw-r--r--clang/lib/Sema/Sema.cpp5
-rw-r--r--clang/lib/Sema/SemaCodeComplete.cpp8
-rw-r--r--clang/lib/Sema/SemaConcept.cpp4
-rw-r--r--clang/lib/Sema/SemaDecl.cpp4
-rw-r--r--clang/lib/Sema/SemaModule.cpp14
-rw-r--r--clang/lib/Sema/SemaTemplate.cpp2
-rw-r--r--clang/lib/Sema/SemaTypeTraits.cpp72
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp38
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp146
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp30
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp98
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp79
72 files changed, 974 insertions, 978 deletions
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index 0f2762d..22bb4cb 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -456,7 +456,9 @@ CheckStructurallyEquivalentAttributes(StructuralEquivalenceContext &Context,
const Decl *D1, const Decl *D2,
const Decl *PrimaryDecl = nullptr) {
// If either declaration has an attribute on it, we treat the declarations
- // as not being structurally equivalent.
+ // as not being structurally equivalent unless both declarations are implicit
+ // (ones generated by the compiler like __NSConstantString_tag).
+ //
// FIXME: this should be handled on a case-by-case basis via tablegen in
// Attr.td. There are multiple cases to consider: one declaration with the
// attribute, another without it; different attribute syntax|spellings for
@@ -468,7 +470,7 @@ CheckStructurallyEquivalentAttributes(StructuralEquivalenceContext &Context,
D1Attr = *D1->getAttrs().begin();
if (D2->hasAttrs())
D2Attr = *D2->getAttrs().begin();
- if (D1Attr || D2Attr) {
+ if ((D1Attr || D2Attr) && !D1->isImplicit() && !D2->isImplicit()) {
const auto *DiagnoseDecl = cast<TypeDecl>(PrimaryDecl ? PrimaryDecl : D2);
Context.Diag2(DiagnoseDecl->getLocation(),
diag::warn_odr_tag_type_with_attributes)
@@ -870,7 +872,27 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
else if (T1->getTypeClass() == Type::FunctionNoProto &&
T2->getTypeClass() == Type::FunctionProto)
TC = Type::FunctionNoProto;
- else
+ else if (Context.LangOpts.C23 && !Context.StrictTypeSpelling &&
+ (T1->getTypeClass() == Type::Enum ||
+ T2->getTypeClass() == Type::Enum)) {
+ // In C23, if not being strict about token equivalence, we need to handle
+ // the case where one type is an enumeration and the other type is an
+ // integral type.
+ //
+ // C23 6.7.3.3p16: The enumerated type is compatible with the underlying
+ // type of the enumeration.
+ //
+ // Treat the enumeration as its underlying type and use the builtin type
+ // class comparison.
+ if (T1->getTypeClass() == Type::Enum) {
+ T1 = T1->getAs<EnumType>()->getDecl()->getIntegerType();
+ assert(T2->isBuiltinType() && !T1.isNull()); // Sanity check
+ } else if (T2->getTypeClass() == Type::Enum) {
+ T2 = T2->getAs<EnumType>()->getDecl()->getIntegerType();
+ assert(T1->isBuiltinType() && !T2.isNull()); // Sanity check
+ }
+ TC = Type::Builtin;
+ } else
return false;
}
@@ -2071,6 +2093,48 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
!CheckStructurallyEquivalentAttributes(Context, D1, D2))
return false;
+ // In C23, if one enumeration has a fixed underlying type, the other shall
+ // have a compatible fixed underlying type (6.2.7).
+ if (Context.LangOpts.C23) {
+ if (D1->isFixed() != D2->isFixed()) {
+ if (Context.Complain) {
+ Context.Diag2(D2->getLocation(),
+ Context.getApplicableDiagnostic(
+ diag::err_odr_tag_type_inconsistent))
+ << Context.ToCtx.getTypeDeclType(D2)
+ << (&Context.FromCtx != &Context.ToCtx);
+ Context.Diag1(D1->getLocation(),
+ D1->isFixed()
+ ? diag::note_odr_fixed_underlying_type
+ : diag::note_odr_missing_fixed_underlying_type)
+ << D1;
+ Context.Diag2(D2->getLocation(),
+ D2->isFixed()
+ ? diag::note_odr_fixed_underlying_type
+ : diag::note_odr_missing_fixed_underlying_type)
+ << D2;
+ }
+ return false;
+ }
+ if (D1->isFixed()) {
+ assert(D2->isFixed() && "enums expected to have fixed underlying types");
+ if (!IsStructurallyEquivalent(Context, D1->getIntegerType(),
+ D2->getIntegerType())) {
+ if (Context.Complain) {
+ Context.Diag2(D2->getLocation(),
+ Context.getApplicableDiagnostic(
+ diag::err_odr_tag_type_inconsistent))
+ << Context.ToCtx.getTypeDeclType(D2)
+ << (&Context.FromCtx != &Context.ToCtx);
+ Context.Diag2(D2->getLocation(),
+ diag::note_odr_incompatible_fixed_underlying_type)
+ << D2 << D2->getIntegerType() << D1->getIntegerType();
+ }
+ return false;
+ }
+ }
+ }
+
llvm::SmallVector<const EnumConstantDecl *, 8> D1Enums, D2Enums;
auto CopyEnumerators =
[](auto &&Range, llvm::SmallVectorImpl<const EnumConstantDecl *> &Cont) {
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index d0ddb2e..2f22d5c 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -457,13 +457,17 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
assert(isPtrType(*FromT));
assert(isPtrType(*ToT));
if (FromT == ToT) {
- if (CE->getType()->isVoidPointerType())
+ if (CE->getType()->isVoidPointerType() &&
+ !SubExprTy->isFunctionPointerType()) {
return this->delegate(SubExpr);
+ }
if (!this->visit(SubExpr))
return false;
- if (CE->getType()->isFunctionPointerType())
- return true;
+ if (CE->getType()->isFunctionPointerType() ||
+ SubExprTy->isFunctionPointerType()) {
+ return this->emitFnPtrCast(CE);
+ }
if (FromT == PT_Ptr)
return this->emitPtrPtrCast(SubExprTy->isVoidPointerType(), CE);
return true;
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index 3a26342..16f108f 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -21,7 +21,6 @@
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/StmtVisitor.h"
-#include "clang/Basic/TargetInfo.h"
namespace clang {
class QualType;
diff --git a/clang/lib/AST/ByteCode/Disasm.cpp b/clang/lib/AST/ByteCode/Disasm.cpp
index 74399d1..5049a65 100644
--- a/clang/lib/AST/ByteCode/Disasm.cpp
+++ b/clang/lib/AST/ByteCode/Disasm.cpp
@@ -531,7 +531,7 @@ LLVM_DUMP_METHOD void Block::dump(llvm::raw_ostream &OS) const {
Desc->dump(OS);
OS << ")\n";
unsigned NPointers = 0;
- for (const Pointer *P = Pointers; P; P = P->Next) {
+ for (const Pointer *P = Pointers; P; P = P->asBlockPointer().Next) {
++NPointers;
}
OS << " EvalID: " << EvalID << '\n';
diff --git a/clang/lib/AST/ByteCode/DynamicAllocator.cpp b/clang/lib/AST/ByteCode/DynamicAllocator.cpp
index 4f0f511..169250c 100644
--- a/clang/lib/AST/ByteCode/DynamicAllocator.cpp
+++ b/clang/lib/AST/ByteCode/DynamicAllocator.cpp
@@ -27,7 +27,7 @@ void DynamicAllocator::cleanup() {
B->invokeDtor();
if (B->hasPointers()) {
while (B->Pointers) {
- Pointer *Next = B->Pointers->Next;
+ Pointer *Next = B->Pointers->asBlockPointer().Next;
B->Pointers->PointeeStorage.BS.Pointee = nullptr;
B->Pointers = Next;
}
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 9012442..9a325ab 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -25,7 +25,6 @@
#include "InterpStack.h"
#include "InterpState.h"
#include "MemberPointer.h"
-#include "Opcode.h"
#include "PrimType.h"
#include "Program.h"
#include "State.h"
@@ -481,13 +480,11 @@ inline bool Mulc(InterpState &S, CodePtr OpPC) {
Floating RA = S.allocFloat(A.getSemantics());
RA.copy(ResR);
Result.elem<Floating>(0) = RA; // Floating(ResR);
- Result.atIndex(0).initialize();
Floating RI = S.allocFloat(A.getSemantics());
RI.copy(ResI);
Result.elem<Floating>(1) = RI; // Floating(ResI);
- Result.atIndex(1).initialize();
- Result.initialize();
+ Result.initializeAllElements();
} else {
// Integer element type.
const T &LHSR = LHS.elem<T>(0);
@@ -505,7 +502,6 @@ inline bool Mulc(InterpState &S, CodePtr OpPC) {
return false;
if (T::sub(A, B, Bits, &Result.elem<T>(0)))
return false;
- Result.atIndex(0).initialize();
// imag(Result) = (real(LHS) * imag(RHS)) + (imag(LHS) * real(RHS))
if (T::mul(LHSR, RHSI, Bits, &A))
@@ -514,8 +510,8 @@ inline bool Mulc(InterpState &S, CodePtr OpPC) {
return false;
if (T::add(A, B, Bits, &Result.elem<T>(1)))
return false;
- Result.atIndex(1).initialize();
Result.initialize();
+ Result.initializeAllElements();
}
return true;
@@ -541,14 +537,12 @@ inline bool Divc(InterpState &S, CodePtr OpPC) {
Floating RA = S.allocFloat(A.getSemantics());
RA.copy(ResR);
Result.elem<Floating>(0) = RA; // Floating(ResR);
- Result.atIndex(0).initialize();
Floating RI = S.allocFloat(A.getSemantics());
RI.copy(ResI);
Result.elem<Floating>(1) = RI; // Floating(ResI);
- Result.atIndex(1).initialize();
- Result.initialize();
+ Result.initializeAllElements();
} else {
// Integer element type.
const T &LHSR = LHS.elem<T>(0);
@@ -590,7 +584,6 @@ inline bool Divc(InterpState &S, CodePtr OpPC) {
return false;
if (T::div(ResultR, Den, Bits, &ResultR))
return false;
- Result.atIndex(0).initialize();
// imag(Result) = ((imag(LHS) * real(RHS)) - (real(LHS) * imag(RHS))) / Den
if (T::mul(LHSI, RHSR, Bits, &A) || T::mul(LHSR, RHSI, Bits, &B))
@@ -599,8 +592,7 @@ inline bool Divc(InterpState &S, CodePtr OpPC) {
return false;
if (T::div(ResultI, Den, Bits, &ResultI))
return false;
- Result.atIndex(1).initialize();
- Result.initialize();
+ Result.initializeAllElements();
}
return true;
@@ -1138,8 +1130,9 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_past_end)
<< LHS.toDiagnosticString(S.getASTContext());
return false;
- } else if (RHS.isOnePastEnd() && !LHS.isOnePastEnd() && !LHS.isZero() &&
- LHS.getOffset() == 0) {
+ }
+ if (RHS.isOnePastEnd() && !LHS.isOnePastEnd() && !LHS.isZero() &&
+ LHS.getOffset() == 0) {
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_past_end)
<< RHS.toDiagnosticString(S.getASTContext());
@@ -1157,8 +1150,9 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.FFDiag(Loc, diag::note_constexpr_literal_comparison);
return false;
- } else if (const auto *CE = dyn_cast<CallExpr>(E);
- CE && IsOpaqueConstantCall(CE)) {
+ }
+ if (const auto *CE = dyn_cast<CallExpr>(E);
+ CE && IsOpaqueConstantCall(CE)) {
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.FFDiag(Loc, diag::note_constexpr_opaque_call_comparison)
<< P.toDiagnosticString(S.getASTContext());
@@ -2688,6 +2682,14 @@ static inline bool CastFixedPointIntegral(InterpState &S, CodePtr OpPC) {
return true;
}
+static inline bool FnPtrCast(InterpState &S, CodePtr OpPC) {
+ const SourceInfo &E = S.Current->getSource(OpPC);
+ S.CCEDiag(E, diag::note_constexpr_invalid_cast)
+ << diag::ConstexprInvalidCastKind::ThisConversionOrReinterpret
+ << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC);
+ return true;
+}
+
static inline bool PtrPtrCast(InterpState &S, CodePtr OpPC, bool SrcIsVoidPtr) {
const auto &Ptr = S.Stk.peek<Pointer>();
@@ -3273,7 +3275,8 @@ inline bool InvalidCast(InterpState &S, CodePtr OpPC, CastKind Kind,
S.CCEDiag(Loc, diag::note_constexpr_invalid_cast)
<< static_cast<unsigned>(Kind) << S.Current->getRange(OpPC);
return !Fatal;
- } else if (Kind == CastKind::Volatile) {
+ }
+ if (Kind == CastKind::Volatile) {
if (!S.checkingPotentialConstantExpression()) {
const auto *E = cast<CastExpr>(S.Current->getExpr(OpPC));
if (S.getLangOpts().CPlusPlus)
@@ -3284,7 +3287,8 @@ inline bool InvalidCast(InterpState &S, CodePtr OpPC, CastKind Kind,
}
return false;
- } else if (Kind == CastKind::Dynamic) {
+ }
+ if (Kind == CastKind::Dynamic) {
assert(!S.getLangOpts().CPlusPlus20);
S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_invalid_cast)
<< diag::ConstexprInvalidCastKind::Dynamic;
diff --git a/clang/lib/AST/ByteCode/InterpBlock.cpp b/clang/lib/AST/ByteCode/InterpBlock.cpp
index f603078..963b54e 100644
--- a/clang/lib/AST/ByteCode/InterpBlock.cpp
+++ b/clang/lib/AST/ByteCode/InterpBlock.cpp
@@ -27,9 +27,9 @@ void Block::addPointer(Pointer *P) {
assert(!hasPointer(P));
#endif
if (Pointers)
- Pointers->Prev = P;
- P->Next = Pointers;
- P->Prev = nullptr;
+ Pointers->PointeeStorage.BS.Prev = P;
+ P->PointeeStorage.BS.Next = Pointers;
+ P->PointeeStorage.BS.Prev = nullptr;
Pointers = P;
#ifndef NDEBUG
assert(hasPointer(P));
@@ -48,13 +48,15 @@ void Block::removePointer(Pointer *P) {
assert(hasPointer(P));
#endif
+ BlockPointer &BP = P->PointeeStorage.BS;
+
if (Pointers == P)
- Pointers = P->Next;
+ Pointers = BP.Next;
- if (P->Prev)
- P->Prev->Next = P->Next;
- if (P->Next)
- P->Next->Prev = P->Prev;
+ if (BP.Prev)
+ BP.Prev->PointeeStorage.BS.Next = BP.Next;
+ if (BP.Next)
+ BP.Next->PointeeStorage.BS.Prev = BP.Prev;
P->PointeeStorage.BS.Pointee = nullptr;
#ifndef NDEBUG
assert(!hasPointer(P));
@@ -68,7 +70,9 @@ void Block::cleanup() {
void Block::replacePointer(Pointer *Old, Pointer *New) {
assert(Old);
+ assert(Old->isBlockPointer());
assert(New);
+ assert(New->isBlockPointer());
assert(Old != New);
if (IsStatic) {
assert(!Pointers);
@@ -78,17 +82,20 @@ void Block::replacePointer(Pointer *Old, Pointer *New) {
assert(hasPointer(Old));
#endif
- if (Old->Prev)
- Old->Prev->Next = New;
- if (Old->Next)
- Old->Next->Prev = New;
- New->Prev = Old->Prev;
- New->Next = Old->Next;
+ BlockPointer &OldBP = Old->PointeeStorage.BS;
+ BlockPointer &NewBP = New->PointeeStorage.BS;
+
+ if (OldBP.Prev)
+ OldBP.Prev->PointeeStorage.BS.Next = New;
+ if (OldBP.Next)
+ OldBP.Next->PointeeStorage.BS.Prev = New;
+ NewBP.Prev = OldBP.Prev;
+ NewBP.Next = OldBP.Next;
if (Pointers == Old)
Pointers = New;
- Old->PointeeStorage.BS.Pointee = nullptr;
- New->PointeeStorage.BS.Pointee = this;
+ OldBP.Pointee = nullptr;
+ NewBP.Pointee = this;
#ifndef NDEBUG
assert(!hasPointer(Old));
assert(hasPointer(New));
@@ -97,7 +104,7 @@ void Block::replacePointer(Pointer *Old, Pointer *New) {
#ifndef NDEBUG
bool Block::hasPointer(const Pointer *P) const {
- for (const Pointer *C = Pointers; C; C = C->Next) {
+ for (const Pointer *C = Pointers; C; C = C->asBlockPointer().Next) {
if (C == P)
return true;
}
@@ -120,7 +127,7 @@ DeadBlock::DeadBlock(DeadBlock *&Root, Block *Blk)
// Transfer pointers.
B.Pointers = Blk->Pointers;
- for (Pointer *P = Blk->Pointers; P; P = P->Next)
+ for (Pointer *P = Blk->Pointers; P; P = P->asBlockPointer().Next)
P->PointeeStorage.BS.Pointee = &B;
Blk->Pointers = nullptr;
}
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 3ece7054..f908d02 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -1099,10 +1099,8 @@ static bool interp__builtin_complex(InterpState &S, CodePtr OpPC,
Pointer &Result = S.Stk.peek<Pointer>();
Result.elem<Floating>(0) = Arg1;
- Result.atIndex(0).initialize();
Result.elem<Floating>(1) = Arg2;
- Result.atIndex(1).initialize();
- Result.initialize();
+ Result.initializeAllElements();
return true;
}
@@ -1728,9 +1726,9 @@ static bool interp__builtin_elementwise_popcount(InterpState &S, CodePtr OpPC,
Dst.elem<T>(I) =
T::from(Arg.elem<T>(I).toAPSInt().reverseBits().getZExtValue());
}
- Dst.atIndex(I).initialize();
});
}
+ Dst.initializeAllElements();
return true;
}
@@ -2314,12 +2312,10 @@ static bool interp__builtin_elementwise_sat(InterpState &S, CodePtr OpPC,
llvm_unreachable("Wrong builtin ID");
}
- INT_TYPE_SWITCH_NO_BOOL(ElemT, {
- const Pointer &E = Dst.atIndex(I);
- E.deref<T>() = static_cast<T>(Result);
- E.initialize();
- });
+ INT_TYPE_SWITCH_NO_BOOL(ElemT,
+ { Dst.elem<T>(I) = static_cast<T>(Result); });
}
+ Dst.initializeAllElements();
return true;
}
diff --git a/clang/lib/AST/ByteCode/InterpState.cpp b/clang/lib/AST/ByteCode/InterpState.cpp
index 7848f29..3010847 100644
--- a/clang/lib/AST/ByteCode/InterpState.cpp
+++ b/clang/lib/AST/ByteCode/InterpState.cpp
@@ -52,7 +52,7 @@ void InterpState::cleanup() {
// As a last resort, make sure all pointers still pointing to a dead block
// don't point to it anymore.
for (DeadBlock *DB = DeadBlocks; DB; DB = DB->Next) {
- for (Pointer *P = DB->B.Pointers; P; P = P->Next) {
+ for (Pointer *P = DB->B.Pointers; P; P = P->asBlockPointer().Next) {
P->PointeeStorage.BS.Pointee = nullptr;
}
}
diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td
index abfed77..95a4433 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -412,7 +412,7 @@ def CheckDecl : Opcode {
def CheckEnumValue : Opcode {
let Args = [ArgEnumDecl];
- let Types = [FixedSizeIntegralTypeClass];
+ let Types = [IntegralTypeClass];
let HasGroup = 1;
}
@@ -735,6 +735,8 @@ def PtrPtrCast : Opcode {
}
+def FnPtrCast : Opcode;
+
def DecayPtr : Opcode {
let Types = [PtrTypeClass, PtrTypeClass];
let HasGroup = 1;
diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp
index 9341bc1..dec2088 100644
--- a/clang/lib/AST/ByteCode/Pointer.cpp
+++ b/clang/lib/AST/ByteCode/Pointer.cpp
@@ -42,7 +42,7 @@ Pointer::Pointer(Block *Pointee, unsigned Base, uint64_t Offset)
: Offset(Offset), StorageKind(Storage::Block) {
assert((Base == RootPtrMark || Base % alignof(void *) == 0) && "wrong base");
- PointeeStorage.BS = {Pointee, Base};
+ PointeeStorage.BS = {Pointee, Base, nullptr, nullptr};
if (Pointee)
Pointee->addPointer(this);
@@ -89,7 +89,6 @@ Pointer &Pointer::operator=(const Pointer &P) {
if (P.isBlockPointer()) {
PointeeStorage.BS = P.PointeeStorage.BS;
- PointeeStorage.BS.Pointee = P.PointeeStorage.BS.Pointee;
if (PointeeStorage.BS.Pointee)
PointeeStorage.BS.Pointee->addPointer(this);
@@ -127,7 +126,6 @@ Pointer &Pointer::operator=(Pointer &&P) {
if (P.isBlockPointer()) {
PointeeStorage.BS = P.PointeeStorage.BS;
- PointeeStorage.BS.Pointee = P.PointeeStorage.BS.Pointee;
if (PointeeStorage.BS.Pointee)
PointeeStorage.BS.Pointee->addPointer(this);
@@ -495,6 +493,19 @@ void Pointer::initialize() const {
getInlineDesc()->IsInitialized = true;
}
+void Pointer::initializeAllElements() const {
+ assert(getFieldDesc()->isPrimitiveArray());
+ assert(isArrayRoot());
+
+ InitMapPtr &IM = getInitMap();
+ if (!IM) {
+ IM = std::make_pair(true, nullptr);
+ } else {
+ IM->first = true;
+ IM->second.reset();
+ }
+}
+
void Pointer::activate() const {
// Field has its bit in an inline descriptor.
assert(PointeeStorage.BS.Base != 0 &&
diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h
index 059f176..5bafc5b 100644
--- a/clang/lib/AST/ByteCode/Pointer.h
+++ b/clang/lib/AST/ByteCode/Pointer.h
@@ -39,6 +39,10 @@ struct BlockPointer {
Block *Pointee;
/// Start of the current subfield.
unsigned Base;
+ /// Previous link in the pointer chain.
+ Pointer *Prev;
+ /// Next link in the pointer chain.
+ Pointer *Next;
};
struct IntPointer {
@@ -725,6 +729,10 @@ public:
/// Initializes a field.
void initialize() const;
+ /// Initialize all elements of a primitive array at once. This can be
+ /// used in situations where we *know* we have initialized *all* elements
+ /// of a primtive array.
+ void initializeAllElements() const;
/// Activats a field.
void activate() const;
/// Deactivates an entire strurcutre.
@@ -828,15 +836,10 @@ private:
/// Offset into the storage.
uint64_t Offset = 0;
- /// Previous link in the pointer chain.
- Pointer *Prev = nullptr;
- /// Next link in the pointer chain.
- Pointer *Next = nullptr;
-
Storage StorageKind = Storage::Int;
union {
- BlockPointer BS;
IntPointer Int;
+ BlockPointer BS;
FunctionPointer Fn;
TypeidPointer Typeid;
} PointeeStorage;
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 83fcd87..1588be4 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -5988,11 +5988,10 @@ bool clang::IsArmStreamingFunction(const FunctionDecl *FD,
if (FD->hasAttr<ArmLocallyStreamingAttr>())
return true;
- if (const Type *Ty = FD->getType().getTypePtrOrNull())
- if (const auto *FPT = Ty->getAs<FunctionProtoType>())
- if (FPT->getAArch64SMEAttributes() &
- FunctionType::SME_PStateSMEnabledMask)
- return true;
+ assert(!FD->getType().isNull() && "Expected a valid FunctionDecl");
+ if (const auto *FPT = FD->getType()->getAs<FunctionProtoType>())
+ if (FPT->getAArch64SMEAttributes() & FunctionType::SME_PStateSMEnabledMask)
+ return true;
return false;
}
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index d85655b..cd9672d 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -4233,8 +4233,15 @@ bool Expr::isSameComparisonOperand(const Expr* E1, const Expr* E2) {
// template parameters.
const auto *DRE1 = cast<DeclRefExpr>(E1);
const auto *DRE2 = cast<DeclRefExpr>(E2);
- return DRE1->isPRValue() && DRE2->isPRValue() &&
- DRE1->getDecl() == DRE2->getDecl();
+
+ if (DRE1->getDecl() != DRE2->getDecl())
+ return false;
+
+ if ((DRE1->isPRValue() && DRE2->isPRValue()) ||
+ (DRE1->isLValue() && DRE2->isLValue()))
+ return true;
+
+ return false;
}
case ImplicitCastExprClass: {
// Peel off implicit casts.
@@ -4244,7 +4251,8 @@ bool Expr::isSameComparisonOperand(const Expr* E1, const Expr* E2) {
if (!ICE1 || !ICE2)
return false;
if (ICE1->getCastKind() != ICE2->getCastKind())
- return false;
+ return isSameComparisonOperand(ICE1->IgnoreParenImpCasts(),
+ ICE2->IgnoreParenImpCasts());
E1 = ICE1->getSubExpr()->IgnoreParens();
E2 = ICE2->getSubExpr()->IgnoreParens();
// The final cast must be one of these types.
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 9808298..993b64b 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -9741,10 +9741,19 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) {
case CK_AddressSpaceConversion:
if (!Visit(SubExpr))
return false;
- // Bitcasts to cv void* are static_casts, not reinterpret_casts, so are
- // permitted in constant expressions in C++11. Bitcasts from cv void* are
- // also static_casts, but we disallow them as a resolution to DR1312.
- if (!E->getType()->isVoidPointerType()) {
+ if (E->getType()->isFunctionPointerType() ||
+ SubExpr->getType()->isFunctionPointerType()) {
+ // Casting between two function pointer types, or between a function
+ // pointer and an object pointer, is always a reinterpret_cast.
+ CCEDiag(E, diag::note_constexpr_invalid_cast)
+ << diag::ConstexprInvalidCastKind::ThisConversionOrReinterpret
+ << Info.Ctx.getLangOpts().CPlusPlus;
+ Result.Designator.setInvalid();
+ } else if (!E->getType()->isVoidPointerType()) {
+ // Bitcasts to cv void* are static_casts, not reinterpret_casts, so are
+ // permitted in constant expressions in C++11. Bitcasts from cv void* are
+ // also static_casts, but we disallow them as a resolution to DR1312.
+ //
// In some circumstances, we permit casting from void* to cv1 T*, when the
// actual pointee object is actually a cv2 T.
bool HasValidResult = !Result.InvalidBase && !Result.Designator.Invalid &&
diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp
index 6a74e98..760b2fc 100644
--- a/clang/lib/AST/RecordLayoutBuilder.cpp
+++ b/clang/lib/AST/RecordLayoutBuilder.cpp
@@ -1953,7 +1953,7 @@ void ItaniumRecordLayoutBuilder::LayoutField(const FieldDecl *D,
// silently there. For other targets that have ms_struct enabled
// (most probably via a pragma or attribute), trigger a diagnostic
// that defaults to an error.
- if (!Context.getTargetInfo().getTriple().isWindowsGNUEnvironment())
+ if (!Context.getTargetInfo().getTriple().isOSCygMing())
Diag(D->getLocation(), diag::warn_npot_ms_struct);
}
if (TypeSize > FieldAlign &&
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index ac47b12..40dff7e 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -25,6 +25,7 @@
#include "clang/Basic/SourceLocation.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/APInt.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/ADT/SmallSet.h"
@@ -809,28 +810,86 @@ static bool hasUnsafeFormatOrSArg(const CallExpr *Call, const Expr *&UnsafeArg,
const CallExpr *Call;
unsigned FmtArgIdx;
const Expr *&UnsafeArg;
+ ASTContext &Ctx;
+
+ // Returns an `Expr` representing the precision if specified, null
+ // otherwise.
+ // The parameter `Call` is a printf call and the parameter `Precision` is
+ // the precision of a format specifier of the `Call`.
+ //
+ // For example, for the `printf("%d, %.10s", 10, p)` call
+ // `Precision` can be the precision of either "%d" or "%.10s". The former
+ // one will have `NotSpecified` kind.
+ const Expr *
+ getPrecisionAsExpr(const analyze_printf::OptionalAmount &Precision,
+ const CallExpr *Call) {
+ unsigned PArgIdx = -1;
+
+ if (Precision.hasDataArgument())
+ PArgIdx = Precision.getPositionalArgIndex() + FmtArgIdx;
+ if (0 < PArgIdx && PArgIdx < Call->getNumArgs()) {
+ const Expr *PArg = Call->getArg(PArgIdx);
+
+ // Strip the cast if `PArg` is a cast-to-int expression:
+ if (auto *CE = dyn_cast<CastExpr>(PArg);
+ CE && CE->getType()->isSignedIntegerType())
+ PArg = CE->getSubExpr();
+ return PArg;
+ }
+ if (Precision.getHowSpecified() ==
+ analyze_printf::OptionalAmount::HowSpecified::Constant) {
+ auto SizeTy = Ctx.getSizeType();
+ llvm::APSInt PArgVal = llvm::APSInt(
+ llvm::APInt(Ctx.getTypeSize(SizeTy), Precision.getConstantAmount()),
+ true);
+
+ return IntegerLiteral::Create(Ctx, PArgVal, Ctx.getSizeType(), {});
+ }
+ return nullptr;
+ }
public:
StringFormatStringHandler(const CallExpr *Call, unsigned FmtArgIdx,
- const Expr *&UnsafeArg)
- : Call(Call), FmtArgIdx(FmtArgIdx), UnsafeArg(UnsafeArg) {}
+ const Expr *&UnsafeArg, ASTContext &Ctx)
+ : Call(Call), FmtArgIdx(FmtArgIdx), UnsafeArg(UnsafeArg), Ctx(Ctx) {}
bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS,
const char *startSpecifier,
unsigned specifierLen,
const TargetInfo &Target) override {
- if (FS.getConversionSpecifier().getKind() ==
- analyze_printf::PrintfConversionSpecifier::sArg) {
- unsigned ArgIdx = FS.getPositionalArgIndex() + FmtArgIdx;
-
- if (0 < ArgIdx && ArgIdx < Call->getNumArgs())
- if (!isNullTermPointer(Call->getArg(ArgIdx))) {
- UnsafeArg = Call->getArg(ArgIdx); // output
- // returning false stops parsing immediately
- return false;
- }
- }
- return true; // continue parsing
+ if (FS.getConversionSpecifier().getKind() !=
+ analyze_printf::PrintfConversionSpecifier::sArg)
+ return true; // continue parsing
+
+ unsigned ArgIdx = FS.getPositionalArgIndex() + FmtArgIdx;
+
+ if (!(0 < ArgIdx && ArgIdx < Call->getNumArgs()))
+ // If the `ArgIdx` is invalid, give up.
+ return true; // continue parsing
+
+ const Expr *Arg = Call->getArg(ArgIdx);
+
+ if (isNullTermPointer(Arg))
+ // If Arg is a null-terminated pointer, it is safe anyway.
+ return true; // continue parsing
+
+ // Otherwise, check if the specifier has a precision and if the character
+ // pointer is safely bound by the precision:
+ auto LengthModifier = FS.getLengthModifier();
+ QualType ArgType = Arg->getType();
+ bool IsArgTypeValid = // Is ArgType a character pointer type?
+ ArgType->isPointerType() &&
+ (LengthModifier.getKind() == LengthModifier.AsWideChar
+ ? ArgType->getPointeeType()->isWideCharType()
+ : ArgType->getPointeeType()->isCharType());
+
+ if (auto *Precision = getPrecisionAsExpr(FS.getPrecision(), Call);
+ Precision && IsArgTypeValid)
+ if (isPtrBufferSafe(Arg, Precision, Ctx))
+ return true;
+ // Handle unsafe case:
+ UnsafeArg = Call->getArg(ArgIdx); // output
+ return false; // returning false stops parsing immediately
}
};
@@ -846,7 +905,7 @@ static bool hasUnsafeFormatOrSArg(const CallExpr *Call, const Expr *&UnsafeArg,
else
goto CHECK_UNSAFE_PTR;
- StringFormatStringHandler Handler(Call, FmtArgIdx, UnsafeArg);
+ StringFormatStringHandler Handler(Call, FmtArgIdx, UnsafeArg, Ctx);
return analyze_format_string::ParsePrintfString(
Handler, FmtStr.begin(), FmtStr.end(), Ctx.getLangOpts(),
diff --git a/clang/lib/Basic/FileManager.cpp b/clang/lib/Basic/FileManager.cpp
index fc4ec78..7481e1e 100644
--- a/clang/lib/Basic/FileManager.cpp
+++ b/clang/lib/Basic/FileManager.cpp
@@ -368,11 +368,6 @@ void FileManager::trackVFSUsage(bool Active) {
});
}
-const FileEntry *FileManager::getVirtualFile(StringRef Filename, off_t Size,
- time_t ModificationTime) {
- return &getVirtualFileRef(Filename, Size, ModificationTime).getFileEntry();
-}
-
FileEntryRef FileManager::getVirtualFileRef(StringRef Filename, off_t Size,
time_t ModificationTime) {
++NumFileLookups;
diff --git a/clang/lib/Basic/Targets/AMDGPU.cpp b/clang/lib/Basic/Targets/AMDGPU.cpp
index cebcfa3..52cbdbc 100644
--- a/clang/lib/Basic/Targets/AMDGPU.cpp
+++ b/clang/lib/Basic/Targets/AMDGPU.cpp
@@ -266,8 +266,11 @@ AMDGPUTargetInfo::AMDGPUTargetInfo(const llvm::Triple &Triple,
MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 64;
CUMode = !(GPUFeatures & llvm::AMDGPU::FEATURE_WGP);
- for (auto F : {"image-insts", "gws", "vmem-to-lds-load-insts"})
- ReadOnlyFeatures.insert(F);
+
+ for (auto F : {"image-insts", "gws", "vmem-to-lds-load-insts"}) {
+ if (GPUKind != llvm::AMDGPU::GK_NONE)
+ ReadOnlyFeatures.insert(F);
+ }
HalfArgsAndReturns = true;
}
diff --git a/clang/lib/Basic/Targets/WebAssembly.cpp b/clang/lib/Basic/Targets/WebAssembly.cpp
index e362350e..55ffe1d 100644
--- a/clang/lib/Basic/Targets/WebAssembly.cpp
+++ b/clang/lib/Basic/Targets/WebAssembly.cpp
@@ -59,12 +59,12 @@ bool WebAssemblyTargetInfo::hasFeature(StringRef Feature) const {
.Case("exception-handling", HasExceptionHandling)
.Case("extended-const", HasExtendedConst)
.Case("fp16", HasFP16)
+ .Case("gc", HasGC)
.Case("multimemory", HasMultiMemory)
.Case("multivalue", HasMultivalue)
.Case("mutable-globals", HasMutableGlobals)
.Case("nontrapping-fptoint", HasNontrappingFPToInt)
.Case("reference-types", HasReferenceTypes)
- .Case("gc", HasGC)
.Case("relaxed-simd", SIMDLevel >= RelaxedSIMD)
.Case("sign-ext", HasSignExt)
.Case("simd128", SIMDLevel >= SIMD128)
@@ -99,6 +99,8 @@ void WebAssemblyTargetInfo::getTargetDefines(const LangOptions &Opts,
Builder.defineMacro("__wasm_multimemory__");
if (HasFP16)
Builder.defineMacro("__wasm_fp16__");
+ if (HasGC)
+ Builder.defineMacro("__wasm_gc__");
if (HasMultivalue)
Builder.defineMacro("__wasm_multivalue__");
if (HasMutableGlobals)
@@ -107,8 +109,6 @@ void WebAssemblyTargetInfo::getTargetDefines(const LangOptions &Opts,
Builder.defineMacro("__wasm_nontrapping_fptoint__");
if (HasReferenceTypes)
Builder.defineMacro("__wasm_reference_types__");
- if (HasGC)
- Builder.defineMacro("__wasm_gc__");
if (SIMDLevel >= RelaxedSIMD)
Builder.defineMacro("__wasm_relaxed_simd__");
if (HasSignExt)
@@ -194,6 +194,7 @@ bool WebAssemblyTargetInfo::initFeatureMap(
Features["exception-handling"] = true;
Features["extended-const"] = true;
Features["fp16"] = true;
+ Features["gc"] = true;
Features["multimemory"] = true;
Features["tail-call"] = true;
Features["wide-arithmetic"] = true;
@@ -270,6 +271,14 @@ bool WebAssemblyTargetInfo::handleTargetFeatures(
HasFP16 = false;
continue;
}
+ if (Feature == "+gc") {
+ HasGC = true;
+ continue;
+ }
+ if (Feature == "-gc") {
+ HasGC = false;
+ continue;
+ }
if (Feature == "+multimemory") {
HasMultiMemory = true;
continue;
@@ -310,14 +319,6 @@ bool WebAssemblyTargetInfo::handleTargetFeatures(
HasReferenceTypes = false;
continue;
}
- if (Feature == "+gc") {
- HasGC = true;
- continue;
- }
- if (Feature == "-gc") {
- HasGC = false;
- continue;
- }
if (Feature == "+relaxed-simd") {
SIMDLevel = std::max(SIMDLevel, RelaxedSIMD);
continue;
diff --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h
index c47c8cc..eba7422 100644
--- a/clang/lib/Basic/Targets/WebAssembly.h
+++ b/clang/lib/Basic/Targets/WebAssembly.h
@@ -64,12 +64,12 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo {
bool HasExceptionHandling = false;
bool HasExtendedConst = false;
bool HasFP16 = false;
+ bool HasGC = false;
bool HasMultiMemory = false;
bool HasMultivalue = false;
bool HasMutableGlobals = false;
bool HasNontrappingFPToInt = false;
bool HasReferenceTypes = false;
- bool HasGC = false;
bool HasSignExt = false;
bool HasTailCall = false;
bool HasWideArithmetic = false;
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index ef136f8..9049a01 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -190,6 +190,11 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
assert(!cir::MissingFeatures::builtinCheckKind());
return emitBuiltinBitOp<cir::BitClzOp>(*this, e, /*poisonZero=*/true);
+ case Builtin::BI__builtin_ffs:
+ case Builtin::BI__builtin_ffsl:
+ case Builtin::BI__builtin_ffsll:
+ return emitBuiltinBitOp<cir::BitFfsOp>(*this, e);
+
case Builtin::BI__builtin_parity:
case Builtin::BI__builtin_parityl:
case Builtin::BI__builtin_parityll:
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 938d143..fc208ff 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -582,6 +582,14 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo,
cir::FuncOp directFuncOp;
if (auto fnOp = dyn_cast<cir::FuncOp>(calleePtr)) {
directFuncOp = fnOp;
+ } else if (auto getGlobalOp = mlir::dyn_cast<cir::GetGlobalOp>(calleePtr)) {
+ // FIXME(cir): This peephole optimization avoids indirect calls for
+ // builtins. This should be fixed in the builtin declaration instead by
+ // not emitting an unecessary get_global in the first place.
+ // However, this is also used for no-prototype functions.
+ mlir::Operation *globalOp = cgm.getGlobalValue(getGlobalOp.getName());
+ assert(globalOp && "undefined global function");
+ directFuncOp = mlir::cast<cir::FuncOp>(globalOp);
} else {
[[maybe_unused]] mlir::ValueTypeRange<mlir::ResultRange> resultTypes =
calleePtr->getResultTypes();
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.h b/clang/lib/CIR/CodeGen/CIRGenCall.h
index bd11329..a78956b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.h
@@ -116,6 +116,11 @@ public:
assert(isOrdinary());
return reinterpret_cast<mlir::Operation *>(kindOrFunctionPtr);
}
+
+ void setFunctionPointer(mlir::Operation *functionPtr) {
+ assert(isOrdinary());
+ kindOrFunctionPtr = SpecialKind(reinterpret_cast<uintptr_t>(functionPtr));
+ }
};
/// Type for representing both the decl and type of parameters to a function.
diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
index a28ac3c..6527fb5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -649,6 +649,38 @@ void CIRGenFunction::emitNullabilityCheck(LValue lhs, mlir::Value rhs,
assert(!cir::MissingFeatures::sanitizers());
}
+/// Destroys all the elements of the given array, beginning from last to first.
+/// The array cannot be zero-length.
+///
+/// \param begin - a type* denoting the first element of the array
+/// \param end - a type* denoting one past the end of the array
+/// \param elementType - the element type of the array
+/// \param destroyer - the function to call to destroy elements
+void CIRGenFunction::emitArrayDestroy(mlir::Value begin, mlir::Value end,
+ QualType elementType,
+ CharUnits elementAlign,
+ Destroyer *destroyer) {
+ assert(!elementType->isArrayType());
+
+ // Differently from LLVM traditional codegen, use a higher level
+ // representation instead of lowering directly to a loop.
+ mlir::Type cirElementType = convertTypeForMem(elementType);
+ cir::PointerType ptrToElmType = builder.getPointerTo(cirElementType);
+
+ // Emit the dtor call that will execute for every array element.
+ cir::ArrayDtor::create(
+ builder, *currSrcLoc, begin, [&](mlir::OpBuilder &b, mlir::Location loc) {
+ auto arg = b.getInsertionBlock()->addArgument(ptrToElmType, loc);
+ Address curAddr = Address(arg, cirElementType, elementAlign);
+ assert(!cir::MissingFeatures::dtorCleanups());
+
+ // Perform the actual destruction there.
+ destroyer(*this, curAddr, elementType);
+
+ cir::YieldOp::create(builder, loc);
+ });
+}
+
/// Immediately perform the destruction of the given object.
///
/// \param addr - the address of the object; a type*
@@ -658,10 +690,34 @@ void CIRGenFunction::emitNullabilityCheck(LValue lhs, mlir::Value rhs,
/// elements
void CIRGenFunction::emitDestroy(Address addr, QualType type,
Destroyer *destroyer) {
- if (getContext().getAsArrayType(type))
- cgm.errorNYI("emitDestroy: array type");
+ const ArrayType *arrayType = getContext().getAsArrayType(type);
+ if (!arrayType)
+ return destroyer(*this, addr, type);
+
+ mlir::Value length = emitArrayLength(arrayType, type, addr);
+
+ CharUnits elementAlign = addr.getAlignment().alignmentOfArrayElement(
+ getContext().getTypeSizeInChars(type));
+
+ auto constantCount = length.getDefiningOp<cir::ConstantOp>();
+ if (!constantCount) {
+ assert(!cir::MissingFeatures::vlas());
+ cgm.errorNYI("emitDestroy: variable length array");
+ return;
+ }
+
+ auto constIntAttr = mlir::dyn_cast<cir::IntAttr>(constantCount.getValue());
+ // If it's constant zero, we can just skip the entire thing.
+ if (constIntAttr && constIntAttr.getUInt() == 0)
+ return;
+
+ mlir::Value begin = addr.getPointer();
+ mlir::Value end; // This will be used for future non-constant counts.
+ emitArrayDestroy(begin, end, type, elementAlign, destroyer);
- return destroyer(*this, addr, type);
+ // If the array destroy didn't use the length op, we can erase it.
+ if (constantCount.use_empty())
+ constantCount.erase();
}
CIRGenFunction::Destroyer *
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 64dc1ce..c18498f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1280,7 +1280,7 @@ RValue CIRGenFunction::getUndefRValue(QualType ty) {
}
RValue CIRGenFunction::emitCall(clang::QualType calleeTy,
- const CIRGenCallee &callee,
+ const CIRGenCallee &origCallee,
const clang::CallExpr *e,
ReturnValueSlot returnValue) {
// Get the actual function type. The callee type will always be a pointer to
@@ -1291,6 +1291,8 @@ RValue CIRGenFunction::emitCall(clang::QualType calleeTy,
calleeTy = getContext().getCanonicalType(calleeTy);
auto pointeeTy = cast<PointerType>(calleeTy)->getPointeeType();
+ CIRGenCallee callee = origCallee;
+
if (getLangOpts().CPlusPlus)
assert(!cir::MissingFeatures::sanitizers());
@@ -1307,7 +1309,44 @@ RValue CIRGenFunction::emitCall(clang::QualType calleeTy,
const CIRGenFunctionInfo &funcInfo =
cgm.getTypes().arrangeFreeFunctionCall(args, fnType);
- assert(!cir::MissingFeatures::opCallNoPrototypeFunc());
+ // C99 6.5.2.2p6:
+ // If the expression that denotes the called function has a type that does
+ // not include a prototype, [the default argument promotions are performed].
+ // If the number of arguments does not equal the number of parameters, the
+ // behavior is undefined. If the function is defined with a type that
+ // includes a prototype, and either the prototype ends with an ellipsis (,
+ // ...) or the types of the arguments after promotion are not compatible
+ // with the types of the parameters, the behavior is undefined. If the
+ // function is defined with a type that does not include a prototype, and
+ // the types of the arguments after promotion are not compatible with those
+ // of the parameters after promotion, the behavior is undefined [except in
+ // some trivial cases].
+ // That is, in the general case, we should assume that a call through an
+ // unprototyped function type works like a *non-variadic* call. The way we
+ // make this work is to cast to the exxact type fo the promoted arguments.
+ if (isa<FunctionNoProtoType>(fnType)) {
+ assert(!cir::MissingFeatures::opCallChain());
+ assert(!cir::MissingFeatures::addressSpace());
+ cir::FuncType calleeTy = getTypes().getFunctionType(funcInfo);
+ // get non-variadic function type
+ calleeTy = cir::FuncType::get(calleeTy.getInputs(),
+ calleeTy.getReturnType(), false);
+ auto calleePtrTy = cir::PointerType::get(calleeTy);
+
+ mlir::Operation *fn = callee.getFunctionPointer();
+ mlir::Value addr;
+ if (auto funcOp = mlir::dyn_cast<cir::FuncOp>(fn)) {
+ addr = builder.create<cir::GetGlobalOp>(
+ getLoc(e->getSourceRange()),
+ cir::PointerType::get(funcOp.getFunctionType()), funcOp.getSymName());
+ } else {
+ addr = fn->getResult(0);
+ }
+
+ fn = builder.createBitcast(addr, calleePtrTy).getDefiningOp();
+ callee.setFunctionPointer(fn);
+ }
+
assert(!cir::MissingFeatures::opCallFnInfoOpts());
assert(!cir::MissingFeatures::hip());
assert(!cir::MissingFeatures::opCallMustTail());
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp b/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp
index a09d739..18d2860 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp
@@ -91,6 +91,14 @@ public:
}
mlir::Value VisitUnaryDeref(const Expr *e);
+
+ mlir::Value VisitUnaryPlus(const UnaryOperator *e);
+
+ mlir::Value VisitPlusMinus(const UnaryOperator *e, cir::UnaryOpKind kind,
+ QualType promotionType);
+
+ mlir::Value VisitUnaryMinus(const UnaryOperator *e);
+
mlir::Value VisitUnaryNot(const UnaryOperator *e);
struct BinOpInfo {
@@ -282,6 +290,41 @@ mlir::Value ComplexExprEmitter::emitCast(CastKind ck, Expr *op,
llvm_unreachable("unknown cast resulting in complex value");
}
+mlir::Value ComplexExprEmitter::VisitUnaryPlus(const UnaryOperator *e) {
+ QualType promotionTy = getPromotionType(e->getSubExpr()->getType());
+ mlir::Value result = VisitPlusMinus(e, cir::UnaryOpKind::Plus, promotionTy);
+ if (!promotionTy.isNull()) {
+ cgf.cgm.errorNYI("ComplexExprEmitter::VisitUnaryPlus emitUnPromotedValue");
+ return {};
+ }
+ return result;
+}
+
+mlir::Value ComplexExprEmitter::VisitPlusMinus(const UnaryOperator *e,
+ cir::UnaryOpKind kind,
+ QualType promotionType) {
+ assert(kind == cir::UnaryOpKind::Plus ||
+ kind == cir::UnaryOpKind::Minus &&
+ "Invalid UnaryOp kind for ComplexType Plus or Minus");
+
+ mlir::Value op;
+ if (!promotionType.isNull())
+ op = cgf.emitPromotedComplexExpr(e->getSubExpr(), promotionType);
+ else
+ op = Visit(e->getSubExpr());
+ return builder.createUnaryOp(cgf.getLoc(e->getExprLoc()), kind, op);
+}
+
+mlir::Value ComplexExprEmitter::VisitUnaryMinus(const UnaryOperator *e) {
+ QualType promotionTy = getPromotionType(e->getSubExpr()->getType());
+ mlir::Value result = VisitPlusMinus(e, cir::UnaryOpKind::Minus, promotionTy);
+ if (!promotionTy.isNull()) {
+ cgf.cgm.errorNYI("ComplexExprEmitter::VisitUnaryMinus emitUnPromotedValue");
+ return {};
+ }
+ return result;
+}
+
mlir::Value ComplexExprEmitter::emitConstant(
const CIRGenFunction::ConstantEmission &constant, Expr *e) {
assert(constant && "not a constant");
@@ -538,9 +581,17 @@ mlir::Value ComplexExprEmitter::emitPromoted(const Expr *e,
default:
break;
}
- } else if (isa<UnaryOperator>(e)) {
- cgf.cgm.errorNYI("emitPromoted UnaryOperator");
- return {};
+ } else if (const auto *unaryOp = dyn_cast<UnaryOperator>(e)) {
+ switch (unaryOp->getOpcode()) {
+ case UO_Minus:
+ case UO_Plus: {
+ auto kind = unaryOp->getOpcode() == UO_Plus ? cir::UnaryOpKind::Plus
+ : cir::UnaryOpKind::Minus;
+ return VisitPlusMinus(unaryOp, kind, promotionTy);
+ }
+ default:
+ break;
+ }
}
mlir::Value result = Visit(const_cast<Expr *>(e));
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 77539d7..603f750 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -848,6 +848,10 @@ public:
/// even if no aggregate location is provided.
RValue emitAnyExprToTemp(const clang::Expr *e);
+ void emitArrayDestroy(mlir::Value begin, mlir::Value end,
+ QualType elementType, CharUnits elementAlign,
+ Destroyer *destroyer);
+
mlir::Value emitArrayLength(const clang::ArrayType *arrayType,
QualType &baseType, Address &addr);
LValue emitArraySubscriptExpr(const clang::ArraySubscriptExpr *e);
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 0724cb1..623b84f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -1103,6 +1103,60 @@ cir::GlobalLinkageKind CIRGenModule::getCIRLinkageForDeclarator(
return cir::GlobalLinkageKind::ExternalLinkage;
}
+/// This function is called when we implement a function with no prototype, e.g.
+/// "int foo() {}". If there are existing call uses of the old function in the
+/// module, this adjusts them to call the new function directly.
+///
+/// This is not just a cleanup: the always_inline pass requires direct calls to
+/// functions to be able to inline them. If there is a bitcast in the way, it
+/// won't inline them. Instcombine normally deletes these calls, but it isn't
+/// run at -O0.
+void CIRGenModule::replaceUsesOfNonProtoTypeWithRealFunction(
+ mlir::Operation *old, cir::FuncOp newFn) {
+ // If we're redefining a global as a function, don't transform it.
+ auto oldFn = mlir::dyn_cast<cir::FuncOp>(old);
+ if (!oldFn)
+ return;
+
+ // TODO(cir): this RAUW ignores the features below.
+ assert(!cir::MissingFeatures::opFuncExceptions());
+ assert(!cir::MissingFeatures::opFuncParameterAttributes());
+ assert(!cir::MissingFeatures::opFuncOperandBundles());
+ if (oldFn->getAttrs().size() <= 1)
+ errorNYI(old->getLoc(),
+ "replaceUsesOfNonProtoTypeWithRealFunction: Attribute forwarding");
+
+ // Mark new function as originated from a no-proto declaration.
+ newFn.setNoProto(oldFn.getNoProto());
+
+ // Iterate through all calls of the no-proto function.
+ std::optional<mlir::SymbolTable::UseRange> symUses =
+ oldFn.getSymbolUses(oldFn->getParentOp());
+ for (const mlir::SymbolTable::SymbolUse &use : symUses.value()) {
+ mlir::OpBuilder::InsertionGuard guard(builder);
+
+ if (auto noProtoCallOp = mlir::dyn_cast<cir::CallOp>(use.getUser())) {
+ builder.setInsertionPoint(noProtoCallOp);
+
+ // Patch call type with the real function type.
+ cir::CallOp realCallOp = builder.createCallOp(
+ noProtoCallOp.getLoc(), newFn, noProtoCallOp.getOperands());
+
+ // Replace old no proto call with fixed call.
+ noProtoCallOp.replaceAllUsesWith(realCallOp);
+ noProtoCallOp.erase();
+ } else if (auto getGlobalOp =
+ mlir::dyn_cast<cir::GetGlobalOp>(use.getUser())) {
+ // Replace type
+ getGlobalOp.getAddr().setType(
+ cir::PointerType::get(newFn.getFunctionType()));
+ } else {
+ errorNYI(use.getUser()->getLoc(),
+ "replaceUsesOfNonProtoTypeWithRealFunction: unexpected use");
+ }
+ }
+}
+
cir::GlobalLinkageKind
CIRGenModule::getCIRLinkageVarDefinition(const VarDecl *vd, bool isConstant) {
assert(!isConstant && "constant variables NYI");
@@ -1244,6 +1298,7 @@ void CIRGenModule::emitTopLevelDecl(Decl *decl) {
decl->getDeclKindName());
break;
+ case Decl::CXXConversion:
case Decl::CXXMethod:
case Decl::Function: {
auto *fd = cast<FunctionDecl>(decl);
@@ -1539,10 +1594,10 @@ static bool shouldAssumeDSOLocal(const CIRGenModule &cgm,
const llvm::Triple &tt = cgm.getTriple();
const CodeGenOptions &cgOpts = cgm.getCodeGenOpts();
- if (tt.isWindowsGNUEnvironment()) {
- // In MinGW, variables without DLLImport can still be automatically
- // imported from a DLL by the linker; don't mark variables that
- // potentially could come from another DLL as DSO local.
+ if (tt.isOSCygMing()) {
+ // In MinGW and Cygwin, variables without DLLImport can still be
+ // automatically imported from a DLL by the linker; don't mark variables
+ // that potentially could come from another DLL as DSO local.
// With EmulatedTLS, TLS variables can be autoimported from other DLLs
// (and this actually happens in the public interface of libstdc++), so
@@ -1701,8 +1756,7 @@ cir::FuncOp CIRGenModule::getOrCreateCIRFunction(
// Lookup the entry, lazily creating it if necessary.
mlir::Operation *entry = getGlobalValue(mangledName);
if (entry) {
- if (!isa<cir::FuncOp>(entry))
- errorNYI(d->getSourceRange(), "getOrCreateCIRFunction: non-FuncOp");
+ assert(mlir::isa<cir::FuncOp>(entry));
assert(!cir::MissingFeatures::weakRefReference());
@@ -1738,6 +1792,30 @@ cir::FuncOp CIRGenModule::getOrCreateCIRFunction(
invalidLoc ? theModule->getLoc() : getLoc(funcDecl->getSourceRange()),
mangledName, mlir::cast<cir::FuncType>(funcType), funcDecl);
+ // If we already created a function with the same mangled name (but different
+ // type) before, take its name and add it to the list of functions to be
+ // replaced with F at the end of CodeGen.
+ //
+ // This happens if there is a prototype for a function (e.g. "int f()") and
+ // then a definition of a different type (e.g. "int f(int x)").
+ if (entry) {
+
+ // Fetch a generic symbol-defining operation and its uses.
+ auto symbolOp = mlir::cast<mlir::SymbolOpInterface>(entry);
+
+ // This might be an implementation of a function without a prototype, in
+ // which case, try to do special replacement of calls which match the new
+ // prototype. The really key thing here is that we also potentially drop
+ // arguments from the call site so as to make a direct call, which makes the
+ // inliner happier and suppresses a number of optimizer warnings (!) about
+ // dropping arguments.
+ if (symbolOp.getSymbolUses(symbolOp->getParentOp()))
+ replaceUsesOfNonProtoTypeWithRealFunction(entry, funcOp);
+
+ // Obliterate no-proto declaration.
+ entry->erase();
+ }
+
if (d)
setFunctionAttributes(gd, funcOp, /*isIncompleteFunction=*/false, isThunk);
@@ -1814,7 +1892,9 @@ CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name,
func = builder.create<cir::FuncOp>(loc, name, funcType);
assert(!cir::MissingFeatures::opFuncAstDeclAttr());
- assert(!cir::MissingFeatures::opFuncNoProto());
+
+ if (funcDecl && !funcDecl->hasPrototype())
+ func.setNoProto(true);
assert(func.isDeclaration() && "expected empty body");
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 22519ff..5d07d38 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -313,6 +313,9 @@ public:
static void setInitializer(cir::GlobalOp &op, mlir::Attribute value);
+ void replaceUsesOfNonProtoTypeWithRealFunction(mlir::Operation *old,
+ cir::FuncOp newFn);
+
cir::FuncOp
getOrCreateCIRFunction(llvm::StringRef mangledName, mlir::Type funcType,
clang::GlobalDecl gd, bool forVTable,
diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
index 05e8848..e4ec380 100644
--- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
@@ -438,9 +438,7 @@ CIRRecordLowering::accumulateBitFields(RecordDecl::field_iterator field,
} else if (cirGenTypes.getCGModule()
.getCodeGenOpts()
.FineGrainedBitfieldAccesses) {
- assert(!cir::MissingFeatures::nonFineGrainedBitfields());
- cirGenTypes.getCGModule().errorNYI(field->getSourceRange(),
- "NYI FineGrainedBitfield");
+ installBest = true;
} else {
// Otherwise, we're not installing. Update the bit size
// of the current span to go all the way to limitOffset, which is
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 2213c75..1c3a310 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -1470,10 +1470,14 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
llvm::SMLoc loc = parser.getCurrentLocation();
mlir::Builder &builder = parser.getBuilder();
+ mlir::StringAttr noProtoNameAttr = getNoProtoAttrName(state.name);
mlir::StringAttr visNameAttr = getSymVisibilityAttrName(state.name);
mlir::StringAttr visibilityNameAttr = getGlobalVisibilityAttrName(state.name);
mlir::StringAttr dsoLocalNameAttr = getDsoLocalAttrName(state.name);
+ if (parser.parseOptionalKeyword(noProtoNameAttr).succeeded())
+ state.addAttribute(noProtoNameAttr, parser.getBuilder().getUnitAttr());
+
// Default to external linkage if no keyword is provided.
state.addAttribute(getLinkageAttrNameString(),
GlobalLinkageKindAttr::get(
@@ -1578,6 +1582,9 @@ mlir::Region *cir::FuncOp::getCallableRegion() {
}
void cir::FuncOp::print(OpAsmPrinter &p) {
+ if (getNoProto())
+ p << " no_proto";
+
if (getComdat())
p << " comdat";
@@ -2295,6 +2302,15 @@ OpFoldResult BitCtzOp::fold(FoldAdaptor adaptor) {
getPoisonZero());
}
+OpFoldResult BitFfsOp::fold(FoldAdaptor adaptor) {
+ return foldUnaryBitOp(adaptor.getInput(), [](const llvm::APInt &inputValue) {
+ unsigned trailingZeros = inputValue.countTrailingZeros();
+ unsigned result =
+ trailingZeros == inputValue.getBitWidth() ? 0 : trailingZeros + 1;
+ return llvm::APInt(inputValue.getBitWidth(), result);
+ });
+}
+
OpFoldResult BitParityOp::fold(FoldAdaptor adaptor) {
return foldUnaryBitOp(adaptor.getInput(), [](const llvm::APInt &inputValue) {
return llvm::APInt(inputValue.getBitWidth(), inputValue.popcount() % 2);
diff --git a/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp b/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp
index 2143f16..2eaa60c 100644
--- a/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp
@@ -143,7 +143,7 @@ void CIRCanonicalizePass::runOnOperation() {
if (isa<BrOp, BrCondOp, CastOp, ScopeOp, SwitchOp, SelectOp, UnaryOp,
ComplexCreateOp, ComplexImagOp, ComplexRealOp, VecCmpOp,
VecCreateOp, VecExtractOp, VecShuffleOp, VecShuffleDynamicOp,
- VecTernaryOp, BitClrsbOp, BitClzOp, BitCtzOp, BitParityOp,
+ VecTernaryOp, BitClrsbOp, BitClzOp, BitCtzOp, BitFfsOp, BitParityOp,
BitPopcountOp, BitReverseOp, ByteSwapOp, RotateOp>(op))
ops.push_back(op);
});
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index cef83ea..670ddaf 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -29,7 +29,8 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
void runOnOp(mlir::Operation *op);
void lowerCastOp(cir::CastOp op);
void lowerUnaryOp(cir::UnaryOp op);
- void lowerArrayCtor(ArrayCtor op);
+ void lowerArrayDtor(cir::ArrayDtor op);
+ void lowerArrayCtor(cir::ArrayCtor op);
///
/// AST related
@@ -154,7 +155,8 @@ void LoweringPreparePass::lowerUnaryOp(cir::UnaryOp op) {
case cir::UnaryOpKind::Plus:
case cir::UnaryOpKind::Minus:
- llvm_unreachable("Complex unary Plus/Minus NYI");
+ resultReal = builder.createUnaryOp(loc, opKind, operandReal);
+ resultImag = builder.createUnaryOp(loc, opKind, operandImag);
break;
case cir::UnaryOpKind::Not:
@@ -172,28 +174,30 @@ void LoweringPreparePass::lowerUnaryOp(cir::UnaryOp op) {
static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
clang::ASTContext *astCtx,
mlir::Operation *op, mlir::Type eltTy,
- mlir::Value arrayAddr,
- uint64_t arrayLen) {
+ mlir::Value arrayAddr, uint64_t arrayLen,
+ bool isCtor) {
// Generate loop to call into ctor/dtor for every element.
mlir::Location loc = op->getLoc();
- // TODO: instead of fixed integer size, create alias for PtrDiffTy and unify
- // with CIRGen stuff.
+ // TODO: instead of getting the size from the AST context, create alias for
+ // PtrDiffTy and unify with CIRGen stuff.
const unsigned sizeTypeSize =
astCtx->getTypeSize(astCtx->getSignedSizeType());
- auto ptrDiffTy =
- cir::IntType::get(builder.getContext(), sizeTypeSize, /*isSigned=*/false);
- mlir::Value numArrayElementsConst = builder.getUnsignedInt(loc, arrayLen, 64);
+ uint64_t endOffset = isCtor ? arrayLen : arrayLen - 1;
+ mlir::Value endOffsetVal =
+ builder.getUnsignedInt(loc, endOffset, sizeTypeSize);
- auto begin = builder.create<cir::CastOp>(
- loc, eltTy, cir::CastKind::array_to_ptrdecay, arrayAddr);
- mlir::Value end = builder.create<cir::PtrStrideOp>(loc, eltTy, begin,
- numArrayElementsConst);
+ auto begin = cir::CastOp::create(builder, loc, eltTy,
+ cir::CastKind::array_to_ptrdecay, arrayAddr);
+ mlir::Value end =
+ cir::PtrStrideOp::create(builder, loc, eltTy, begin, endOffsetVal);
+ mlir::Value start = isCtor ? begin : end;
+ mlir::Value stop = isCtor ? end : begin;
mlir::Value tmpAddr = builder.createAlloca(
loc, /*addr type*/ builder.getPointerTo(eltTy),
/*var type*/ eltTy, "__array_idx", builder.getAlignmentAttr(1));
- builder.createStore(loc, begin, tmpAddr);
+ builder.createStore(loc, start, tmpAddr);
cir::DoWhileOp loop = builder.createDoWhile(
loc,
@@ -202,7 +206,7 @@ static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
auto currentElement = b.create<cir::LoadOp>(loc, eltTy, tmpAddr);
mlir::Type boolTy = cir::BoolType::get(b.getContext());
auto cmp = builder.create<cir::CmpOp>(loc, boolTy, cir::CmpOpKind::ne,
- currentElement, end);
+ currentElement, stop);
builder.createCondition(cmp);
},
/*bodyBuilder=*/
@@ -213,15 +217,19 @@ static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
op->walk([&](cir::CallOp c) { ctorCall = c; });
assert(ctorCall && "expected ctor call");
- auto one = builder.create<cir::ConstantOp>(
- loc, ptrDiffTy, cir::IntAttr::get(ptrDiffTy, 1));
+ // Array elements get constructed in order but destructed in reverse.
+ mlir::Value stride;
+ if (isCtor)
+ stride = builder.getUnsignedInt(loc, 1, sizeTypeSize);
+ else
+ stride = builder.getSignedInt(loc, -1, sizeTypeSize);
- ctorCall->moveAfter(one);
+ ctorCall->moveBefore(stride.getDefiningOp());
ctorCall->setOperand(0, currentElement);
+ auto nextElement = cir::PtrStrideOp::create(builder, loc, eltTy,
+ currentElement, stride);
- // Advance pointer and store them to temporary variable
- auto nextElement =
- builder.create<cir::PtrStrideOp>(loc, eltTy, currentElement, one);
+ // Store the element pointer to the temporary variable
builder.createStore(loc, nextElement, tmpAddr);
builder.createYield(loc);
});
@@ -230,6 +238,18 @@ static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
op->erase();
}
+void LoweringPreparePass::lowerArrayDtor(cir::ArrayDtor op) {
+ CIRBaseBuilderTy builder(getContext());
+ builder.setInsertionPointAfter(op.getOperation());
+
+ mlir::Type eltTy = op->getRegion(0).getArgument(0).getType();
+ assert(!cir::MissingFeatures::vlas());
+ auto arrayLen =
+ mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
+ lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(), arrayLen,
+ false);
+}
+
void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) {
cir::CIRBaseBuilderTy builder(getContext());
builder.setInsertionPointAfter(op.getOperation());
@@ -238,13 +258,15 @@ void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) {
assert(!cir::MissingFeatures::vlas());
auto arrayLen =
mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
- lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(),
- arrayLen);
+ lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(), arrayLen,
+ true);
}
void LoweringPreparePass::runOnOp(mlir::Operation *op) {
if (auto arrayCtor = dyn_cast<ArrayCtor>(op))
lowerArrayCtor(arrayCtor);
+ else if (auto arrayDtor = dyn_cast<cir::ArrayDtor>(op))
+ lowerArrayDtor(arrayDtor);
else if (auto cast = mlir::dyn_cast<cir::CastOp>(op))
lowerCastOp(cast);
else if (auto unary = mlir::dyn_cast<cir::UnaryOp>(op))
@@ -257,7 +279,8 @@ void LoweringPreparePass::runOnOperation() {
llvm::SmallVector<mlir::Operation *> opsToTransform;
op->walk([&](mlir::Operation *op) {
- if (mlir::isa<cir::ArrayCtor, cir::CastOp, cir::UnaryOp>(op))
+ if (mlir::isa<cir::ArrayCtor, cir::ArrayDtor, cir::CastOp, cir::UnaryOp>(
+ op))
opsToTransform.push_back(op);
});
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index c27b889..957a51a 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -521,6 +521,32 @@ mlir::LogicalResult CIRToLLVMBitCtzOpLowering::matchAndRewrite(
return mlir::LogicalResult::success();
}
+mlir::LogicalResult CIRToLLVMBitFfsOpLowering::matchAndRewrite(
+ cir::BitFfsOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ auto resTy = getTypeConverter()->convertType(op.getType());
+ auto ctz = rewriter.create<mlir::LLVM::CountTrailingZerosOp>(
+ op.getLoc(), resTy, adaptor.getInput(), /*is_zero_poison=*/true);
+
+ auto one = rewriter.create<mlir::LLVM::ConstantOp>(op.getLoc(), resTy, 1);
+ auto ctzAddOne = rewriter.create<mlir::LLVM::AddOp>(op.getLoc(), ctz, one);
+
+ auto zeroInputTy = rewriter.create<mlir::LLVM::ConstantOp>(
+ op.getLoc(), adaptor.getInput().getType(), 0);
+ auto isZero = rewriter.create<mlir::LLVM::ICmpOp>(
+ op.getLoc(),
+ mlir::LLVM::ICmpPredicateAttr::get(rewriter.getContext(),
+ mlir::LLVM::ICmpPredicate::eq),
+ adaptor.getInput(), zeroInputTy);
+
+ auto zero = rewriter.create<mlir::LLVM::ConstantOp>(op.getLoc(), resTy, 0);
+ auto res = rewriter.create<mlir::LLVM::SelectOp>(op.getLoc(), isZero, zero,
+ ctzAddOne);
+ rewriter.replaceOp(op, res);
+
+ return mlir::LogicalResult::success();
+}
+
mlir::LogicalResult CIRToLLVMBitParityOpLowering::matchAndRewrite(
cir::BitParityOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
@@ -919,13 +945,45 @@ rewriteCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands,
memoryEffects, noUnwind, willReturn);
mlir::LLVM::LLVMFunctionType llvmFnTy;
+
+ // Temporary to handle the case where we need to prepend an operand if the
+ // callee is an alias.
+ SmallVector<mlir::Value> adjustedCallOperands;
+
if (calleeAttr) { // direct call
- mlir::FunctionOpInterface fn =
- mlir::SymbolTable::lookupNearestSymbolFrom<mlir::FunctionOpInterface>(
- op, calleeAttr);
- assert(fn && "Did not find function for call");
- llvmFnTy = cast<mlir::LLVM::LLVMFunctionType>(
- converter->convertType(fn.getFunctionType()));
+ mlir::Operation *callee =
+ mlir::SymbolTable::lookupNearestSymbolFrom(op, calleeAttr);
+ if (auto fn = mlir::dyn_cast<mlir::FunctionOpInterface>(callee)) {
+ llvmFnTy = converter->convertType<mlir::LLVM::LLVMFunctionType>(
+ fn.getFunctionType());
+ assert(llvmFnTy && "Failed to convert function type");
+ } else if (auto alias = mlir::cast<mlir::LLVM::AliasOp>(callee)) {
+ // If the callee was an alias. In that case,
+ // we need to prepend the address of the alias to the operands. The
+ // way aliases work in the LLVM dialect is a little counter-intuitive.
+ // The AliasOp itself is a pseudo-function that returns the address of
+ // the global value being aliased, but when we generate the call we
+ // need to insert an operation that gets the address of the AliasOp.
+ // This all gets sorted out when the LLVM dialect is lowered to LLVM IR.
+ auto symAttr = mlir::cast<mlir::FlatSymbolRefAttr>(calleeAttr);
+ auto addrOfAlias =
+ mlir::LLVM::AddressOfOp::create(
+ rewriter, op->getLoc(),
+ mlir::LLVM::LLVMPointerType::get(rewriter.getContext()), symAttr)
+ .getResult();
+ adjustedCallOperands.push_back(addrOfAlias);
+
+ // Now add the regular operands and assign this to the range value.
+ llvm::append_range(adjustedCallOperands, callOperands);
+ callOperands = adjustedCallOperands;
+
+ // Clear the callee attribute because we're calling an alias.
+ calleeAttr = {};
+ llvmFnTy = mlir::cast<mlir::LLVM::LLVMFunctionType>(alias.getType());
+ } else {
+ // Was this an ifunc?
+ return op->emitError("Unexpected callee type!");
+ }
} else { // indirect call
assert(!op->getOperands().empty() &&
"operands list must no be empty for the indirect call");
@@ -1172,6 +1230,30 @@ void CIRToLLVMFuncOpLowering::lowerFuncAttributes(
}
}
+mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewriteAlias(
+ cir::FuncOp op, llvm::StringRef aliasee, mlir::Type ty, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ SmallVector<mlir::NamedAttribute, 4> attributes;
+ lowerFuncAttributes(op, /*filterArgAndResAttrs=*/false, attributes);
+
+ mlir::Location loc = op.getLoc();
+ auto aliasOp = rewriter.replaceOpWithNewOp<mlir::LLVM::AliasOp>(
+ op, ty, convertLinkage(op.getLinkage()), op.getName(), op.getDsoLocal(),
+ /*threadLocal=*/false, attributes);
+
+ // Create the alias body
+ mlir::OpBuilder builder(op.getContext());
+ mlir::Block *block = builder.createBlock(&aliasOp.getInitializerRegion());
+ builder.setInsertionPointToStart(block);
+ // The type of AddressOfOp is always a pointer.
+ assert(!cir::MissingFeatures::addressSpace());
+ mlir::Type ptrTy = mlir::LLVM::LLVMPointerType::get(ty.getContext());
+ auto addrOp = mlir::LLVM::AddressOfOp::create(builder, loc, ptrTy, aliasee);
+ mlir::LLVM::ReturnOp::create(builder, loc, addrOp);
+
+ return mlir::success();
+}
+
mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite(
cir::FuncOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
@@ -1196,6 +1278,11 @@ mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite(
resultType ? resultType : mlir::LLVM::LLVMVoidType::get(getContext()),
signatureConversion.getConvertedTypes(),
/*isVarArg=*/fnType.isVarArg());
+
+ // If this is an alias, it needs to be lowered to llvm::AliasOp.
+ if (std::optional<llvm::StringRef> aliasee = op.getAliasee())
+ return matchAndRewriteAlias(op, *aliasee, llvmFnTy, adaptor, rewriter);
+
// LLVMFuncOp expects a single FileLine Location instead of a fused
// location.
mlir::Location loc = op.getLoc();
@@ -2089,6 +2176,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
CIRToLLVMBitClrsbOpLowering,
CIRToLLVMBitClzOpLowering,
CIRToLLVMBitCtzOpLowering,
+ CIRToLLVMBitFfsOpLowering,
CIRToLLVMBitParityOpLowering,
CIRToLLVMBitPopcountOpLowering,
CIRToLLVMBitReverseOpLowering,
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
index 2911ced..f339d43 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -84,6 +84,16 @@ public:
mlir::ConversionPatternRewriter &) const override;
};
+class CIRToLLVMBitFfsOpLowering
+ : public mlir::OpConversionPattern<cir::BitFfsOp> {
+public:
+ using mlir::OpConversionPattern<cir::BitFfsOp>::OpConversionPattern;
+
+ mlir::LogicalResult
+ matchAndRewrite(cir::BitFfsOp op, OpAdaptor,
+ mlir::ConversionPatternRewriter &) const override;
+};
+
class CIRToLLVMBitParityOpLowering
: public mlir::OpConversionPattern<cir::BitParityOp> {
public:
@@ -257,6 +267,11 @@ class CIRToLLVMFuncOpLowering : public mlir::OpConversionPattern<cir::FuncOp> {
cir::FuncOp func, bool filterArgAndResAttrs,
mlir::SmallVectorImpl<mlir::NamedAttribute> &result) const;
+ mlir::LogicalResult
+ matchAndRewriteAlias(cir::FuncOp op, llvm::StringRef aliasee, mlir::Type ty,
+ OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const;
+
public:
using mlir::OpConversionPattern<cir::FuncOp>::OpConversionPattern;
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 3f784fc..e1f7ea0 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -1148,7 +1148,8 @@ llvm::Value *CodeGenFunction::emitCountedByPointerSize(
assert(E->getCastKind() == CK_LValueToRValue &&
"must be an LValue to RValue cast");
- const MemberExpr *ME = dyn_cast<MemberExpr>(E->getSubExpr());
+ const MemberExpr *ME =
+ dyn_cast<MemberExpr>(E->getSubExpr()->IgnoreParenNoopCasts(getContext()));
if (!ME)
return nullptr;
diff --git a/clang/lib/CodeGen/CGCoroutine.cpp b/clang/lib/CodeGen/CGCoroutine.cpp
index 5ee9089..827385f 100644
--- a/clang/lib/CodeGen/CGCoroutine.cpp
+++ b/clang/lib/CodeGen/CGCoroutine.cpp
@@ -435,7 +435,7 @@ CodeGenFunction::generateAwaitSuspendWrapper(Twine const &CoroName,
llvm::FunctionType *LTy = CGM.getTypes().GetFunctionType(FI);
llvm::Function *Fn = llvm::Function::Create(
- LTy, llvm::GlobalValue::PrivateLinkage, FuncName, &CGM.getModule());
+ LTy, llvm::GlobalValue::InternalLinkage, FuncName, &CGM.getModule());
Fn->addParamAttr(0, llvm::Attribute::AttrKind::NonNull);
Fn->addParamAttr(0, llvm::Attribute::AttrKind::NoUndef);
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 77fc3a2..7a69b5d 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -2641,6 +2641,8 @@ void CGDebugInfo::emitVTableSymbol(llvm::GlobalVariable *VTable,
const CXXRecordDecl *RD) {
if (!CGM.getTarget().getCXXABI().isItaniumFamily())
return;
+ if (DebugKind <= llvm::codegenoptions::DebugLineTablesOnly)
+ return;
ASTContext &Context = CGM.getContext();
StringRef SymbolName = "_vtable$";
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 90aed79..5a3d4e4 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -93,6 +93,7 @@ static llvm::StringRef GetUBSanTrapForHandler(SanitizerHandler ID) {
LIST_SANITIZER_CHECKS
#undef SANITIZER_CHECK
}
+ llvm_unreachable("unhandled switch case");
}
/// CreateTempAlloca - This creates a alloca and inserts it into the entry
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 99de951..586f287 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -3606,7 +3606,7 @@ class OffloadingActionBuilder final {
if (!CompileDeviceOnly) {
C.getDriver().Diag(diag::err_opt_not_valid_without_opt)
<< "-fhip-emit-relocatable"
- << "--cuda-device-only";
+ << "--offload-device-only";
}
}
}
@@ -4774,6 +4774,21 @@ Action *Driver::BuildOffloadingActions(Compilation &C,
C.isOffloadingHostKind(Action::OFK_HIP) &&
!Args.hasFlag(options::OPT_fgpu_rdc, options::OPT_fno_gpu_rdc, false);
+ bool HIPRelocatableObj =
+ C.isOffloadingHostKind(Action::OFK_HIP) &&
+ Args.hasFlag(options::OPT_fhip_emit_relocatable,
+ options::OPT_fno_hip_emit_relocatable, false);
+
+ if (!HIPNoRDC && HIPRelocatableObj)
+ C.getDriver().Diag(diag::err_opt_not_valid_with_opt)
+ << "-fhip-emit-relocatable"
+ << "-fgpu-rdc";
+
+ if (!offloadDeviceOnly() && HIPRelocatableObj)
+ C.getDriver().Diag(diag::err_opt_not_valid_without_opt)
+ << "-fhip-emit-relocatable"
+ << "--offload-device-only";
+
// For HIP non-rdc non-device-only compilation, create a linker wrapper
// action for each host object to link, bundle and wrap device files in
// it.
@@ -4894,7 +4909,7 @@ Action *Driver::BuildOffloadingActions(Compilation &C,
A->getOffloadingToolChain()->getTriple().isSPIRV();
if ((A->getType() != types::TY_Object && !IsAMDGCNSPIRV &&
A->getType() != types::TY_LTO_BC) ||
- !HIPNoRDC || !offloadDeviceOnly())
+ HIPRelocatableObj || !HIPNoRDC || !offloadDeviceOnly())
continue;
ActionList LinkerInput = {A};
A = C.MakeAction<LinkJobAction>(LinkerInput, types::TY_Image);
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 1d7dad0..25c6b5a 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -191,9 +191,10 @@ static void getAArch64MultilibFlags(const Driver &D,
for (const auto &ArchInfo : AArch64::ArchInfos)
if (FeatureSet.contains(ArchInfo->ArchFeature))
ArchName = ArchInfo->Name;
- assert(!ArchName.empty() && "at least one architecture should be found");
- MArch.insert(MArch.begin(), ("-march=" + ArchName).str());
- Result.push_back(llvm::join(MArch, "+"));
+ if (!ArchName.empty()) {
+ MArch.insert(MArch.begin(), ("-march=" + ArchName).str());
+ Result.push_back(llvm::join(MArch, "+"));
+ }
const Arg *BranchProtectionArg =
Args.getLastArgNoClaim(options::OPT_mbranch_protection_EQ);
@@ -760,7 +761,7 @@ std::string ToolChain::buildCompilerRTBasename(const llvm::opt::ArgList &Args,
break;
case ToolChain::FT_Shared:
if (TT.isOSWindows())
- Suffix = TT.isWindowsGNUEnvironment() ? ".dll.a" : ".lib";
+ Suffix = TT.isOSCygMing() ? ".dll.a" : ".lib";
else if (TT.isOSAIX())
Suffix = ".a";
else
diff --git a/clang/lib/Driver/ToolChains/AMDGPU.h b/clang/lib/Driver/ToolChains/AMDGPU.h
index 513c77d..e5d41e2 100644
--- a/clang/lib/Driver/ToolChains/AMDGPU.h
+++ b/clang/lib/Driver/ToolChains/AMDGPU.h
@@ -10,7 +10,6 @@
#define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_AMDGPU_H
#include "Gnu.h"
-#include "ROCm.h"
#include "clang/Basic/TargetID.h"
#include "clang/Driver/Options.h"
#include "clang/Driver/Tool.h"
diff --git a/clang/lib/Driver/ToolChains/BareMetal.cpp b/clang/lib/Driver/ToolChains/BareMetal.cpp
index 207150e..25a16fe 100644
--- a/clang/lib/Driver/ToolChains/BareMetal.cpp
+++ b/clang/lib/Driver/ToolChains/BareMetal.cpp
@@ -671,7 +671,8 @@ void baremetal::Linker::ConstructJob(Compilation &C, const JobAction &JA,
if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
CmdArgs.push_back("--start-group");
AddRunTimeLibs(TC, D, CmdArgs, Args);
- CmdArgs.push_back("-lc");
+ if (!Args.hasArg(options::OPT_nolibc))
+ CmdArgs.push_back("-lc");
if (TC.hasValidGCCInstallation() || detectGCCToolchainAdjacent(D))
CmdArgs.push_back("-lgloss");
CmdArgs.push_back("--end-group");
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 9d882db..f4674a5 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -16,6 +16,7 @@
#include "Arch/SystemZ.h"
#include "Hexagon.h"
#include "PS4CPU.h"
+#include "ToolChains/Cuda.h"
#include "clang/Basic/CLWarnings.h"
#include "clang/Basic/CodeGenOptions.h"
#include "clang/Basic/HeaderInclude.h"
@@ -5944,7 +5945,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back("-mms-bitfields");
}
- if (Triple.isWindowsGNUEnvironment()) {
+ if (Triple.isOSCygMing()) {
Args.addOptOutFlag(CmdArgs, options::OPT_fauto_import,
options::OPT_fno_auto_import);
}
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index 3086c14..0771c7c 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -23,6 +23,7 @@
#include "Hexagon.h"
#include "MSP430.h"
#include "Solaris.h"
+#include "ToolChains/Cuda.h"
#include "clang/Basic/CodeGenOptions.h"
#include "clang/Config/config.h"
#include "clang/Driver/Action.h"
@@ -294,17 +295,22 @@ static void renderRemarksOptions(const ArgList &Args, ArgStringList &CmdArgs,
Format = A->getValue();
SmallString<128> F;
- const Arg *A = Args.getLastArg(options::OPT_foptimization_record_file_EQ);
- if (A)
+ if (const Arg *A =
+ Args.getLastArg(options::OPT_foptimization_record_file_EQ)) {
+ F = A->getValue();
+ F += ".";
+ } else if (const Arg *A = Args.getLastArg(options::OPT_dumpdir)) {
F = A->getValue();
- else if (Output.isFilename())
+ } else if (Output.isFilename()) {
F = Output.getFilename();
+ F += ".";
+ }
assert(!F.empty() && "Cannot determine remarks output name.");
// Append "opt.ld.<format>" to the end of the file name.
CmdArgs.push_back(Args.MakeArgString(Twine(PluginOptPrefix) +
- "opt-remarks-filename=" + F +
- ".opt.ld." + Format));
+ "opt-remarks-filename=" + F + "opt.ld." +
+ Format));
if (const Arg *A =
Args.getLastArg(options::OPT_foptimization_record_passes_EQ))
@@ -1074,9 +1080,17 @@ void tools::addLTOOptions(const ToolChain &ToolChain, const ArgList &Args,
}
}
- if (Args.hasArg(options::OPT_gsplit_dwarf))
- CmdArgs.push_back(Args.MakeArgString(
- Twine(PluginOptPrefix) + "dwo_dir=" + Output.getFilename() + "_dwo"));
+ if (Args.hasArg(options::OPT_gsplit_dwarf)) {
+ SmallString<128> F;
+ if (const Arg *A = Args.getLastArg(options::OPT_dumpdir)) {
+ F = A->getValue();
+ } else {
+ F = Output.getFilename();
+ F += "_";
+ }
+ CmdArgs.push_back(
+ Args.MakeArgString(Twine(PluginOptPrefix) + "dwo_dir=" + F + "dwo"));
+ }
if (IsThinLTO && !IsOSAIX)
CmdArgs.push_back(Args.MakeArgString(Twine(PluginOptPrefix) + "thinlto"));
diff --git a/clang/lib/Driver/ToolChains/Cuda.h b/clang/lib/Driver/ToolChains/Cuda.h
index 259eda6..8aeba53 100644
--- a/clang/lib/Driver/ToolChains/Cuda.h
+++ b/clang/lib/Driver/ToolChains/Cuda.h
@@ -11,6 +11,7 @@
#include "clang/Basic/Cuda.h"
#include "clang/Driver/Action.h"
+#include "clang/Driver/CudaInstallationDetector.h"
#include "clang/Driver/Multilib.h"
#include "clang/Driver/Tool.h"
#include "clang/Driver/ToolChain.h"
@@ -22,61 +23,6 @@
namespace clang {
namespace driver {
-
-/// A class to find a viable CUDA installation
-class CudaInstallationDetector {
-private:
- const Driver &D;
- bool IsValid = false;
- CudaVersion Version = CudaVersion::UNKNOWN;
- std::string InstallPath;
- std::string BinPath;
- std::string LibDevicePath;
- std::string IncludePath;
- llvm::StringMap<std::string> LibDeviceMap;
-
- // CUDA architectures for which we have raised an error in
- // CheckCudaVersionSupportsArch.
- mutable std::bitset<(int)OffloadArch::LAST> ArchsWithBadVersion;
-
-public:
- CudaInstallationDetector(const Driver &D, const llvm::Triple &HostTriple,
- const llvm::opt::ArgList &Args);
-
- void AddCudaIncludeArgs(const llvm::opt::ArgList &DriverArgs,
- llvm::opt::ArgStringList &CC1Args) const;
-
- /// Emit an error if Version does not support the given Arch.
- ///
- /// If either Version or Arch is unknown, does not emit an error. Emits at
- /// most one error per Arch.
- void CheckCudaVersionSupportsArch(OffloadArch Arch) const;
-
- /// Check whether we detected a valid Cuda install.
- bool isValid() const { return IsValid; }
- /// Print information about the detected CUDA installation.
- void print(raw_ostream &OS) const;
-
- /// Get the detected Cuda install's version.
- CudaVersion version() const {
- return Version == CudaVersion::NEW ? CudaVersion::PARTIALLY_SUPPORTED
- : Version;
- }
- /// Get the detected Cuda installation path.
- StringRef getInstallPath() const { return InstallPath; }
- /// Get the detected path to Cuda's bin directory.
- StringRef getBinPath() const { return BinPath; }
- /// Get the detected Cuda Include path.
- StringRef getIncludePath() const { return IncludePath; }
- /// Get the detected Cuda device library path.
- StringRef getLibDevicePath() const { return LibDevicePath; }
- /// Get libdevice file for given architecture
- std::string getLibDeviceFile(StringRef Gpu) const {
- return LibDeviceMap.lookup(Gpu);
- }
- void WarnIfUnsupportedVersion() const;
-};
-
namespace tools {
namespace NVPTX {
diff --git a/clang/lib/Driver/ToolChains/Darwin.h b/clang/lib/Driver/ToolChains/Darwin.h
index b38bfe6..d1cfb6f 100644
--- a/clang/lib/Driver/ToolChains/Darwin.h
+++ b/clang/lib/Driver/ToolChains/Darwin.h
@@ -9,12 +9,12 @@
#ifndef LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_DARWIN_H
#define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_DARWIN_H
-#include "Cuda.h"
-#include "LazyDetector.h"
-#include "ROCm.h"
-#include "SYCL.h"
#include "clang/Basic/DarwinSDKInfo.h"
#include "clang/Basic/LangOptions.h"
+#include "clang/Driver/CudaInstallationDetector.h"
+#include "clang/Driver/LazyDetector.h"
+#include "clang/Driver/RocmInstallationDetector.h"
+#include "clang/Driver/SyclInstallationDetector.h"
#include "clang/Driver/Tool.h"
#include "clang/Driver/ToolChain.h"
#include "clang/Driver/XRayArgs.h"
diff --git a/clang/lib/Driver/ToolChains/Gnu.h b/clang/lib/Driver/ToolChains/Gnu.h
index 3b8df71..4c42a5e5 100644
--- a/clang/lib/Driver/ToolChains/Gnu.h
+++ b/clang/lib/Driver/ToolChains/Gnu.h
@@ -9,10 +9,10 @@
#ifndef LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_GNU_H
#define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_GNU_H
-#include "Cuda.h"
-#include "LazyDetector.h"
-#include "ROCm.h"
-#include "SYCL.h"
+#include "clang/Driver/CudaInstallationDetector.h"
+#include "clang/Driver/LazyDetector.h"
+#include "clang/Driver/RocmInstallationDetector.h"
+#include "clang/Driver/SyclInstallationDetector.h"
#include "clang/Driver/Tool.h"
#include "clang/Driver/ToolChain.h"
#include <set>
diff --git a/clang/lib/Driver/ToolChains/HIPAMD.h b/clang/lib/Driver/ToolChains/HIPAMD.h
index bcc3ebb..30fc01a 100644
--- a/clang/lib/Driver/ToolChains/HIPAMD.h
+++ b/clang/lib/Driver/ToolChains/HIPAMD.h
@@ -10,6 +10,7 @@
#define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_HIPAMD_H
#include "AMDGPU.h"
+#include "clang/Driver/SyclInstallationDetector.h"
#include "clang/Driver/Tool.h"
#include "clang/Driver/ToolChain.h"
diff --git a/clang/lib/Driver/ToolChains/LazyDetector.h b/clang/lib/Driver/ToolChains/LazyDetector.h
deleted file mode 100644
index 813d00a..0000000
--- a/clang/lib/Driver/ToolChains/LazyDetector.h
+++ /dev/null
@@ -1,45 +0,0 @@
-//===--- LazyDetector.h - Lazy ToolChain Detection --------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_LAZYDETECTOR_H
-#define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_LAZYDETECTOR_H
-
-#include "clang/Driver/Tool.h"
-#include "clang/Driver/ToolChain.h"
-#include <optional>
-
-namespace clang {
-
-/// Simple wrapper for toolchain detector with costly initialization. This
-/// delays the creation of the actual detector until its first usage.
-
-template <class T> class LazyDetector {
- const driver::Driver &D;
- llvm::Triple Triple;
- const llvm::opt::ArgList &Args;
-
- std::optional<T> Detector;
-
-public:
- LazyDetector(const driver::Driver &D, const llvm::Triple &Triple,
- const llvm::opt::ArgList &Args)
- : D(D), Triple(Triple), Args(Args) {}
- T *operator->() {
- if (!Detector)
- Detector.emplace(D, Triple, Args);
- return &*Detector;
- }
- const T *operator->() const {
- return const_cast<T const *>(
- const_cast<LazyDetector &>(*this).operator->());
- }
-};
-
-} // end namespace clang
-
-#endif // LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_LAZYDETECTOR_H
diff --git a/clang/lib/Driver/ToolChains/MSVC.h b/clang/lib/Driver/ToolChains/MSVC.h
index b35390c..5c17edc 100644
--- a/clang/lib/Driver/ToolChains/MSVC.h
+++ b/clang/lib/Driver/ToolChains/MSVC.h
@@ -9,11 +9,11 @@
#ifndef LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_MSVC_H
#define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_MSVC_H
-#include "AMDGPU.h"
-#include "Cuda.h"
-#include "LazyDetector.h"
-#include "SYCL.h"
#include "clang/Driver/Compilation.h"
+#include "clang/Driver/CudaInstallationDetector.h"
+#include "clang/Driver/LazyDetector.h"
+#include "clang/Driver/RocmInstallationDetector.h"
+#include "clang/Driver/SyclInstallationDetector.h"
#include "clang/Driver/Tool.h"
#include "clang/Driver/ToolChain.h"
#include "llvm/Frontend/Debug/Options.h"
diff --git a/clang/lib/Driver/ToolChains/MinGW.cpp b/clang/lib/Driver/ToolChains/MinGW.cpp
index 6abd0c0..1bb9bcf 100644
--- a/clang/lib/Driver/ToolChains/MinGW.cpp
+++ b/clang/lib/Driver/ToolChains/MinGW.cpp
@@ -85,11 +85,18 @@ void tools::MinGW::Linker::AddLibGCC(const ArgList &Args,
CmdArgs.push_back("-lmoldname");
CmdArgs.push_back("-lmingwex");
- for (auto Lib : Args.getAllArgValues(options::OPT_l))
+ for (auto Lib : Args.getAllArgValues(options::OPT_l)) {
if (StringRef(Lib).starts_with("msvcr") ||
StringRef(Lib).starts_with("ucrt") ||
- StringRef(Lib).starts_with("crtdll"))
+ StringRef(Lib).starts_with("crtdll")) {
+ std::string CRTLib = (llvm::Twine("-l") + Lib).str();
+ // Respect the user's chosen crt variant, but still provide it
+ // again as the last linker argument, because some of the libraries
+ // we added above may depend on it.
+ CmdArgs.push_back(Args.MakeArgStringRef(CRTLib));
return;
+ }
+ }
CmdArgs.push_back("-lmsvcrt");
}
diff --git a/clang/lib/Driver/ToolChains/MinGW.h b/clang/lib/Driver/ToolChains/MinGW.h
index a9963d8..1730da4 100644
--- a/clang/lib/Driver/ToolChains/MinGW.h
+++ b/clang/lib/Driver/ToolChains/MinGW.h
@@ -11,8 +11,9 @@
#include "Cuda.h"
#include "Gnu.h"
-#include "LazyDetector.h"
-#include "ROCm.h"
+#include "clang/Driver/CudaInstallationDetector.h"
+#include "clang/Driver/LazyDetector.h"
+#include "clang/Driver/RocmInstallationDetector.h"
#include "clang/Driver/Tool.h"
#include "clang/Driver/ToolChain.h"
#include "llvm/Support/ErrorOr.h"
diff --git a/clang/lib/Driver/ToolChains/ROCm.h b/clang/lib/Driver/ToolChains/ROCm.h
deleted file mode 100644
index ebd5443..0000000
--- a/clang/lib/Driver/ToolChains/ROCm.h
+++ /dev/null
@@ -1,314 +0,0 @@
-//===--- ROCm.h - ROCm installation detector --------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_ROCM_H
-#define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_ROCM_H
-
-#include "clang/Basic/Cuda.h"
-#include "clang/Basic/LLVM.h"
-#include "clang/Driver/CommonArgs.h"
-#include "clang/Driver/Driver.h"
-#include "clang/Driver/Options.h"
-#include "clang/Driver/SanitizerArgs.h"
-#include "llvm/ADT/SmallString.h"
-#include "llvm/ADT/StringMap.h"
-#include "llvm/Option/ArgList.h"
-#include "llvm/Support/VersionTuple.h"
-#include "llvm/TargetParser/TargetParser.h"
-#include "llvm/TargetParser/Triple.h"
-
-namespace clang {
-namespace driver {
-
-/// ABI version of device library.
-struct DeviceLibABIVersion {
- unsigned ABIVersion = 0;
- DeviceLibABIVersion(unsigned V) : ABIVersion(V) {}
- static DeviceLibABIVersion fromCodeObjectVersion(unsigned CodeObjectVersion) {
- if (CodeObjectVersion < 4)
- CodeObjectVersion = 4;
- return DeviceLibABIVersion(CodeObjectVersion * 100);
- }
- /// Whether ABI version bc file is requested.
- /// ABIVersion is code object version multiplied by 100. Code object v4
- /// and below works with ROCm 5.0 and below which does not have
- /// abi_version_*.bc. Code object v5 requires abi_version_500.bc.
- bool requiresLibrary() { return ABIVersion >= 500; }
- std::string toString() { return Twine(getAsCodeObjectVersion()).str(); }
-
- unsigned getAsCodeObjectVersion() const {
- assert(ABIVersion % 100 == 0 && "Not supported");
- return ABIVersion / 100;
- }
-};
-
-/// A class to find a viable ROCM installation
-/// TODO: Generalize to handle libclc.
-class RocmInstallationDetector {
-private:
- struct ConditionalLibrary {
- SmallString<0> On;
- SmallString<0> Off;
-
- bool isValid() const { return !On.empty() && !Off.empty(); }
-
- StringRef get(bool Enabled) const {
- assert(isValid());
- return Enabled ? On : Off;
- }
- };
-
- // Installation path candidate.
- struct Candidate {
- llvm::SmallString<0> Path;
- bool StrictChecking;
- // Release string for ROCm packages built with SPACK if not empty. The
- // installation directories of ROCm packages built with SPACK follow the
- // convention <package_name>-<rocm_release_string>-<hash>.
- std::string SPACKReleaseStr;
-
- bool isSPACK() const { return !SPACKReleaseStr.empty(); }
- Candidate(std::string Path, bool StrictChecking = false,
- StringRef SPACKReleaseStr = {})
- : Path(Path), StrictChecking(StrictChecking),
- SPACKReleaseStr(SPACKReleaseStr.str()) {}
- };
-
- struct CommonBitcodeLibsPreferences {
- CommonBitcodeLibsPreferences(const Driver &D,
- const llvm::opt::ArgList &DriverArgs,
- StringRef GPUArch,
- const Action::OffloadKind DeviceOffloadingKind,
- const bool NeedsASanRT);
-
- DeviceLibABIVersion ABIVer;
- bool IsOpenMP;
- bool Wave64;
- bool DAZ;
- bool FiniteOnly;
- bool UnsafeMathOpt;
- bool FastRelaxedMath;
- bool CorrectSqrt;
- bool GPUSan;
- };
-
- const Driver &D;
- bool HasHIPRuntime = false;
- bool HasDeviceLibrary = false;
- bool HasHIPStdParLibrary = false;
- bool HasRocThrustLibrary = false;
- bool HasRocPrimLibrary = false;
-
- // Default version if not detected or specified.
- const unsigned DefaultVersionMajor = 3;
- const unsigned DefaultVersionMinor = 5;
- const char *DefaultVersionPatch = "0";
-
- // The version string in Major.Minor.Patch format.
- std::string DetectedVersion;
- // Version containing major and minor.
- llvm::VersionTuple VersionMajorMinor;
- // Version containing patch.
- std::string VersionPatch;
-
- // ROCm path specified by --rocm-path.
- StringRef RocmPathArg;
- // ROCm device library paths specified by --rocm-device-lib-path.
- std::vector<std::string> RocmDeviceLibPathArg;
- // HIP runtime path specified by --hip-path.
- StringRef HIPPathArg;
- // HIP Standard Parallel Algorithm acceleration library specified by
- // --hipstdpar-path
- StringRef HIPStdParPathArg;
- // rocThrust algorithm library specified by --hipstdpar-thrust-path
- StringRef HIPRocThrustPathArg;
- // rocPrim algorithm library specified by --hipstdpar-prim-path
- StringRef HIPRocPrimPathArg;
- // HIP version specified by --hip-version.
- StringRef HIPVersionArg;
- // Wheter -nogpulib is specified.
- bool NoBuiltinLibs = false;
-
- // Paths
- SmallString<0> InstallPath;
- SmallString<0> BinPath;
- SmallString<0> LibPath;
- SmallString<0> LibDevicePath;
- SmallString<0> IncludePath;
- SmallString<0> SharePath;
- llvm::StringMap<std::string> LibDeviceMap;
-
- // Libraries that are always linked.
- SmallString<0> OCML;
- SmallString<0> OCKL;
-
- // Libraries that are always linked depending on the language
- SmallString<0> OpenCL;
-
- // Asan runtime library
- SmallString<0> AsanRTL;
-
- // Libraries swapped based on compile flags.
- ConditionalLibrary WavefrontSize64;
- ConditionalLibrary FiniteOnly;
- ConditionalLibrary UnsafeMath;
- ConditionalLibrary DenormalsAreZero;
- ConditionalLibrary CorrectlyRoundedSqrt;
-
- // Maps ABI version to library path. The version number is in the format of
- // three digits as used in the ABI version library name.
- std::map<unsigned, std::string> ABIVersionMap;
-
- // Cache ROCm installation search paths.
- SmallVector<Candidate, 4> ROCmSearchDirs;
- bool PrintROCmSearchDirs;
- bool Verbose;
-
- bool allGenericLibsValid() const {
- return !OCML.empty() && !OCKL.empty() && !OpenCL.empty() &&
- WavefrontSize64.isValid() && FiniteOnly.isValid() &&
- UnsafeMath.isValid() && DenormalsAreZero.isValid() &&
- CorrectlyRoundedSqrt.isValid();
- }
-
- void scanLibDevicePath(llvm::StringRef Path);
- bool parseHIPVersionFile(llvm::StringRef V);
- const SmallVectorImpl<Candidate> &getInstallationPathCandidates();
-
- /// Find the path to a SPACK package under the ROCm candidate installation
- /// directory if the candidate is a SPACK ROCm candidate. \returns empty
- /// string if the candidate is not SPACK ROCm candidate or the requested
- /// package is not found.
- llvm::SmallString<0> findSPACKPackage(const Candidate &Cand,
- StringRef PackageName);
-
-public:
- RocmInstallationDetector(const Driver &D, const llvm::Triple &HostTriple,
- const llvm::opt::ArgList &Args,
- bool DetectHIPRuntime = true,
- bool DetectDeviceLib = false);
-
- /// Get file paths of default bitcode libraries common to AMDGPU based
- /// toolchains.
- llvm::SmallVector<ToolChain::BitCodeLibraryInfo, 12>
- getCommonBitcodeLibs(const llvm::opt::ArgList &DriverArgs,
- StringRef LibDeviceFile, StringRef GPUArch,
- const Action::OffloadKind DeviceOffloadingKind,
- const bool NeedsASanRT) const;
- /// Check file paths of default bitcode libraries common to AMDGPU based
- /// toolchains. \returns false if there are invalid or missing files.
- bool checkCommonBitcodeLibs(StringRef GPUArch, StringRef LibDeviceFile,
- DeviceLibABIVersion ABIVer) const;
-
- /// Check whether we detected a valid HIP runtime.
- bool hasHIPRuntime() const { return HasHIPRuntime; }
-
- /// Check whether we detected a valid ROCm device library.
- bool hasDeviceLibrary() const { return HasDeviceLibrary; }
-
- /// Check whether we detected a valid HIP STDPAR Acceleration library.
- bool hasHIPStdParLibrary() const { return HasHIPStdParLibrary; }
-
- /// Print information about the detected ROCm installation.
- void print(raw_ostream &OS) const;
-
- /// Get the detected Rocm install's version.
- // RocmVersion version() const { return Version; }
-
- /// Get the detected Rocm installation path.
- StringRef getInstallPath() const { return InstallPath; }
-
- /// Get the detected path to Rocm's bin directory.
- // StringRef getBinPath() const { return BinPath; }
-
- /// Get the detected Rocm Include path.
- StringRef getIncludePath() const { return IncludePath; }
-
- /// Get the detected Rocm library path.
- StringRef getLibPath() const { return LibPath; }
-
- /// Get the detected Rocm device library path.
- StringRef getLibDevicePath() const { return LibDevicePath; }
-
- StringRef getOCMLPath() const {
- assert(!OCML.empty());
- return OCML;
- }
-
- StringRef getOCKLPath() const {
- assert(!OCKL.empty());
- return OCKL;
- }
-
- StringRef getOpenCLPath() const {
- assert(!OpenCL.empty());
- return OpenCL;
- }
-
- /// Returns empty string of Asan runtime library is not available.
- StringRef getAsanRTLPath() const { return AsanRTL; }
-
- StringRef getWavefrontSize64Path(bool Enabled) const {
- return WavefrontSize64.get(Enabled);
- }
-
- StringRef getFiniteOnlyPath(bool Enabled) const {
- return FiniteOnly.get(Enabled);
- }
-
- StringRef getUnsafeMathPath(bool Enabled) const {
- return UnsafeMath.get(Enabled);
- }
-
- StringRef getDenormalsAreZeroPath(bool Enabled) const {
- return DenormalsAreZero.get(Enabled);
- }
-
- StringRef getCorrectlyRoundedSqrtPath(bool Enabled) const {
- return CorrectlyRoundedSqrt.get(Enabled);
- }
-
- StringRef getABIVersionPath(DeviceLibABIVersion ABIVer) const {
- auto Loc = ABIVersionMap.find(ABIVer.ABIVersion);
- if (Loc == ABIVersionMap.end())
- return StringRef();
- return Loc->second;
- }
-
- /// Get libdevice file for given architecture
- StringRef getLibDeviceFile(StringRef Gpu) const {
- auto Loc = LibDeviceMap.find(Gpu);
- if (Loc == LibDeviceMap.end())
- return "";
- return Loc->second;
- }
-
- void AddHIPIncludeArgs(const llvm::opt::ArgList &DriverArgs,
- llvm::opt::ArgStringList &CC1Args) const;
-
- void detectDeviceLibrary();
- void detectHIPRuntime();
-
- /// Get the values for --rocm-device-lib-path arguments
- ArrayRef<std::string> getRocmDeviceLibPathArg() const {
- return RocmDeviceLibPathArg;
- }
-
- /// Get the value for --rocm-path argument
- StringRef getRocmPathArg() const { return RocmPathArg; }
-
- /// Get the value for --hip-version argument
- StringRef getHIPVersionArg() const { return HIPVersionArg; }
-
- StringRef getHIPVersion() const { return DetectedVersion; }
-};
-
-} // end namespace driver
-} // end namespace clang
-
-#endif // LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_ROCM_H
diff --git a/clang/lib/Driver/ToolChains/SYCL.h b/clang/lib/Driver/ToolChains/SYCL.h
index 2a8b4ec..be4ba47 100644
--- a/clang/lib/Driver/ToolChains/SYCL.h
+++ b/clang/lib/Driver/ToolChains/SYCL.h
@@ -9,21 +9,12 @@
#ifndef LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_SYCL_H
#define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_SYCL_H
+#include "clang/Driver/SyclInstallationDetector.h"
#include "clang/Driver/Tool.h"
#include "clang/Driver/ToolChain.h"
namespace clang {
namespace driver {
-
-class SYCLInstallationDetector {
-public:
- SYCLInstallationDetector(const Driver &D, const llvm::Triple &HostTriple,
- const llvm::opt::ArgList &Args);
-
- void addSYCLIncludeArgs(const llvm::opt::ArgList &DriverArgs,
- llvm::opt::ArgStringList &CC1Args) const;
-};
-
namespace toolchains {
class LLVM_LIBRARY_VISIBILITY SYCLToolChain : public ToolChain {
diff --git a/clang/lib/Format/IntegerLiteralSeparatorFixer.cpp b/clang/lib/Format/IntegerLiteralSeparatorFixer.cpp
index 80487fa..7772a56 100644
--- a/clang/lib/Format/IntegerLiteralSeparatorFixer.cpp
+++ b/clang/lib/Format/IntegerLiteralSeparatorFixer.cpp
@@ -45,15 +45,18 @@ std::pair<tooling::Replacements, unsigned>
IntegerLiteralSeparatorFixer::process(const Environment &Env,
const FormatStyle &Style) {
switch (Style.Language) {
- case FormatStyle::LK_Cpp:
- case FormatStyle::LK_ObjC:
- Separator = '\'';
- break;
case FormatStyle::LK_CSharp:
case FormatStyle::LK_Java:
case FormatStyle::LK_JavaScript:
Separator = '_';
break;
+ case FormatStyle::LK_Cpp:
+ case FormatStyle::LK_ObjC:
+ if (Style.Standard >= FormatStyle::LS_Cpp14) {
+ Separator = '\'';
+ break;
+ }
+ [[fallthrough]];
default:
return {};
}
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 382ccd6..008a35d 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -945,8 +945,8 @@ static void InitializePredefinedMacros(const TargetInfo &TI,
if (LangOpts.GNUCVersion && LangOpts.CPlusPlus11)
Builder.defineMacro("__GXX_EXPERIMENTAL_CXX0X__");
- if (TI.getTriple().isWindowsGNUEnvironment()) {
- // Set ABI defining macros for libstdc++ for MinGW, where the
+ if (TI.getTriple().isOSCygMing()) {
+ // Set ABI defining macros for libstdc++ for MinGW and Cygwin, where the
// default in libstdc++ differs from the defaults for this target.
Builder.defineMacro("__GXX_TYPEINFO_EQUALITY_INLINE", "0");
}
diff --git a/clang/lib/Headers/opencl-c.h b/clang/lib/Headers/opencl-c.h
index e1e0fde..f65b4b3 100644
--- a/clang/lib/Headers/opencl-c.h
+++ b/clang/lib/Headers/opencl-c.h
@@ -18410,6 +18410,22 @@ intel_sub_group_avc_mce_convert_to_sic_result(
#pragma OPENCL EXTENSION cl_intel_device_side_avc_motion_estimation : end
#endif // cl_intel_device_side_avc_motion_estimation
+#if defined(cl_intel_bfloat16_conversions)
+ushort __ovld intel_convert_bfloat16_as_ushort(float source);
+ushort2 __ovld intel_convert_bfloat162_as_ushort2(float2 source);
+ushort3 __ovld intel_convert_bfloat163_as_ushort3(float3 source);
+ushort4 __ovld intel_convert_bfloat164_as_ushort4(float4 source);
+ushort8 __ovld intel_convert_bfloat168_as_ushort8(float8 source);
+ushort16 __ovld intel_convert_bfloat1616_as_ushort16(float16 source);
+
+float __ovld intel_convert_as_bfloat16_float(ushort source);
+float2 __ovld intel_convert_as_bfloat162_float2(ushort2 source);
+float3 __ovld intel_convert_as_bfloat163_float3(ushort3 source);
+float4 __ovld intel_convert_as_bfloat164_float4(ushort4 source);
+float8 __ovld intel_convert_as_bfloat168_float8(ushort8 source);
+float16 __ovld intel_convert_as_bfloat1616_float16(ushort16 source);
+#endif // cl_intel_bfloat16_conversions
+
#ifdef cl_amd_media_ops
uint __ovld amd_bitalign(uint, uint, uint);
uint2 __ovld amd_bitalign(uint2, uint2, uint2);
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 829c81b..35ad0b5 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -503,8 +503,12 @@ static bool areAllValuesNoReturn(const VarDecl *VD, const CFGBlock &VarBlk,
TransferFunctions TF(VD);
BackwardDataflowWorklist Worklist(*AC.getCFG(), AC);
+ llvm::DenseSet<const CFGBlock *> Visited;
Worklist.enqueueBlock(&VarBlk);
while (const CFGBlock *B = Worklist.dequeue()) {
+ if (Visited.contains(B))
+ continue;
+ Visited.insert(B);
// First check the current block.
for (CFGBlock::const_reverse_iterator ri = B->rbegin(), re = B->rend();
ri != re; ++ri) {
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index d50eeff..43a7f9e 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -2251,16 +2251,15 @@ void Sema::checkTypeSupport(QualType Ty, SourceLocation Loc, ValueDecl *D) {
}
// Don't allow SVE types in functions without a SVE target.
- if (Ty->isSVESizelessBuiltinType() && FD) {
+ if (Ty->isSVESizelessBuiltinType() && FD && !FD->getType().isNull()) {
llvm::StringMap<bool> CallerFeatureMap;
Context.getFunctionFeatureMap(CallerFeatureMap, FD);
if (!Builtin::evaluateRequiredTargetFeatures("sve", CallerFeatureMap)) {
if (!Builtin::evaluateRequiredTargetFeatures("sme", CallerFeatureMap))
Diag(Loc, diag::err_sve_vector_in_non_sve_target) << Ty;
else if (!IsArmStreamingFunction(FD,
- /*IncludeLocallyStreaming=*/true)) {
+ /*IncludeLocallyStreaming=*/true))
Diag(Loc, diag::err_sve_vector_in_non_streaming_function) << Ty;
- }
}
}
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index a43ac9e..0de5580 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -4034,6 +4034,14 @@ static void AddOverloadParameterChunks(
return;
}
+ // C++23 introduces an explicit object parameter, a.k.a. "deducing this"
+ // Skip it for autocomplete and treat the next parameter as the first
+ // parameter
+ if (Function && FirstParameter &&
+ Function->getParamDecl(P)->isExplicitObjectParameter()) {
+ continue;
+ }
+
if (FirstParameter)
FirstParameter = false;
else
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 044cf5c..da85959 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -1105,10 +1105,6 @@ static bool CheckFunctionConstraintsWithoutInstantiation(
}
Sema::ContextRAII SavedContext(SemaRef, FD);
- std::optional<Sema::CXXThisScopeRAII> ThisScope;
- if (auto *Method = dyn_cast<CXXMethodDecl>(FD))
- ThisScope.emplace(SemaRef, /*Record=*/Method->getParent(),
- /*ThisQuals=*/Method->getMethodQualifiers());
return SemaRef.CheckConstraintSatisfaction(
Template, TemplateAC, MLTAL, PointOfInstantiation, Satisfaction);
}
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index d7420bd..c7e7507 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -12586,9 +12586,9 @@ static bool isDefaultStdCall(FunctionDecl *FD, Sema &S) {
if (FD->getName() == "main" || FD->getName() == "wmain")
return false;
- // Default calling convention for MinGW is __cdecl
+ // Default calling convention for MinGW and Cygwin is __cdecl
const llvm::Triple &T = S.Context.getTargetInfo().getTriple();
- if (T.isWindowsGNUEnvironment())
+ if (T.isOSCygMing())
return false;
// Default calling convention for WinMain, wWinMain and DllMain
diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp
index 98ebd70..b137549 100644
--- a/clang/lib/Sema/SemaModule.cpp
+++ b/clang/lib/Sema/SemaModule.cpp
@@ -13,7 +13,7 @@
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTMutationListener.h"
-#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/DynamicRecursiveASTVisitor.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/ParsedAttr.h"
@@ -1422,14 +1422,14 @@ bool ExposureChecker::checkExposure(const CXXRecordDecl *RD, bool Diag) {
return IsExposure;
}
-template <typename CallbackTy>
-class ReferenceTULocalChecker
- : public clang::RecursiveASTVisitor<ReferenceTULocalChecker<CallbackTy>> {
+class ReferenceTULocalChecker : public DynamicRecursiveASTVisitor {
public:
+ using CallbackTy = std::function<void(DeclRefExpr *, ValueDecl *)>;
+
ReferenceTULocalChecker(ExposureChecker &C, CallbackTy &&Callback)
: Checker(C), Callback(std::move(Callback)) {}
- bool VisitDeclRefExpr(DeclRefExpr *DRE) {
+ bool VisitDeclRefExpr(DeclRefExpr *DRE) override {
ValueDecl *Referenced = DRE->getDecl();
if (!Referenced)
return true;
@@ -1468,10 +1468,6 @@ public:
CallbackTy Callback;
};
-template <typename CallbackTy>
-ReferenceTULocalChecker(ExposureChecker &, CallbackTy &&)
- -> ReferenceTULocalChecker<CallbackTy>;
-
bool ExposureChecker::checkExposure(const Stmt *S, bool Diag) {
if (!S)
return false;
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 698d127..21fed2e 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4749,8 +4749,6 @@ Sema::CheckConceptTemplateId(const CXXScopeSpec &SS,
EnterExpressionEvaluationContext EECtx{
*this, ExpressionEvaluationContext::Unevaluated, CSD};
- ContextRAII CurContext(*this, CSD->getDeclContext(),
- /*NewThisContext=*/false);
if (!AreArgsDependent &&
CheckConstraintSatisfaction(
NamedConcept, AssociatedConstraint(NamedConcept->getConstraintExpr()),
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp
index 1d8687e..c2f0600 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -11,9 +11,7 @@
//===----------------------------------------------------------------------===//
#include "clang/AST/DeclCXX.h"
-#include "clang/AST/TemplateBase.h"
#include "clang/AST/Type.h"
-#include "clang/Basic/DiagnosticIDs.h"
#include "clang/Basic/DiagnosticParse.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/TypeTraits.h"
@@ -1965,7 +1963,6 @@ static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) {
.Case("is_assignable", TypeTrait::BTT_IsAssignable)
.Case("is_empty", TypeTrait::UTT_IsEmpty)
.Case("is_standard_layout", TypeTrait::UTT_IsStandardLayout)
- .Case("is_constructible", TypeTrait::TT_IsConstructible)
.Default(std::nullopt);
}
@@ -2002,16 +1999,8 @@ static ExtractedTypeTraitInfo ExtractTypeTraitFromExpression(const Expr *E) {
Trait = StdNameToTypeTrait(Name);
if (!Trait)
return std::nullopt;
- for (const auto &Arg : VD->getTemplateArgs().asArray()) {
- if (Arg.getKind() == TemplateArgument::ArgKind::Pack) {
- for (const auto &InnerArg : Arg.pack_elements())
- Args.push_back(InnerArg.getAsType());
- } else if (Arg.getKind() == TemplateArgument::ArgKind::Type) {
- Args.push_back(Arg.getAsType());
- } else {
- llvm_unreachable("Unexpected kind");
- }
- }
+ for (const auto &Arg : VD->getTemplateArgs().asArray())
+ Args.push_back(Arg.getAsType());
return {{Trait.value(), std::move(Args)}};
}
@@ -2284,60 +2273,6 @@ static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef,
}
}
-static void DiagnoseNonConstructibleReason(
- Sema &SemaRef, SourceLocation Loc,
- const llvm::SmallVector<clang::QualType, 1> &Ts) {
- if (Ts.empty()) {
- return;
- }
-
- bool ContainsVoid = false;
- for (const QualType &ArgTy : Ts) {
- ContainsVoid |= ArgTy->isVoidType();
- }
-
- if (ContainsVoid)
- SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
- << diag::TraitNotSatisfiedReason::CVVoidType;
-
- QualType T = Ts[0];
- if (T->isFunctionType())
- SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
- << diag::TraitNotSatisfiedReason::FunctionType;
-
- if (T->isIncompleteArrayType())
- SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
- << diag::TraitNotSatisfiedReason::IncompleteArrayType;
-
- const CXXRecordDecl *D = T->getAsCXXRecordDecl();
- if (!D || D->isInvalidDecl() || !D->hasDefinition())
- return;
-
- llvm::BumpPtrAllocator OpaqueExprAllocator;
- SmallVector<Expr *, 2> ArgExprs;
- ArgExprs.reserve(Ts.size() - 1);
- for (unsigned I = 1, N = Ts.size(); I != N; ++I) {
- QualType ArgTy = Ts[I];
- if (ArgTy->isObjectType() || ArgTy->isFunctionType())
- ArgTy = SemaRef.Context.getRValueReferenceType(ArgTy);
- ArgExprs.push_back(
- new (OpaqueExprAllocator.Allocate<OpaqueValueExpr>())
- OpaqueValueExpr(Loc, ArgTy.getNonLValueExprType(SemaRef.Context),
- Expr::getValueKindForType(ArgTy)));
- }
-
- EnterExpressionEvaluationContext Unevaluated(
- SemaRef, Sema::ExpressionEvaluationContext::Unevaluated);
- Sema::ContextRAII TUContext(SemaRef,
- SemaRef.Context.getTranslationUnitDecl());
- InitializedEntity To(InitializedEntity::InitializeTemporary(T));
- InitializationKind InitKind(InitializationKind::CreateDirect(Loc, Loc, Loc));
- InitializationSequence Init(SemaRef, To, InitKind, ArgExprs);
-
- Init.Diagnose(SemaRef, To, InitKind, ArgExprs);
- SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
-}
-
static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef,
SourceLocation Loc, QualType T) {
SemaRef.Diag(Loc, diag::note_unsatisfied_trait)
@@ -2624,9 +2559,6 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
case UTT_IsStandardLayout:
DiagnoseNonStandardLayoutReason(*this, E->getBeginLoc(), Args[0]);
break;
- case TT_IsConstructible:
- DiagnoseNonConstructibleReason(*this, E->getBeginLoc(), Args);
- break;
default:
break;
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
index 88feb6a..e682c4e 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
@@ -99,7 +99,7 @@ class NilArgChecker : public Checker<check::PreObjCMessage,
check::PostStmt<ObjCDictionaryLiteral>,
check::PostStmt<ObjCArrayLiteral>,
EventDispatcher<ImplicitNullDerefEvent>> {
- mutable std::unique_ptr<APIMisuse> BT;
+ const APIMisuse BT{this, "nil argument"};
mutable llvm::SmallDenseMap<Selector, unsigned, 16> StringSelectors;
mutable Selector ArrayWithObjectSel;
@@ -218,10 +218,7 @@ void NilArgChecker::generateBugReport(ExplodedNode *N,
SourceRange Range,
const Expr *E,
CheckerContext &C) const {
- if (!BT)
- BT.reset(new APIMisuse(this, "nil argument"));
-
- auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
+ auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
R->addRange(Range);
bugreporter::trackExpressionValue(N, E, *R);
C.emitReport(std::move(R));
@@ -350,7 +347,7 @@ void NilArgChecker::checkPostStmt(const ObjCDictionaryLiteral *DL,
namespace {
class CFNumberChecker : public Checker< check::PreStmt<CallExpr> > {
- mutable std::unique_ptr<APIMisuse> BT;
+ const APIMisuse BT{this, "Bad use of CFNumber APIs"};
mutable IdentifierInfo *ICreate = nullptr, *IGetValue = nullptr;
public:
CFNumberChecker() = default;
@@ -524,10 +521,7 @@ void CFNumberChecker::checkPreStmt(const CallExpr *CE,
<< " bits of the integer value will be "
<< (isCreate ? "lost." : "garbage.");
- if (!BT)
- BT.reset(new APIMisuse(this, "Bad use of CFNumber APIs"));
-
- auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
+ auto report = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N);
report->addRange(CE->getArg(2)->getSourceRange());
C.emitReport(std::move(report));
}
@@ -539,7 +533,7 @@ void CFNumberChecker::checkPreStmt(const CallExpr *CE,
namespace {
class CFRetainReleaseChecker : public Checker<check::PreCall> {
- mutable APIMisuse BT{this, "null passed to CF memory management function"};
+ const APIMisuse BT{this, "null passed to CF memory management function"};
const CallDescriptionSet ModelledCalls = {
{CDM::CLibrary, {"CFRetain"}, 1},
{CDM::CLibrary, {"CFRelease"}, 1},
@@ -600,7 +594,8 @@ class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
mutable Selector retainS;
mutable Selector autoreleaseS;
mutable Selector drainS;
- mutable std::unique_ptr<BugType> BT;
+ const APIMisuse BT{
+ this, "message incorrectly sent to class instead of class instance"};
public:
void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
@@ -609,10 +604,7 @@ public:
void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
CheckerContext &C) const {
- if (!BT) {
- BT.reset(new APIMisuse(
- this, "message incorrectly sent to class instead of class instance"));
-
+ if (releaseS.isNull()) {
ASTContext &Ctx = C.getASTContext();
releaseS = GetNullarySelector("release", Ctx);
retainS = GetNullarySelector("retain", Ctx);
@@ -639,7 +631,7 @@ void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
"of class '" << Class->getName()
<< "' and not the class directly";
- auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
+ auto report = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N);
report->addRange(msg.getSourceRange());
C.emitReport(std::move(report));
}
@@ -658,7 +650,8 @@ class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
mutable Selector orderedSetWithObjectsS;
mutable Selector initWithObjectsS;
mutable Selector initWithObjectsAndKeysS;
- mutable std::unique_ptr<BugType> BT;
+ const APIMisuse BT{this, "Arguments passed to variadic method aren't all "
+ "Objective-C pointer types"};
bool isVariadicMessage(const ObjCMethodCall &msg) const;
@@ -717,11 +710,7 @@ VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const {
void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
CheckerContext &C) const {
- if (!BT) {
- BT.reset(new APIMisuse(this,
- "Arguments passed to variadic method aren't all "
- "Objective-C pointer types"));
-
+ if (arrayWithObjectsS.isNull()) {
ASTContext &Ctx = C.getASTContext();
arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
dictionaryWithObjectsAndKeysS =
@@ -792,8 +781,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
ArgTy.print(os, C.getLangOpts());
os << "'";
- auto R =
- std::make_unique<PathSensitiveBugReport>(*BT, os.str(), *errorNode);
+ auto R = std::make_unique<PathSensitiveBugReport>(BT, os.str(), *errorNode);
R->addRange(msg.getArgSourceRange(I));
C.emitReport(std::move(R));
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
index 31cb150..fd0a398 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -78,35 +78,30 @@ static QualType getCharPtrType(ASTContext &Ctx, CharKind CK) {
: Ctx.WideCharTy);
}
-class CStringChecker : public Checker< eval::Call,
- check::PreStmt<DeclStmt>,
- check::LiveSymbols,
- check::DeadSymbols,
- check::RegionChanges
- > {
- mutable std::unique_ptr<BugType> BT_Null, BT_Bounds, BT_Overlap,
- BT_NotCString, BT_AdditionOverflow, BT_UninitRead;
-
+class CStringChecker
+ : public CheckerFamily<eval::Call, check::PreStmt<DeclStmt>,
+ check::LiveSymbols, check::DeadSymbols,
+ check::RegionChanges> {
mutable const char *CurrentFunctionDescription = nullptr;
public:
- /// The filter is used to filter out the diagnostics which are not enabled by
- /// the user.
- struct CStringChecksFilter {
- bool CheckCStringNullArg = false;
- bool CheckCStringOutOfBounds = false;
- bool CheckCStringBufferOverlap = false;
- bool CheckCStringNotNullTerm = false;
- bool CheckCStringUninitializedRead = false;
-
- CheckerNameRef CheckNameCStringNullArg;
- CheckerNameRef CheckNameCStringOutOfBounds;
- CheckerNameRef CheckNameCStringBufferOverlap;
- CheckerNameRef CheckNameCStringNotNullTerm;
- CheckerNameRef CheckNameCStringUninitializedRead;
- };
-
- CStringChecksFilter Filter;
+ // FIXME: The bug types emitted by this checker family have confused garbage
+ // in their Description and Category fields (e.g. `categories::UnixAPI` is
+ // passed as the description in several cases and `uninitialized` is mistyped
+ // as `unitialized`). This should be cleaned up.
+ CheckerFrontendWithBugType NullArg{categories::UnixAPI};
+ CheckerFrontendWithBugType OutOfBounds{"Out-of-bound array access"};
+ CheckerFrontendWithBugType BufferOverlap{categories::UnixAPI,
+ "Improper arguments"};
+ CheckerFrontendWithBugType NotNullTerm{categories::UnixAPI};
+ CheckerFrontendWithBugType UninitializedRead{
+ "Accessing unitialized/garbage values"};
+
+ // FIXME: This bug type should be removed because it is only emitted in a
+ // situation that is practically impossible.
+ const BugType AdditionOverflow{&OutOfBounds, "API"};
+
+ StringRef getDebugTag() const override { return "MallocChecker"; }
static void *getTag() { static int tag; return &tag; }
@@ -384,7 +379,7 @@ ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C,
assumeZero(C, State, l, Arg.Expression->getType());
if (stateNull && !stateNonNull) {
- if (Filter.CheckCStringNullArg) {
+ if (NullArg.isEnabled()) {
SmallString<80> buf;
llvm::raw_svector_ostream OS(buf);
assert(CurrentFunctionDescription);
@@ -468,7 +463,7 @@ ProgramStateRef CStringChecker::checkInit(CheckerContext &C,
return State;
// Ensure that we wouldn't read uninitialized value.
- if (Filter.CheckCStringUninitializedRead &&
+ if (UninitializedRead.isEnabled() &&
State->getSVal(*FirstElementVal).isUndef()) {
llvm::SmallString<258> Buf;
llvm::raw_svector_ostream OS(Buf);
@@ -524,7 +519,7 @@ ProgramStateRef CStringChecker::checkInit(CheckerContext &C,
if (!isa<Loc>(LastElementVal))
return State;
- if (Filter.CheckCStringUninitializedRead &&
+ if (UninitializedRead.isEnabled() &&
State->getSVal(LastElementVal.castAs<Loc>()).isUndef()) {
const llvm::APSInt *IdxInt = LastIdx.getAsInteger();
// If we can't get emit a sensible last element index, just bail out --
@@ -581,13 +576,9 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
auto [StInBound, StOutBound] = state->assumeInBoundDual(*Idx, Size);
if (StOutBound && !StInBound) {
- // These checks are either enabled by the CString out-of-bounds checker
- // explicitly or implicitly by the Malloc checker.
- // In the latter case we only do modeling but do not emit warning.
- if (!Filter.CheckCStringOutOfBounds)
+ if (!OutOfBounds.isEnabled())
return nullptr;
- // Emit a bug report.
ErrorMessage Message =
createOutOfBoundErrorMsg(CurrentFunctionDescription, Access);
emitOutOfBoundsBug(C, StOutBound, Buffer.Expression, Message);
@@ -620,7 +611,7 @@ CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State,
return nullptr;
// If out-of-bounds checking is turned off, skip the rest.
- if (!Filter.CheckCStringOutOfBounds)
+ if (!OutOfBounds.isEnabled())
return State;
SVal BufStart =
@@ -670,7 +661,7 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
SizeArgExpr Size, AnyArgExpr First,
AnyArgExpr Second,
CharKind CK) const {
- if (!Filter.CheckCStringBufferOverlap)
+ if (!BufferOverlap.isEnabled())
return state;
// Do a simple check for overlap: if the two arguments are from the same
@@ -789,13 +780,9 @@ void CStringChecker::emitOverlapBug(CheckerContext &C, ProgramStateRef state,
if (!N)
return;
- if (!BT_Overlap)
- BT_Overlap.reset(new BugType(Filter.CheckNameCStringBufferOverlap,
- categories::UnixAPI, "Improper arguments"));
-
// Generate a report for this bug.
auto report = std::make_unique<PathSensitiveBugReport>(
- *BT_Overlap, "Arguments must not be overlapping buffers", N);
+ BufferOverlap, "Arguments must not be overlapping buffers", N);
report->addRange(First->getSourceRange());
report->addRange(Second->getSourceRange());
@@ -805,15 +792,8 @@ void CStringChecker::emitOverlapBug(CheckerContext &C, ProgramStateRef state,
void CStringChecker::emitNullArgBug(CheckerContext &C, ProgramStateRef State,
const Stmt *S, StringRef WarningMsg) const {
if (ExplodedNode *N = C.generateErrorNode(State)) {
- if (!BT_Null) {
- // FIXME: This call uses the string constant 'categories::UnixAPI' as the
- // description of the bug; it should be replaced by a real description.
- BT_Null.reset(
- new BugType(Filter.CheckNameCStringNullArg, categories::UnixAPI));
- }
-
auto Report =
- std::make_unique<PathSensitiveBugReport>(*BT_Null, WarningMsg, N);
+ std::make_unique<PathSensitiveBugReport>(NullArg, WarningMsg, N);
Report->addRange(S->getSourceRange());
if (const auto *Ex = dyn_cast<Expr>(S))
bugreporter::trackExpressionValue(N, Ex, *Report);
@@ -826,12 +806,8 @@ void CStringChecker::emitUninitializedReadBug(CheckerContext &C,
const Expr *E, const MemRegion *R,
StringRef Msg) const {
if (ExplodedNode *N = C.generateErrorNode(State)) {
- if (!BT_UninitRead)
- BT_UninitRead.reset(new BugType(Filter.CheckNameCStringUninitializedRead,
- "Accessing unitialized/garbage values"));
-
auto Report =
- std::make_unique<PathSensitiveBugReport>(*BT_UninitRead, Msg, N);
+ std::make_unique<PathSensitiveBugReport>(UninitializedRead, Msg, N);
Report->addNote("Other elements might also be undefined",
Report->getLocation());
Report->addRange(E->getSourceRange());
@@ -845,17 +821,11 @@ void CStringChecker::emitOutOfBoundsBug(CheckerContext &C,
ProgramStateRef State, const Stmt *S,
StringRef WarningMsg) const {
if (ExplodedNode *N = C.generateErrorNode(State)) {
- if (!BT_Bounds)
- BT_Bounds.reset(new BugType(Filter.CheckCStringOutOfBounds
- ? Filter.CheckNameCStringOutOfBounds
- : Filter.CheckNameCStringNullArg,
- "Out-of-bound array access"));
-
// FIXME: It would be nice to eventually make this diagnostic more clear,
// e.g., by referencing the original declaration or by saying *why* this
// reference is outside the range.
auto Report =
- std::make_unique<PathSensitiveBugReport>(*BT_Bounds, WarningMsg, N);
+ std::make_unique<PathSensitiveBugReport>(OutOfBounds, WarningMsg, N);
Report->addRange(S->getSourceRange());
C.emitReport(std::move(Report));
}
@@ -865,15 +835,8 @@ void CStringChecker::emitNotCStringBug(CheckerContext &C, ProgramStateRef State,
const Stmt *S,
StringRef WarningMsg) const {
if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
- if (!BT_NotCString) {
- // FIXME: This call uses the string constant 'categories::UnixAPI' as the
- // description of the bug; it should be replaced by a real description.
- BT_NotCString.reset(
- new BugType(Filter.CheckNameCStringNotNullTerm, categories::UnixAPI));
- }
-
auto Report =
- std::make_unique<PathSensitiveBugReport>(*BT_NotCString, WarningMsg, N);
+ std::make_unique<PathSensitiveBugReport>(NotNullTerm, WarningMsg, N);
Report->addRange(S->getSourceRange());
C.emitReport(std::move(Report));
@@ -883,14 +846,6 @@ void CStringChecker::emitNotCStringBug(CheckerContext &C, ProgramStateRef State,
void CStringChecker::emitAdditionOverflowBug(CheckerContext &C,
ProgramStateRef State) const {
if (ExplodedNode *N = C.generateErrorNode(State)) {
- if (!BT_AdditionOverflow) {
- // FIXME: This call uses the word "API" as the description of the bug;
- // it should be replaced by a better error message (if this unlikely
- // situation continues to exist as a separate bug type).
- BT_AdditionOverflow.reset(
- new BugType(Filter.CheckNameCStringOutOfBounds, "API"));
- }
-
// This isn't a great error message, but this should never occur in real
// code anyway -- you'd have to create a buffer longer than a size_t can
// represent, which is sort of a contradiction.
@@ -898,7 +853,7 @@ void CStringChecker::emitAdditionOverflowBug(CheckerContext &C,
"This expression will create a string whose length is too big to "
"be represented as a size_t";
- auto Report = std::make_unique<PathSensitiveBugReport>(*BT_AdditionOverflow,
+ auto Report = std::make_unique<PathSensitiveBugReport>(AdditionOverflow,
WarningMsg, N);
C.emitReport(std::move(Report));
}
@@ -909,7 +864,7 @@ ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C,
NonLoc left,
NonLoc right) const {
// If out-of-bounds checking is turned off, skip the rest.
- if (!Filter.CheckCStringOutOfBounds)
+ if (!OutOfBounds.isEnabled())
return state;
// If a previous check has failed, propagate the failure.
@@ -1048,7 +1003,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
// C string. In the context of locations, the only time we can issue such
// a warning is for labels.
if (std::optional<loc::GotoLabel> Label = Buf.getAs<loc::GotoLabel>()) {
- if (Filter.CheckCStringNotNullTerm) {
+ if (NotNullTerm.isEnabled()) {
SmallString<120> buf;
llvm::raw_svector_ostream os(buf);
assert(CurrentFunctionDescription);
@@ -1110,7 +1065,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
// Other regions (mostly non-data) can't have a reliable C string length.
// In this case, an error is emitted and UndefinedVal is returned.
// The caller should always be prepared to handle this case.
- if (Filter.CheckCStringNotNullTerm) {
+ if (NotNullTerm.isEnabled()) {
SmallString<120> buf;
llvm::raw_svector_ostream os(buf);
@@ -2873,24 +2828,27 @@ void CStringChecker::checkDeadSymbols(SymbolReaper &SR,
}
void ento::registerCStringModeling(CheckerManager &Mgr) {
- Mgr.registerChecker<CStringChecker>();
+ // Other checker relies on the modeling implemented in this checker family,
+ // so this "modeling checker" can register the 'CStringChecker' backend for
+ // its callbacks without enabling any of its frontends.
+ Mgr.getChecker<CStringChecker>();
}
-bool ento::shouldRegisterCStringModeling(const CheckerManager &mgr) {
+bool ento::shouldRegisterCStringModeling(const CheckerManager &) {
return true;
}
-#define REGISTER_CHECKER(name) \
- void ento::register##name(CheckerManager &mgr) { \
- CStringChecker *checker = mgr.getChecker<CStringChecker>(); \
- checker->Filter.Check##name = true; \
- checker->Filter.CheckName##name = mgr.getCurrentCheckerName(); \
+#define REGISTER_CHECKER(NAME) \
+ void ento::registerCString##NAME(CheckerManager &Mgr) { \
+ Mgr.getChecker<CStringChecker>()->NAME.enable(Mgr); \
} \
\
- bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
+ bool ento::shouldRegisterCString##NAME(const CheckerManager &) { \
+ return true; \
+ }
-REGISTER_CHECKER(CStringNullArg)
-REGISTER_CHECKER(CStringOutOfBounds)
-REGISTER_CHECKER(CStringBufferOverlap)
-REGISTER_CHECKER(CStringNotNullTerm)
-REGISTER_CHECKER(CStringUninitializedRead)
+REGISTER_CHECKER(NullArg)
+REGISTER_CHECKER(OutOfBounds)
+REGISTER_CHECKER(BufferOverlap)
+REGISTER_CHECKER(NotNullTerm)
+REGISTER_CHECKER(UninitializedRead)
diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
index 839c8bc..a227ca0 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
@@ -111,32 +111,12 @@ bool PlacementNewChecker::checkPlaceCapacityIsSufficient(
if (!SizeOfPlaceCI)
return true;
- if ((SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue()) ||
- (IsArrayTypeAllocated &&
- SizeOfPlaceCI->getValue() >= SizeOfTargetCI->getValue())) {
+ if ((SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue())) {
if (ExplodedNode *N = C.generateErrorNode(C.getState())) {
- std::string Msg;
- // TODO: use clang constant
- if (IsArrayTypeAllocated &&
- SizeOfPlaceCI->getValue() > SizeOfTargetCI->getValue())
- Msg = std::string(llvm::formatv(
- "{0} bytes is possibly not enough for array allocation which "
- "requires {1} bytes. Current overhead requires the size of {2} "
- "bytes",
- SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue(),
- *SizeOfPlaceCI->getValue() - SizeOfTargetCI->getValue()));
- else if (IsArrayTypeAllocated &&
- SizeOfPlaceCI->getValue() == SizeOfTargetCI->getValue())
- Msg = std::string(llvm::formatv(
- "Storage provided to placement new is only {0} bytes, "
- "whereas the allocated array type requires more space for "
- "internal needs",
- SizeOfPlaceCI->getValue()));
- else
- Msg = std::string(llvm::formatv(
- "Storage provided to placement new is only {0} bytes, "
- "whereas the allocated type requires {1} bytes",
- SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue()));
+ std::string Msg =
+ llvm::formatv("Storage provided to placement new is only {0} bytes, "
+ "whereas the allocated type requires {1} bytes",
+ SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue());
auto R = std::make_unique<PathSensitiveBugReport>(SBT, Msg, N);
bugreporter::trackExpressionValue(N, NE->getPlacementArg(0), *R);
diff --git a/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
index 15fd9a0..d2760ca 100644
--- a/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
@@ -142,34 +142,19 @@ void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D,
//===----------------------------------------------------------------------===//
namespace {
+class NSOrCFErrorDerefChecker
+ : public CheckerFamily<check::Location,
+ check::Event<ImplicitNullDerefEvent>> {
+ mutable IdentifierInfo *NSErrorII = nullptr;
+ mutable IdentifierInfo *CFErrorII = nullptr;
-class NSErrorDerefBug : public BugType {
-public:
- NSErrorDerefBug(const CheckerNameRef Checker)
- : BugType(Checker, "NSError** null dereference",
- "Coding conventions (Apple)") {}
-};
-
-class CFErrorDerefBug : public BugType {
public:
- CFErrorDerefBug(const CheckerNameRef Checker)
- : BugType(Checker, "CFErrorRef* null dereference",
- "Coding conventions (Apple)") {}
-};
-
-}
+ CheckerFrontendWithBugType NSError{"NSError** null dereference",
+ "Coding conventions (Apple)"};
+ CheckerFrontendWithBugType CFError{"CFErrorRef* null dereference",
+ "Coding conventions (Apple)"};
-namespace {
-class NSOrCFErrorDerefChecker
- : public Checker< check::Location,
- check::Event<ImplicitNullDerefEvent> > {
- mutable IdentifierInfo *NSErrorII, *CFErrorII;
- mutable std::unique_ptr<NSErrorDerefBug> NSBT;
- mutable std::unique_ptr<CFErrorDerefBug> CFBT;
-public:
- bool ShouldCheckNSError = false, ShouldCheckCFError = false;
- CheckerNameRef NSErrorName, CFErrorName;
- NSOrCFErrorDerefChecker() : NSErrorII(nullptr), CFErrorII(nullptr) {}
+ StringRef getDebugTag() const override { return "NSOrCFErrorDerefChecker"; }
void checkLocation(SVal loc, bool isLoad, const Stmt *S,
CheckerContext &C) const;
@@ -236,12 +221,12 @@ void NSOrCFErrorDerefChecker::checkLocation(SVal loc, bool isLoad,
if (!CFErrorII)
CFErrorII = &Ctx.Idents.get("CFErrorRef");
- if (ShouldCheckNSError && IsNSError(parmT, NSErrorII)) {
+ if (NSError.isEnabled() && IsNSError(parmT, NSErrorII)) {
setFlag<NSErrorOut>(state, state->getSVal(loc.castAs<Loc>()), C);
return;
}
- if (ShouldCheckCFError && IsCFError(parmT, CFErrorII)) {
+ if (CFError.isEnabled() && IsCFError(parmT, CFErrorII)) {
setFlag<CFErrorOut>(state, state->getSVal(loc.castAs<Loc>()), C);
return;
}
@@ -274,19 +259,9 @@ void NSOrCFErrorDerefChecker::checkEvent(ImplicitNullDerefEvent event) const {
os << " may be null";
- BugType *bug = nullptr;
- if (isNSError) {
- if (!NSBT)
- NSBT.reset(new NSErrorDerefBug(NSErrorName));
- bug = NSBT.get();
- }
- else {
- if (!CFBT)
- CFBT.reset(new CFErrorDerefBug(CFErrorName));
- bug = CFBT.get();
- }
+ const BugType &BT = isNSError ? NSError : CFError;
BR.emitReport(
- std::make_unique<PathSensitiveBugReport>(*bug, os.str(), event.SinkNode));
+ std::make_unique<PathSensitiveBugReport>(BT, os.str(), event.SinkNode));
}
static bool IsNSError(QualType T, IdentifierInfo *II) {
@@ -320,32 +295,21 @@ static bool IsCFError(QualType T, IdentifierInfo *II) {
return TT->getDecl()->getIdentifier() == II;
}
-void ento::registerNSOrCFErrorDerefChecker(CheckerManager &mgr) {
- mgr.registerChecker<NSOrCFErrorDerefChecker>();
-}
-
-bool ento::shouldRegisterNSOrCFErrorDerefChecker(const CheckerManager &mgr) {
- return true;
-}
-
-void ento::registerNSErrorChecker(CheckerManager &mgr) {
- mgr.registerChecker<NSErrorMethodChecker>();
- NSOrCFErrorDerefChecker *checker = mgr.getChecker<NSOrCFErrorDerefChecker>();
- checker->ShouldCheckNSError = true;
- checker->NSErrorName = mgr.getCurrentCheckerName();
-}
-
-bool ento::shouldRegisterNSErrorChecker(const CheckerManager &mgr) {
- return true;
-}
-
-void ento::registerCFErrorChecker(CheckerManager &mgr) {
- mgr.registerChecker<CFErrorFunctionChecker>();
- NSOrCFErrorDerefChecker *checker = mgr.getChecker<NSOrCFErrorDerefChecker>();
- checker->ShouldCheckCFError = true;
- checker->CFErrorName = mgr.getCurrentCheckerName();
-}
+// This source file implements two user-facing checkers ("osx.cocoa.NSError"
+// and "osx.coreFoundation.CFError") which are both implemented as the
+// combination of two `CheckerFrontend`s that are registered under the same
+// name (but otherwise act independently). Among these 2+2 `CheckerFrontend`s
+// two are coming from the checker family `NSOrCFErrorDerefChecker` while the
+// other two (the `ADDITIONAL_PART`s) are small standalone checkers.
+#define REGISTER_CHECKER(NAME, ADDITIONAL_PART) \
+ void ento::register##NAME##Checker(CheckerManager &Mgr) { \
+ Mgr.getChecker<NSOrCFErrorDerefChecker>()->NAME.enable(Mgr); \
+ Mgr.registerChecker<ADDITIONAL_PART>(); \
+ } \
+ \
+ bool ento::shouldRegister##NAME##Checker(const CheckerManager &) { \
+ return true; \
+ }
-bool ento::shouldRegisterCFErrorChecker(const CheckerManager &mgr) {
- return true;
-}
+REGISTER_CHECKER(NSError, NSErrorMethodChecker)
+REGISTER_CHECKER(CFError, CFErrorFunctionChecker)
diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
index a63497c..019e81f 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
@@ -28,23 +28,22 @@ using namespace ento;
namespace {
class StackAddrEscapeChecker
- : public Checker<check::PreCall, check::PreStmt<ReturnStmt>,
- check::EndFunction> {
+ : public CheckerFamily<check::PreCall, check::PreStmt<ReturnStmt>,
+ check::EndFunction> {
mutable IdentifierInfo *dispatch_semaphore_tII = nullptr;
- mutable std::unique_ptr<BugType> BT_stackleak;
- mutable std::unique_ptr<BugType> BT_returnstack;
- mutable std::unique_ptr<BugType> BT_capturedstackasync;
- mutable std::unique_ptr<BugType> BT_capturedstackret;
public:
- enum CheckKind {
- CK_StackAddrEscapeChecker,
- CK_StackAddrAsyncEscapeChecker,
- CK_NumCheckKinds
- };
+ StringRef getDebugTag() const override { return "StackAddrEscapeChecker"; }
+
+ CheckerFrontend StackAddrEscape;
+ CheckerFrontend StackAddrAsyncEscape;
- bool ChecksEnabled[CK_NumCheckKinds] = {false};
- CheckerNameRef CheckNames[CK_NumCheckKinds];
+ const BugType StackLeak{&StackAddrEscape,
+ "Stack address leaks outside of stack frame"};
+ const BugType ReturnStack{&StackAddrEscape,
+ "Return of address to stack-allocated memory"};
+ const BugType CapturedStackAsync{
+ &StackAddrAsyncEscape, "Address of stack-allocated memory is captured"};
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
@@ -170,10 +169,6 @@ void StackAddrEscapeChecker::EmitReturnLeakError(CheckerContext &C,
ExplodedNode *N = C.generateNonFatalErrorNode();
if (!N)
return;
- if (!BT_returnstack)
- BT_returnstack = std::make_unique<BugType>(
- CheckNames[CK_StackAddrEscapeChecker],
- "Return of address to stack-allocated memory");
// Generate a report for this bug.
SmallString<128> buf;
@@ -184,7 +179,7 @@ void StackAddrEscapeChecker::EmitReturnLeakError(CheckerContext &C,
EmitReturnedAsPartOfError(os, C.getSVal(RetE), R);
auto report =
- std::make_unique<PathSensitiveBugReport>(*BT_returnstack, os.str(), N);
+ std::make_unique<PathSensitiveBugReport>(ReturnStack, os.str(), N);
report->addRange(RetE->getSourceRange());
if (range.isValid())
report->addRange(range);
@@ -215,16 +210,12 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
ExplodedNode *N = C.generateNonFatalErrorNode();
if (!N)
continue;
- if (!BT_capturedstackasync)
- BT_capturedstackasync = std::make_unique<BugType>(
- CheckNames[CK_StackAddrAsyncEscapeChecker],
- "Address of stack-allocated memory is captured");
SmallString<128> Buf;
llvm::raw_svector_ostream Out(Buf);
SourceRange Range = genName(Out, Region, C.getASTContext());
Out << " is captured by an asynchronously-executed block";
- auto Report = std::make_unique<PathSensitiveBugReport>(
- *BT_capturedstackasync, Out.str(), N);
+ auto Report = std::make_unique<PathSensitiveBugReport>(CapturedStackAsync,
+ Out.str(), N);
if (Range.isValid())
Report->addRange(Range);
C.emitReport(std::move(Report));
@@ -233,7 +224,7 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
- if (!ChecksEnabled[CK_StackAddrAsyncEscapeChecker])
+ if (!StackAddrAsyncEscape.isEnabled())
return;
if (!Call.isGlobalCFunction("dispatch_after") &&
!Call.isGlobalCFunction("dispatch_async"))
@@ -357,7 +348,7 @@ FindEscapingStackRegions(CheckerContext &C, const Expr *RetE, SVal RetVal) {
void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
CheckerContext &C) const {
- if (!ChecksEnabled[CK_StackAddrEscapeChecker])
+ if (!StackAddrEscape.isEnabled())
return;
const Expr *RetE = RS->getRetValue();
@@ -456,7 +447,7 @@ static bool isInvalidatedSymbolRegion(const MemRegion *Region) {
void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
CheckerContext &Ctx) const {
- if (!ChecksEnabled[CK_StackAddrEscapeChecker])
+ if (!StackAddrEscape.isEnabled())
return;
ExplodedNode *Node = Ctx.getPredecessor();
@@ -581,11 +572,6 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
if (!N)
return;
- if (!BT_stackleak)
- BT_stackleak =
- std::make_unique<BugType>(CheckNames[CK_StackAddrEscapeChecker],
- "Stack address leaks outside of stack frame");
-
for (const auto &P : Cb.V) {
const MemRegion *Referrer = P.first->getBaseRegion();
const MemRegion *Referred = P.second;
@@ -604,7 +590,7 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
Out << " is still referred to by a temporary object on the stack"
<< CommonSuffix;
auto Report =
- std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N);
+ std::make_unique<PathSensitiveBugReport>(StackLeak, Out.str(), N);
if (Range.isValid())
Report->addRange(Range);
Ctx.emitReport(std::move(Report));
@@ -618,7 +604,7 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
Out << " is still referred to by the " << *ReferrerVariable << CommonSuffix;
auto Report =
- std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N);
+ std::make_unique<PathSensitiveBugReport>(StackLeak, Out.str(), N);
if (Range.isValid())
Report->addRange(Range);
@@ -626,23 +612,14 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
}
}
-void ento::registerStackAddrEscapeBase(CheckerManager &mgr) {
- mgr.registerChecker<StackAddrEscapeChecker>();
-}
-
-bool ento::shouldRegisterStackAddrEscapeBase(const CheckerManager &mgr) {
- return true;
-}
-
-#define REGISTER_CHECKER(name) \
- void ento::register##name(CheckerManager &Mgr) { \
- StackAddrEscapeChecker *Chk = Mgr.getChecker<StackAddrEscapeChecker>(); \
- Chk->ChecksEnabled[StackAddrEscapeChecker::CK_##name] = true; \
- Chk->CheckNames[StackAddrEscapeChecker::CK_##name] = \
- Mgr.getCurrentCheckerName(); \
+#define REGISTER_CHECKER(NAME) \
+ void ento::register##NAME##Checker(CheckerManager &Mgr) { \
+ Mgr.getChecker<StackAddrEscapeChecker>()->NAME.enable(Mgr); \
} \
\
- bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
+ bool ento::shouldRegister##NAME##Checker(const CheckerManager &) { \
+ return true; \
+ }
-REGISTER_CHECKER(StackAddrEscapeChecker)
-REGISTER_CHECKER(StackAddrAsyncEscapeChecker)
+REGISTER_CHECKER(StackAddrEscape)
+REGISTER_CHECKER(StackAddrAsyncEscape)