131 changes: 71 additions & 60 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3711,7 +3711,8 @@ void CodeGenModule::EmitGlobal(GlobalDecl GD) {

// Forward declarations are emitted lazily on first use.
if (!FD->doesThisDeclarationHaveABody()) {
if (!FD->doesDeclarationForceExternallyVisibleDefinition())
if (!FD->doesDeclarationForceExternallyVisibleDefinition() &&
!FD->isTargetVersionMultiVersion())
return;

StringRef MangledName = getMangledName(GD);
Expand Down Expand Up @@ -4092,77 +4093,78 @@ llvm::GlobalValue::LinkageTypes getMultiversionLinkage(CodeGenModule &CGM,
return llvm::GlobalValue::WeakODRLinkage;
}

static FunctionDecl *createDefaultTargetVersionFrom(const FunctionDecl *FD) {
DeclContext *DeclCtx = FD->getASTContext().getTranslationUnitDecl();
TypeSourceInfo *TInfo = FD->getTypeSourceInfo();
StorageClass SC = FD->getStorageClass();
DeclarationName Name = FD->getNameInfo().getName();

FunctionDecl *NewDecl =
FunctionDecl::Create(FD->getASTContext(), DeclCtx, FD->getBeginLoc(),
FD->getEndLoc(), Name, TInfo->getType(), TInfo, SC);

NewDecl->setIsMultiVersion();
NewDecl->addAttr(TargetVersionAttr::CreateImplicit(
NewDecl->getASTContext(), "default", NewDecl->getSourceRange()));

return NewDecl;
}

void CodeGenModule::emitMultiVersionFunctions() {
std::vector<GlobalDecl> MVFuncsToEmit;
MultiVersionFuncs.swap(MVFuncsToEmit);
for (GlobalDecl GD : MVFuncsToEmit) {
const auto *FD = cast<FunctionDecl>(GD.getDecl());
assert(FD && "Expected a FunctionDecl");

bool EmitResolver = !FD->isTargetVersionMultiVersion();
auto createFunction = [&](const FunctionDecl *Decl, unsigned MVIdx = 0) {
GlobalDecl CurGD{Decl->isDefined() ? Decl->getDefinition() : Decl, MVIdx};
StringRef MangledName = getMangledName(CurGD);
llvm::Constant *Func = GetGlobalValue(MangledName);
if (!Func) {
if (Decl->isDefined()) {
EmitGlobalFunctionDefinition(CurGD, nullptr);
Func = GetGlobalValue(MangledName);
} else {
const CGFunctionInfo &FI = getTypes().arrangeGlobalDeclaration(CurGD);
llvm::FunctionType *Ty = getTypes().GetFunctionType(FI);
Func = GetAddrOfFunction(CurGD, Ty, /*ForVTable=*/false,
/*DontDefer=*/false, ForDefinition);
}
assert(Func && "This should have just been created");
}
return cast<llvm::Function>(Func);
};

bool HasDefaultDecl = !FD->isTargetVersionMultiVersion();
bool ShouldEmitResolver = !FD->isTargetVersionMultiVersion();
SmallVector<CodeGenFunction::MultiVersionResolverOption, 10> Options;
if (FD->isTargetMultiVersion()) {
getContext().forEachMultiversionedFunctionVersion(
FD, [this, &GD, &Options, &EmitResolver](const FunctionDecl *CurFD) {
GlobalDecl CurGD{
(CurFD->isDefined() ? CurFD->getDefinition() : CurFD)};
StringRef MangledName = getMangledName(CurGD);
llvm::Constant *Func = GetGlobalValue(MangledName);
if (!Func) {
if (CurFD->isDefined()) {
EmitGlobalFunctionDefinition(CurGD, nullptr);
Func = GetGlobalValue(MangledName);
} else {
const CGFunctionInfo &FI =
getTypes().arrangeGlobalDeclaration(GD);
llvm::FunctionType *Ty = getTypes().GetFunctionType(FI);
Func = GetAddrOfFunction(CurGD, Ty, /*ForVTable=*/false,
/*DontDefer=*/false, ForDefinition);
}
assert(Func && "This should have just been created");
}
if (CurFD->getMultiVersionKind() == MultiVersionKind::Target) {
const auto *TA = CurFD->getAttr<TargetAttr>();
llvm::SmallVector<StringRef, 8> Feats;
FD, [&](const FunctionDecl *CurFD) {
llvm::SmallVector<StringRef, 8> Feats;
llvm::Function *Func = createFunction(CurFD);

if (const auto *TA = CurFD->getAttr<TargetAttr>()) {
TA->getAddedFeatures(Feats);
Options.emplace_back(cast<llvm::Function>(Func),
TA->getArchitecture(), Feats);
} else {
const auto *TVA = CurFD->getAttr<TargetVersionAttr>();
if (CurFD->isUsed() || (TVA->isDefaultVersion() &&
CurFD->doesThisDeclarationHaveABody()))
EmitResolver = true;
llvm::SmallVector<StringRef, 8> Feats;
Options.emplace_back(Func, TA->getArchitecture(), Feats);
} else if (const auto *TVA = CurFD->getAttr<TargetVersionAttr>()) {
bool HasDefaultDef = TVA->isDefaultVersion() &&
CurFD->doesThisDeclarationHaveABody();
HasDefaultDecl |= TVA->isDefaultVersion();
ShouldEmitResolver |= (CurFD->isUsed() || HasDefaultDef);
TVA->getFeatures(Feats);
Options.emplace_back(cast<llvm::Function>(Func),
/*Architecture*/ "", Feats);
}
Options.emplace_back(Func, /*Architecture*/ "", Feats);
} else
llvm_unreachable("unexpected MultiVersionKind");
});
} else if (FD->isTargetClonesMultiVersion()) {
const auto *TC = FD->getAttr<TargetClonesAttr>();
for (unsigned VersionIndex = 0; VersionIndex < TC->featuresStrs_size();
++VersionIndex) {
if (!TC->isFirstOfVersion(VersionIndex))
} else if (const auto *TC = FD->getAttr<TargetClonesAttr>()) {
for (unsigned I = 0; I < TC->featuresStrs_size(); ++I) {
if (!TC->isFirstOfVersion(I))
continue;
GlobalDecl CurGD{(FD->isDefined() ? FD->getDefinition() : FD),
VersionIndex};
StringRef Version = TC->getFeatureStr(VersionIndex);
StringRef MangledName = getMangledName(CurGD);
llvm::Constant *Func = GetGlobalValue(MangledName);
if (!Func) {
if (FD->isDefined()) {
EmitGlobalFunctionDefinition(CurGD, nullptr);
Func = GetGlobalValue(MangledName);
} else {
const CGFunctionInfo &FI =
getTypes().arrangeGlobalDeclaration(CurGD);
llvm::FunctionType *Ty = getTypes().GetFunctionType(FI);
Func = GetAddrOfFunction(CurGD, Ty, /*ForVTable=*/false,
/*DontDefer=*/false, ForDefinition);
}
assert(Func && "This should have just been created");
}

llvm::Function *Func = createFunction(FD, I);
StringRef Version = TC->getFeatureStr(I);
StringRef Architecture;
llvm::SmallVector<StringRef, 1> Feature;

Expand All @@ -4180,16 +4182,23 @@ void CodeGenModule::emitMultiVersionFunctions() {
Feature.push_back(Version);
}

Options.emplace_back(cast<llvm::Function>(Func), Architecture, Feature);
Options.emplace_back(Func, Architecture, Feature);
}
} else {
assert(0 && "Expected a target or target_clones multiversion function");
continue;
}

if (!EmitResolver)
if (!ShouldEmitResolver)
continue;

if (!HasDefaultDecl) {
FunctionDecl *NewFD = createDefaultTargetVersionFrom(FD);
llvm::Function *Func = createFunction(NewFD);
llvm::SmallVector<StringRef, 1> Feats;
Options.emplace_back(Func, /*Architecture*/ "", Feats);
}

llvm::Constant *ResolverConstant = GetOrCreateMultiVersionResolver(GD);
if (auto *IFunc = dyn_cast<llvm::GlobalIFunc>(ResolverConstant)) {
ResolverConstant = IFunc->getResolver();
Expand Down Expand Up @@ -4480,7 +4489,9 @@ llvm::Constant *CodeGenModule::GetOrCreateLLVMFunction(

if (FD->isMultiVersion()) {
UpdateMultiVersionNames(GD, FD, MangledName);
if (!IsForDefinition)
if (FD->isTargetVersionMultiVersion() && !FD->isUsed())
AddDeferredMultiVersionResolverToEmit(GD);
else if (!IsForDefinition)
return GetOrCreateMultiVersionResolver(GD);
}
}
Expand Down
6 changes: 5 additions & 1 deletion clang/lib/Driver/ToolChains/CommonArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1142,7 +1142,11 @@ void tools::addArchSpecificRPath(const ToolChain &TC, const ArgList &Args,
options::OPT_fno_rtlib_add_rpath, false))
return;

for (const auto &CandidateRPath : TC.getArchSpecificLibPaths()) {
SmallVector<std::string> CandidateRPaths(TC.getArchSpecificLibPaths());
if (const auto CandidateRPath = TC.getStdlibPath())
CandidateRPaths.emplace_back(*CandidateRPath);

for (const auto &CandidateRPath : CandidateRPaths) {
if (TC.getVFS().exists(CandidateRPath)) {
CmdArgs.push_back("-rpath");
CmdArgs.push_back(Args.MakeArgString(CandidateRPath));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ StringRef getLanguageName(Language Lang) {
case Language::Unknown:
case Language::Asm:
case Language::LLVM_IR:
case Language::CIR:
llvm_unreachable("Unsupported language kind");
}

Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Format/Format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,8 @@ template <> struct MappingTraits<FormatStyle> {
IO.mapOptional("AlignConsecutiveMacros", Style.AlignConsecutiveMacros);
IO.mapOptional("AlignConsecutiveShortCaseStatements",
Style.AlignConsecutiveShortCaseStatements);
IO.mapOptional("AlignConsecutiveTableGenBreakingDAGArgColons",
Style.AlignConsecutiveTableGenBreakingDAGArgColons);
IO.mapOptional("AlignConsecutiveTableGenCondOperatorColons",
Style.AlignConsecutiveTableGenCondOperatorColons);
IO.mapOptional("AlignConsecutiveTableGenDefinitionColons",
Expand Down Expand Up @@ -1408,6 +1410,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.AlignConsecutiveDeclarations = {};
LLVMStyle.AlignConsecutiveMacros = {};
LLVMStyle.AlignConsecutiveShortCaseStatements = {};
LLVMStyle.AlignConsecutiveTableGenBreakingDAGArgColons = {};
LLVMStyle.AlignConsecutiveTableGenCondOperatorColons = {};
LLVMStyle.AlignConsecutiveTableGenDefinitionColons = {};
LLVMStyle.AlignEscapedNewlines = FormatStyle::ENAS_Right;
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Format/FormatToken.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ namespace format {
TYPE(TableGenCondOperatorComma) \
TYPE(TableGenDAGArgCloser) \
TYPE(TableGenDAGArgListColon) \
TYPE(TableGenDAGArgListColonToAlign) \
TYPE(TableGenDAGArgListComma) \
TYPE(TableGenDAGArgListCommaToBreak) \
TYPE(TableGenDAGArgOpener) \
Expand Down
60 changes: 38 additions & 22 deletions clang/lib/Format/TokenAnnotator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -975,12 +975,15 @@ class AnnotatingParser {

// DagArg ::= Value [":" TokVarName] | TokVarName
// Appears as a part of SimpleValue6.
bool parseTableGenDAGArg() {
bool parseTableGenDAGArg(bool AlignColon = false) {
if (tryToParseTableGenTokVar())
return true;
if (parseTableGenValue()) {
if (CurrentToken && CurrentToken->is(tok::colon)) {
CurrentToken->setType(TT_TableGenDAGArgListColon);
if (AlignColon)
CurrentToken->setType(TT_TableGenDAGArgListColonToAlign);
else
CurrentToken->setType(TT_TableGenDAGArgListColon);
skipToNextNonComment();
return tryToParseTableGenTokVar();
}
Expand Down Expand Up @@ -1051,8 +1054,11 @@ class AnnotatingParser {
skipToNextNonComment();
return true;
}
if (!parseTableGenDAGArg())
if (!parseTableGenDAGArg(
BreakInside &&
Style.AlignConsecutiveTableGenBreakingDAGArgColons.Enabled)) {
return false;
}
FirstDAGArgListElm = false;
}
return false;
Expand Down Expand Up @@ -2747,10 +2753,9 @@ class AnnotatingParser {
}

// Heuristically try to determine whether the parentheses contain a type.
auto IsQualifiedPointerOrReference = [this](FormatToken *T) {
auto IsQualifiedPointerOrReference = [](FormatToken *T, bool IsCpp) {
// This is used to handle cases such as x = (foo *const)&y;
assert(!T->isTypeName(IsCpp) && "Should have already been checked");
(void)IsCpp; // Avoid -Wunused-lambda-capture when assertion is disabled.
// Strip trailing qualifiers such as const or volatile when checking
// whether the parens could be a cast to a pointer/reference type.
while (T) {
Expand Down Expand Up @@ -2783,7 +2788,7 @@ class AnnotatingParser {
!Tok.Previous ||
Tok.Previous->isOneOf(TT_TemplateCloser, TT_TypeDeclarationParen) ||
Tok.Previous->isTypeName(IsCpp) ||
IsQualifiedPointerOrReference(Tok.Previous);
IsQualifiedPointerOrReference(Tok.Previous, IsCpp);
bool ParensCouldEndDecl =
Tok.Next->isOneOf(tok::equal, tok::semi, tok::l_brace, tok::greater);
if (ParensAreType && !ParensCouldEndDecl)
Expand Down Expand Up @@ -4351,9 +4356,11 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
if (Left.is(tok::kw_auto) && Right.isOneOf(tok::l_paren, tok::l_brace))
return false;

const auto *BeforeLeft = Left.Previous;

// operator co_await(x)
if (Right.is(tok::l_paren) && Left.is(tok::kw_co_await) && Left.Previous &&
Left.Previous->is(tok::kw_operator)) {
if (Right.is(tok::l_paren) && Left.is(tok::kw_co_await) && BeforeLeft &&
BeforeLeft->is(tok::kw_operator)) {
return false;
}
// co_await (x), co_yield (x), co_return (x)
Expand Down Expand Up @@ -4388,8 +4395,10 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
}
if (Left.is(tok::colon))
return Left.isNot(TT_ObjCMethodExpr);
if (Left.is(tok::coloncolon))
return false;
if (Left.is(tok::coloncolon)) {
return Right.is(tok::star) && Right.is(TT_PointerOrReference) &&
Style.PointerAlignment != FormatStyle::PAS_Left;
}
if (Left.is(tok::less) || Right.isOneOf(tok::greater, tok::less)) {
if (Style.Language == FormatStyle::LK_TextProto ||
(Style.Language == FormatStyle::LK_Proto &&
Expand All @@ -4404,8 +4413,8 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
return false;
}
if (Right.is(tok::ellipsis)) {
return Left.Tok.isLiteral() || (Left.is(tok::identifier) && Left.Previous &&
Left.Previous->is(tok::kw_case));
return Left.Tok.isLiteral() || (Left.is(tok::identifier) && BeforeLeft &&
BeforeLeft->is(tok::kw_case));
}
if (Left.is(tok::l_square) && Right.is(tok::amp))
return Style.SpacesInSquareBrackets;
Expand Down Expand Up @@ -4473,8 +4482,8 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
if (Right.is(tok::l_brace) && Right.is(BK_Block))
return true;
// for (auto a = 0, b = 0; const auto& c : {1, 2, 3})
if (Left.Previous && Left.Previous->isTypeOrIdentifier(IsCpp) &&
Right.Next && Right.Next->is(TT_RangeBasedForLoopColon)) {
if (BeforeLeft && BeforeLeft->isTypeOrIdentifier(IsCpp) && Right.Next &&
Right.Next->is(TT_RangeBasedForLoopColon)) {
return getTokenPointerOrReferenceAlignment(Left) !=
FormatStyle::PAS_Right;
}
Expand All @@ -4496,12 +4505,17 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
startsWithInitStatement(Line)))) {
return false;
}
return Left.Previous && !Left.Previous->isOneOf(
tok::l_paren, tok::coloncolon, tok::l_square);
if (!BeforeLeft)
return false;
if (BeforeLeft->is(tok::coloncolon)) {
return Left.is(tok::star) &&
Style.PointerAlignment != FormatStyle::PAS_Right;
}
return !BeforeLeft->isOneOf(tok::l_paren, tok::l_square);
}
// Ensure right pointer alignment with ellipsis e.g. int *...P
if (Left.is(tok::ellipsis) && Left.Previous &&
Left.Previous->isPointerOrReference()) {
if (Left.is(tok::ellipsis) && BeforeLeft &&
BeforeLeft->isPointerOrReference()) {
return Style.PointerAlignment != FormatStyle::PAS_Right;
}

Expand Down Expand Up @@ -4663,13 +4677,13 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
return Style.SpaceBeforeParensOptions.AfterFunctionDefinitionName ||
spaceRequiredBeforeParens(Right);
}
if (!Left.Previous || !Left.Previous->isOneOf(tok::period, tok::arrow)) {
if (!BeforeLeft || !BeforeLeft->isOneOf(tok::period, tok::arrow)) {
if (Left.isOneOf(tok::kw_try, Keywords.kw___except, tok::kw_catch)) {
return Style.SpaceBeforeParensOptions.AfterControlStatements ||
spaceRequiredBeforeParens(Right);
}
if (Left.isOneOf(tok::kw_new, tok::kw_delete)) {
return ((!Line.MightBeFunctionDecl || !Left.Previous) &&
return ((!Line.MightBeFunctionDecl || !BeforeLeft) &&
Style.SpaceBeforeParens != FormatStyle::SBPO_Never) ||
spaceRequiredBeforeParens(Right);
}
Expand Down Expand Up @@ -5130,8 +5144,10 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line,
if (Left.is(tok::r_brace) && Right.is(tok::r_square))
return true;
// Do not insert around colon in DAGArg and cond operator.
if (Right.is(TT_TableGenDAGArgListColon) ||
Left.is(TT_TableGenDAGArgListColon)) {
if (Right.isOneOf(TT_TableGenDAGArgListColon,
TT_TableGenDAGArgListColonToAlign) ||
Left.isOneOf(TT_TableGenDAGArgListColon,
TT_TableGenDAGArgListColonToAlign)) {
return false;
}
if (Right.is(TT_TableGenCondOperatorColon))
Expand Down
8 changes: 7 additions & 1 deletion clang/lib/Format/WhitespaceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ const tooling::Replacements &WhitespaceManager::generateReplacements() {
alignConsecutiveBitFields();
alignConsecutiveAssignments();
if (Style.isTableGen()) {
alignConsecutiveTableGenBreakingDAGArgColons();
alignConsecutiveTableGenCondOperatorColons();
alignConsecutiveTableGenDefinitions();
}
Expand Down Expand Up @@ -981,6 +982,11 @@ void WhitespaceManager::alignConsecutiveShortCaseStatements() {
Changes);
}

void WhitespaceManager::alignConsecutiveTableGenBreakingDAGArgColons() {
alignConsecutiveColons(Style.AlignConsecutiveTableGenBreakingDAGArgColons,
TT_TableGenDAGArgListColonToAlign);
}

void WhitespaceManager::alignConsecutiveTableGenCondOperatorColons() {
alignConsecutiveColons(Style.AlignConsecutiveTableGenCondOperatorColons,
TT_TableGenCondOperatorColon);
Expand Down Expand Up @@ -1485,7 +1491,7 @@ WhitespaceManager::CellDescriptions WhitespaceManager::getCells(unsigned Start,
: Cell);
// Go to the next non-comment and ensure there is a break in front
const auto *NextNonComment = C.Tok->getNextNonComment();
while (NextNonComment->is(tok::comma))
while (NextNonComment && NextNonComment->is(tok::comma))
NextNonComment = NextNonComment->getNextNonComment();
auto j = i;
while (j < End && Changes[j].Tok != NextNonComment)
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Format/WhitespaceManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,9 @@ class WhitespaceManager {
/// Align consecutive short case statements over all \c Changes.
void alignConsecutiveShortCaseStatements();

/// Align consecutive TableGen DAGArg colon over all \c Changes.
void alignConsecutiveTableGenBreakingDAGArgColons();

/// Align consecutive TableGen cond operator colon over all \c Changes.
void alignConsecutiveTableGenCondOperatorColons();

Expand Down
13 changes: 11 additions & 2 deletions clang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2757,6 +2757,9 @@ static void GenerateFrontendArgs(const FrontendOptions &Opts,
case Language::HLSL:
Lang = "hlsl";
break;
case Language::CIR:
Lang = "cir";
break;
}

GenerateArg(Consumer, OPT_x,
Expand Down Expand Up @@ -2958,6 +2961,7 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
.Cases("ast", "pcm", "precompiled-header",
InputKind(Language::Unknown, InputKind::Precompiled))
.Case("ir", Language::LLVM_IR)
.Case("cir", Language::CIR)
.Default(Language::Unknown);

if (DashX.isUnknown())
Expand Down Expand Up @@ -3323,6 +3327,7 @@ static bool IsInputCompatibleWithStandard(InputKind IK,
switch (IK.getLanguage()) {
case Language::Unknown:
case Language::LLVM_IR:
case Language::CIR:
llvm_unreachable("should not parse language flags for this input");

case Language::C:
Expand Down Expand Up @@ -3388,6 +3393,8 @@ static StringRef GetInputKindName(InputKind IK) {
return "Asm";
case Language::LLVM_IR:
return "LLVM IR";
case Language::CIR:
return "Clang IR";

case Language::HLSL:
return "HLSL";
Expand All @@ -3403,7 +3410,8 @@ void CompilerInvocationBase::GenerateLangArgs(const LangOptions &Opts,
const llvm::Triple &T,
InputKind IK) {
if (IK.getFormat() == InputKind::Precompiled ||
IK.getLanguage() == Language::LLVM_IR) {
IK.getLanguage() == Language::LLVM_IR ||
IK.getLanguage() == Language::CIR) {
if (Opts.ObjCAutoRefCount)
GenerateArg(Consumer, OPT_fobjc_arc);
if (Opts.PICLevel != 0)
Expand Down Expand Up @@ -3689,7 +3697,8 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args,
unsigned NumErrorsBefore = Diags.getNumErrors();

if (IK.getFormat() == InputKind::Precompiled ||
IK.getLanguage() == Language::LLVM_IR) {
IK.getLanguage() == Language::LLVM_IR ||
IK.getLanguage() == Language::CIR) {
// ObjCAAutoRefCount and Sanitize LangOpts are used to setup the
// PassManager in BackendUtil.cpp. They need to be initialized no matter
// what the input type is.
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Frontend/FrontendActions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,7 @@ void PrintPreambleAction::ExecuteAction() {
case Language::CUDA:
case Language::HIP:
case Language::HLSL:
case Language::CIR:
break;

case Language::Unknown:
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Frontend/FrontendOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@ InputKind FrontendOptions::getInputKindForExtension(StringRef Extension) {
.Case("hip", Language::HIP)
.Cases("ll", "bc", Language::LLVM_IR)
.Case("hlsl", Language::HLSL)
.Case("cir", Language::CIR)
.Default(Language::Unknown);
}
80 changes: 19 additions & 61 deletions clang/lib/Headers/hlsl/hlsl_intrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -737,15 +737,6 @@ float3 log(float3);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_log)
float4 log(float4);

_HLSL_BUILTIN_ALIAS(__builtin_elementwise_log)
double log(double);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_log)
double2 log(double2);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_log)
double3 log(double3);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_log)
double4 log(double4);

//===----------------------------------------------------------------------===//
// log10 builtins
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -779,15 +770,6 @@ float3 log10(float3);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_log10)
float4 log10(float4);

_HLSL_BUILTIN_ALIAS(__builtin_elementwise_log10)
double log10(double);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_log10)
double2 log10(double2);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_log10)
double3 log10(double3);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_log10)
double4 log10(double4);

//===----------------------------------------------------------------------===//
// log2 builtins
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -821,15 +803,6 @@ float3 log2(float3);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_log2)
float4 log2(float4);

_HLSL_BUILTIN_ALIAS(__builtin_elementwise_log2)
double log2(double);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_log2)
double2 log2(double2);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_log2)
double3 log2(double3);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_log2)
double4 log2(double4);

//===----------------------------------------------------------------------===//
// mad builtins
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -1174,15 +1147,6 @@ float3 pow(float3, float3);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_pow)
float4 pow(float4, float4);

_HLSL_BUILTIN_ALIAS(__builtin_elementwise_pow)
double pow(double, double);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_pow)
double2 pow(double2, double2);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_pow)
double3 pow(double3, double3);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_pow)
double4 pow(double4, double4);

//===----------------------------------------------------------------------===//
// reversebits builtins
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -1393,15 +1357,6 @@ float3 sin(float3);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_sin)
float4 sin(float4);

_HLSL_BUILTIN_ALIAS(__builtin_elementwise_sin)
double sin(double);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_sin)
double2 sin(double2);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_sin)
double3 sin(double3);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_sin)
double4 sin(double4);

//===----------------------------------------------------------------------===//
// sqrt builtins
//===----------------------------------------------------------------------===//
Expand All @@ -1411,14 +1366,26 @@ double4 sin(double4);
/// \param Val The input value.

_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_sqrtf16)
half sqrt(half In);

_HLSL_BUILTIN_ALIAS(__builtin_sqrtf)
float sqrt(float In);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_sqrt)
half sqrt(half);
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_sqrt)
half2 sqrt(half2);
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_sqrt)
half3 sqrt(half3);
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_sqrt)
half4 sqrt(half4);

_HLSL_BUILTIN_ALIAS(__builtin_sqrt)
double sqrt(double In);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_sqrt)
float sqrt(float);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_sqrt)
float2 sqrt(float2);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_sqrt)
float3 sqrt(float3);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_sqrt)
float4 sqrt(float4);

//===----------------------------------------------------------------------===//
// trunc builtins
Expand Down Expand Up @@ -1450,15 +1417,6 @@ float3 trunc(float3);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_trunc)
float4 trunc(float4);

_HLSL_BUILTIN_ALIAS(__builtin_elementwise_trunc)
double trunc(double);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_trunc)
double2 trunc(double2);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_trunc)
double3 trunc(double3);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_trunc)
double4 trunc(double4);

//===----------------------------------------------------------------------===//
// Wave* builtins
//===----------------------------------------------------------------------===//
Expand Down
12 changes: 6 additions & 6 deletions clang/lib/Headers/mmintrin.h
Original file line number Diff line number Diff line change
Expand Up @@ -1141,7 +1141,7 @@ _mm_xor_si64(__m64 __m1, __m64 __m2)
/// [8 x i8] to determine if the element of the first vector is equal to the
/// corresponding element of the second vector.
///
/// The comparison yields 0 for false, 0xFF for true.
/// Each comparison returns 0 for false, 0xFF for true.
///
/// \headerfile <x86intrin.h>
///
Expand All @@ -1163,7 +1163,7 @@ _mm_cmpeq_pi8(__m64 __m1, __m64 __m2)
/// [4 x i16] to determine if the element of the first vector is equal to the
/// corresponding element of the second vector.
///
/// The comparison yields 0 for false, 0xFFFF for true.
/// Each comparison returns 0 for false, 0xFFFF for true.
///
/// \headerfile <x86intrin.h>
///
Expand All @@ -1185,7 +1185,7 @@ _mm_cmpeq_pi16(__m64 __m1, __m64 __m2)
/// [2 x i32] to determine if the element of the first vector is equal to the
/// corresponding element of the second vector.
///
/// The comparison yields 0 for false, 0xFFFFFFFF for true.
/// Each comparison returns 0 for false, 0xFFFFFFFF for true.
///
/// \headerfile <x86intrin.h>
///
Expand All @@ -1207,7 +1207,7 @@ _mm_cmpeq_pi32(__m64 __m1, __m64 __m2)
/// [8 x i8] to determine if the element of the first vector is greater than
/// the corresponding element of the second vector.
///
/// The comparison yields 0 for false, 0xFF for true.
/// Each comparison returns 0 for false, 0xFF for true.
///
/// \headerfile <x86intrin.h>
///
Expand All @@ -1229,7 +1229,7 @@ _mm_cmpgt_pi8(__m64 __m1, __m64 __m2)
/// [4 x i16] to determine if the element of the first vector is greater than
/// the corresponding element of the second vector.
///
/// The comparison yields 0 for false, 0xFFFF for true.
/// Each comparison returns 0 for false, 0xFFFF for true.
///
/// \headerfile <x86intrin.h>
///
Expand All @@ -1251,7 +1251,7 @@ _mm_cmpgt_pi16(__m64 __m1, __m64 __m2)
/// [2 x i32] to determine if the element of the first vector is greater than
/// the corresponding element of the second vector.
///
/// The comparison yields 0 for false, 0xFFFFFFFF for true.
/// Each comparison returns 0 for false, 0xFFFFFFFF for true.
///
/// \headerfile <x86intrin.h>
///
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Headers/smmintrin.h
Original file line number Diff line number Diff line change
Expand Up @@ -1188,7 +1188,7 @@ static __inline__ int __DEFAULT_FN_ATTRS _mm_testnzc_si128(__m128i __M,
/// Compares each of the corresponding 64-bit values of the 128-bit
/// integer vectors for equality.
///
/// Each comparison yields 0x0 for false, 0xFFFFFFFFFFFFFFFF for true.
/// Each comparison returns 0x0 for false, 0xFFFFFFFFFFFFFFFF for true.
///
/// \headerfile <x86intrin.h>
///
Expand Down Expand Up @@ -2303,7 +2303,7 @@ static __inline__ __m128i __DEFAULT_FN_ATTRS _mm_minpos_epu16(__m128i __V) {
/// integer vectors to determine if the values in the first operand are
/// greater than those in the second operand.
///
/// Each comparison yields 0x0 for false, 0xFFFFFFFFFFFFFFFF for true.
/// Each comparison returns 0x0 for false, 0xFFFFFFFFFFFFFFFF for true.
///
/// \headerfile <x86intrin.h>
///
Expand Down
44 changes: 22 additions & 22 deletions clang/lib/Headers/xmmintrin.h
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ _mm_xor_ps(__m128 __a, __m128 __b)
/// Compares two 32-bit float values in the low-order bits of both
/// operands for equality.
///
/// The comparison yields 0x0 for false, 0xFFFFFFFF for true, in the
/// The comparison returns 0x0 for false, 0xFFFFFFFF for true, in the
/// low-order bits of a vector [4 x float].
/// If either value in a comparison is NaN, returns false.
///
Expand All @@ -509,7 +509,7 @@ _mm_cmpeq_ss(__m128 __a, __m128 __b)
/// Compares each of the corresponding 32-bit float values of the
/// 128-bit vectors of [4 x float] for equality.
///
/// Each comparison yields 0x0 for false, 0xFFFFFFFF for true.
/// Each comparison returns 0x0 for false, 0xFFFFFFFF for true.
/// If either value in a comparison is NaN, returns false.
///
/// \headerfile <x86intrin.h>
Expand All @@ -531,7 +531,7 @@ _mm_cmpeq_ps(__m128 __a, __m128 __b)
/// operands to determine if the value in the first operand is less than the
/// corresponding value in the second operand.
///
/// The comparison yields 0x0 for false, 0xFFFFFFFF for true, in the
/// The comparison returns 0x0 for false, 0xFFFFFFFF for true, in the
/// low-order bits of a vector of [4 x float].
/// If either value in a comparison is NaN, returns false.
///
Expand All @@ -557,7 +557,7 @@ _mm_cmplt_ss(__m128 __a, __m128 __b)
/// 128-bit vectors of [4 x float] to determine if the values in the first
/// operand are less than those in the second operand.
///
/// Each comparison yields 0x0 for false, 0xFFFFFFFFFFFFFFFF for true.
/// Each comparison returns 0x0 for false, 0xFFFFFFFFFFFFFFFF for true.
/// If either value in a comparison is NaN, returns false.
///
/// \headerfile <x86intrin.h>
Expand All @@ -579,7 +579,7 @@ _mm_cmplt_ps(__m128 __a, __m128 __b)
/// operands to determine if the value in the first operand is less than or
/// equal to the corresponding value in the second operand.
///
/// The comparison yields 0x0 for false, 0xFFFFFFFFFFFFFFFF for true, in
/// The comparison returns 0x0 for false, 0xFFFFFFFFFFFFFFFF for true, in
/// the low-order bits of a vector of [4 x float].
/// If either value in a comparison is NaN, returns false.
///
Expand All @@ -605,7 +605,7 @@ _mm_cmple_ss(__m128 __a, __m128 __b)
/// 128-bit vectors of [4 x float] to determine if the values in the first
/// operand are less than or equal to those in the second operand.
///
/// Each comparison yields 0x0 for false, 0xFFFFFFFF for true.
/// Each comparison returns 0x0 for false, 0xFFFFFFFF for true.
/// If either value in a comparison is NaN, returns false.
///
/// \headerfile <x86intrin.h>
Expand All @@ -627,7 +627,7 @@ _mm_cmple_ps(__m128 __a, __m128 __b)
/// operands to determine if the value in the first operand is greater than
/// the corresponding value in the second operand.
///
/// The comparison yields 0x0 for false, 0xFFFFFFFF for true, in the
/// The comparison returns 0x0 for false, 0xFFFFFFFF for true, in the
/// low-order bits of a vector of [4 x float].
/// If either value in a comparison is NaN, returns false.
///
Expand Down Expand Up @@ -655,7 +655,7 @@ _mm_cmpgt_ss(__m128 __a, __m128 __b)
/// 128-bit vectors of [4 x float] to determine if the values in the first
/// operand are greater than those in the second operand.
///
/// Each comparison yields 0x0 for false, 0xFFFFFFFF for true.
/// Each comparison returns 0x0 for false, 0xFFFFFFFF for true.
/// If either value in a comparison is NaN, returns false.
///
/// \headerfile <x86intrin.h>
Expand All @@ -677,7 +677,7 @@ _mm_cmpgt_ps(__m128 __a, __m128 __b)
/// operands to determine if the value in the first operand is greater than
/// or equal to the corresponding value in the second operand.
///
/// Each comparison yields 0x0 for false, 0xFFFFFFFF for true, in the
/// Each comparison returns 0x0 for false, 0xFFFFFFFF for true, in the
/// low-order bits of a vector of [4 x float].
/// If either value in a comparison is NaN, returns false.
///
Expand Down Expand Up @@ -705,7 +705,7 @@ _mm_cmpge_ss(__m128 __a, __m128 __b)
/// 128-bit vectors of [4 x float] to determine if the values in the first
/// operand are greater than or equal to those in the second operand.
///
/// Each comparison yields 0x0 for false, 0xFFFFFFFFFFFFFFFF for true.
/// Each comparison returns 0x0 for false, 0xFFFFFFFFFFFFFFFF for true.
/// If either value in a comparison is NaN, returns false.
///
/// \headerfile <x86intrin.h>
Expand All @@ -726,7 +726,7 @@ _mm_cmpge_ps(__m128 __a, __m128 __b)
/// Compares two 32-bit float values in the low-order bits of both operands
/// for inequality.
///
/// The comparison yields 0x0 for false, 0xFFFFFFFF for true, in the
/// The comparison returns 0x0 for false, 0xFFFFFFFF for true, in the
/// low-order bits of a vector of [4 x float].
/// If either value in a comparison is NaN, returns true.
///
Expand All @@ -752,7 +752,7 @@ _mm_cmpneq_ss(__m128 __a, __m128 __b)
/// Compares each of the corresponding 32-bit float values of the
/// 128-bit vectors of [4 x float] for inequality.
///
/// Each comparison yields 0x0 for false, 0xFFFFFFFF for true.
/// Each comparison returns 0x0 for false, 0xFFFFFFFF for true.
/// If either value in a comparison is NaN, returns true.
///
/// \headerfile <x86intrin.h>
Expand All @@ -775,7 +775,7 @@ _mm_cmpneq_ps(__m128 __a, __m128 __b)
/// operands to determine if the value in the first operand is not less than
/// the corresponding value in the second operand.
///
/// Each comparison yields 0x0 for false, 0xFFFFFFFF for true, in the
/// Each comparison returns 0x0 for false, 0xFFFFFFFF for true, in the
/// low-order bits of a vector of [4 x float].
/// If either value in a comparison is NaN, returns true.
///
Expand All @@ -802,7 +802,7 @@ _mm_cmpnlt_ss(__m128 __a, __m128 __b)
/// 128-bit vectors of [4 x float] to determine if the values in the first
/// operand are not less than those in the second operand.
///
/// Each comparison yields 0x0 for false, 0xFFFFFFFF for true.
/// Each comparison returns 0x0 for false, 0xFFFFFFFF for true.
/// If either value in a comparison is NaN, returns true.
///
/// \headerfile <x86intrin.h>
Expand All @@ -825,7 +825,7 @@ _mm_cmpnlt_ps(__m128 __a, __m128 __b)
/// operands to determine if the value in the first operand is not less than
/// or equal to the corresponding value in the second operand.
///
/// Each comparison yields 0x0 for false, 0xFFFFFFFF for true, in the
/// Each comparison returns 0x0 for false, 0xFFFFFFFF for true, in the
/// low-order bits of a vector of [4 x float].
/// If either value in a comparison is NaN, returns true.
///
Expand All @@ -852,7 +852,7 @@ _mm_cmpnle_ss(__m128 __a, __m128 __b)
/// 128-bit vectors of [4 x float] to determine if the values in the first
/// operand are not less than or equal to those in the second operand.
///
/// Each comparison yields 0x0 for false, 0xFFFFFFFF for true.
/// Each comparison returns 0x0 for false, 0xFFFFFFFF for true.
/// If either value in a comparison is NaN, returns true.
///
/// \headerfile <x86intrin.h>
Expand All @@ -875,7 +875,7 @@ _mm_cmpnle_ps(__m128 __a, __m128 __b)
/// operands to determine if the value in the first operand is not greater
/// than the corresponding value in the second operand.
///
/// Each comparison yields 0x0 for false, 0xFFFFFFFF for true, in the
/// Each comparison returns 0x0 for false, 0xFFFFFFFF for true, in the
/// low-order bits of a vector of [4 x float].
/// If either value in a comparison is NaN, returns true.
///
Expand Down Expand Up @@ -904,7 +904,7 @@ _mm_cmpngt_ss(__m128 __a, __m128 __b)
/// 128-bit vectors of [4 x float] to determine if the values in the first
/// operand are not greater than those in the second operand.
///
/// Each comparison yields 0x0 for false, 0xFFFFFFFF for true.
/// Each comparison returns 0x0 for false, 0xFFFFFFFF for true.
/// If either value in a comparison is NaN, returns true.
///
/// \headerfile <x86intrin.h>
Expand All @@ -927,7 +927,7 @@ _mm_cmpngt_ps(__m128 __a, __m128 __b)
/// operands to determine if the value in the first operand is not greater
/// than or equal to the corresponding value in the second operand.
///
/// Each comparison yields 0x0 for false, 0xFFFFFFFF for true, in the
/// Each comparison returns 0x0 for false, 0xFFFFFFFF for true, in the
/// low-order bits of a vector of [4 x float].
/// If either value in a comparison is NaN, returns true.
///
Expand Down Expand Up @@ -956,7 +956,7 @@ _mm_cmpnge_ss(__m128 __a, __m128 __b)
/// 128-bit vectors of [4 x float] to determine if the values in the first
/// operand are not greater than or equal to those in the second operand.
///
/// Each comparison yields 0x0 for false, 0xFFFFFFFF for true.
/// Each comparison returns 0x0 for false, 0xFFFFFFFF for true.
/// If either value in a comparison is NaN, returns true.
///
/// \headerfile <x86intrin.h>
Expand Down Expand Up @@ -3061,7 +3061,7 @@ _mm_movemask_ps(__m128 __a)
/// [4 x float], using the operation specified by the immediate integer
/// operand.
///
/// Each comparison yields 0x0 for false, 0xFFFFFFFF for true.
/// Each comparison returns 0x0 for false, 0xFFFFFFFF for true.
/// If either value in a comparison is NaN, comparisons that are ordered
/// return false, and comparisons that are unordered return true.
///
Expand Down Expand Up @@ -3096,7 +3096,7 @@ _mm_movemask_ps(__m128 __a)
/// vectors of [4 x float], using the operation specified by the immediate
/// integer operand.
///
/// Each comparison yields 0x0 for false, 0xFFFFFFFF for true.
/// Each comparison returns 0x0 for false, 0xFFFFFFFF for true.
/// If either value in a comparison is NaN, comparisons that are ordered
/// return false, and comparisons that are unordered return true.
///
Expand Down
171 changes: 154 additions & 17 deletions clang/lib/InstallAPI/DylibVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,15 @@ std::string DylibVerifier::getAnnotatedName(const Record *R,
Annotation += "(tlv) ";

// Check if symbol represents only part of a @interface declaration.
const bool IsAnnotatedObjCClass =
((SymCtx.ObjCIFKind != ObjCIFSymbolKind::None) &&
(SymCtx.ObjCIFKind <= ObjCIFSymbolKind::EHType));

if (IsAnnotatedObjCClass) {
if (SymCtx.ObjCIFKind == ObjCIFSymbolKind::EHType)
Annotation += "Exception Type of ";
if (SymCtx.ObjCIFKind == ObjCIFSymbolKind::MetaClass)
Annotation += "Metaclass of ";
if (SymCtx.ObjCIFKind == ObjCIFSymbolKind::Class)
Annotation += "Class of ";
switch (SymCtx.ObjCIFKind) {
default:
break;
case ObjCIFSymbolKind::EHType:
return Annotation + "Exception Type of " + PrettyName;
case ObjCIFSymbolKind::MetaClass:
return Annotation + "Metaclass of " + PrettyName;
case ObjCIFSymbolKind::Class:
return Annotation + "Class of " + PrettyName;
}

// Only print symbol type prefix or leading "_" if there is no source location
Expand All @@ -90,9 +88,6 @@ std::string DylibVerifier::getAnnotatedName(const Record *R,
return Annotation + PrettyName;
}

if (IsAnnotatedObjCClass)
return Annotation + PrettyName;

switch (SymCtx.Kind) {
case EncodeKind::GlobalSymbol:
return Annotation + PrettyName;
Expand Down Expand Up @@ -332,9 +327,9 @@ bool DylibVerifier::compareSymbolFlags(const Record *R, SymbolContext &SymCtx,
}
if (!DR->isThreadLocalValue() && R->isThreadLocalValue()) {
Ctx.emitDiag([&]() {
SymCtx.FA->D->getLocation(),
Ctx.Diag->Report(diag::err_header_symbol_flags_mismatch)
<< getAnnotatedName(DR, SymCtx) << R->isThreadLocalValue();
Ctx.Diag->Report(SymCtx.FA->D->getLocation(),
diag::err_header_symbol_flags_mismatch)
<< getAnnotatedName(R, SymCtx) << R->isThreadLocalValue();
});
return false;
}
Expand Down Expand Up @@ -520,5 +515,147 @@ void DylibVerifier::VerifierContext::emitDiag(
Report();
}

// The existence of weak-defined RTTI can not always be inferred from the
// header files because they can be generated as part of an implementation
// file.
// InstallAPI doesn't warn about weak-defined RTTI, because this doesn't affect
// static linking and so can be ignored for text-api files.
static bool shouldIgnoreCpp(StringRef Name, bool IsWeakDef) {
return (IsWeakDef &&
(Name.starts_with("__ZTI") || Name.starts_with("__ZTS")));
}
void DylibVerifier::visitSymbolInDylib(const Record &R, SymbolContext &SymCtx) {
// Undefined symbols should not be in InstallAPI generated text-api files.
if (R.isUndefined()) {
updateState(Result::Valid);
return;
}

// Internal symbols should not be in InstallAPI generated text-api files.
if (R.isInternal()) {
updateState(Result::Valid);
return;
}

// Allow zippered symbols with potentially mismatching availability
// between macOS and macCatalyst in the final text-api file.
const StringRef SymbolName(SymCtx.SymbolName);
if (const Symbol *Sym = Exports->findSymbol(SymCtx.Kind, SymCtx.SymbolName,
SymCtx.ObjCIFKind)) {
if (Sym->hasArchitecture(Ctx.Target.Arch)) {
updateState(Result::Ignore);
return;
}
}

if (shouldIgnoreCpp(SymbolName, R.isWeakDefined())) {
updateState(Result::Valid);
return;
}

// All checks at this point classify as some kind of violation that should be
// reported.

// Regardless of verification mode, error out on mismatched special linker
// symbols.
if (SymbolName.starts_with("$ld$")) {
Ctx.emitDiag([&]() {
Ctx.Diag->Report(diag::err_header_symbol_missing)
<< getAnnotatedName(&R, SymCtx, /*ValidSourceLoc=*/false);
});
updateState(Result::Invalid);
return;
}

// Missing declarations for exported symbols are hard errors on Pedantic mode.
if (Mode == VerificationMode::Pedantic) {
Ctx.emitDiag([&]() {
Ctx.Diag->Report(diag::err_header_symbol_missing)
<< getAnnotatedName(&R, SymCtx, /*ValidSourceLoc=*/false);
});
updateState(Result::Invalid);
return;
}

// Missing declarations for exported symbols are warnings on ErrorsAndWarnings
// mode.
if (Mode == VerificationMode::ErrorsAndWarnings) {
Ctx.emitDiag([&]() {
Ctx.Diag->Report(diag::warn_header_symbol_missing)
<< getAnnotatedName(&R, SymCtx, /*ValidSourceLoc=*/false);
});
updateState(Result::Ignore);
return;
}

// Missing declarations are dropped for ErrorsOnly mode. It is the last
// remaining mode.
updateState(Result::Ignore);
return;
}

void DylibVerifier::visitGlobal(const GlobalRecord &R) {
if (R.isVerified())
return;
SymbolContext SymCtx;
SimpleSymbol Sym = parseSymbol(R.getName());
SymCtx.SymbolName = Sym.Name;
SymCtx.Kind = Sym.Kind;
visitSymbolInDylib(R, SymCtx);
}

void DylibVerifier::visitObjCIVar(const ObjCIVarRecord &R,
const StringRef Super) {
if (R.isVerified())
return;
SymbolContext SymCtx;
SymCtx.SymbolName = ObjCIVarRecord::createScopedName(Super, R.getName());
SymCtx.Kind = EncodeKind::ObjectiveCInstanceVariable;
visitSymbolInDylib(R, SymCtx);
}

void DylibVerifier::visitObjCInterface(const ObjCInterfaceRecord &R) {
if (R.isVerified())
return;
SymbolContext SymCtx;
SymCtx.SymbolName = R.getName();
SymCtx.ObjCIFKind = assignObjCIFSymbolKind(&R);
if (SymCtx.ObjCIFKind > ObjCIFSymbolKind::EHType) {
if (R.hasExceptionAttribute()) {
SymCtx.Kind = EncodeKind::ObjectiveCClassEHType;
visitSymbolInDylib(R, SymCtx);
}
SymCtx.Kind = EncodeKind::ObjectiveCClass;
visitSymbolInDylib(R, SymCtx);
} else {
SymCtx.Kind = R.hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType
: EncodeKind::ObjectiveCClass;
visitSymbolInDylib(R, SymCtx);
}

for (const ObjCIVarRecord *IV : R.getObjCIVars())
visitObjCIVar(*IV, R.getName());
}

void DylibVerifier::visitObjCCategory(const ObjCCategoryRecord &R) {
for (const ObjCIVarRecord *IV : R.getObjCIVars())
visitObjCIVar(*IV, R.getSuperClassName());
}

DylibVerifier::Result DylibVerifier::verifyRemainingSymbols() {
if (getState() == Result::NoVerify)
return Result::NoVerify;
assert(!Dylib.empty() && "No binary to verify against");

Ctx.DiscoveredFirstError = false;
Ctx.PrintArch = true;
for (std::shared_ptr<RecordsSlice> Slice : Dylib) {
Ctx.Target = Slice->getTarget();
Ctx.DylibSlice = Slice.get();
Slice->visit(*this);
}
return getState();
}

} // namespace installapi
} // namespace clang
2 changes: 2 additions & 0 deletions clang/lib/InstallAPI/Frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ std::unique_ptr<MemoryBuffer> createInputBuffer(InstallAPIContext &Ctx) {
SmallString<4096> Contents;
raw_svector_ostream OS(Contents);
for (const HeaderFile &H : Ctx.InputHeaders) {
if (H.isExcluded())
continue;
if (H.getType() != Ctx.Type)
continue;
if (Ctx.LangMode == Language::C || Ctx.LangMode == Language::CXX)
Expand Down
51 changes: 51 additions & 0 deletions clang/lib/InstallAPI/HeaderFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "clang/InstallAPI/HeaderFile.h"
#include "llvm/TextAPI/Utils.h"

using namespace llvm;
namespace clang::installapi {
Expand Down Expand Up @@ -34,4 +35,54 @@ std::optional<std::string> createIncludeHeaderName(const StringRef FullPath) {
return Matches[1].drop_front(Matches[1].rfind('/') + 1).str() + "/" +
Matches[3].str();
}

bool isHeaderFile(StringRef Path) {
return StringSwitch<bool>(sys::path::extension(Path))
.Cases(".h", ".H", ".hh", ".hpp", ".hxx", true)
.Default(false);
}

llvm::Expected<PathSeq> enumerateFiles(FileManager &FM, StringRef Directory) {
PathSeq Files;
std::error_code EC;
auto &FS = FM.getVirtualFileSystem();
for (llvm::vfs::recursive_directory_iterator i(FS, Directory, EC), ie;
i != ie; i.increment(EC)) {
if (EC)
return errorCodeToError(EC);

// Skip files that do not exist. This usually happens for broken symlinks.
if (FS.status(i->path()) == std::errc::no_such_file_or_directory)
continue;

StringRef Path = i->path();
if (isHeaderFile(Path))
Files.emplace_back(Path);
}

return Files;
}

HeaderGlob::HeaderGlob(StringRef GlobString, Regex &&Rule, HeaderType Type)
: GlobString(GlobString), Rule(std::move(Rule)), Type(Type) {}

bool HeaderGlob::match(const HeaderFile &Header) {
if (Header.getType() != Type)
return false;

bool Match = Rule.match(Header.getPath());
if (Match)
FoundMatch = true;
return Match;
}

Expected<std::unique_ptr<HeaderGlob>> HeaderGlob::create(StringRef GlobString,
HeaderType Type) {
auto Rule = MachO::createRegexFromGlob(GlobString);
if (!Rule)
return Rule.takeError();

return std::make_unique<HeaderGlob>(GlobString, std::move(*Rule), Type);
}

} // namespace clang::installapi
7 changes: 4 additions & 3 deletions clang/lib/InstallAPI/Visitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,10 @@ bool InstallAPIVisitor::VisitObjCCategoryDecl(const ObjCCategoryDecl *D) {
const ObjCInterfaceDecl *InterfaceD = D->getClassInterface();
const StringRef InterfaceName = InterfaceD->getName();

auto [Category, FA] = Ctx.Slice->addObjCCategory(InterfaceName, CategoryName,
Avail, D, *Access);
recordObjCInstanceVariables(D->getASTContext(), Category, InterfaceName,
std::pair<ObjCCategoryRecord *, FrontendAttrs *> Category =
Ctx.Slice->addObjCCategory(InterfaceName, CategoryName, Avail, D,
*Access);
recordObjCInstanceVariables(D->getASTContext(), Category.first, InterfaceName,
D->ivars());
return true;
}
Expand Down
33 changes: 18 additions & 15 deletions clang/lib/Interpreter/IncrementalExecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupport.h"
#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"
#include "llvm/ExecutionEngine/Orc/LLJIT.h"
#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
Expand All @@ -36,26 +37,28 @@ LLVM_ATTRIBUTE_USED void linkComponents() {

namespace clang {

llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
IncrementalExecutor::createDefaultJITBuilder(
llvm::orc::JITTargetMachineBuilder JTMB) {
auto JITBuilder = std::make_unique<llvm::orc::LLJITBuilder>();
JITBuilder->setJITTargetMachineBuilder(std::move(JTMB));
JITBuilder->setPrePlatformSetup([](llvm::orc::LLJIT &J) {
// Try to enable debugging of JIT'd code (only works with JITLink for
// ELF and MachO).
consumeError(llvm::orc::enableDebuggerSupport(J));
return llvm::Error::success();
});
return std::move(JITBuilder);
}

IncrementalExecutor::IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC,
llvm::Error &Err,
const clang::TargetInfo &TI)
llvm::orc::LLJITBuilder &JITBuilder,
llvm::Error &Err)
: TSCtx(TSC) {
using namespace llvm::orc;
llvm::ErrorAsOutParameter EAO(&Err);

auto JTMB = JITTargetMachineBuilder(TI.getTriple());
JTMB.addFeatures(TI.getTargetOpts().Features);
LLJITBuilder Builder;
Builder.setJITTargetMachineBuilder(JTMB);
Builder.setPrePlatformSetup(
[](LLJIT &J) {
// Try to enable debugging of JIT'd code (only works with JITLink for
// ELF and MachO).
consumeError(enableDebuggerSupport(J));
return llvm::Error::success();
});

if (auto JitOrErr = Builder.create())
if (auto JitOrErr = JITBuilder.create())
Jit = std::move(*JitOrErr);
else {
Err = JitOrErr.takeError();
Expand Down
9 changes: 7 additions & 2 deletions clang/lib/Interpreter/IncrementalExecutor.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
namespace llvm {
class Error;
namespace orc {
class JITTargetMachineBuilder;
class LLJIT;
class LLJITBuilder;
class ThreadSafeContext;
} // namespace orc
} // namespace llvm
Expand All @@ -44,8 +46,8 @@ class IncrementalExecutor {
public:
enum SymbolNameKind { IRName, LinkerName };

IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC, llvm::Error &Err,
const clang::TargetInfo &TI);
IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC,
llvm::orc::LLJITBuilder &JITBuilder, llvm::Error &Err);
~IncrementalExecutor();

llvm::Error addModule(PartialTranslationUnit &PTU);
Expand All @@ -56,6 +58,9 @@ class IncrementalExecutor {
getSymbolAddress(llvm::StringRef Name, SymbolNameKind NameKind) const;

llvm::orc::LLJIT &GetExecutionEngine() { return *Jit; }

static llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
createDefaultJITBuilder(llvm::orc::JITTargetMachineBuilder JTMB);
};

} // end namespace clang
Expand Down
26 changes: 23 additions & 3 deletions clang/lib/Interpreter/Interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -372,15 +372,35 @@ Interpreter::Parse(llvm::StringRef Code) {
return IncrParser->Parse(Code);
}

static llvm::Expected<llvm::orc::JITTargetMachineBuilder>
createJITTargetMachineBuilder(const std::string &TT) {
if (TT == llvm::sys::getProcessTriple())
// This fails immediately if the target backend is not registered
return llvm::orc::JITTargetMachineBuilder::detectHost();

// If the target backend is not registered, LLJITBuilder::create() will fail
return llvm::orc::JITTargetMachineBuilder(llvm::Triple(TT));
}

llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
Interpreter::CreateJITBuilder(CompilerInstance &CI) {
auto JTMB = createJITTargetMachineBuilder(CI.getTargetOpts().Triple);
if (!JTMB)
return JTMB.takeError();
return IncrementalExecutor::createDefaultJITBuilder(std::move(*JTMB));
}

llvm::Error Interpreter::CreateExecutor() {
const clang::TargetInfo &TI =
getCompilerInstance()->getASTContext().getTargetInfo();
if (IncrExecutor)
return llvm::make_error<llvm::StringError>("Operation failed. "
"Execution engine exists",
std::error_code());
llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>> JB =
CreateJITBuilder(*getCompilerInstance());
if (!JB)
return JB.takeError();
llvm::Error Err = llvm::Error::success();
auto Executor = std::make_unique<IncrementalExecutor>(*TSCtx, Err, TI);
auto Executor = std::make_unique<IncrementalExecutor>(*TSCtx, **JB, Err);
if (!Err)
IncrExecutor = std::move(Executor);

Expand Down
8 changes: 8 additions & 0 deletions clang/lib/Parse/ParseDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,14 @@ Parser::DeclGroupPtrTy Parser::ParseNamespace(DeclaratorContext Context,
SkipUntil(tok::semi);
return nullptr;
}
if (!ExtraNSs.empty()) {
Diag(ExtraNSs.front().NamespaceLoc,
diag::err_unexpected_qualified_namespace_alias)
<< SourceRange(ExtraNSs.front().NamespaceLoc,
ExtraNSs.back().IdentLoc);
SkipUntil(tok::semi);
return nullptr;
}
if (attrLoc.isValid())
Diag(attrLoc, diag::err_unexpected_namespace_attributes_alias);
if (InlineLoc.isValid())
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11441,9 +11441,9 @@ static bool CheckMultiVersionFirstFunction(Sema &S, FunctionDecl *FD) {
"Function lacks multiversion attribute");
const auto *TA = FD->getAttr<TargetAttr>();
const auto *TVA = FD->getAttr<TargetVersionAttr>();
// Target and target_version only causes MV if it is default, otherwise this
// is a normal function.
if ((TA && !TA->isDefaultVersion()) || (TVA && !TVA->isDefaultVersion()))
// The target attribute only causes MV if this declaration is the default,
// otherwise it is treated as a normal function.
if (TA && !TA->isDefaultVersion())
return false;

if ((TA || TVA) && CheckMultiVersionValue(S, FD)) {
Expand Down
44 changes: 29 additions & 15 deletions clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6865,6 +6865,32 @@ static bool IsAcceptableNonMemberOperatorCandidate(ASTContext &Context,
return false;
}

static bool isNonViableMultiVersionOverload(FunctionDecl *FD) {
if (FD->isTargetMultiVersionDefault())
return false;

if (!FD->getASTContext().getTargetInfo().getTriple().isAArch64())
return FD->isTargetMultiVersion();

if (!FD->isMultiVersion())
return false;

// Among multiple target versions consider either the default,
// or the first non-default in the absence of default version.
unsigned SeenAt = 0;
unsigned I = 0;
bool HasDefault = false;
FD->getASTContext().forEachMultiversionedFunctionVersion(
FD, [&](const FunctionDecl *CurFD) {
if (FD == CurFD)
SeenAt = I;
else if (CurFD->isTargetMultiVersionDefault())
HasDefault = true;
++I;
});
return HasDefault || SeenAt != 0;
}

/// AddOverloadCandidate - Adds the given function to the set of
/// candidate functions, using the given function call arguments. If
/// @p SuppressUserConversions, then don't allow user-defined
Expand Down Expand Up @@ -6970,11 +6996,7 @@ void Sema::AddOverloadCandidate(
}
}

if (Function->isMultiVersion() &&
((Function->hasAttr<TargetAttr>() &&
!Function->getAttr<TargetAttr>()->isDefaultVersion()) ||
(Function->hasAttr<TargetVersionAttr>() &&
!Function->getAttr<TargetVersionAttr>()->isDefaultVersion()))) {
if (isNonViableMultiVersionOverload(Function)) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_non_default_multiversion_function;
return;
Expand Down Expand Up @@ -7637,11 +7659,7 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
return;
}

if (Method->isMultiVersion() &&
((Method->hasAttr<TargetAttr>() &&
!Method->getAttr<TargetAttr>()->isDefaultVersion()) ||
(Method->hasAttr<TargetVersionAttr>() &&
!Method->getAttr<TargetVersionAttr>()->isDefaultVersion()))) {
if (isNonViableMultiVersionOverload(Method)) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_non_default_multiversion_function;
}
Expand Down Expand Up @@ -8127,11 +8145,7 @@ void Sema::AddConversionCandidate(
return;
}

if (Conversion->isMultiVersion() &&
((Conversion->hasAttr<TargetAttr>() &&
!Conversion->getAttr<TargetAttr>()->isDefaultVersion()) ||
(Conversion->hasAttr<TargetVersionAttr>() &&
!Conversion->getAttr<TargetVersionAttr>()->isDefaultVersion()))) {
if (isNonViableMultiVersionOverload(Conversion)) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_non_default_multiversion_function;
}
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/StaticAnalyzer/Checkers/CXXDeleteChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,11 +220,11 @@ CXXDeleteChecker::PtrCastVisitor::VisitNode(const ExplodedNode *N,
/*addPosRange=*/true);
}

void ento::registerCXXArrayDeleteChecker(CheckerManager &mgr) {
void ento::registerArrayDeleteChecker(CheckerManager &mgr) {
mgr.registerChecker<CXXArrayDeleteChecker>();
}

bool ento::shouldRegisterCXXArrayDeleteChecker(const CheckerManager &mgr) {
bool ento::shouldRegisterArrayDeleteChecker(const CheckerManager &mgr) {
return true;
}

Expand Down
35 changes: 26 additions & 9 deletions clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -394,8 +394,10 @@ class MallocChecker
const CallEvent &Call, CheckerContext &C)>;

const CallDescriptionMap<CheckFn> PreFnMap{
{{{"getline"}, 3}, &MallocChecker::preGetdelim},
{{{"getdelim"}, 4}, &MallocChecker::preGetdelim},
// NOTE: the following CallDescription also matches the C++ standard
// library function std::getline(); the callback will filter it out.
{{CDM::CLibrary, {"getline"}, 3}, &MallocChecker::preGetdelim},
{{CDM::CLibrary, {"getdelim"}, 4}, &MallocChecker::preGetdelim},
};

const CallDescriptionMap<CheckFn> FreeingMemFnMap{
Expand Down Expand Up @@ -446,8 +448,11 @@ class MallocChecker
std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)},
{{{"g_realloc_n"}, 3}, &MallocChecker::checkReallocN},
{{{"g_try_realloc_n"}, 3}, &MallocChecker::checkReallocN},
{{{"getline"}, 3}, &MallocChecker::checkGetdelim},
{{{"getdelim"}, 4}, &MallocChecker::checkGetdelim},

// NOTE: the following CallDescription also matches the C++ standard
// library function std::getline(); the callback will filter it out.
{{CDM::CLibrary, {"getline"}, 3}, &MallocChecker::checkGetdelim},
{{CDM::CLibrary, {"getdelim"}, 4}, &MallocChecker::checkGetdelim},
};

bool isMemCall(const CallEvent &Call) const;
Expand Down Expand Up @@ -1435,13 +1440,21 @@ void MallocChecker::checkGMallocN0(const CallEvent &Call,
C.addTransition(State);
}

static bool isFromStdNamespace(const CallEvent &Call) {
const Decl *FD = Call.getDecl();
assert(FD && "a CallDescription cannot match a call without a Decl");
return FD->isInStdNamespace();
}

void MallocChecker::preGetdelim(const CallEvent &Call,
CheckerContext &C) const {
if (!Call.isGlobalCFunction())
// Discard calls to the C++ standard library function std::getline(), which
// is completely unrelated to the POSIX getline() that we're checking.
if (isFromStdNamespace(Call))
return;

ProgramStateRef State = C.getState();
const auto LinePtr = getPointeeDefVal(Call.getArgSVal(0), State);
const auto LinePtr = getPointeeVal(Call.getArgSVal(0), State);
if (!LinePtr)
return;

Expand All @@ -1458,7 +1471,9 @@ void MallocChecker::preGetdelim(const CallEvent &Call,

void MallocChecker::checkGetdelim(const CallEvent &Call,
CheckerContext &C) const {
if (!Call.isGlobalCFunction())
// Discard calls to the C++ standard library function std::getline(), which
// is completely unrelated to the POSIX getline() that we're checking.
if (isFromStdNamespace(Call))
return;

ProgramStateRef State = C.getState();
Expand All @@ -1470,8 +1485,10 @@ void MallocChecker::checkGetdelim(const CallEvent &Call,

SValBuilder &SVB = C.getSValBuilder();

const auto LinePtr = getPointeeDefVal(Call.getArgSVal(0), State);
const auto Size = getPointeeDefVal(Call.getArgSVal(1), State);
const auto LinePtr =
getPointeeVal(Call.getArgSVal(0), State)->getAs<DefinedSVal>();
const auto Size =
getPointeeVal(Call.getArgSVal(1), State)->getAs<DefinedSVal>();
if (!LinePtr || !Size || !LinePtr->getAsRegion())
return;

Expand Down
23 changes: 21 additions & 2 deletions clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1200,10 +1200,25 @@ void StreamChecker::evalGetdelim(const FnDescription *Desc,

// Add transition for the successful state.
NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
ProgramStateRef StateNotFailed =
State->BindExpr(E.CE, C.getLocationContext(), RetVal);
ProgramStateRef StateNotFailed = E.bindReturnValue(State, C, RetVal);
StateNotFailed =
E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call));

// On success, a buffer is allocated.
auto NewLinePtr = getPointeeVal(Call.getArgSVal(0), State);
if (NewLinePtr && isa<DefinedOrUnknownSVal>(*NewLinePtr))
StateNotFailed = StateNotFailed->assume(
NewLinePtr->castAs<DefinedOrUnknownSVal>(), true);

// The buffer size `*n` must be enough to hold the whole line, and
// greater than the return value, since it has to account for '\0'.
SVal SizePtrSval = Call.getArgSVal(1);
auto NVal = getPointeeVal(SizePtrSval, State);
if (NVal && isa<NonLoc>(*NVal)) {
StateNotFailed = E.assumeBinOpNN(StateNotFailed, BO_GT,
NVal->castAs<NonLoc>(), RetVal);
StateNotFailed = E.bindReturnValue(StateNotFailed, C, RetVal);
}
if (!StateNotFailed)
return;
C.addTransition(StateNotFailed);
Expand All @@ -1217,6 +1232,10 @@ void StreamChecker::evalGetdelim(const FnDescription *Desc,
E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError;
StateFailed = E.setStreamState(
StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
// On failure, the content of the buffer is undefined.
if (auto NewLinePtr = getPointeeVal(Call.getArgSVal(0), State))
StateFailed = StateFailed->bindLoc(*NewLinePtr, UndefinedVal(),
C.getLocationContext());
C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
}

Expand Down
192 changes: 160 additions & 32 deletions clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
Expand All @@ -41,25 +43,38 @@ enum class OpenVariant {
namespace {

class UnixAPIMisuseChecker
: public Checker<check::PreStmt<CallExpr>,
check::ASTDecl<TranslationUnitDecl>> {
: public Checker<check::PreCall, check::ASTDecl<TranslationUnitDecl>> {
const BugType BT_open{this, "Improper use of 'open'", categories::UnixAPI};
const BugType BT_getline{this, "Improper use of getdelim",
categories::UnixAPI};
const BugType BT_pthreadOnce{this, "Improper use of 'pthread_once'",
categories::UnixAPI};
const BugType BT_ArgumentNull{this, "NULL pointer", categories::UnixAPI};
mutable std::optional<uint64_t> Val_O_CREAT;

ProgramStateRef
EnsurePtrNotNull(SVal PtrVal, const Expr *PtrExpr, CheckerContext &C,
ProgramStateRef State, const StringRef PtrDescr,
std::optional<std::reference_wrapper<const BugType>> BT =
std::nullopt) const;

ProgramStateRef EnsureGetdelimBufferAndSizeCorrect(
SVal LinePtrPtrSVal, SVal SizePtrSVal, const Expr *LinePtrPtrExpr,
const Expr *SizePtrExpr, CheckerContext &C, ProgramStateRef State) const;

public:
void checkASTDecl(const TranslationUnitDecl *TU, AnalysisManager &Mgr,
BugReporter &BR) const;

void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;

void CheckOpen(CheckerContext &C, const CallExpr *CE) const;
void CheckOpenAt(CheckerContext &C, const CallExpr *CE) const;
void CheckPthreadOnce(CheckerContext &C, const CallExpr *CE) const;
void CheckOpen(CheckerContext &C, const CallEvent &Call) const;
void CheckOpenAt(CheckerContext &C, const CallEvent &Call) const;
void CheckGetDelim(CheckerContext &C, const CallEvent &Call) const;
void CheckPthreadOnce(CheckerContext &C, const CallEvent &Call) const;

void CheckOpenVariant(CheckerContext &C,
const CallExpr *CE, OpenVariant Variant) const;
void CheckOpenVariant(CheckerContext &C, const CallEvent &Call,
OpenVariant Variant) const;

void ReportOpenBug(CheckerContext &C, ProgramStateRef State, const char *Msg,
SourceRange SR) const;
Expand Down Expand Up @@ -95,6 +110,30 @@ class UnixAPIPortabilityChecker : public Checker< check::PreStmt<CallExpr> > {

} // end anonymous namespace

ProgramStateRef UnixAPIMisuseChecker::EnsurePtrNotNull(
SVal PtrVal, const Expr *PtrExpr, CheckerContext &C, ProgramStateRef State,
const StringRef PtrDescr,
std::optional<std::reference_wrapper<const BugType>> BT) const {
const auto Ptr = PtrVal.getAs<DefinedSVal>();
if (!Ptr)
return State;

const auto [PtrNotNull, PtrNull] = State->assume(*Ptr);
if (!PtrNotNull && PtrNull) {
if (ExplodedNode *N = C.generateErrorNode(PtrNull)) {
auto R = std::make_unique<PathSensitiveBugReport>(
BT.value_or(std::cref(BT_ArgumentNull)),
(PtrDescr + " pointer might be NULL.").str(), N);
if (PtrExpr)
bugreporter::trackExpressionValue(N, PtrExpr, *R);
C.emitReport(std::move(R));
}
return nullptr;
}

return PtrNotNull;
}

void UnixAPIMisuseChecker::checkASTDecl(const TranslationUnitDecl *TU,
AnalysisManager &Mgr,
BugReporter &) const {
Expand All @@ -113,9 +152,9 @@ void UnixAPIMisuseChecker::checkASTDecl(const TranslationUnitDecl *TU,
// "open" (man 2 open)
//===----------------------------------------------------------------------===/

void UnixAPIMisuseChecker::checkPreStmt(const CallExpr *CE,
void UnixAPIMisuseChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
const FunctionDecl *FD = C.getCalleeDecl(CE);
const FunctionDecl *FD = dyn_cast_if_present<FunctionDecl>(Call.getDecl());
if (!FD || FD->getKind() != Decl::Function)
return;

Expand All @@ -130,13 +169,16 @@ void UnixAPIMisuseChecker::checkPreStmt(const CallExpr *CE,
return;

if (FName == "open")
CheckOpen(C, CE);
CheckOpen(C, Call);

else if (FName == "openat")
CheckOpenAt(C, CE);
CheckOpenAt(C, Call);

else if (FName == "pthread_once")
CheckPthreadOnce(C, CE);
CheckPthreadOnce(C, Call);

else if (is_contained({"getdelim", "getline"}, FName))
CheckGetDelim(C, Call);
}
void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &C,
ProgramStateRef State,
Expand All @@ -152,17 +194,17 @@ void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &C,
}

void UnixAPIMisuseChecker::CheckOpen(CheckerContext &C,
const CallExpr *CE) const {
CheckOpenVariant(C, CE, OpenVariant::Open);
const CallEvent &Call) const {
CheckOpenVariant(C, Call, OpenVariant::Open);
}

void UnixAPIMisuseChecker::CheckOpenAt(CheckerContext &C,
const CallExpr *CE) const {
CheckOpenVariant(C, CE, OpenVariant::OpenAt);
const CallEvent &Call) const {
CheckOpenVariant(C, Call, OpenVariant::OpenAt);
}

void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,
const CallExpr *CE,
const CallEvent &Call,
OpenVariant Variant) const {
// The index of the argument taking the flags open flags (O_RDONLY,
// O_WRONLY, O_CREAT, etc.),
Expand Down Expand Up @@ -191,11 +233,11 @@ void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,

ProgramStateRef state = C.getState();

if (CE->getNumArgs() < MinArgCount) {
if (Call.getNumArgs() < MinArgCount) {
// The frontend should issue a warning for this case. Just return.
return;
} else if (CE->getNumArgs() == MaxArgCount) {
const Expr *Arg = CE->getArg(CreateModeArgIndex);
} else if (Call.getNumArgs() == MaxArgCount) {
const Expr *Arg = Call.getArgExpr(CreateModeArgIndex);
QualType QT = Arg->getType();
if (!QT->isIntegerType()) {
SmallString<256> SBuf;
Expand All @@ -209,15 +251,14 @@ void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,
Arg->getSourceRange());
return;
}
} else if (CE->getNumArgs() > MaxArgCount) {
} else if (Call.getNumArgs() > MaxArgCount) {
SmallString<256> SBuf;
llvm::raw_svector_ostream OS(SBuf);
OS << "Call to '" << VariantName << "' with more than " << MaxArgCount
<< " arguments";

ReportOpenBug(C, state,
SBuf.c_str(),
CE->getArg(MaxArgCount)->getSourceRange());
ReportOpenBug(C, state, SBuf.c_str(),
Call.getArgExpr(MaxArgCount)->getSourceRange());
return;
}

Expand All @@ -226,8 +267,8 @@ void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,
}

// Now check if oflags has O_CREAT set.
const Expr *oflagsEx = CE->getArg(FlagsArgIndex);
const SVal V = C.getSVal(oflagsEx);
const Expr *oflagsEx = Call.getArgExpr(FlagsArgIndex);
const SVal V = Call.getArgSVal(FlagsArgIndex);
if (!isa<NonLoc>(V)) {
// The case where 'V' can be a location can only be due to a bad header,
// so in this case bail out.
Expand All @@ -253,7 +294,7 @@ void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,
if (!(trueState && !falseState))
return;

if (CE->getNumArgs() < MaxArgCount) {
if (Call.getNumArgs() < MaxArgCount) {
SmallString<256> SBuf;
llvm::raw_svector_ostream OS(SBuf);
OS << "Call to '" << VariantName << "' requires a "
Expand All @@ -266,23 +307,110 @@ void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,
}
}

//===----------------------------------------------------------------------===//
// getdelim and getline
//===----------------------------------------------------------------------===//

ProgramStateRef UnixAPIMisuseChecker::EnsureGetdelimBufferAndSizeCorrect(
SVal LinePtrPtrSVal, SVal SizePtrSVal, const Expr *LinePtrPtrExpr,
const Expr *SizePtrExpr, CheckerContext &C, ProgramStateRef State) const {
static constexpr llvm::StringLiteral SizeGreaterThanBufferSize =
"The buffer from the first argument is smaller than the size "
"specified by the second parameter";
static constexpr llvm::StringLiteral SizeUndef =
"The buffer from the first argument is not NULL, but the size specified "
"by the second parameter is undefined.";

auto EmitBugReport = [this, &C, SizePtrExpr, LinePtrPtrExpr](
ProgramStateRef BugState, StringRef ErrMsg) {
if (ExplodedNode *N = C.generateErrorNode(BugState)) {
auto R = std::make_unique<PathSensitiveBugReport>(BT_getline, ErrMsg, N);
bugreporter::trackExpressionValue(N, SizePtrExpr, *R);
bugreporter::trackExpressionValue(N, LinePtrPtrExpr, *R);
C.emitReport(std::move(R));
}
};

// We have a pointer to a pointer to the buffer, and a pointer to the size.
// We want what they point at.
auto LinePtrSVal = getPointeeVal(LinePtrPtrSVal, State)->getAs<DefinedSVal>();
auto NSVal = getPointeeVal(SizePtrSVal, State);
if (!LinePtrSVal || !NSVal || NSVal->isUnknown())
return nullptr;

assert(LinePtrPtrExpr && SizePtrExpr);

const auto [LinePtrNotNull, LinePtrNull] = State->assume(*LinePtrSVal);
if (LinePtrNotNull && !LinePtrNull) {
// If `*lineptr` is not null, but `*n` is undefined, there is UB.
if (NSVal->isUndef()) {
EmitBugReport(LinePtrNotNull, SizeUndef);
return nullptr;
}

// If it is defined, and known, its size must be less than or equal to
// the buffer size.
auto NDefSVal = NSVal->getAs<DefinedSVal>();
auto &SVB = C.getSValBuilder();
auto LineBufSize =
getDynamicExtent(LinePtrNotNull, LinePtrSVal->getAsRegion(), SVB);
auto LineBufSizeGtN = SVB.evalBinOp(LinePtrNotNull, BO_GE, LineBufSize,
*NDefSVal, SVB.getConditionType())
.getAs<DefinedOrUnknownSVal>();
if (!LineBufSizeGtN)
return LinePtrNotNull;
if (auto LineBufSizeOk = LinePtrNotNull->assume(*LineBufSizeGtN, true))
return LineBufSizeOk;

EmitBugReport(LinePtrNotNull, SizeGreaterThanBufferSize);
return nullptr;
}
return State;
}

void UnixAPIMisuseChecker::CheckGetDelim(CheckerContext &C,
const CallEvent &Call) const {
ProgramStateRef State = C.getState();

// The parameter `n` must not be NULL.
SVal SizePtrSval = Call.getArgSVal(1);
State = EnsurePtrNotNull(SizePtrSval, Call.getArgExpr(1), C, State, "Size");
if (!State)
return;

// The parameter `lineptr` must not be NULL.
SVal LinePtrPtrSVal = Call.getArgSVal(0);
State =
EnsurePtrNotNull(LinePtrPtrSVal, Call.getArgExpr(0), C, State, "Line");
if (!State)
return;

State = EnsureGetdelimBufferAndSizeCorrect(LinePtrPtrSVal, SizePtrSval,
Call.getArgExpr(0),
Call.getArgExpr(1), C, State);
if (!State)
return;

C.addTransition(State);
}

//===----------------------------------------------------------------------===//
// pthread_once
//===----------------------------------------------------------------------===//

void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C,
const CallExpr *CE) const {
const CallEvent &Call) const {

// This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker.
// They can possibly be refactored.

if (CE->getNumArgs() < 1)
if (Call.getNumArgs() < 1)
return;

// Check if the first argument is stack allocated. If so, issue a warning
// because that's likely to be bad news.
ProgramStateRef state = C.getState();
const MemRegion *R = C.getSVal(CE->getArg(0)).getAsRegion();
const MemRegion *R = Call.getArgSVal(0).getAsRegion();
if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
return;

Expand All @@ -304,7 +432,7 @@ void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C,

auto report =
std::make_unique<PathSensitiveBugReport>(BT_pthreadOnce, os.str(), N);
report->addRange(CE->getArg(0)->getSourceRange());
report->addRange(Call.getArgExpr(0)->getSourceRange());
C.emitReport(std::move(report));
}

Expand Down
36 changes: 26 additions & 10 deletions clang/lib/StaticAnalyzer/Core/BugReporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ class PathDiagnosticConstruct {
public:
PathDiagnosticConstruct(const PathDiagnosticConsumer *PDC,
const ExplodedNode *ErrorNode,
const PathSensitiveBugReport *R);
const PathSensitiveBugReport *R,
const Decl *AnalysisEntryPoint);

/// \returns the location context associated with the current position in the
/// bug path.
Expand Down Expand Up @@ -1323,24 +1324,26 @@ void PathDiagnosticBuilder::generatePathDiagnosticsForNode(
}

static std::unique_ptr<PathDiagnostic>
generateDiagnosticForBasicReport(const BasicBugReport *R) {
generateDiagnosticForBasicReport(const BasicBugReport *R,
const Decl *AnalysisEntryPoint) {
const BugType &BT = R->getBugType();
return std::make_unique<PathDiagnostic>(
BT.getCheckerName(), R->getDeclWithIssue(), BT.getDescription(),
R->getDescription(), R->getShortDescription(/*UseFallback=*/false),
BT.getCategory(), R->getUniqueingLocation(), R->getUniqueingDecl(),
std::make_unique<FilesToLineNumsMap>());
AnalysisEntryPoint, std::make_unique<FilesToLineNumsMap>());
}

static std::unique_ptr<PathDiagnostic>
generateEmptyDiagnosticForReport(const PathSensitiveBugReport *R,
const SourceManager &SM) {
const SourceManager &SM,
const Decl *AnalysisEntryPoint) {
const BugType &BT = R->getBugType();
return std::make_unique<PathDiagnostic>(
BT.getCheckerName(), R->getDeclWithIssue(), BT.getDescription(),
R->getDescription(), R->getShortDescription(/*UseFallback=*/false),
BT.getCategory(), R->getUniqueingLocation(), R->getUniqueingDecl(),
findExecutedLines(SM, R->getErrorNode()));
AnalysisEntryPoint, findExecutedLines(SM, R->getErrorNode()));
}

static const Stmt *getStmtParent(const Stmt *S, const ParentMap &PM) {
Expand Down Expand Up @@ -1976,10 +1979,11 @@ static void updateExecutedLinesWithDiagnosticPieces(PathDiagnostic &PD) {

PathDiagnosticConstruct::PathDiagnosticConstruct(
const PathDiagnosticConsumer *PDC, const ExplodedNode *ErrorNode,
const PathSensitiveBugReport *R)
const PathSensitiveBugReport *R, const Decl *AnalysisEntryPoint)
: Consumer(PDC), CurrentNode(ErrorNode),
SM(CurrentNode->getCodeDecl().getASTContext().getSourceManager()),
PD(generateEmptyDiagnosticForReport(R, getSourceManager())) {
PD(generateEmptyDiagnosticForReport(R, getSourceManager(),
AnalysisEntryPoint)) {
LCM[&PD->getActivePath()] = ErrorNode->getLocationContext();
}

Expand All @@ -1993,13 +1997,14 @@ PathDiagnosticBuilder::PathDiagnosticBuilder(

std::unique_ptr<PathDiagnostic>
PathDiagnosticBuilder::generate(const PathDiagnosticConsumer *PDC) const {
PathDiagnosticConstruct Construct(PDC, ErrorNode, R);
const Decl *EntryPoint = getBugReporter().getAnalysisEntryPoint();
PathDiagnosticConstruct Construct(PDC, ErrorNode, R, EntryPoint);

const SourceManager &SM = getSourceManager();
const AnalyzerOptions &Opts = getAnalyzerOptions();

if (!PDC->shouldGenerateDiagnostics())
return generateEmptyDiagnosticForReport(R, getSourceManager());
return generateEmptyDiagnosticForReport(R, getSourceManager(), EntryPoint);

// Construct the final (warning) event for the bug report.
auto EndNotes = VisitorsDiagnostics->find(ErrorNode);
Expand Down Expand Up @@ -3123,6 +3128,16 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) {
Pieces.back()->addFixit(I);

updateExecutedLinesWithDiagnosticPieces(*PD);

// If we are debugging, let's have the entry point as the first note.
if (getAnalyzerOptions().AnalyzerDisplayProgress ||
getAnalyzerOptions().AnalyzerNoteAnalysisEntryPoints) {
const Decl *EntryPoint = getAnalysisEntryPoint();
Pieces.push_front(std::make_shared<PathDiagnosticEventPiece>(
PathDiagnosticLocation{EntryPoint->getLocation(), getSourceManager()},
"[debug] analyzing from " +
AnalysisDeclContext::getFunctionName(EntryPoint)));
}
Consumer->HandlePathDiagnostic(std::move(PD));
}
}
Expand Down Expand Up @@ -3211,7 +3226,8 @@ BugReporter::generateDiagnosticForConsumerMap(
auto *basicReport = cast<BasicBugReport>(exampleReport);
auto Out = std::make_unique<DiagnosticForConsumerMapTy>();
for (auto *Consumer : consumers)
(*Out)[Consumer] = generateDiagnosticForBasicReport(basicReport);
(*Out)[Consumer] =
generateDiagnosticForBasicReport(basicReport, AnalysisEntryPoint);
return Out;
}

Expand Down
5 changes: 4 additions & 1 deletion clang/lib/StaticAnalyzer/Core/CallEvent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1408,9 +1408,12 @@ CallEventManager::getSimpleCall(const CallExpr *CE, ProgramStateRef State,

if (const auto *OpCE = dyn_cast<CXXOperatorCallExpr>(CE)) {
const FunctionDecl *DirectCallee = OpCE->getDirectCallee();
if (const auto *MD = dyn_cast<CXXMethodDecl>(DirectCallee))
if (const auto *MD = dyn_cast<CXXMethodDecl>(DirectCallee)) {
if (MD->isImplicitObjectMemberFunction())
return create<CXXMemberOperatorCall>(OpCE, State, LCtx, ElemRef);
if (MD->isStatic())
return create<CXXStaticOperatorCall>(OpCE, State, LCtx, ElemRef);
}

} else if (CE->getCallee()->getType()->isBlockPointerType()) {
return create<BlockCall>(CE, State, LCtx, ElemRef);
Expand Down
8 changes: 5 additions & 3 deletions clang/lib/StaticAnalyzer/Core/CheckerContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,11 @@ bool CheckerContext::isCLibraryFunction(const FunctionDecl *FD,
if (!II)
return false;

// Look through 'extern "C"' and anything similar invented in the future.
// If this function is not in TU directly, it is not a C library function.
if (!FD->getDeclContext()->getRedeclContext()->isTranslationUnit())
// C library functions are either declared directly within a TU (the common
// case) or they are accessed through the namespace `std` (when they are used
// in C++ via headers like <cstdlib>).
const DeclContext *DC = FD->getDeclContext()->getRedeclContext();
if (!(DC->isTranslationUnit() || DC->isStdNamespace()))
return false;

// If this function is not externally visible, it is not a C library function.
Expand Down
5 changes: 2 additions & 3 deletions clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,9 @@ OperatorKind operationKindFromOverloadedOperator(OverloadedOperatorKind OOK,
}
}

std::optional<DefinedSVal> getPointeeDefVal(SVal PtrSVal,
ProgramStateRef State) {
std::optional<SVal> getPointeeVal(SVal PtrSVal, ProgramStateRef State) {
if (const auto *Ptr = PtrSVal.getAsRegion()) {
return State->getSVal(Ptr).getAs<DefinedSVal>();
return State->getSVal(Ptr);
}
return std::nullopt;
}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,7 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
const StackFrameContext *CallerSFC = CurLC->getStackFrame();
switch (Call.getKind()) {
case CE_Function:
case CE_CXXStaticOperator:
case CE_Block:
break;
case CE_CXXMember:
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,8 @@ static void reportAnalyzerFunctionMisuse(const AnalyzerOptions &Opts,

void AnalysisConsumer::runAnalysisOnTranslationUnit(ASTContext &C) {
BugReporter BR(*Mgr);
TranslationUnitDecl *TU = C.getTranslationUnitDecl();
const TranslationUnitDecl *TU = C.getTranslationUnitDecl();
BR.setAnalysisEntryPoint(TU);
if (SyntaxCheckTimer)
SyntaxCheckTimer->startTimer();
checkerMgr->runCheckersOnASTDecl(TU, *Mgr, BR);
Expand Down Expand Up @@ -675,6 +676,7 @@ void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode,

DisplayFunction(D, Mode, IMode);
BugReporter BR(*Mgr);
BR.setAnalysisEntryPoint(D);

if (Mode & AM_Syntax) {
llvm::TimeRecord CheckerStartTime;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,24 +41,25 @@ DependencyScanningWorkerFilesystem::readFile(StringRef Filename) {
return TentativeEntry(Stat, std::move(Buffer));
}

EntryRef DependencyScanningWorkerFilesystem::scanForDirectivesIfNecessary(
const CachedFileSystemEntry &Entry, StringRef Filename, bool Disable) {
if (Entry.isError() || Entry.isDirectory() || Disable ||
!shouldScanForDirectives(Filename))
return EntryRef(Filename, Entry);
bool DependencyScanningWorkerFilesystem::ensureDirectiveTokensArePopulated(
EntryRef Ref) {
auto &Entry = Ref.Entry;

if (Entry.isError() || Entry.isDirectory())
return false;

CachedFileContents *Contents = Entry.getCachedContents();
assert(Contents && "contents not initialized");

// Double-checked locking.
if (Contents->DepDirectives.load())
return EntryRef(Filename, Entry);
return true;

std::lock_guard<std::mutex> GuardLock(Contents->ValueLock);

// Double-checked locking.
if (Contents->DepDirectives.load())
return EntryRef(Filename, Entry);
return true;

SmallVector<dependency_directives_scan::Directive, 64> Directives;
// Scan the file for preprocessor directives that might affect the
Expand All @@ -69,16 +70,16 @@ EntryRef DependencyScanningWorkerFilesystem::scanForDirectivesIfNecessary(
Contents->DepDirectiveTokens.clear();
// FIXME: Propagate the diagnostic if desired by the client.
Contents->DepDirectives.store(new std::optional<DependencyDirectivesTy>());
return EntryRef(Filename, Entry);
return false;
}

// This function performed double-checked locking using `DepDirectives`.
// Assigning it must be the last thing this function does, otherwise other
// threads may skip the
// critical section (`DepDirectives != nullptr`), leading to a data race.
// threads may skip the critical section (`DepDirectives != nullptr`), leading
// to a data race.
Contents->DepDirectives.store(
new std::optional<DependencyDirectivesTy>(std::move(Directives)));
return EntryRef(Filename, Entry);
return true;
}

DependencyScanningFilesystemSharedCache::
Expand Down Expand Up @@ -161,34 +162,11 @@ DependencyScanningFilesystemSharedCache::CacheShard::
return *EntriesByFilename.insert({Filename, &Entry}).first->getValue();
}

/// Whitelist file extensions that should be minimized, treating no extension as
/// a source file that should be minimized.
///
/// This is kinda hacky, it would be better if we knew what kind of file Clang
/// was expecting instead.
static bool shouldScanForDirectivesBasedOnExtension(StringRef Filename) {
StringRef Ext = llvm::sys::path::extension(Filename);
if (Ext.empty())
return true; // C++ standard library
return llvm::StringSwitch<bool>(Ext)
.CasesLower(".c", ".cc", ".cpp", ".c++", ".cxx", true)
.CasesLower(".h", ".hh", ".hpp", ".h++", ".hxx", true)
.CasesLower(".m", ".mm", true)
.CasesLower(".i", ".ii", ".mi", ".mmi", true)
.CasesLower(".def", ".inc", true)
.Default(false);
}

static bool shouldCacheStatFailures(StringRef Filename) {
StringRef Ext = llvm::sys::path::extension(Filename);
if (Ext.empty())
return false; // This may be the module cache directory.
// Only cache stat failures on files that are not expected to change during
// the build.
StringRef FName = llvm::sys::path::filename(Filename);
if (FName == "module.modulemap" || FName == "module.map")
return true;
return shouldScanForDirectivesBasedOnExtension(Filename);
return true;
}

DependencyScanningWorkerFilesystem::DependencyScanningWorkerFilesystem(
Expand All @@ -201,11 +179,6 @@ DependencyScanningWorkerFilesystem::DependencyScanningWorkerFilesystem(
updateWorkingDirForCacheLookup();
}

bool DependencyScanningWorkerFilesystem::shouldScanForDirectives(
StringRef Filename) {
return shouldScanForDirectivesBasedOnExtension(Filename);
}

const CachedFileSystemEntry &
DependencyScanningWorkerFilesystem::getOrEmplaceSharedEntryForUID(
TentativeEntry TEntry) {
Expand Down Expand Up @@ -259,7 +232,7 @@ DependencyScanningWorkerFilesystem::computeAndStoreResult(

llvm::ErrorOr<EntryRef>
DependencyScanningWorkerFilesystem::getOrCreateFileSystemEntry(
StringRef OriginalFilename, bool DisableDirectivesScanning) {
StringRef OriginalFilename) {
StringRef FilenameForLookup;
SmallString<256> PathBuf;
if (llvm::sys::path::is_absolute_gnu(OriginalFilename)) {
Expand All @@ -276,15 +249,11 @@ DependencyScanningWorkerFilesystem::getOrCreateFileSystemEntry(
assert(llvm::sys::path::is_absolute_gnu(FilenameForLookup));
if (const auto *Entry =
findEntryByFilenameWithWriteThrough(FilenameForLookup))
return scanForDirectivesIfNecessary(*Entry, OriginalFilename,
DisableDirectivesScanning)
.unwrapError();
return EntryRef(OriginalFilename, *Entry).unwrapError();
auto MaybeEntry = computeAndStoreResult(OriginalFilename, FilenameForLookup);
if (!MaybeEntry)
return MaybeEntry.getError();
return scanForDirectivesIfNecessary(*MaybeEntry, OriginalFilename,
DisableDirectivesScanning)
.unwrapError();
return EntryRef(OriginalFilename, *MaybeEntry).unwrapError();
}

llvm::ErrorOr<llvm::vfs::Status>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,8 @@ class DependencyScanningAction : public tooling::ToolAction {
-> std::optional<ArrayRef<dependency_directives_scan::Directive>> {
if (llvm::ErrorOr<EntryRef> Entry =
LocalDepFS->getOrCreateFileSystemEntry(File.getName()))
return Entry->getDirectiveTokens();
if (LocalDepFS->ensureDirectiveTokensArePopulated(*Entry))
return Entry->getDirectiveTokens();
return std::nullopt;
};
}
Expand Down
2 changes: 1 addition & 1 deletion clang/test/Analysis/ArrayDelete.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.cplusplus.ArrayDelete -std=c++11 -verify -analyzer-output=text %s
// RUN: %clang_cc1 -analyze -analyzer-checker=cplusplus.ArrayDelete -std=c++11 -verify -analyzer-output=text %s

struct Base {
virtual ~Base() = default;
Expand Down
11 changes: 10 additions & 1 deletion clang/test/Analysis/Inputs/system-header-simulator-cxx.h
Original file line number Diff line number Diff line change
Expand Up @@ -1106,11 +1106,20 @@ using ostream = basic_ostream<char>;
extern std::ostream cout;

ostream &operator<<(ostream &, const string &);

#if __cplusplus >= 202002L
template <class T>
ostream &operator<<(ostream &, const std::unique_ptr<T> &);
#endif

template <class CharT>
class basic_istream;

using istream = basic_istream<char>;

extern std::istream cin;

istream &getline(istream &, string &, char);
istream &getline(istream &, string &);
} // namespace std

#ifdef TEST_INLINABLE_ALLOCATORS
Expand Down
42 changes: 33 additions & 9 deletions clang/test/Analysis/analyzer-display-progress.cpp
Original file line number Diff line number Diff line change
@@ -1,22 +1,46 @@
// RUN: %clang_analyze_cc1 -analyzer-display-progress %s 2>&1 | FileCheck %s
// RUN: %clang_analyze_cc1 -verify %s 2>&1 \
// RUN: -analyzer-display-progress \
// RUN: -analyzer-checker=debug.ExprInspection \
// RUN: -analyzer-output=text \
// RUN: | FileCheck %s

void f() {};
void g() {};
void h() {}
void clang_analyzer_warnIfReached();

// expected-note@+2 {{[debug] analyzing from f()}}
// expected-warning@+1 {{REACHABLE}} expected-note@+1 {{REACHABLE}}
void f() { clang_analyzer_warnIfReached(); }

// expected-note@+2 {{[debug] analyzing from g()}}
// expected-warning@+1 {{REACHABLE}} expected-note@+1 {{REACHABLE}}
void g() { clang_analyzer_warnIfReached(); }

// expected-note@+2 {{[debug] analyzing from h()}}
// expected-warning@+1 {{REACHABLE}} expected-note@+1 {{REACHABLE}}
void h() { clang_analyzer_warnIfReached(); }

struct SomeStruct {
void f() {}
// expected-note@+2 {{[debug] analyzing from SomeStruct::f()}}
// expected-warning@+1 {{REACHABLE}} expected-note@+1 {{REACHABLE}}
void f() { clang_analyzer_warnIfReached(); }
};

struct SomeOtherStruct {
void f() {}
// expected-note@+2 {{[debug] analyzing from SomeOtherStruct::f()}}
// expected-warning@+1 {{REACHABLE}} expected-note@+1 {{REACHABLE}}
void f() { clang_analyzer_warnIfReached(); }
};

namespace ns {
struct SomeStruct {
void f(int) {}
void f(float, ::SomeStruct) {}
void f(float, SomeStruct) {}
// expected-note@+2 {{[debug] analyzing from ns::SomeStruct::f(int)}}
// expected-warning@+1 {{REACHABLE}} expected-note@+1 {{REACHABLE}}
void f(int) { clang_analyzer_warnIfReached(); }
// expected-note@+2 {{[debug] analyzing from ns::SomeStruct::f(float, ::SomeStruct)}}
// expected-warning@+1 {{REACHABLE}} expected-note@+1 {{REACHABLE}}
void f(float, ::SomeStruct) { clang_analyzer_warnIfReached(); }
// expected-note@+2 {{[debug] analyzing from ns::SomeStruct::f(float, SomeStruct)}}
// expected-warning@+1 {{REACHABLE}} expected-note@+1 {{REACHABLE}}
void f(float, SomeStruct) { clang_analyzer_warnIfReached(); }
};
}

Expand Down
31 changes: 22 additions & 9 deletions clang/test/Analysis/analyzer-display-progress.m
Original file line number Diff line number Diff line change
@@ -1,30 +1,43 @@
// RUN: %clang_analyze_cc1 -fblocks -analyzer-display-progress %s 2>&1 | FileCheck %s
// RUN: %clang_analyze_cc1 -fblocks -verify %s 2>&1 \
// RUN: -analyzer-display-progress \
// RUN: -analyzer-checker=debug.ExprInspection \
// RUN: -analyzer-output=text \
// RUN: | FileCheck %s

#include "Inputs/system-header-simulator-objc.h"

static void f(void) {}
void clang_analyzer_warnIfReached();

// expected-note@+2 {{[debug] analyzing from f}}
// expected-warning@+1 {{REACHABLE}} expected-note@+1 {{REACHABLE}}
static void f(void) { clang_analyzer_warnIfReached(); }

@interface I: NSObject
-(void)instanceMethod:(int)arg1 with:(int)arg2;
+(void)classMethod;
@end

@implementation I
-(void)instanceMethod:(int)arg1 with:(int)arg2 {}
+(void)classMethod {}
// expected-warning@+1 {{REACHABLE}} expected-note@+1 {{REACHABLE}}
-(void)instanceMethod:(int)arg1 with:(int)arg2 { clang_analyzer_warnIfReached(); }

// expected-warning@+1 {{REACHABLE}} expected-note@+1 {{REACHABLE}}
+(void)classMethod { clang_analyzer_warnIfReached(); }
@end

// expected-note@+1 3 {{[debug] analyzing from g}}
void g(I *i, int x, int y) {
[I classMethod];
[i instanceMethod: x with: y];
[I classMethod]; // expected-note {{Calling 'classMethod'}}
[i instanceMethod: x with: y]; // expected-note {{Calling 'instanceMethod:with:'}}

void (^block)(void);
block = ^{};
block();
// expected-warning@+1 {{REACHABLE}} expected-note@+1 {{REACHABLE}}
block = ^{ clang_analyzer_warnIfReached(); };
block(); // expected-note {{Calling anonymous block}}
}

// CHECK: analyzer-display-progress.m f
// CHECK: analyzer-display-progress.m -[I instanceMethod:with:]
// CHECK: analyzer-display-progress.m +[I classMethod]
// CHECK: analyzer-display-progress.m g
// CHECK: analyzer-display-progress.m block (line: 22, col: 11)
// CHECK: analyzer-display-progress.m block (line: 35, col: 11)
75 changes: 75 additions & 0 deletions clang/test/Analysis/analyzer-note-analysis-entry-points.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// RUN: %clang_analyze_cc1 -verify=common %s \
// RUN: -analyzer-checker=deadcode.DeadStores,debug.ExprInspection \
// RUN: -analyzer-note-analysis-entry-points

// RUN: %clang_analyze_cc1 -verify=common,textout %s \
// RUN: -analyzer-checker=deadcode.DeadStores,debug.ExprInspection \
// RUN: -analyzer-note-analysis-entry-points \
// RUN: -analyzer-output=text

// Test the actual source locations/ranges of entry point notes.
// RUN: %clang_analyze_cc1 %s \
// RUN: -analyzer-checker=deadcode.DeadStores,debug.ExprInspection \
// RUN: -analyzer-note-analysis-entry-points \
// RUN: -analyzer-output=text 2>&1 \
// RUN: | FileCheck --strict-whitespace %s


void clang_analyzer_warnIfReached();

void other() {
// common-warning@+1 {{REACHABLE}} textout-note@+1 {{REACHABLE}}
clang_analyzer_warnIfReached();
}

struct SomeOtherStruct {
// CHECK: note: [debug] analyzing from SomeOtherStruct::f()
// CHECK-NEXT: | void f() {
// CHECK-NEXT: | ^
// textout-note@+1 {{[debug] analyzing from SomeOtherStruct::f()}}
void f() {
other(); // textout-note {{Calling 'other'}}
}
};

// CHECK: note: [debug] analyzing from operator""_w(const char *)
// CHECK-NEXT: | unsigned operator ""_w(const char*) {
// CHECK-NEXT: | ^
// textout-note@+1 {{[debug] analyzing from operator""_w(const char *)}}
unsigned operator ""_w(const char*) {
// common-warning@+1 {{REACHABLE}} textout-note@+1 {{REACHABLE}}
clang_analyzer_warnIfReached();
return 404;
}

// textout-note@+1 {{[debug] analyzing from checkASTCodeBodyHasAnalysisEntryPoints()}}
void checkASTCodeBodyHasAnalysisEntryPoints() {
int z = 1;
z = 2;
// common-warning@-1 {{Value stored to 'z' is never read}}
// textout-note@-2 {{Value stored to 'z' is never read}}
}

void notInvokedLambdaScope() {
// CHECK: note: [debug] analyzing from notInvokedLambdaScope()::(anonymous class)::operator()()
// CHECK-NEXT: | auto notInvokedLambda = []() {
// CHECK-NEXT: | ^
// textout-note@+1 {{[debug] analyzing from notInvokedLambdaScope()::(anonymous class)::operator()()}}
auto notInvokedLambda = []() {
// common-warning@+1 {{REACHABLE}} textout-note@+1 {{REACHABLE}}
clang_analyzer_warnIfReached();
};
(void)notInvokedLambda; // Not invoking the lambda.
}

// CHECK: note: [debug] analyzing from invokedLambdaScope()
// CHECK-NEXT: | void invokedLambdaScope() {
// CHECK-NEXT: | ^
// textout-note@+1 {{[debug] analyzing from invokedLambdaScope()}}
void invokedLambdaScope() {
auto invokedLambda = []() {
// common-warning@+1 {{REACHABLE}} textout-note@+1 {{REACHABLE}}
clang_analyzer_warnIfReached();
};
invokedLambda(); // textout-note {{Calling 'operator()'}}
}
38 changes: 38 additions & 0 deletions clang/test/Analysis/cxx23-static-operator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// RUN: %clang_analyze_cc1 -std=c++2b -verify %s \
// RUN: -analyzer-checker=core,debug.ExprInspection

template <typename T> void clang_analyzer_dump(T);

struct Adder {
int data;
static int operator()(int x, int y) {
clang_analyzer_dump(x); // expected-warning {{1}}
clang_analyzer_dump(y); // expected-warning {{2}}
return x + y;
}
};

void static_operator_call_inlines() {
Adder s{10};
clang_analyzer_dump(s(1, 2)); // expected-warning {{3}}
}

struct DataWithCtor {
int x;
int y;
DataWithCtor(int parm) : x(parm + 10), y(parm + 20) {
clang_analyzer_dump(this); // expected-warning {{&v}}
}
};

struct StaticSubscript {
static void operator[](DataWithCtor v) {
clang_analyzer_dump(v.x); // expected-warning {{20}}
clang_analyzer_dump(v.y); // expected-warning {{30}}
}
};

void top() {
StaticSubscript s;
s[DataWithCtor{10}];
}
15 changes: 15 additions & 0 deletions clang/test/Analysis/getline-cpp.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix,debug.ExprInspection -verify %s

// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix,alpha.unix,debug.ExprInspection -verify %s
//
// expected-no-diagnostics

#include "Inputs/system-header-simulator-cxx.h"

void test_std_getline() {
std::string userid, comment;
// MallocChecker should not confuse the POSIX function getline() and the
// unrelated C++ standard library function std::getline.
std::getline(std::cin, userid, ' '); // no-crash
std::getline(std::cin, comment); // no-crash
}
Loading