14 changes: 2 additions & 12 deletions clang/lib/Basic/Targets/AArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -705,18 +705,8 @@ AArch64TargetInfo::getVScaleRange(const LangOptions &LangOpts) const {
return std::nullopt;
}

unsigned AArch64TargetInfo::multiVersionSortPriority(StringRef Name) const {
if (Name == "default")
return 0;
if (auto Ext = llvm::AArch64::parseFMVExtension(Name))
return Ext->Priority;
return 0;
}

unsigned AArch64TargetInfo::multiVersionFeatureCost() const {
// Take the maximum priority as per feature cost, so more features win.
constexpr unsigned MaxFMVPriority = 1000;
return MaxFMVPriority;
unsigned AArch64TargetInfo::getFMVPriority(ArrayRef<StringRef> Features) const {
return llvm::AArch64::getFMVPriority(Features);
}

bool AArch64TargetInfo::doesFeatureAffectCodeGen(StringRef Name) const {
Expand Down
3 changes: 1 addition & 2 deletions clang/lib/Basic/Targets/AArch64.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,7 @@ class LLVM_LIBRARY_VISIBILITY AArch64TargetInfo : public TargetInfo {
void fillValidCPUList(SmallVectorImpl<StringRef> &Values) const override;
bool setCPU(const std::string &Name) override;

unsigned multiVersionSortPriority(StringRef Name) const override;
unsigned multiVersionFeatureCost() const override;
unsigned getFMVPriority(ArrayRef<StringRef> Features) const override;

bool useFP16ConversionIntrinsics() const override {
return false;
Expand Down
57 changes: 43 additions & 14 deletions clang/lib/Basic/Targets/RISCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,24 @@ ParsedTargetAttr RISCVTargetInfo::parseTargetAttr(StringRef Features) const {
Features.split(AttrFeatures, ";");
bool FoundArch = false;

auto handleArchExtension = [](StringRef AttrString,
std::vector<std::string> &Features) {
SmallVector<StringRef, 1> Exts;
AttrString.split(Exts, ",");
for (auto Ext : Exts) {
if (Ext.empty())
continue;

StringRef ExtName = Ext.substr(1);
std::string TargetFeature =
llvm::RISCVISAInfo::getTargetFeatureForExtension(ExtName);
if (!TargetFeature.empty())
Features.push_back(Ext.front() + TargetFeature);
else
Features.push_back(Ext.str());
}
};

for (auto &Feature : AttrFeatures) {
Feature = Feature.trim();
StringRef AttrString = Feature.split("=").second.trim();
Expand All @@ -436,20 +454,7 @@ ParsedTargetAttr RISCVTargetInfo::parseTargetAttr(StringRef Features) const {

if (AttrString.starts_with("+")) {
// EXTENSION like arch=+v,+zbb
SmallVector<StringRef, 1> Exts;
AttrString.split(Exts, ",");
for (auto Ext : Exts) {
if (Ext.empty())
continue;

StringRef ExtName = Ext.substr(1);
std::string TargetFeature =
llvm::RISCVISAInfo::getTargetFeatureForExtension(ExtName);
if (!TargetFeature.empty())
Ret.Features.push_back(Ext.front() + TargetFeature);
else
Ret.Features.push_back(Ext.str());
}
handleArchExtension(AttrString, Ret.Features);
} else {
// full-arch-string like arch=rv64gcv
handleFullArchString(AttrString, Ret.Features);
Expand All @@ -475,11 +480,35 @@ ParsedTargetAttr RISCVTargetInfo::parseTargetAttr(StringRef Features) const {
Ret.Tune = AttrString;
} else if (Feature.starts_with("priority")) {
// Skip because it only use for FMV.
} else if (Feature.starts_with("+")) {
// Handle target_version/target_clones attribute strings
// that are already delimited by ','
handleArchExtension(Feature, Ret.Features);
}
}
return Ret;
}

unsigned RISCVTargetInfo::getFMVPriority(ArrayRef<StringRef> Features) const {
// Priority is explicitly specified on RISC-V unlike on other targets, where
// it is derived by all the features of a specific version. Therefore if a
// feature contains the priority string, then return it immediately.
for (StringRef Feature : Features) {
auto [LHS, RHS] = Feature.rsplit(';');
if (LHS.consume_front("priority="))
Feature = LHS;
else if (RHS.consume_front("priority="))
Feature = RHS;
else
continue;
unsigned Priority;
if (!Feature.getAsInteger(0, Priority))
return Priority;
}
// Default Priority is zero.
return 0;
}

TargetInfo::CallingConvCheckResult
RISCVTargetInfo::checkCallingConvention(CallingConv CC) const {
switch (CC) {
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Basic/Targets/RISCV.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ class RISCVTargetInfo : public TargetInfo {
void fillValidTuneCPUList(SmallVectorImpl<StringRef> &Values) const override;
bool supportsTargetAttributeTune() const override { return true; }
ParsedTargetAttr parseTargetAttr(StringRef Str) const override;
unsigned getFMVPriority(ArrayRef<StringRef> Features) const override;

std::pair<unsigned, unsigned> hardwareInterferenceSizes() const override {
return std::make_pair(32, 32);
Expand Down
33 changes: 20 additions & 13 deletions clang/lib/Basic/Targets/X86.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1364,19 +1364,26 @@ static llvm::X86::ProcessorFeatures getFeature(StringRef Name) {
// correct, so it asserts if the value is out of range.
}

unsigned X86TargetInfo::multiVersionSortPriority(StringRef Name) const {
// Valid CPUs have a 'key feature' that compares just better than its key
// feature.
using namespace llvm::X86;
CPUKind Kind = parseArchX86(Name);
if (Kind != CK_None) {
ProcessorFeatures KeyFeature = getKeyFeature(Kind);
return (getFeaturePriority(KeyFeature) << 1) + 1;
}

// Now we know we have a feature, so get its priority and shift it a few so
// that we have sufficient room for the CPUs (above).
return getFeaturePriority(getFeature(Name)) << 1;
unsigned X86TargetInfo::getFMVPriority(ArrayRef<StringRef> Features) const {
auto getPriority = [](StringRef Feature) -> unsigned {
// Valid CPUs have a 'key feature' that compares just better than its key
// feature.
using namespace llvm::X86;
CPUKind Kind = parseArchX86(Feature);
if (Kind != CK_None) {
ProcessorFeatures KeyFeature = getKeyFeature(Kind);
return (getFeaturePriority(KeyFeature) << 1) + 1;
}
// Now we know we have a feature, so get its priority and shift it a few so
// that we have sufficient room for the CPUs (above).
return getFeaturePriority(getFeature(Feature)) << 1;
};

unsigned Priority = 0;
for (StringRef Feature : Features)
if (!Feature.empty())
Priority = std::max(Priority, getPriority(Feature));
return Priority;
}

bool X86TargetInfo::validateCPUSpecificCPUDispatch(StringRef Name) const {
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Basic/Targets/X86.h
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ class LLVM_LIBRARY_VISIBILITY X86TargetInfo : public TargetInfo {
return CPU != llvm::X86::CK_None;
}

unsigned multiVersionSortPriority(StringRef Name) const override;
unsigned getFMVPriority(ArrayRef<StringRef> Features) const override;

bool setFPMath(StringRef Name) override;

Expand Down
4 changes: 2 additions & 2 deletions clang/lib/CodeGen/ABIInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,8 @@ void ABIInfo::appendAttributeMangling(StringRef AttrStr,
// only have "+" prefixes here.
assert(LHS.starts_with("+") && RHS.starts_with("+") &&
"Features should always have a prefix.");
return TI.multiVersionSortPriority(LHS.substr(1)) >
TI.multiVersionSortPriority(RHS.substr(1));
return TI.getFMVPriority({LHS.substr(1)}) >
TI.getFMVPriority({RHS.substr(1)});
});

bool IsFirst = true;
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10859,6 +10859,10 @@ Value *CodeGenFunction::EmitAArch64SVEBuiltinExpr(unsigned BuiltinID,
else if (TypeFlags.isUndef())
return UndefValue::get(Ty);
else if (Builtin->LLVMIntrinsic != 0) {
// Emit set FPMR for intrinsics that require it
if (TypeFlags.setsFPMR())
Builder.CreateCall(CGM.getIntrinsic(Intrinsic::aarch64_set_fpmr),
Ops.pop_back_val());
if (TypeFlags.getMergeType() == SVETypeFlags::MergeZeroExp)
InsertExplicitZeroOperand(Builder, Ty, Ops);

Expand Down
4 changes: 2 additions & 2 deletions clang/lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1314,10 +1314,10 @@ static llvm::Value *CreateCoercedLoad(Address Src, llvm::Type *Ty,
}
if (ScalableDstTy->getElementType() == FixedSrcTy->getElementType()) {
auto *Load = CGF.Builder.CreateLoad(Src);
auto *UndefVec = llvm::UndefValue::get(ScalableDstTy);
auto *PoisonVec = llvm::PoisonValue::get(ScalableDstTy);
auto *Zero = llvm::Constant::getNullValue(CGF.CGM.Int64Ty);
llvm::Value *Result = CGF.Builder.CreateInsertVector(
ScalableDstTy, UndefVec, Load, Zero, "cast.scalable");
ScalableDstTy, PoisonVec, Load, Zero, "cast.scalable");
if (ScalableDstTy != Ty)
Result = CGF.Builder.CreateBitCast(Result, Ty);
return Result;
Expand Down
18 changes: 12 additions & 6 deletions clang/lib/CodeGen/CGOpenMPRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4666,7 +4666,7 @@ void CGOpenMPRuntime::emitTaskLoopCall(CodeGenFunction &CGF, SourceLocation Loc,
CGF.getContext().VoidPtrTy);
}
enum { NoSchedule = 0, Grainsize = 1, NumTasks = 2 };
llvm::Value *TaskArgs[] = {
llvm::SmallVector<llvm::Value *, 12> TaskArgs{
UpLoc,
ThreadID,
Result.NewTask,
Expand All @@ -4683,12 +4683,18 @@ void CGOpenMPRuntime::emitTaskLoopCall(CodeGenFunction &CGF, SourceLocation Loc,
Data.Schedule.getPointer()
? CGF.Builder.CreateIntCast(Data.Schedule.getPointer(), CGF.Int64Ty,
/*isSigned=*/false)
: llvm::ConstantInt::get(CGF.Int64Ty, /*V=*/0),
Result.TaskDupFn ? CGF.Builder.CreatePointerBitCastOrAddrSpaceCast(
Result.TaskDupFn, CGF.VoidPtrTy)
: llvm::ConstantPointerNull::get(CGF.VoidPtrTy)};
: llvm::ConstantInt::get(CGF.Int64Ty, /*V=*/0)};
if (Data.HasModifier)
TaskArgs.push_back(llvm::ConstantInt::get(CGF.Int32Ty, 1));

TaskArgs.push_back(Result.TaskDupFn
? CGF.Builder.CreatePointerBitCastOrAddrSpaceCast(
Result.TaskDupFn, CGF.VoidPtrTy)
: llvm::ConstantPointerNull::get(CGF.VoidPtrTy));
CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
CGM.getModule(), OMPRTL___kmpc_taskloop),
CGM.getModule(), Data.HasModifier
? OMPRTL___kmpc_taskloop_5
: OMPRTL___kmpc_taskloop),
TaskArgs);
}

Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGOpenMPRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ struct OMPTaskDataTy final {
bool IsReductionWithTaskMod = false;
bool IsWorksharingReduction = false;
bool HasNowaitClause = false;
bool HasModifier = false;
};

/// Class intended to support codegen of all kind of the reduction clauses.
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CodeGen/CGStmtOpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7831,10 +7831,14 @@ void CodeGenFunction::EmitOMPTaskLoopBasedDirective(const OMPLoopDirective &S) {
// grainsize clause
Data.Schedule.setInt(/*IntVal=*/false);
Data.Schedule.setPointer(EmitScalarExpr(Clause->getGrainsize()));
Data.HasModifier =
(Clause->getModifier() == OMPC_GRAINSIZE_strict) ? true : false;
} else if (const auto *Clause = S.getSingleClause<OMPNumTasksClause>()) {
// num_tasks clause
Data.Schedule.setInt(/*IntVal=*/true);
Data.Schedule.setPointer(EmitScalarExpr(Clause->getNumTasks()));
Data.HasModifier =
(Clause->getModifier() == OMPC_NUMTASKS_strict) ? true : false;
}

auto &&BodyGen = [CS, &S](CodeGenFunction &CGF, PrePostActionTy &) {
Expand Down
109 changes: 38 additions & 71 deletions clang/lib/CodeGen/CodeGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2828,23 +2828,17 @@ void CodeGenFunction::EmitKCFIOperandBundle(
Bundles.emplace_back("kcfi", CGM.CreateKCFITypeId(FP->desugar()));
}

llvm::Value *CodeGenFunction::FormAArch64ResolverCondition(
const MultiVersionResolverOption &RO) {
llvm::SmallVector<StringRef, 8> CondFeatures;
for (const StringRef &Feature : RO.Conditions.Features)
CondFeatures.push_back(Feature);
if (!CondFeatures.empty()) {
return EmitAArch64CpuSupports(CondFeatures);
}
return nullptr;
llvm::Value *
CodeGenFunction::FormAArch64ResolverCondition(const FMVResolverOption &RO) {
return RO.Features.empty() ? nullptr : EmitAArch64CpuSupports(RO.Features);
}

llvm::Value *CodeGenFunction::FormX86ResolverCondition(
const MultiVersionResolverOption &RO) {
llvm::Value *
CodeGenFunction::FormX86ResolverCondition(const FMVResolverOption &RO) {
llvm::Value *Condition = nullptr;

if (!RO.Conditions.Architecture.empty()) {
StringRef Arch = RO.Conditions.Architecture;
if (RO.Architecture) {
StringRef Arch = *RO.Architecture;
// If arch= specifies an x86-64 micro-architecture level, test the feature
// with __builtin_cpu_supports, otherwise use __builtin_cpu_is.
if (Arch.starts_with("x86-64"))
Expand All @@ -2853,8 +2847,8 @@ llvm::Value *CodeGenFunction::FormX86ResolverCondition(
Condition = EmitX86CpuIs(Arch);
}

if (!RO.Conditions.Features.empty()) {
llvm::Value *FeatureCond = EmitX86CpuSupports(RO.Conditions.Features);
if (!RO.Features.empty()) {
llvm::Value *FeatureCond = EmitX86CpuSupports(RO.Features);
Condition =
Condition ? Builder.CreateAnd(Condition, FeatureCond) : FeatureCond;
}
Expand Down Expand Up @@ -2884,7 +2878,7 @@ static void CreateMultiVersionResolverReturn(CodeGenModule &CGM,
}

void CodeGenFunction::EmitMultiVersionResolver(
llvm::Function *Resolver, ArrayRef<MultiVersionResolverOption> Options) {
llvm::Function *Resolver, ArrayRef<FMVResolverOption> Options) {

llvm::Triple::ArchType ArchType =
getContext().getTargetInfo().getTriple().getArch();
Expand All @@ -2907,26 +2901,8 @@ void CodeGenFunction::EmitMultiVersionResolver(
}
}

static unsigned getPriorityFromAttrString(StringRef AttrStr) {
SmallVector<StringRef, 8> Attrs;

AttrStr.split(Attrs, ';');

// Default Priority is zero.
unsigned Priority = 0;
for (auto Attr : Attrs) {
if (Attr.consume_front("priority=")) {
unsigned Result;
if (!Attr.getAsInteger(0, Result))
Priority = Result;
}
}

return Priority;
}

void CodeGenFunction::EmitRISCVMultiVersionResolver(
llvm::Function *Resolver, ArrayRef<MultiVersionResolverOption> Options) {
llvm::Function *Resolver, ArrayRef<FMVResolverOption> Options) {

if (getContext().getTargetInfo().getTriple().getOS() !=
llvm::Triple::OSType::Linux) {
Expand All @@ -2942,36 +2918,17 @@ void CodeGenFunction::EmitRISCVMultiVersionResolver(
bool HasDefault = false;
unsigned DefaultIndex = 0;

SmallVector<CodeGenFunction::MultiVersionResolverOption, 10> CurrOptions(
Options);

llvm::stable_sort(
CurrOptions, [](const CodeGenFunction::MultiVersionResolverOption &LHS,
const CodeGenFunction::MultiVersionResolverOption &RHS) {
return getPriorityFromAttrString(LHS.Conditions.Features[0]) >
getPriorityFromAttrString(RHS.Conditions.Features[0]);
});

// Check the each candidate function.
for (unsigned Index = 0; Index < CurrOptions.size(); Index++) {
for (unsigned Index = 0; Index < Options.size(); Index++) {

if (CurrOptions[Index].Conditions.Features[0].starts_with("default")) {
if (Options[Index].Features.empty()) {
HasDefault = true;
DefaultIndex = Index;
continue;
}

Builder.SetInsertPoint(CurBlock);

std::vector<std::string> TargetAttrFeats =
getContext()
.getTargetInfo()
.parseTargetAttr(CurrOptions[Index].Conditions.Features[0])
.Features;

if (TargetAttrFeats.empty())
continue;

// FeaturesCondition: The bitmask of the required extension has been
// enabled by the runtime object.
// (__riscv_feature_bits.features[i] & REQUIRED_BITMASK) ==
Expand All @@ -2994,20 +2951,32 @@ void CodeGenFunction::EmitRISCVMultiVersionResolver(
// Without checking the length first, we may access an incorrect memory
// address when using different versions.
llvm::SmallVector<StringRef, 8> CurrTargetAttrFeats;
llvm::SmallVector<std::string, 8> TargetAttrFeats;

for (auto &Feat : TargetAttrFeats) {
StringRef CurrFeat = Feat;
if (CurrFeat.starts_with('+'))
CurrTargetAttrFeats.push_back(CurrFeat.substr(1));
for (StringRef Feat : Options[Index].Features) {
std::vector<std::string> FeatStr =
getContext().getTargetInfo().parseTargetAttr(Feat).Features;

assert(FeatStr.size() == 1 && "Feature string not delimited");

std::string &CurrFeat = FeatStr.front();
if (CurrFeat[0] == '+')
TargetAttrFeats.push_back(CurrFeat.substr(1));
}

if (TargetAttrFeats.empty())
continue;

for (std::string &Feat : TargetAttrFeats)
CurrTargetAttrFeats.push_back(Feat);

Builder.SetInsertPoint(CurBlock);
llvm::Value *FeatsCondition = EmitRISCVCpuSupports(CurrTargetAttrFeats);

llvm::BasicBlock *RetBlock = createBasicBlock("resolver_return", Resolver);
CGBuilderTy RetBuilder(*this, RetBlock);
CreateMultiVersionResolverReturn(
CGM, Resolver, RetBuilder, CurrOptions[Index].Function, SupportsIFunc);
CreateMultiVersionResolverReturn(CGM, Resolver, RetBuilder,
Options[Index].Function, SupportsIFunc);
llvm::BasicBlock *ElseBlock = createBasicBlock("resolver_else", Resolver);

Builder.SetInsertPoint(CurBlock);
Expand All @@ -3019,9 +2988,8 @@ void CodeGenFunction::EmitRISCVMultiVersionResolver(
// Finally, emit the default one.
if (HasDefault) {
Builder.SetInsertPoint(CurBlock);
CreateMultiVersionResolverReturn(CGM, Resolver, Builder,
CurrOptions[DefaultIndex].Function,
SupportsIFunc);
CreateMultiVersionResolverReturn(
CGM, Resolver, Builder, Options[DefaultIndex].Function, SupportsIFunc);
return;
}

Expand All @@ -3035,17 +3003,16 @@ void CodeGenFunction::EmitRISCVMultiVersionResolver(
}

void CodeGenFunction::EmitAArch64MultiVersionResolver(
llvm::Function *Resolver, ArrayRef<MultiVersionResolverOption> Options) {
llvm::Function *Resolver, ArrayRef<FMVResolverOption> Options) {
assert(!Options.empty() && "No multiversion resolver options found");
assert(Options.back().Conditions.Features.size() == 0 &&
"Default case must be last");
assert(Options.back().Features.size() == 0 && "Default case must be last");
bool SupportsIFunc = getContext().getTargetInfo().supportsIFunc();
assert(SupportsIFunc &&
"Multiversion resolver requires target IFUNC support");
bool AArch64CpuInitialized = false;
llvm::BasicBlock *CurBlock = createBasicBlock("resolver_entry", Resolver);

for (const MultiVersionResolverOption &RO : Options) {
for (const FMVResolverOption &RO : Options) {
Builder.SetInsertPoint(CurBlock);
llvm::Value *Condition = FormAArch64ResolverCondition(RO);

Expand Down Expand Up @@ -3081,7 +3048,7 @@ void CodeGenFunction::EmitAArch64MultiVersionResolver(
}

void CodeGenFunction::EmitX86MultiVersionResolver(
llvm::Function *Resolver, ArrayRef<MultiVersionResolverOption> Options) {
llvm::Function *Resolver, ArrayRef<FMVResolverOption> Options) {

bool SupportsIFunc = getContext().getTargetInfo().supportsIFunc();

Expand All @@ -3090,7 +3057,7 @@ void CodeGenFunction::EmitX86MultiVersionResolver(
Builder.SetInsertPoint(CurBlock);
EmitX86CpuInit();

for (const MultiVersionResolverOption &RO : Options) {
for (const FMVResolverOption &RO : Options) {
Builder.SetInsertPoint(CurBlock);
llvm::Value *Condition = FormX86ResolverCondition(RO);

Expand Down
39 changes: 15 additions & 24 deletions clang/lib/CodeGen/CodeGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -5334,35 +5334,27 @@ class CodeGenFunction : public CodeGenTypeCache {

void EmitSanitizerStatReport(llvm::SanitizerStatKind SSK);

struct MultiVersionResolverOption {
struct FMVResolverOption {
llvm::Function *Function;
struct Conds {
StringRef Architecture;
llvm::SmallVector<StringRef, 8> Features;
llvm::SmallVector<StringRef, 8> Features;
std::optional<StringRef> Architecture;

Conds(StringRef Arch, ArrayRef<StringRef> Feats)
: Architecture(Arch), Features(Feats) {}
} Conditions;

MultiVersionResolverOption(llvm::Function *F, StringRef Arch,
ArrayRef<StringRef> Feats)
: Function(F), Conditions(Arch, Feats) {}
FMVResolverOption(llvm::Function *F, ArrayRef<StringRef> Feats,
std::optional<StringRef> Arch = std::nullopt)
: Function(F), Features(Feats), Architecture(Arch) {}
};

// Emits the body of a multiversion function's resolver. Assumes that the
// options are already sorted in the proper order, with the 'default' option
// last (if it exists).
void EmitMultiVersionResolver(llvm::Function *Resolver,
ArrayRef<MultiVersionResolverOption> Options);
void
EmitX86MultiVersionResolver(llvm::Function *Resolver,
ArrayRef<MultiVersionResolverOption> Options);
void
EmitAArch64MultiVersionResolver(llvm::Function *Resolver,
ArrayRef<MultiVersionResolverOption> Options);
void
EmitRISCVMultiVersionResolver(llvm::Function *Resolver,
ArrayRef<MultiVersionResolverOption> Options);
ArrayRef<FMVResolverOption> Options);
void EmitX86MultiVersionResolver(llvm::Function *Resolver,
ArrayRef<FMVResolverOption> Options);
void EmitAArch64MultiVersionResolver(llvm::Function *Resolver,
ArrayRef<FMVResolverOption> Options);
void EmitRISCVMultiVersionResolver(llvm::Function *Resolver,
ArrayRef<FMVResolverOption> Options);

private:
QualType getVarArgType(const Expr *Arg);
Expand All @@ -5381,10 +5373,9 @@ class CodeGenFunction : public CodeGenTypeCache {
llvm::Value *EmitX86CpuSupports(ArrayRef<StringRef> FeatureStrs);
llvm::Value *EmitX86CpuSupports(std::array<uint32_t, 4> FeatureMask);
llvm::Value *EmitX86CpuInit();
llvm::Value *FormX86ResolverCondition(const MultiVersionResolverOption &RO);
llvm::Value *FormX86ResolverCondition(const FMVResolverOption &RO);
llvm::Value *EmitAArch64CpuInit();
llvm::Value *
FormAArch64ResolverCondition(const MultiVersionResolverOption &RO);
llvm::Value *FormAArch64ResolverCondition(const FMVResolverOption &RO);
llvm::Value *EmitAArch64CpuSupports(const CallExpr *E);
llvm::Value *EmitAArch64CpuSupports(ArrayRef<StringRef> FeatureStrs);
};
Expand Down
86 changes: 32 additions & 54 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4223,23 +4223,12 @@ void CodeGenModule::EmitGlobalDefinition(GlobalDecl GD, llvm::GlobalValue *GV) {
static void ReplaceUsesOfNonProtoTypeWithRealFunction(llvm::GlobalValue *Old,
llvm::Function *NewFn);

static unsigned
TargetMVPriority(const TargetInfo &TI,
const CodeGenFunction::MultiVersionResolverOption &RO) {
unsigned Priority = 0;
unsigned NumFeatures = 0;
for (StringRef Feat : RO.Conditions.Features) {
Priority = std::max(Priority, TI.multiVersionSortPriority(Feat));
NumFeatures++;
}

if (!RO.Conditions.Architecture.empty())
Priority = std::max(
Priority, TI.multiVersionSortPriority(RO.Conditions.Architecture));

Priority += TI.multiVersionFeatureCost() * NumFeatures;

return Priority;
static unsigned getFMVPriority(const TargetInfo &TI,
const CodeGenFunction::FMVResolverOption &RO) {
llvm::SmallVector<StringRef, 8> Features{RO.Features};
if (RO.Architecture)
Features.push_back(*RO.Architecture);
return TI.getFMVPriority(Features);
}

// Multiversion functions should be at most 'WeakODRLinkage' so that a different
Expand Down Expand Up @@ -4285,28 +4274,25 @@ void CodeGenModule::emitMultiVersionFunctions() {
// target_version("default")) or target_clones() is present and defined
// in this TU. For other architectures it is always emitted.
bool ShouldEmitResolver = !getTarget().getTriple().isAArch64();
SmallVector<CodeGenFunction::MultiVersionResolverOption, 10> Options;
SmallVector<CodeGenFunction::FMVResolverOption, 10> Options;

getContext().forEachMultiversionedFunctionVersion(
FD, [&](const FunctionDecl *CurFD) {
llvm::SmallVector<StringRef, 8> Feats;
bool IsDefined = CurFD->doesThisDeclarationHaveABody();

if (const auto *TA = CurFD->getAttr<TargetAttr>()) {
TA->getAddedFeatures(Feats);
assert(getTarget().getTriple().isX86() && "Unsupported target");
TA->getX86AddedFeatures(Feats);
llvm::Function *Func = createFunction(CurFD);
Options.emplace_back(Func, TA->getArchitecture(), Feats);
Options.emplace_back(Func, Feats, TA->getX86Architecture());
} else if (const auto *TVA = CurFD->getAttr<TargetVersionAttr>()) {
if (TVA->isDefaultVersion() && IsDefined)
ShouldEmitResolver = true;
llvm::Function *Func = createFunction(CurFD);
if (getTarget().getTriple().isRISCV()) {
Feats.push_back(TVA->getName());
} else {
assert(getTarget().getTriple().isAArch64());
TVA->getFeatures(Feats);
}
Options.emplace_back(Func, /*Architecture*/ "", Feats);
char Delim = getTarget().getTriple().isAArch64() ? '+' : ',';
TVA->getFeatures(Feats, Delim);
Options.emplace_back(Func, Feats);
} else if (const auto *TC = CurFD->getAttr<TargetClonesAttr>()) {
if (IsDefined)
ShouldEmitResolver = true;
Expand All @@ -4315,21 +4301,15 @@ void CodeGenModule::emitMultiVersionFunctions() {
continue;

llvm::Function *Func = createFunction(CurFD, I);
StringRef Architecture;
Feats.clear();
if (getTarget().getTriple().isAArch64())
TC->getFeatures(Feats, I);
else if (getTarget().getTriple().isRISCV()) {
StringRef Version = TC->getFeatureStr(I);
Feats.push_back(Version);
if (getTarget().getTriple().isX86()) {
TC->getX86Feature(Feats, I);
Options.emplace_back(Func, Feats, TC->getX86Architecture(I));
} else {
StringRef Version = TC->getFeatureStr(I);
if (Version.starts_with("arch="))
Architecture = Version.drop_front(sizeof("arch=") - 1);
else if (Version != "default")
Feats.push_back(Version);
char Delim = getTarget().getTriple().isAArch64() ? '+' : ',';
TC->getFeatures(Feats, I, Delim);
Options.emplace_back(Func, Feats);
}
Options.emplace_back(Func, Architecture, Feats);
}
} else
llvm_unreachable("unexpected MultiVersionKind");
Expand Down Expand Up @@ -4368,9 +4348,9 @@ void CodeGenModule::emitMultiVersionFunctions() {

const TargetInfo &TI = getTarget();
llvm::stable_sort(
Options, [&TI](const CodeGenFunction::MultiVersionResolverOption &LHS,
const CodeGenFunction::MultiVersionResolverOption &RHS) {
return TargetMVPriority(TI, LHS) > TargetMVPriority(TI, RHS);
Options, [&TI](const CodeGenFunction::FMVResolverOption &LHS,
const CodeGenFunction::FMVResolverOption &RHS) {
return getFMVPriority(TI, LHS) > getFMVPriority(TI, RHS);
});
CodeGenFunction CGF(*this);
CGF.EmitMultiVersionResolver(ResolverFunc, Options);
Expand Down Expand Up @@ -4429,7 +4409,7 @@ void CodeGenModule::emitCPUDispatchDefinition(GlobalDecl GD) {
ResolverFunc->setComdat(
getModule().getOrInsertComdat(ResolverFunc->getName()));

SmallVector<CodeGenFunction::MultiVersionResolverOption, 10> Options;
SmallVector<CodeGenFunction::FMVResolverOption, 10> Options;
const TargetInfo &Target = getTarget();
unsigned Index = 0;
for (const IdentifierInfo *II : DD->cpus()) {
Expand Down Expand Up @@ -4463,25 +4443,23 @@ void CodeGenModule::emitCPUDispatchDefinition(GlobalDecl GD) {
llvm::erase_if(Features, [&Target](StringRef Feat) {
return !Target.validateCpuSupports(Feat);
});
Options.emplace_back(cast<llvm::Function>(Func), StringRef{}, Features);
Options.emplace_back(cast<llvm::Function>(Func), Features);
++Index;
}

llvm::stable_sort(
Options, [](const CodeGenFunction::MultiVersionResolverOption &LHS,
const CodeGenFunction::MultiVersionResolverOption &RHS) {
return llvm::X86::getCpuSupportsMask(LHS.Conditions.Features) >
llvm::X86::getCpuSupportsMask(RHS.Conditions.Features);
});
llvm::stable_sort(Options, [](const CodeGenFunction::FMVResolverOption &LHS,
const CodeGenFunction::FMVResolverOption &RHS) {
return llvm::X86::getCpuSupportsMask(LHS.Features) >
llvm::X86::getCpuSupportsMask(RHS.Features);
});

// If the list contains multiple 'default' versions, such as when it contains
// 'pentium' and 'generic', don't emit the call to the generic one (since we
// always run on at least a 'pentium'). We do this by deleting the 'least
// advanced' (read, lowest mangling letter).
while (Options.size() > 1 &&
llvm::all_of(llvm::X86::getCpuSupportsMask(
(Options.end() - 2)->Conditions.Features),
[](auto X) { return X == 0; })) {
while (Options.size() > 1 && llvm::all_of(llvm::X86::getCpuSupportsMask(
(Options.end() - 2)->Features),
[](auto X) { return X == 0; })) {
StringRef LHSName = (Options.end() - 2)->Function->getName();
StringRef RHSName = (Options.end() - 1)->Function->getName();
if (LHSName.compare(RHSName) < 0)
Expand Down
45 changes: 16 additions & 29 deletions clang/lib/Driver/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -998,14 +998,12 @@ void Driver::CreateOffloadingDeviceToolChains(Compilation &C,
//
}

static void appendOneArg(InputArgList &Args, const Arg *Opt,
const Arg *BaseArg) {
static void appendOneArg(InputArgList &Args, const Arg *Opt) {
// The args for config files or /clang: flags belong to different InputArgList
// objects than Args. This copies an Arg from one of those other InputArgLists
// to the ownership of Args.
unsigned Index = Args.MakeIndex(Opt->getSpelling());
Arg *Copy = new llvm::opt::Arg(Opt->getOption(), Args.getArgString(Index),
Index, BaseArg);
Arg *Copy = new Arg(Opt->getOption(), Args.getArgString(Index), Index);
Copy->getValues() = Opt->getValues();
if (Opt->isClaimed())
Copy->claim();
Expand Down Expand Up @@ -1052,7 +1050,7 @@ bool Driver::readConfigFile(StringRef FileName,
llvm::SmallString<128> CfgFileName(FileName);
llvm::sys::path::native(CfgFileName);
bool ContainErrors;
std::unique_ptr<InputArgList> NewOptions = std::make_unique<InputArgList>(
auto NewOptions = std::make_unique<InputArgList>(
ParseArgStrings(NewCfgArgs, /*UseDriverMode=*/true, ContainErrors));
if (ContainErrors)
return true;
Expand All @@ -1066,12 +1064,8 @@ bool Driver::readConfigFile(StringRef FileName,
CfgOptions = std::move(NewOptions);
else {
// If this is a subsequent config file, append options to the previous one.
for (auto *Opt : *NewOptions) {
const Arg *BaseArg = &Opt->getBaseArg();
if (BaseArg == Opt)
BaseArg = nullptr;
appendOneArg(*CfgOptions, Opt, BaseArg);
}
for (auto *Opt : *NewOptions)
appendOneArg(*CfgOptions, Opt);
}
ConfigFiles.push_back(std::string(CfgFileName));
return false;
Expand Down Expand Up @@ -1256,14 +1250,9 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
: std::move(*CLOptions));

if (HasConfigFile)
for (auto *Opt : *CLOptions) {
if (Opt->getOption().matches(options::OPT_config))
continue;
const Arg *BaseArg = &Opt->getBaseArg();
if (BaseArg == Opt)
BaseArg = nullptr;
appendOneArg(Args, Opt, BaseArg);
}
for (auto *Opt : *CLOptions)
if (!Opt->getOption().matches(options::OPT_config))
appendOneArg(Args, Opt);

// In CL mode, look for any pass-through arguments
if (IsCLMode() && !ContainsError) {
Expand All @@ -1281,9 +1270,8 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
ContainsError));

if (!ContainsError)
for (auto *Opt : *CLModePassThroughOptions) {
appendOneArg(Args, Opt, nullptr);
}
for (auto *Opt : *CLModePassThroughOptions)
appendOneArg(Args, Opt);
}
}

Expand Down Expand Up @@ -4063,17 +4051,18 @@ void Driver::handleArguments(Compilation &C, DerivedArgList &Args,
YcArg = YuArg = nullptr;
}

unsigned LastPLSize = 0;
bool LinkOnly = phases::Link == FinalPhase && Inputs.size() > 0;
for (auto &I : Inputs) {
types::ID InputType = I.first;
const Arg *InputArg = I.second;

auto PL = types::getCompilationPhases(InputType);
LastPLSize = PL.size();

phases::ID InitialPhase = PL[0];
LinkOnly = LinkOnly && phases::Link == InitialPhase && PL.size() == 1;

// If the first step comes after the final phase we are doing as part of
// this compilation, warn the user about it.
phases::ID InitialPhase = PL[0];
if (InitialPhase > FinalPhase) {
if (InputArg->isClaimed())
continue;
Expand Down Expand Up @@ -4128,10 +4117,8 @@ void Driver::handleArguments(Compilation &C, DerivedArgList &Args,
}
}

// If we are linking, claim any options which are obviously only used for
// compilation.
// FIXME: Understand why the last Phase List length is used here.
if (FinalPhase == phases::Link && LastPLSize == 1) {
// Claim any options which are obviously only used for compilation.
if (LinkOnly) {
Args.ClaimAllArgs(options::OPT_CompileOnly_Group);
Args.ClaimAllArgs(options::OPT_cl_compile_Group);
}
Expand Down
12 changes: 6 additions & 6 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4690,15 +4690,15 @@ renderDebugOptions(const ToolChain &TC, const Driver &D, const llvm::Triple &T,
Args.ClaimAllArgs(options::OPT_g_flags_Group);

// Column info is included by default for everything except SCE and
// CodeView. Clang doesn't track end columns, just starting columns, which,
// in theory, is fine for CodeView (and PDB). In practice, however, the
// Microsoft debuggers don't handle missing end columns well, and the AIX
// debugger DBX also doesn't handle the columns well, so it's better not to
// include any column info.
// CodeView if not use sampling PGO. Clang doesn't track end columns, just
// starting columns, which, in theory, is fine for CodeView (and PDB). In
// practice, however, the Microsoft debuggers don't handle missing end columns
// well, and the AIX debugger DBX also doesn't handle the columns well, so
// it's better not to include any column info.
if (const Arg *A = Args.getLastArg(options::OPT_gcolumn_info))
(void)checkDebugInfoOption(A, Args, D, TC);
if (!Args.hasFlag(options::OPT_gcolumn_info, options::OPT_gno_column_info,
!EmitCodeView &&
!(EmitCodeView && !getLastProfileSampleUseArg(Args)) &&
(DebuggerTuning != llvm::DebuggerKind::SCE &&
DebuggerTuning != llvm::DebuggerKind::DBX)))
CmdArgs.push_back("-gno-column-info");
Expand Down
13 changes: 13 additions & 0 deletions clang/lib/Driver/ToolChains/Flang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,18 @@ void Flang::AddAArch64TargetArgs(const ArgList &Args,
}
}

void Flang::AddLoongArch64TargetArgs(const ArgList &Args,
ArgStringList &CmdArgs) const {
const Driver &D = getToolChain().getDriver();
// Currently, flang only support `-mabi=lp64d` in LoongArch64.
if (const Arg *A = Args.getLastArg(options::OPT_mabi_EQ)) {
StringRef V = A->getValue();
if (V != "lp64d") {
D.Diag(diag::err_drv_argument_not_allowed_with) << "-mabi" << V;
}
}
}

void Flang::AddPPCTargetArgs(const ArgList &Args,
ArgStringList &CmdArgs) const {
const Driver &D = getToolChain().getDriver();
Expand Down Expand Up @@ -416,6 +428,7 @@ void Flang::addTargetOptions(const ArgList &Args,
break;
case llvm::Triple::loongarch64:
getTargetFeatures(D, Triple, Args, CmdArgs, /*ForAs*/ false);
AddLoongArch64TargetArgs(Args, CmdArgs);
break;
}

Expand Down
7 changes: 7 additions & 0 deletions clang/lib/Driver/ToolChains/Flang.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ class LLVM_LIBRARY_VISIBILITY Flang : public Tool {
void AddAMDGPUTargetArgs(const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs) const;

/// Add specific options for LoongArch64 target.
///
/// \param [in] Args The list of input driver arguments
/// \param [out] CmdArgs The list of output command arguments
void AddLoongArch64TargetArgs(const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs) const;

/// Add specific options for RISC-V target.
///
/// \param [in] Args The list of input driver arguments
Expand Down
7 changes: 4 additions & 3 deletions clang/lib/Driver/ToolChains/Hexagon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -294,9 +294,10 @@ constructHexagonLinkArgs(Compilation &C, const JobAction &JA,
bool IncStartFiles = !Args.hasArg(options::OPT_nostartfiles);
bool IncDefLibs = !Args.hasArg(options::OPT_nodefaultlibs);
bool UseG0 = false;
const char *Exec = Args.MakeArgString(HTC.GetLinkerPath());
bool UseLLD = (llvm::sys::path::filename(Exec).equals_insensitive("ld.lld") ||
llvm::sys::path::stem(Exec).equals_insensitive("ld.lld"));
bool UseLLD = false;
const char *Exec = Args.MakeArgString(HTC.GetLinkerPath(&UseLLD));
UseLLD = UseLLD || llvm::sys::path::filename(Exec).ends_with("ld.lld") ||
llvm::sys::path::stem(Exec).ends_with("ld.lld");
bool UseShared = IsShared && !IsStatic;
StringRef CpuVer = toolchains::HexagonToolChain::GetTargetCPUVersion(Args);

Expand Down
12 changes: 10 additions & 2 deletions clang/lib/Headers/avx512vpopcntdqintrin.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,14 @@
__target__("avx512vpopcntdq,evex512"), \
__min_vector_width__(512)))

static __inline__ __m512i __DEFAULT_FN_ATTRS _mm512_popcnt_epi64(__m512i __A) {
#if defined(__cplusplus) && (__cplusplus >= 201103L)
#define __DEFAULT_FN_ATTRS_CONSTEXPR __DEFAULT_FN_ATTRS constexpr
#else
#define __DEFAULT_FN_ATTRS_CONSTEXPR __DEFAULT_FN_ATTRS
#endif

static __inline__ __m512i __DEFAULT_FN_ATTRS_CONSTEXPR
_mm512_popcnt_epi64(__m512i __A) {
return (__m512i)__builtin_elementwise_popcount((__v8du)__A);
}

Expand All @@ -36,7 +43,8 @@ _mm512_maskz_popcnt_epi64(__mmask8 __U, __m512i __A) {
return _mm512_mask_popcnt_epi64((__m512i)_mm512_setzero_si512(), __U, __A);
}

static __inline__ __m512i __DEFAULT_FN_ATTRS _mm512_popcnt_epi32(__m512i __A) {
static __inline__ __m512i __DEFAULT_FN_ATTRS_CONSTEXPR
_mm512_popcnt_epi32(__m512i __A) {
return (__m512i)__builtin_elementwise_popcount((__v16su)__A);
}

Expand Down
16 changes: 12 additions & 4 deletions clang/lib/Headers/avx512vpopcntdqvlintrin.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,15 @@
__target__("avx512vpopcntdq,avx512vl,no-evex512"), \
__min_vector_width__(256)))

static __inline__ __m128i __DEFAULT_FN_ATTRS128
#if defined(__cplusplus) && (__cplusplus >= 201103L)
#define __DEFAULT_FN_ATTRS128_CONSTEXPR __DEFAULT_FN_ATTRS128 constexpr
#define __DEFAULT_FN_ATTRS256_CONSTEXPR __DEFAULT_FN_ATTRS256 constexpr
#else
#define __DEFAULT_FN_ATTRS128_CONSTEXPR __DEFAULT_FN_ATTRS128
#define __DEFAULT_FN_ATTRS256_CONSTEXPR __DEFAULT_FN_ATTRS256
#endif

static __inline__ __m128i __DEFAULT_FN_ATTRS128_CONSTEXPR
_mm_popcnt_epi64(__m128i __A) {
return (__m128i)__builtin_elementwise_popcount((__v2du)__A);
}
Expand All @@ -41,7 +49,7 @@ _mm_maskz_popcnt_epi64(__mmask8 __U, __m128i __A) {
return _mm_mask_popcnt_epi64((__m128i)_mm_setzero_si128(), __U, __A);
}

static __inline__ __m128i __DEFAULT_FN_ATTRS128
static __inline__ __m128i __DEFAULT_FN_ATTRS128_CONSTEXPR
_mm_popcnt_epi32(__m128i __A) {
return (__m128i)__builtin_elementwise_popcount((__v4su)__A);
}
Expand All @@ -57,7 +65,7 @@ _mm_maskz_popcnt_epi32(__mmask8 __U, __m128i __A) {
return _mm_mask_popcnt_epi32((__m128i)_mm_setzero_si128(), __U, __A);
}

static __inline__ __m256i __DEFAULT_FN_ATTRS256
static __inline__ __m256i __DEFAULT_FN_ATTRS256_CONSTEXPR
_mm256_popcnt_epi64(__m256i __A) {
return (__m256i)__builtin_elementwise_popcount((__v4du)__A);
}
Expand All @@ -73,7 +81,7 @@ _mm256_maskz_popcnt_epi64(__mmask8 __U, __m256i __A) {
return _mm256_mask_popcnt_epi64((__m256i)_mm256_setzero_si256(), __U, __A);
}

static __inline__ __m256i __DEFAULT_FN_ATTRS256
static __inline__ __m256i __DEFAULT_FN_ATTRS256_CONSTEXPR
_mm256_popcnt_epi32(__m256i __A) {
return (__m256i)__builtin_elementwise_popcount((__v8su)__A);
}
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Interpreter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ set(LLVM_LINK_COMPONENTS
if (EMSCRIPTEN AND "lld" IN_LIST LLVM_ENABLE_PROJECTS)
set(WASM_SRC Wasm.cpp)
set(WASM_LINK lldWasm)
set(COMMON_LINK lldCommon)
endif()

add_clang_library(clangInterpreter
Expand Down Expand Up @@ -47,6 +48,7 @@ add_clang_library(clangInterpreter
clangSema
clangSerialization
${WASM_LINK}
${COMMON_LINK}
)

if ((MINGW OR CYGWIN) AND BUILD_SHARED_LIBS)
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Interpreter/Interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ IncrementalCompilerBuilder::CreateCpp() {
Argv.push_back("-target");
Argv.push_back("wasm32-unknown-emscripten");
Argv.push_back("-shared");
Argv.push_back("-fvisibility=default");
#endif
Argv.insert(Argv.end(), UserArgs.begin(), UserArgs.end());

Expand Down
57 changes: 43 additions & 14 deletions clang/lib/Interpreter/Wasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,31 @@
#include <string>

namespace lld {
enum Flavor {
Invalid,
Gnu, // -flavor gnu
MinGW, // -flavor gnu MinGW
WinLink, // -flavor link
Darwin, // -flavor darwin
Wasm, // -flavor wasm
};

using Driver = bool (*)(llvm::ArrayRef<const char *>, llvm::raw_ostream &,
llvm::raw_ostream &, bool, bool);

struct DriverDef {
Flavor f;
Driver d;
};

struct Result {
int retCode;
bool canRunAgain;
};

Result lldMain(llvm::ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS,
llvm::raw_ostream &stderrOS, llvm::ArrayRef<DriverDef> drivers);

namespace wasm {
bool link(llvm::ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS,
llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput);
Expand Down Expand Up @@ -51,13 +76,14 @@ llvm::Error WasmIncrementalExecutor::addModule(PartialTranslationUnit &PTU) {
llvm::TargetMachine *TargetMachine = Target->createTargetMachine(
PTU.TheModule->getTargetTriple(), "", "", TO, llvm::Reloc::Model::PIC_);
PTU.TheModule->setDataLayout(TargetMachine->createDataLayout());
std::string OutputFileName = PTU.TheModule->getName().str() + ".wasm";
std::string ObjectFileName = PTU.TheModule->getName().str() + ".o";
std::string BinaryFileName = PTU.TheModule->getName().str() + ".wasm";

std::error_code Error;
llvm::raw_fd_ostream OutputFile(llvm::StringRef(OutputFileName), Error);
llvm::raw_fd_ostream ObjectFileOutput(llvm::StringRef(ObjectFileName), Error);

llvm::legacy::PassManager PM;
if (TargetMachine->addPassesToEmitFile(PM, OutputFile, nullptr,
if (TargetMachine->addPassesToEmitFile(PM, ObjectFileOutput, nullptr,
llvm::CodeGenFileType::ObjectFile)) {
return llvm::make_error<llvm::StringError>(
"Wasm backend cannot produce object.", llvm::inconvertibleErrorCode());
Expand All @@ -69,27 +95,30 @@ llvm::Error WasmIncrementalExecutor::addModule(PartialTranslationUnit &PTU) {
llvm::inconvertibleErrorCode());
}

OutputFile.close();
ObjectFileOutput.close();

std::vector<const char *> LinkerArgs = {"wasm-ld",
"-shared",
"--import-memory",
"--no-entry",
"--export-all",
"--experimental-pic",
"--stack-first",
"--allow-undefined",
OutputFileName.c_str(),
ObjectFileName.c_str(),
"-o",
OutputFileName.c_str()};
int Result =
lld::wasm::link(LinkerArgs, llvm::outs(), llvm::errs(), false, false);
if (!Result)
BinaryFileName.c_str()};

const lld::DriverDef WasmDriver = {lld::Flavor::Wasm, &lld::wasm::link};
std::vector<lld::DriverDef> WasmDriverArgs;
WasmDriverArgs.push_back(WasmDriver);
lld::Result Result =
lld::lldMain(LinkerArgs, llvm::outs(), llvm::errs(), WasmDriverArgs);

if (Result.retCode)
return llvm::make_error<llvm::StringError>(
"Failed to link incremental module", llvm::inconvertibleErrorCode());

void *LoadedLibModule =
dlopen(OutputFileName.c_str(), RTLD_NOW | RTLD_GLOBAL);
dlopen(BinaryFileName.c_str(), RTLD_NOW | RTLD_GLOBAL);
if (LoadedLibModule == nullptr) {
llvm::errs() << dlerror() << '\n';
return llvm::make_error<llvm::StringError>(
Expand All @@ -109,12 +138,12 @@ llvm::Error WasmIncrementalExecutor::runCtors() const {
return llvm::Error::success();
}

llvm::Error WasmIncrementalExecutor::cleanUp() const {
llvm::Error WasmIncrementalExecutor::cleanUp() {
// Can't call cleanUp through IncrementalExecutor as it
// tries to deinitialize JIT which hasn't been initialized
return llvm::Error::success();
}

WasmIncrementalExecutor::~WasmIncrementalExecutor() = default;

} // namespace clang
} // namespace clang
8 changes: 8 additions & 0 deletions clang/lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8119,6 +8119,14 @@ void Parser::ParseParameterDeclarationClause(
}

if (TryConsumeToken(tok::ellipsis, EllipsisLoc)) {
if (getLangOpts().CPlusPlus26) {
// C++26 [dcl.dcl.fct]p3:
// A parameter-declaration-clause of the form
// parameter-list '...' is deprecated.
Diag(EllipsisLoc, diag::warn_deprecated_missing_comma_before_ellipsis)
<< FixItHint::CreateInsertion(EllipsisLoc, ", ");
}

if (!getLangOpts().CPlusPlus) {
// We have ellipsis without a preceding ',', which is ill-formed
// in C. Complain and provide the fix.
Expand Down
72 changes: 64 additions & 8 deletions clang/lib/Sema/CheckExprLifetime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "CheckExprLifetime.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/Type.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Sema/Initialization.h"
#include "clang/Sema/Sema.h"
Expand Down Expand Up @@ -203,6 +204,7 @@ struct IndirectLocalPathEntry {
GslPointerInit,
GslPointerAssignment,
DefaultArg,
ParenAggInit,
} Kind;
Expr *E;
union {
Expand Down Expand Up @@ -253,9 +255,35 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path,
LocalVisitor Visit);

template <typename T> static bool isRecordWithAttr(QualType Type) {
if (auto *RD = Type->getAsCXXRecordDecl())
return RD->hasAttr<T>();
return false;
auto *RD = Type->getAsCXXRecordDecl();
if (!RD)
return false;
// Generally, if a primary template class declaration is annotated with an
// attribute, all its specializations generated from template instantiations
// should inherit the attribute.
//
// However, since lifetime analysis occurs during parsing, we may encounter
// cases where a full definition of the specialization is not required. In
// such cases, the specialization declaration remains incomplete and lacks the
// attribute. Therefore, we fall back to checking the primary template class.
//
// Note: it is possible for a specialization declaration to have an attribute
// even if the primary template does not.
//
// FIXME: What if the primary template and explicit specialization
// declarations have conflicting attributes? We should consider diagnosing
// this scenario.
bool Result = RD->hasAttr<T>();

if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
Result |= CTSD->getSpecializedTemplate()->getTemplatedDecl()->hasAttr<T>();

return Result;
}

bool isPointerLikeType(QualType QT) {
return isRecordWithAttr<PointerAttr>(QT) || QT->isPointerType() ||
QT->isNullPtrType();
}

// Decl::isInStdNamespace will return false for iterators in some STL
Expand All @@ -276,11 +304,6 @@ static bool isInStlNamespace(const Decl *D) {
return DC->isStdNamespace();
}

static bool isPointerLikeType(QualType Type) {
return isRecordWithAttr<PointerAttr>(Type) || Type->isPointerType() ||
Type->isNullPtrType();
}

// Returns true if the given Record decl is a form of `GSLOwner<Pointer>`
// type, e.g. std::vector<string_view>, std::optional<string_view>.
static bool isContainerOfPointer(const RecordDecl *Container) {
Expand Down Expand Up @@ -623,6 +646,26 @@ static void visitFunctionCallArguments(IndirectLocalPath &Path, Expr *Call,
}
if (CheckCoroCall || Callee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>())
VisitLifetimeBoundArg(Callee->getParamDecl(I), Arg);
else if (const auto *CaptureAttr =
Callee->getParamDecl(I)->getAttr<LifetimeCaptureByAttr>();
CaptureAttr && isa<CXXConstructorDecl>(Callee) &&
llvm::any_of(CaptureAttr->params(), [](int ArgIdx) {
return ArgIdx == LifetimeCaptureByAttr::THIS;
}))
// `lifetime_capture_by(this)` in a class constructor has the same
// semantics as `lifetimebound`:
//
// struct Foo {
// const int& a;
// // Equivalent to Foo(const int& t [[clang::lifetimebound]])
// Foo(const int& t [[clang::lifetime_capture_by(this)]]) : a(t) {}
// };
//
// In the implementation, `lifetime_capture_by` is treated as an alias for
// `lifetimebound` and shares the same code path. This implies the emitted
// diagnostics will be emitted under `-Wdangling`, not
// `-Wdangling-capture`.
VisitLifetimeBoundArg(Callee->getParamDecl(I), Arg);
else if (EnableGSLAnalysis && I == 0) {
// Perform GSL analysis for the first argument
if (shouldTrackFirstArgument(Callee)) {
Expand Down Expand Up @@ -961,6 +1004,17 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
if (isa<CallExpr>(Init) || isa<CXXConstructExpr>(Init))
return visitFunctionCallArguments(Path, Init, Visit);

if (auto *CPE = dyn_cast<CXXParenListInitExpr>(Init)) {
RevertToOldSizeRAII RAII(Path);
Path.push_back({IndirectLocalPathEntry::ParenAggInit, CPE});
for (auto *I : CPE->getInitExprs()) {
if (I->isGLValue())
visitLocalsRetainedByReferenceBinding(Path, I, RK_ReferenceBinding,
Visit);
else
visitLocalsRetainedByInitializer(Path, I, Visit, true);
}
}
switch (Init->getStmtClass()) {
case Stmt::UnaryOperatorClass: {
auto *UO = cast<UnaryOperator>(Init);
Expand Down Expand Up @@ -1057,6 +1111,7 @@ static SourceRange nextPathEntryRange(const IndirectLocalPath &Path, unsigned I,
case IndirectLocalPathEntry::GslReferenceInit:
case IndirectLocalPathEntry::GslPointerInit:
case IndirectLocalPathEntry::GslPointerAssignment:
case IndirectLocalPathEntry::ParenAggInit:
// These exist primarily to mark the path as not permitting or
// supporting lifetime extension.
break;
Expand Down Expand Up @@ -1368,6 +1423,7 @@ checkExprLifetimeImpl(Sema &SemaRef, const InitializedEntity *InitEntity,
switch (Elem.Kind) {
case IndirectLocalPathEntry::AddressOf:
case IndirectLocalPathEntry::LValToRVal:
case IndirectLocalPathEntry::ParenAggInit:
// These exist primarily to mark the path as not permitting or
// supporting lifetime extension.
break;
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Sema/CheckExprLifetime.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@

namespace clang::sema {

// Tells whether the type is annotated with [[gsl::Pointer]] or is a pointer
// type.
bool isPointerLikeType(QualType QT);

/// Describes an entity that is being assigned.
struct AssignedEntity {
// The left-hand side expression of the assignment.
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaAPINotes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc,
Decl *D = FD;
ObjCMethodDecl *MD = nullptr;
if (!D) {
MD = AnyFunc.get<ObjCMethodDecl *>();
MD = cast<ObjCMethodDecl *>(AnyFunc);
D = MD;
}

Expand Down
65 changes: 36 additions & 29 deletions clang/lib/Sema/SemaAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,42 +269,49 @@ void Sema::inferLifetimeBoundAttribute(FunctionDecl *FD) {
}
}

static bool isPointerLikeType(QualType QT) {
QT = QT.getNonReferenceType();
if (QT->isPointerType())
return true;
auto *RD = QT->getAsCXXRecordDecl();
if (!RD)
return false;
if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
RD = CTSD->getSpecializedTemplate()->getTemplatedDecl();
return RD->hasAttr<PointerAttr>();
}

void Sema::inferLifetimeCaptureByAttribute(FunctionDecl *FD) {
if (!FD)
auto *MD = dyn_cast_if_present<CXXMethodDecl>(FD);
if (!MD || !MD->getParent()->isInStdNamespace())
return;
auto *MD = dyn_cast<CXXMethodDecl>(FD);
if (!MD || !MD->getIdentifier() || !MD->getParent()->isInStdNamespace())
auto Annotate = [this](const FunctionDecl *MD) {
// Do not infer if any parameter is explicitly annotated.
for (ParmVarDecl *PVD : MD->parameters())
if (PVD->hasAttr<LifetimeCaptureByAttr>())
return;
for (ParmVarDecl *PVD : MD->parameters()) {
// Methods in standard containers that capture values typically accept
// reference-type parameters, e.g., `void push_back(const T& value)`.
// We only apply the lifetime_capture_by attribute to parameters of
// pointer-like reference types (`const T&`, `T&&`).
if (PVD->getType()->isReferenceType() &&
sema::isPointerLikeType(PVD->getType().getNonReferenceType())) {
int CaptureByThis[] = {LifetimeCaptureByAttr::THIS};
PVD->addAttr(
LifetimeCaptureByAttr::CreateImplicit(Context, CaptureByThis, 1));
}
}
};

if (!MD->getIdentifier()) {
static const llvm::StringSet<> MapLikeContainer{
"map",
"multimap",
"unordered_map",
"unordered_multimap",
};
// Infer for the map's operator []:
// std::map<string_view, ...> m;
// m[ReturnString(..)] = ...; // !dangling references in m.
if (MD->getOverloadedOperator() == OO_Subscript &&
MapLikeContainer.contains(MD->getParent()->getName()))
Annotate(MD);
return;
// FIXME: Infer for operator[] for map-like containers. For example:
// std::map<string_view, ...> m;
// m[ReturnString(..)] = ...;
}
static const llvm::StringSet<> CapturingMethods{"insert", "push",
"push_front", "push_back"};
if (!CapturingMethods.contains(MD->getName()))
return;
// Do not infer if any parameter is explicitly annotated.
for (ParmVarDecl *PVD : MD->parameters())
if (PVD->hasAttr<LifetimeCaptureByAttr>())
return;
for (ParmVarDecl *PVD : MD->parameters()) {
if (isPointerLikeType(PVD->getType())) {
int CaptureByThis[] = {LifetimeCaptureByAttr::THIS};
PVD->addAttr(
LifetimeCaptureByAttr::CreateImplicit(Context, CaptureByThis, 1));
}
}
Annotate(MD);
}

void Sema::inferNullableClassAttribute(CXXRecordDecl *CRD) {
Expand Down
45 changes: 45 additions & 0 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3240,8 +3240,14 @@ void Sema::checkLifetimeCaptureBy(FunctionDecl *FD, bool IsMemberFunction,
unsigned ArgIdx) {
if (!Attr)
return;

Expr *Captured = const_cast<Expr *>(GetArgAt(ArgIdx));
for (int CapturingParamIdx : Attr->params()) {
// lifetime_capture_by(this) case is handled in the lifetimebound expr
// initialization codepath.
if (CapturingParamIdx == LifetimeCaptureByAttr::THIS &&
isa<CXXConstructorDecl>(FD))
continue;
Expr *Capturing = const_cast<Expr *>(GetArgAt(CapturingParamIdx));
CapturingEntity CE{Capturing};
// Ensure that 'Captured' outlives the 'Capturing' entity.
Expand Down Expand Up @@ -5664,6 +5670,45 @@ bool Sema::BuiltinCountedByRef(CallExpr *TheCall) {
return false;
}

/// The result of __builtin_counted_by_ref cannot be assigned to a variable.
/// It allows leaking and modification of bounds safety information.
bool Sema::CheckInvalidBuiltinCountedByRef(const Expr *E,
BuiltinCountedByRefKind K) {
const CallExpr *CE =
E ? dyn_cast<CallExpr>(E->IgnoreParenImpCasts()) : nullptr;
if (!CE || CE->getBuiltinCallee() != Builtin::BI__builtin_counted_by_ref)
return false;

switch (K) {
case AssignmentKind:
case InitializerKind:
Diag(E->getExprLoc(),
diag::err_builtin_counted_by_ref_cannot_leak_reference)
<< 0 << E->getSourceRange();
break;
case FunctionArgKind:
Diag(E->getExprLoc(),
diag::err_builtin_counted_by_ref_cannot_leak_reference)
<< 1 << E->getSourceRange();
break;
case ReturnArgKind:
Diag(E->getExprLoc(),
diag::err_builtin_counted_by_ref_cannot_leak_reference)
<< 2 << E->getSourceRange();
break;
case ArraySubscriptKind:
Diag(E->getExprLoc(), diag::err_builtin_counted_by_ref_invalid_use)
<< 0 << E->getSourceRange();
break;
case BinaryExprKind:
Diag(E->getExprLoc(), diag::err_builtin_counted_by_ref_invalid_use)
<< 1 << E->getSourceRange();
break;
}

return true;
}

namespace {

class UncoveredArgHandler {
Expand Down
16 changes: 8 additions & 8 deletions clang/lib/Sema/SemaCodeComplete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ class ResultBuilder {
}

// Add the new element to the end of the vector.
DeclOrVector.get<DeclIndexPairVector *>()->push_back(
DeclIndexPair(ND, Index));
cast<DeclIndexPairVector *>(DeclOrVector)
->push_back(DeclIndexPair(ND, Index));
}

~ShadowMapEntry() {
Expand Down Expand Up @@ -659,13 +659,13 @@ class ResultBuilder::ShadowMapEntry::iterator {
: DeclOrIterator(Iterator), SingleDeclIndex(0) {}

iterator &operator++() {
if (DeclOrIterator.is<const NamedDecl *>()) {
if (isa<const NamedDecl *>(DeclOrIterator)) {
DeclOrIterator = (NamedDecl *)nullptr;
SingleDeclIndex = 0;
return *this;
}

const DeclIndexPair *I = DeclOrIterator.get<const DeclIndexPair *>();
const DeclIndexPair *I = cast<const DeclIndexPair *>(DeclOrIterator);
++I;
DeclOrIterator = I;
return *this;
Expand All @@ -681,7 +681,7 @@ class ResultBuilder::ShadowMapEntry::iterator {
if (const NamedDecl *ND = DeclOrIterator.dyn_cast<const NamedDecl *>())
return reference(ND, SingleDeclIndex);

return *DeclOrIterator.get<const DeclIndexPair *>();
return *cast<const DeclIndexPair *>(DeclOrIterator);
}

pointer operator->() const { return pointer(**this); }
Expand All @@ -705,15 +705,15 @@ ResultBuilder::ShadowMapEntry::begin() const {
if (const NamedDecl *ND = DeclOrVector.dyn_cast<const NamedDecl *>())
return iterator(ND, SingleDeclIndex);

return iterator(DeclOrVector.get<DeclIndexPairVector *>()->begin());
return iterator(cast<DeclIndexPairVector *>(DeclOrVector)->begin());
}

ResultBuilder::ShadowMapEntry::iterator
ResultBuilder::ShadowMapEntry::end() const {
if (DeclOrVector.is<const NamedDecl *>() || DeclOrVector.isNull())
if (isa<const NamedDecl *>(DeclOrVector) || DeclOrVector.isNull())
return iterator();

return iterator(DeclOrVector.get<DeclIndexPairVector *>()->end());
return iterator(cast<DeclIndexPairVector *>(DeclOrVector)->end());
}

/// Compute the qualification required to get from the current context
Expand Down
7 changes: 3 additions & 4 deletions clang/lib/Sema/SemaConcept.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1384,8 +1384,7 @@ static void diagnoseUnsatisfiedConstraintExpr(
return;
}

diagnoseWellFormedUnsatisfiedConstraintExpr(S,
Record.template get<Expr *>(), First);
diagnoseWellFormedUnsatisfiedConstraintExpr(S, cast<Expr *>(Record), First);
}

void
Expand Down Expand Up @@ -1557,12 +1556,12 @@ NormalizedConstraint::NormalizedConstraint(ASTContext &C,

NormalizedConstraint &NormalizedConstraint::getLHS() const {
assert(isCompound() && "getLHS called on a non-compound constraint.");
return Constraint.get<CompoundConstraint>().getPointer()->LHS;
return cast<CompoundConstraint>(Constraint).getPointer()->LHS;
}

NormalizedConstraint &NormalizedConstraint::getRHS() const {
assert(isCompound() && "getRHS called on a non-compound constraint.");
return Constraint.get<CompoundConstraint>().getPointer()->RHS;
return cast<CompoundConstraint>(Constraint).getPointer()->RHS;
}

std::optional<NormalizedConstraint>
Expand Down
21 changes: 18 additions & 3 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8762,6 +8762,19 @@ void Sema::CheckVariableDeclarationType(VarDecl *NewVD) {
}
}

// zero sized static arrays are not allowed in HIP device functions
if (getLangOpts().HIP && LangOpts.CUDAIsDevice) {
if (FunctionDecl *FD = getCurFunctionDecl();
FD &&
(FD->hasAttr<CUDADeviceAttr>() || FD->hasAttr<CUDAGlobalAttr>())) {
if (const ConstantArrayType *ArrayT =
getASTContext().getAsConstantArrayType(T);
ArrayT && ArrayT->isZeroSize()) {
Diag(NewVD->getLocation(), diag::err_typecheck_zero_array_size) << 2;
}
}
}

bool isVM = T->isVariablyModifiedType();
if (isVM || NewVD->hasAttr<CleanupAttr>() ||
NewVD->hasAttr<BlocksAttr>())
Expand Down Expand Up @@ -14699,6 +14712,8 @@ void Sema::FinalizeDeclaration(Decl *ThisDecl) {
}
}

CheckInvalidBuiltinCountedByRef(VD->getInit(), InitializerKind);

checkAttributesAfterMerging(*this, *VD);

if (VD->isStaticLocal())
Expand Down Expand Up @@ -17276,7 +17291,7 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
if (TypeSourceInfo *TI = EnumUnderlying.dyn_cast<TypeSourceInfo *>())
ED->setIntegerTypeSourceInfo(TI);
else
ED->setIntegerType(QualType(EnumUnderlying.get<const Type *>(), 0));
ED->setIntegerType(QualType(cast<const Type *>(EnumUnderlying), 0));
QualType EnumTy = ED->getIntegerType();
ED->setPromotionType(Context.isPromotableIntegerType(EnumTy)
? Context.getPromotedIntegerType(EnumTy)
Expand Down Expand Up @@ -17909,7 +17924,7 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
if (TypeSourceInfo *TI = EnumUnderlying.dyn_cast<TypeSourceInfo*>())
ED->setIntegerTypeSourceInfo(TI);
else
ED->setIntegerType(QualType(EnumUnderlying.get<const Type *>(), 0));
ED->setIntegerType(QualType(cast<const Type *>(EnumUnderlying), 0));
QualType EnumTy = ED->getIntegerType();
ED->setPromotionType(Context.isPromotableIntegerType(EnumTy)
? Context.getPromotedIntegerType(EnumTy)
Expand Down Expand Up @@ -19925,7 +19940,7 @@ static void CheckForDuplicateEnumValues(Sema &S, ArrayRef<Decl *> Elements,
continue;
}

ECDVector *Vec = Entry.get<ECDVector*>();
ECDVector *Vec = cast<ECDVector *>(Entry);
// Make sure constants are not added more than once.
if (*Vec->begin() == ECD)
continue;
Expand Down
17 changes: 14 additions & 3 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -776,9 +776,9 @@ static void handleDiagnoseAsBuiltinAttr(Sema &S, Decl *D,
auto DiagnoseType = [&](unsigned Index, AttributeArgumentNType T) {
SourceLocation Loc = [&]() {
auto Union = AL.getArg(Index - 1);
if (Union.is<Expr *>())
return Union.get<Expr *>()->getBeginLoc();
return Union.get<IdentifierLoc *>()->Loc;
if (auto *E = dyn_cast<Expr *>(Union))
return E->getBeginLoc();
return cast<IdentifierLoc *>(Union)->Loc;
}();

S.Diag(Loc, diag::err_attribute_argument_n_type) << AL << Index << T;
Expand Down Expand Up @@ -1212,6 +1212,14 @@ static void handlePreferredName(Sema &S, Decl *D, const ParsedAttr &AL) {
<< TT->getDecl();
}

static void handleNoSpecializations(Sema &S, Decl *D, const ParsedAttr &AL) {
StringRef Message;
if (AL.getNumArgs() != 0)
S.checkStringLiteralArgumentAttr(AL, 0, Message);
D->getDescribedTemplate()->addAttr(
NoSpecializationsAttr::Create(S.Context, Message, AL));
}

bool Sema::isValidPointerAttrType(QualType T, bool RefOkay) {
if (T->isDependentType())
return true;
Expand Down Expand Up @@ -6913,6 +6921,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_PreferredName:
handlePreferredName(S, D, AL);
break;
case ParsedAttr::AT_NoSpecializations:
handleNoSpecializations(S, D, AL);
break;
case ParsedAttr::AT_Section:
handleSectionAttr(S, D, AL);
break;
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9218,7 +9218,7 @@ struct SpecialMemberVisitor {
if (auto *B = Subobj.dyn_cast<CXXBaseSpecifier*>())
return B->getBaseTypeLoc();
else
return Subobj.get<FieldDecl*>()->getLocation();
return cast<FieldDecl *>(Subobj)->getLocation();
}

enum BasesToVisit {
Expand Down Expand Up @@ -9369,7 +9369,7 @@ bool SpecialMemberDeletionInfo::shouldDeleteForSubobjectCall(
<< /*IsField*/ true << Field << DiagKind << IsDtorCallInCtor
<< /*IsObjCPtr*/ false;
} else {
CXXBaseSpecifier *Base = Subobj.get<CXXBaseSpecifier*>();
CXXBaseSpecifier *Base = cast<CXXBaseSpecifier *>(Subobj);
S.Diag(Base->getBeginLoc(),
diag::note_deleted_special_member_class_subobject)
<< llvm::to_underlying(getEffectiveCSM()) << MD->getParent()
Expand Down Expand Up @@ -17493,7 +17493,7 @@ DeclResult Sema::ActOnTemplatedFriendTag(
if (getDepthAndIndex(U).first >= FriendDeclDepth) {
auto *ND = U.first.dyn_cast<NamedDecl *>();
if (!ND)
ND = U.first.get<const TemplateTypeParmType *>()->getDecl();
ND = cast<const TemplateTypeParmType *>(U.first)->getDecl();
Diag(U.second, diag::friend_template_decl_malformed_pack_expansion)
<< ND->getDeclName() << SourceRange(SS.getBeginLoc(), EllipsisLoc);
return true;
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaDeclObjC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1587,7 +1587,7 @@ void SemaObjC::actOnObjCTypeArgsOrProtocolQualifiers(
if (auto *actualTypeDecl = typeDecl.dyn_cast<TypeDecl *>())
type = Context.getTypeDeclType(actualTypeDecl);
else
type = Context.getObjCInterfaceType(typeDecl.get<ObjCInterfaceDecl *>());
type = Context.getObjCInterfaceType(cast<ObjCInterfaceDecl *>(typeDecl));
TypeSourceInfo *parsedTSInfo = Context.getTrivialTypeSourceInfo(type, loc);
ParsedType parsedType = SemaRef.CreateParsedType(type, parsedTSInfo);
DS.SetTypeSpecType(DeclSpec::TST_typename, loc, prevSpec, diagID,
Expand Down
82 changes: 14 additions & 68 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4897,6 +4897,8 @@ ExprResult Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base,
return ExprError();
}

CheckInvalidBuiltinCountedByRef(base, ArraySubscriptKind);

// Handle any non-overload placeholder types in the base and index
// expressions. We can't handle overloads here because the other
// operand might be an overloadable type, in which case the overload
Expand Down Expand Up @@ -6489,6 +6491,12 @@ ExprResult Sema::BuildCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc,
if (CheckArgsForPlaceholders(ArgExprs))
return ExprError();

// The result of __builtin_counted_by_ref cannot be used as a function
// argument. It allows leaking and modification of bounds safety information.
for (const Expr *Arg : ArgExprs)
if (CheckInvalidBuiltinCountedByRef(Arg, FunctionArgKind))
return ExprError();

if (getLangOpts().CPlusPlus) {
// If this is a pseudo-destructor expression, build the call immediately.
if (isa<CXXPseudoDestructorExpr>(Fn)) {
Expand Down Expand Up @@ -9199,38 +9207,6 @@ Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS,
LHSType = Context.getCanonicalType(LHSType).getUnqualifiedType();
RHSType = Context.getCanonicalType(RHSType).getUnqualifiedType();

// __builtin_counted_by_ref cannot be assigned to a variable, used in
// function call, or in a return.
auto FindBuiltinCountedByRefExpr = [&](Expr *E) -> CallExpr * {
struct BuiltinCountedByRefVisitor : DynamicRecursiveASTVisitor {
CallExpr *TheCall = nullptr;
bool VisitCallExpr(CallExpr *CE) override {
if (CE->getBuiltinCallee() == Builtin::BI__builtin_counted_by_ref) {
TheCall = CE;
return false;
}
return true;
}
bool
VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *UE) override {
// A UnaryExprOrTypeTraitExpr---e.g. sizeof, __alignof, etc.---isn't
// the same as a CallExpr, so if we find a __builtin_counted_by_ref()
// call in one, ignore it.
return false;
}
} V;
V.TraverseStmt(E);
return V.TheCall;
};
static llvm::SmallPtrSet<CallExpr *, 4> Diagnosed;
if (auto *CE = FindBuiltinCountedByRefExpr(RHS.get());
CE && !Diagnosed.count(CE)) {
Diagnosed.insert(CE);
Diag(CE->getExprLoc(),
diag::err_builtin_counted_by_ref_cannot_leak_reference)
<< CE->getSourceRange();
}

// Common case: no conversion required.
if (LHSType == RHSType) {
Kind = CK_NoOp;
Expand Down Expand Up @@ -13781,42 +13757,6 @@ QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS,
ConvTy = CheckAssignmentConstraints(Loc, LHSType, RHSType);
}

// __builtin_counted_by_ref can't be used in a binary expression or array
// subscript on the LHS.
int DiagOption = -1;
auto FindInvalidUseOfBoundsSafetyCounter = [&](Expr *E) -> CallExpr * {
struct BuiltinCountedByRefVisitor : DynamicRecursiveASTVisitor {
CallExpr *CE = nullptr;
bool InvalidUse = false;
int Option = -1;

bool VisitCallExpr(CallExpr *E) override {
if (E->getBuiltinCallee() == Builtin::BI__builtin_counted_by_ref) {
CE = E;
return false;
}
return true;
}

bool VisitArraySubscriptExpr(ArraySubscriptExpr *E) override {
InvalidUse = true;
Option = 0; // report 'array expression' in diagnostic.
return true;
}
bool VisitBinaryOperator(BinaryOperator *E) override {
InvalidUse = true;
Option = 1; // report 'binary expression' in diagnostic.
return true;
}
} V;
V.TraverseStmt(E);
DiagOption = V.Option;
return V.InvalidUse ? V.CE : nullptr;
};
if (auto *CE = FindInvalidUseOfBoundsSafetyCounter(LHSExpr))
Diag(CE->getExprLoc(), diag::err_builtin_counted_by_ref_invalid_lhs_use)
<< DiagOption << CE->getSourceRange();

if (DiagnoseAssignmentResult(ConvTy, Loc, LHSType, RHSType, RHS.get(),
AssignmentAction::Assigning))
return QualType();
Expand Down Expand Up @@ -15277,6 +15217,12 @@ ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
if (Kind == tok::TokenKind::slash)
DetectPrecisionLossInComplexDivision(*this, TokLoc, LHSExpr);

BuiltinCountedByRefKind K =
BinaryOperator::isAssignmentOp(Opc) ? AssignmentKind : BinaryExprKind;

CheckInvalidBuiltinCountedByRef(LHSExpr, K);
CheckInvalidBuiltinCountedByRef(RHSExpr, K);

return BuildBinOp(S, TokLoc, Opc, LHSExpr, RHSExpr);
}

Expand Down
14 changes: 6 additions & 8 deletions clang/lib/Sema/SemaFunctionEffects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ class Analyzer {
CompleteFunctionAnalysis *completedAnalysisForDecl(const Decl *D) const {
if (FuncAnalysisPtr AP = lookup(D);
isa_and_nonnull<CompleteFunctionAnalysis *>(AP))
return AP.get<CompleteFunctionAnalysis *>();
return cast<CompleteFunctionAnalysis *>(AP);
return nullptr;
}

Expand All @@ -528,12 +528,10 @@ class Analyzer {
OS << item.first << " " << CI.getNameForDiagnostic(SemaRef) << " : ";
if (AP.isNull()) {
OS << "null\n";
} else if (isa<CompleteFunctionAnalysis *>(AP)) {
auto *CFA = AP.get<CompleteFunctionAnalysis *>();
} else if (auto *CFA = dyn_cast<CompleteFunctionAnalysis *>(AP)) {
OS << CFA << " ";
CFA->dump(OS);
} else if (isa<PendingFunctionAnalysis *>(AP)) {
auto *PFA = AP.get<PendingFunctionAnalysis *>();
} else if (auto *PFA = dyn_cast<PendingFunctionAnalysis *>(AP)) {
OS << PFA << " ";
PFA->dump(SemaRef, OS);
} else
Expand Down Expand Up @@ -1376,10 +1374,10 @@ class Analyzer {
Analyzer::AnalysisMap::~AnalysisMap() {
for (const auto &Item : *this) {
FuncAnalysisPtr AP = Item.second;
if (isa<PendingFunctionAnalysis *>(AP))
delete AP.get<PendingFunctionAnalysis *>();
if (auto *PFA = dyn_cast<PendingFunctionAnalysis *>(AP))
delete PFA;
else
delete AP.get<CompleteFunctionAnalysis *>();
delete cast<CompleteFunctionAnalysis *>(AP);
}
}

Expand Down
36 changes: 20 additions & 16 deletions clang/lib/Sema/SemaOpenACC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -843,10 +843,11 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitFirstPrivateClause(

OpenACCClause *SemaOpenACCClauseVisitor::VisitNoCreateClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// Restrictions only properly implemented on 'compute' constructs, and
// 'compute' constructs are the only construct that can do anything with
// this yet, so skip/treat as unimplemented in this case.
if (!isOpenACCComputeDirectiveKind(Clause.getDirectiveKind()))
// Restrictions only properly implemented on 'compute'/'combined' constructs,
// and 'compute'/'combined' constructs are the only construct that can do
// anything with this yet, so skip/treat as unimplemented in this case.
if (!isOpenACCComputeDirectiveKind(Clause.getDirectiveKind()) &&
!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind()))
return isNotImplemented();
// ActOnVar ensured that everything is a valid variable reference, so there
// really isn't anything to do here. GCC does some duplicate-finding, though
Expand Down Expand Up @@ -940,10 +941,11 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitCreateClause(

OpenACCClause *SemaOpenACCClauseVisitor::VisitAttachClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// Restrictions only properly implemented on 'compute' constructs, and
// 'compute' constructs are the only construct that can do anything with
// this yet, so skip/treat as unimplemented in this case.
if (!isOpenACCComputeDirectiveKind(Clause.getDirectiveKind()))
// Restrictions only properly implemented on 'compute'/'combined' constructs,
// and 'compute'/'combined' constructs are the only construct that can do
// anything with this yet, so skip/treat as unimplemented in this case.
if (!isOpenACCComputeDirectiveKind(Clause.getDirectiveKind()) &&
!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind()))
return isNotImplemented();

// ActOnVar ensured that everything is a valid variable reference, but we
Expand All @@ -961,10 +963,11 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitAttachClause(

OpenACCClause *SemaOpenACCClauseVisitor::VisitDevicePtrClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// Restrictions only properly implemented on 'compute' constructs, and
// 'compute' constructs are the only construct that can do anything with
// this yet, so skip/treat as unimplemented in this case.
if (!isOpenACCComputeDirectiveKind(Clause.getDirectiveKind()))
// Restrictions only properly implemented on 'compute'/'combined' constructs,
// and 'compute'/'combined' constructs are the only construct that can do
// anything with this yet, so skip/treat as unimplemented in this case.
if (!isOpenACCComputeDirectiveKind(Clause.getDirectiveKind()) &&
!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind()))
return isNotImplemented();

// ActOnVar ensured that everything is a valid variable reference, but we
Expand All @@ -983,10 +986,11 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitDevicePtrClause(

OpenACCClause *SemaOpenACCClauseVisitor::VisitWaitClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// Restrictions only properly implemented on 'compute' constructs, and
// 'compute' constructs are the only construct that can do anything with
// this yet, so skip/treat as unimplemented in this case.
if (!isOpenACCComputeDirectiveKind(Clause.getDirectiveKind()))
// Restrictions only properly implemented on 'compute'/'combined' constructs,
// and 'compute'/'combined' constructs are the only construct that can do
// anything with this yet, so skip/treat as unimplemented in this case.
if (!isOpenACCComputeDirectiveKind(Clause.getDirectiveKind()) &&
!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind()))
return isNotImplemented();

return OpenACCWaitClause::Create(
Expand Down
8 changes: 4 additions & 4 deletions clang/lib/Sema/SemaOpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1611,10 +1611,10 @@ const DSAStackTy::DSAVarData DSAStackTy::getTopMostTaskgroupReductionData(
continue;
const ReductionData &ReductionData = I->ReductionMap.lookup(D);
if (!ReductionData.ReductionOp ||
ReductionData.ReductionOp.is<const Expr *>())
isa<const Expr *>(ReductionData.ReductionOp))
return DSAVarData();
SR = ReductionData.ReductionRange;
BOK = ReductionData.ReductionOp.get<ReductionData::BOKPtrType>();
BOK = cast<ReductionData::BOKPtrType>(ReductionData.ReductionOp);
assert(I->TaskgroupReductionRef && "taskgroup reduction reference "
"expression for the descriptor is not "
"set.");
Expand All @@ -1638,10 +1638,10 @@ const DSAStackTy::DSAVarData DSAStackTy::getTopMostTaskgroupReductionData(
continue;
const ReductionData &ReductionData = I->ReductionMap.lookup(D);
if (!ReductionData.ReductionOp ||
!ReductionData.ReductionOp.is<const Expr *>())
!isa<const Expr *>(ReductionData.ReductionOp))
return DSAVarData();
SR = ReductionData.ReductionRange;
ReductionRef = ReductionData.ReductionOp.get<const Expr *>();
ReductionRef = cast<const Expr *>(ReductionData.ReductionOp);
assert(I->TaskgroupReductionRef && "taskgroup reduction reference "
"expression for the descriptor is not "
"set.");
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Sema/SemaStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3778,6 +3778,8 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp,
<< FSI->getFirstCoroutineStmtKeyword();
}

CheckInvalidBuiltinCountedByRef(RetVal.get(), ReturnArgKind);

StmtResult R =
BuildReturnStmt(ReturnLoc, RetVal.get(), /*AllowRecovery=*/true);
if (R.isInvalid() || ExprEvalContexts.back().isDiscardedStatementContext())
Expand Down
22 changes: 22 additions & 0 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4157,6 +4157,13 @@ DeclResult Sema::ActOnVarTemplateSpecialization(
<< IsPartialSpecialization;
}

if (const auto *DSA = VarTemplate->getAttr<NoSpecializationsAttr>()) {
auto Message = DSA->getMessage();
Diag(TemplateNameLoc, diag::warn_invalid_specialization)
<< VarTemplate << !Message.empty() << Message;
Diag(DSA->getLoc(), diag::note_marked_here) << DSA;
}

// Check for unexpanded parameter packs in any of the template arguments.
for (unsigned I = 0, N = TemplateArgs.size(); I != N; ++I)
if (DiagnoseUnexpandedParameterPack(TemplateArgs[I],
Expand Down Expand Up @@ -8291,6 +8298,13 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
return true;
}

if (const auto *DSA = ClassTemplate->getAttr<NoSpecializationsAttr>()) {
auto Message = DSA->getMessage();
Diag(TemplateNameLoc, diag::warn_invalid_specialization)
<< ClassTemplate << !Message.empty() << Message;
Diag(DSA->getLoc(), diag::note_marked_here) << DSA;
}

if (S->isTemplateParamScope())
EnterTemplatedContext(S, ClassTemplate->getTemplatedDecl());

Expand Down Expand Up @@ -9175,6 +9189,14 @@ bool Sema::CheckFunctionTemplateSpecialization(
// Ignore access information; it doesn't figure into redeclaration checking.
FunctionDecl *Specialization = cast<FunctionDecl>(*Result);

if (const auto *PT = Specialization->getPrimaryTemplate();
const auto *DSA = PT->getAttr<NoSpecializationsAttr>()) {
auto Message = DSA->getMessage();
Diag(FD->getLocation(), diag::warn_invalid_specialization)
<< PT << !Message.empty() << Message;
Diag(DSA->getLoc(), diag::note_marked_here) << DSA;
}

// C++23 [except.spec]p13:
// An exception specification is considered to be needed when:
// - [...]
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Sema/SemaTemplateDeduction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -376,14 +376,15 @@ checkDeducedTemplateArguments(ASTContext &Context,
for (TemplateArgument::pack_iterator
XA = X.pack_begin(),
XAEnd = X.pack_end(), YA = Y.pack_begin(), YAEnd = Y.pack_end();
XA != XAEnd; ++XA, ++YA) {
XA != XAEnd; ++XA) {
if (YA != YAEnd) {
TemplateArgument Merged = checkDeducedTemplateArguments(
Context, DeducedTemplateArgument(*XA, X.wasDeducedFromArrayBound()),
DeducedTemplateArgument(*YA, Y.wasDeducedFromArrayBound()));
if (Merged.isNull() && !(XA->isNull() && YA->isNull()))
return DeducedTemplateArgument();
NewPack.push_back(Merged);
++YA;
} else {
NewPack.push_back(*XA);
}
Expand Down
12 changes: 6 additions & 6 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ HandleVarTemplateSpec(const VarTemplateSpecializationDecl *VarTemplSpec,
if (Partial->isMemberSpecialization())
return Response::Done();
} else {
VarTemplateDecl *Tmpl = Specialized.get<VarTemplateDecl *>();
VarTemplateDecl *Tmpl = cast<VarTemplateDecl *>(Specialized);
if (!SkipForSpecialization)
Result.addOuterTemplateArguments(
Tmpl, VarTemplSpec->getTemplateInstantiationArgs().asArray(),
Expand Down Expand Up @@ -2458,7 +2458,7 @@ TemplateInstantiator::TransformFunctionParmPackRefExpr(DeclRefExpr *E,

TransformedDecl = (*Pack)[getSema().ArgumentPackSubstitutionIndex];
} else {
TransformedDecl = Found->get<Decl*>();
TransformedDecl = cast<Decl *>(*Found);
}

// We have either an unexpanded pack or a specific expansion.
Expand Down Expand Up @@ -2827,7 +2827,7 @@ TemplateInstantiator::TransformExprRequirement(concepts::ExprRequirement *Req) {
return RebuildExprRequirement(E, Req->isSimple(), Req->getNoexceptLoc(),
std::move(*TransRetReq));
return RebuildExprRequirement(
TransExpr.get<concepts::Requirement::SubstitutionDiagnostic *>(),
cast<concepts::Requirement::SubstitutionDiagnostic *>(TransExpr),
Req->isSimple(), Req->getNoexceptLoc(), std::move(*TransRetReq));
}

Expand Down Expand Up @@ -4053,7 +4053,7 @@ getPatternForClassTemplateSpecialization(
llvm::PointerUnion<ClassTemplateDecl *,
ClassTemplatePartialSpecializationDecl *>
Specialized = ClassTemplateSpec->getSpecializedTemplateOrPartial();
if (!Specialized.is<ClassTemplatePartialSpecializationDecl *>()) {
if (!isa<ClassTemplatePartialSpecializationDecl *>(Specialized)) {
// Find best matching specialization.
ClassTemplateDecl *Template = ClassTemplateSpec->getSpecializedTemplate();

Expand Down Expand Up @@ -4664,14 +4664,14 @@ void LocalInstantiationScope::InstantiatedLocal(const Decl *D, Decl *Inst) {
} else if (DeclArgumentPack *Pack = Stored.dyn_cast<DeclArgumentPack *>()) {
Pack->push_back(cast<VarDecl>(Inst));
} else {
assert(Stored.get<Decl *>() == Inst && "Already instantiated this local");
assert(cast<Decl *>(Stored) == Inst && "Already instantiated this local");
}
}

void LocalInstantiationScope::InstantiatedLocalPackArg(const Decl *D,
VarDecl *Inst) {
D = getCanonicalParmVarDecl(D);
DeclArgumentPack *Pack = LocalDecls[D].get<DeclArgumentPack *>();
DeclArgumentPack *Pack = cast<DeclArgumentPack *>(LocalDecls[D]);
Pack->push_back(Inst);
}

Expand Down
10 changes: 5 additions & 5 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3720,8 +3720,8 @@ Decl *TemplateDeclInstantiator::VisitOMPDeclareReductionDecl(
auto *PrevDeclInScope = D->getPrevDeclInScope();
if (PrevDeclInScope && !PrevDeclInScope->isInvalidDecl()) {
PrevDeclInScope = cast<OMPDeclareReductionDecl>(
SemaRef.CurrentInstantiationScope->findInstantiationOf(PrevDeclInScope)
->get<Decl *>());
cast<Decl *>(*SemaRef.CurrentInstantiationScope->findInstantiationOf(
PrevDeclInScope)));
}
auto DRD = SemaRef.OpenMP().ActOnOpenMPDeclareReductionDirectiveStart(
/*S=*/nullptr, Owner, D->getDeclName(), ReductionTypes, D->getAccess(),
Expand Down Expand Up @@ -3807,8 +3807,8 @@ TemplateDeclInstantiator::VisitOMPDeclareMapperDecl(OMPDeclareMapperDecl *D) {
auto *PrevDeclInScope = D->getPrevDeclInScope();
if (PrevDeclInScope && !PrevDeclInScope->isInvalidDecl()) {
PrevDeclInScope = cast<OMPDeclareMapperDecl>(
SemaRef.CurrentInstantiationScope->findInstantiationOf(PrevDeclInScope)
->get<Decl *>());
cast<Decl *>(*SemaRef.CurrentInstantiationScope->findInstantiationOf(
PrevDeclInScope)));
}
bool IsCorrect = true;
SmallVector<OMPClause *, 6> Clauses;
Expand Down Expand Up @@ -6186,7 +6186,7 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D,
assert(PackIdx != -1 &&
"found declaration pack but not pack expanding");
typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack;
return cast<NamedDecl>((*Found->get<DeclArgumentPack *>())[PackIdx]);
return cast<NamedDecl>((*cast<DeclArgumentPack *>(*Found))[PackIdx]);
}
}

Expand Down
20 changes: 10 additions & 10 deletions clang/lib/Sema/SemaTemplateVariadic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc,
auto *TTPD = dyn_cast<TemplateTypeParmDecl>(LocalPack);
return TTPD && TTPD->getTypeForDecl() == TTPT;
}
return declaresSameEntity(Pack.first.get<NamedDecl *>(), LocalPack);
return declaresSameEntity(cast<NamedDecl *>(Pack.first), LocalPack);
};
if (llvm::any_of(CSI->LocalPacks, DeclaresThisPack))
ParamPackReferences.push_back(Pack);
Expand Down Expand Up @@ -423,7 +423,7 @@ Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc,
= Unexpanded[I].first.dyn_cast<const TemplateTypeParmType *>())
Name = TTP->getIdentifier();
else
Name = Unexpanded[I].first.get<NamedDecl *>()->getIdentifier();
Name = cast<NamedDecl *>(Unexpanded[I].first)->getIdentifier();

if (Name && NamesKnown.insert(Name).second)
Names.push_back(Name);
Expand Down Expand Up @@ -770,7 +770,7 @@ bool Sema::CheckParameterPacksForExpansion(
Index = TTP->getIndex();
Name = TTP->getIdentifier();
} else {
NamedDecl *ND = ParmPack.first.get<NamedDecl *>();
NamedDecl *ND = cast<NamedDecl *>(ParmPack.first);
if (isa<VarDecl>(ND))
IsVarDeclPack = true;
else
Expand All @@ -787,10 +787,10 @@ bool Sema::CheckParameterPacksForExpansion(

llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation =
CurrentInstantiationScope->findInstantiationOf(
ParmPack.first.get<NamedDecl *>());
if (Instantiation->is<DeclArgumentPack *>()) {
cast<NamedDecl *>(ParmPack.first));
if (isa<DeclArgumentPack *>(*Instantiation)) {
// We could expand this function parameter pack.
NewPackSize = Instantiation->get<DeclArgumentPack *>()->size();
NewPackSize = cast<DeclArgumentPack *>(*Instantiation)->size();
} else {
// We can't expand this function parameter pack, so we can't expand
// the pack expansion.
Expand Down Expand Up @@ -895,20 +895,20 @@ std::optional<unsigned> Sema::getNumArgumentsInExpansionFromUnexpanded(
Depth = TTP->getDepth();
Index = TTP->getIndex();
} else {
NamedDecl *ND = Unexpanded[I].first.get<NamedDecl *>();
NamedDecl *ND = cast<NamedDecl *>(Unexpanded[I].first);
if (isa<VarDecl>(ND)) {
// Function parameter pack or init-capture pack.
typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack;

llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation =
CurrentInstantiationScope->findInstantiationOf(
Unexpanded[I].first.get<NamedDecl *>());
if (Instantiation->is<Decl *>())
cast<NamedDecl *>(Unexpanded[I].first));
if (isa<Decl *>(*Instantiation))
// The pattern refers to an unexpanded pack. We're not ready to expand
// this pack yet.
return std::nullopt;

unsigned Size = Instantiation->get<DeclArgumentPack *>()->size();
unsigned Size = cast<DeclArgumentPack *>(*Instantiation)->size();
assert((!Result || *Result == Size) && "inconsistent pack sizes");
Result = Size;
continue;
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -14454,7 +14454,7 @@ TreeTransform<Derived>::TransformExprRequirement(concepts::ExprRequirement *Req)
Req->getNoexceptLoc(),
std::move(*TransRetReq));
return getDerived().RebuildExprRequirement(
TransExpr.get<concepts::Requirement::SubstitutionDiagnostic *>(),
cast<concepts::Requirement::SubstitutionDiagnostic *>(TransExpr),
Req->isSimple(), Req->getNoexceptLoc(), std::move(*TransRetReq));
}

Expand Down
1 change: 0 additions & 1 deletion clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ add_clang_library(clangStaticAnalyzerCheckers
GCDAntipatternChecker.cpp
GenericTaintChecker.cpp
GTestChecker.cpp
IdenticalExprChecker.cpp
InnerPointerChecker.cpp
InvalidatedIteratorChecker.cpp
cert/InvalidPtrChecker.cpp
Expand Down
182 changes: 112 additions & 70 deletions clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@
//===----------------------------------------------------------------------===//
//
// This file defines chroot checker, which checks improper use of chroot.
// This is described by the SEI Cert C rule POS05-C.
// The checker is a warning not a hard failure since it only checks for a
// recommended rule.
//
//===----------------------------------------------------------------------===//

#include "clang/AST/ASTContext.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
Expand All @@ -25,117 +29,155 @@ using namespace clang;
using namespace ento;

namespace {
enum ChrootKind { NO_CHROOT, ROOT_CHANGED, ROOT_CHANGE_FAILED, JAIL_ENTERED };
} // namespace

// enum value that represent the jail state
enum Kind { NO_CHROOT, ROOT_CHANGED, JAIL_ENTERED };

bool isRootChanged(intptr_t k) { return k == ROOT_CHANGED; }
//bool isJailEntered(intptr_t k) { return k == JAIL_ENTERED; }
// Track chroot state changes for success, failure, state change
// and "jail"
REGISTER_TRAIT_WITH_PROGRAMSTATE(ChrootState, ChrootKind)
namespace {

// This checker checks improper use of chroot.
// The state transition:
// The state transitions
//
// -> ROOT_CHANGE_FAILED
// |
// NO_CHROOT ---chroot(path)--> ROOT_CHANGED ---chdir(/) --> JAIL_ENTERED
// | |
// ROOT_CHANGED<--chdir(..)-- JAIL_ENTERED<--chdir(..)--
// | |
// bug<--foo()-- JAIL_ENTERED<--foo()--
class ChrootChecker : public Checker<eval::Call, check::PreCall> {
// This bug refers to possibly break out of a chroot() jail.
const BugType BT_BreakJail{this, "Break out of jail"};

const CallDescription Chroot{CDM::CLibrary, {"chroot"}, 1},
Chdir{CDM::CLibrary, {"chdir"}, 1};

//
class ChrootChecker final : public Checker<eval::Call, check::PreCall> {
public:
ChrootChecker() {}

static void *getTag() {
static int x;
return &x;
}

bool evalCall(const CallEvent &Call, CheckerContext &C) const;
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;

private:
void evalChroot(const CallEvent &Call, CheckerContext &C) const;
void evalChdir(const CallEvent &Call, CheckerContext &C) const;
};
bool evalChroot(const CallEvent &Call, CheckerContext &C) const;
bool evalChdir(const CallEvent &Call, CheckerContext &C) const;

} // end anonymous namespace
const BugType BreakJailBug{this, "Break out of jail"};
const CallDescription Chroot{CDM::CLibrary, {"chroot"}, 1};
const CallDescription Chdir{CDM::CLibrary, {"chdir"}, 1};
};

bool ChrootChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
if (Chroot.matches(Call)) {
evalChroot(Call, C);
return true;
}
if (Chdir.matches(Call)) {
evalChdir(Call, C);
return true;
}
if (Chroot.matches(Call))
return evalChroot(Call, C);

if (Chdir.matches(Call))
return evalChdir(Call, C);

return false;
}

void ChrootChecker::evalChroot(const CallEvent &Call, CheckerContext &C) const {
ProgramStateRef state = C.getState();
ProgramStateManager &Mgr = state->getStateManager();
bool ChrootChecker::evalChroot(const CallEvent &Call, CheckerContext &C) const {
BasicValueFactory &BVF = C.getSValBuilder().getBasicValueFactory();
const LocationContext *LCtx = C.getLocationContext();
ProgramStateRef State = C.getState();
const auto *CE = cast<CallExpr>(Call.getOriginExpr());

const QualType IntTy = C.getASTContext().IntTy;
SVal Zero = nonloc::ConcreteInt{BVF.getValue(0, IntTy)};
SVal Minus1 = nonloc::ConcreteInt{BVF.getValue(-1, IntTy)};

// Once encouter a chroot(), set the enum value ROOT_CHANGED directly in
// the GDM.
state = Mgr.addGDM(state, ChrootChecker::getTag(), (void*) ROOT_CHANGED);
C.addTransition(state);
ProgramStateRef ChrootFailed = State->BindExpr(CE, LCtx, Minus1);
C.addTransition(ChrootFailed->set<ChrootState>(ROOT_CHANGE_FAILED));

ProgramStateRef ChrootSucceeded = State->BindExpr(CE, LCtx, Zero);
C.addTransition(ChrootSucceeded->set<ChrootState>(ROOT_CHANGED));
return true;
}

void ChrootChecker::evalChdir(const CallEvent &Call, CheckerContext &C) const {
ProgramStateRef state = C.getState();
ProgramStateManager &Mgr = state->getStateManager();
bool ChrootChecker::evalChdir(const CallEvent &Call, CheckerContext &C) const {
ProgramStateRef State = C.getState();

// If there are no jail state in the GDM, just return.
const void *k = state->FindGDM(ChrootChecker::getTag());
if (!k)
return;
// If there are no jail state, just return.
if (State->get<ChrootState>() == NO_CHROOT)
return false;

// After chdir("/"), enter the jail, set the enum value JAIL_ENTERED.
const Expr *ArgExpr = Call.getArgExpr(0);
SVal ArgVal = C.getSVal(ArgExpr);
SVal ArgVal = Call.getArgSVal(0);

if (const MemRegion *R = ArgVal.getAsRegion()) {
R = R->StripCasts();
if (const StringRegion* StrRegion= dyn_cast<StringRegion>(R)) {
const StringLiteral* Str = StrRegion->getStringLiteral();
if (Str->getString() == "/")
state = Mgr.addGDM(state, ChrootChecker::getTag(),
(void*) JAIL_ENTERED);
if (const auto *StrRegion = dyn_cast<StringRegion>(R)) {
if (StrRegion->getStringLiteral()->getString() == "/") {
C.addTransition(State->set<ChrootState>(JAIL_ENTERED));
return true;
}
}
}

C.addTransition(state);
return false;
}

class ChrootInvocationVisitor final : public BugReporterVisitor {
public:
explicit ChrootInvocationVisitor(const CallDescription &Chroot)
: Chroot{Chroot} {}

PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
BugReporterContext &BRC,
PathSensitiveBugReport &BR) override {
if (Satisfied)
return nullptr;

auto StmtP = N->getLocation().getAs<StmtPoint>();
if (!StmtP)
return nullptr;

const CallExpr *Call = StmtP->getStmtAs<CallExpr>();
if (!Call)
return nullptr;

if (!Chroot.matchesAsWritten(*Call))
return nullptr;

Satisfied = true;
PathDiagnosticLocation Pos(Call, BRC.getSourceManager(),
N->getLocationContext());
return std::make_shared<PathDiagnosticEventPiece>(Pos, "chroot called here",
/*addPosRange=*/true);
}

void Profile(llvm::FoldingSetNodeID &ID) const override {
static bool Tag;
ID.AddPointer(&Tag);
}

private:
const CallDescription &Chroot;
bool Satisfied = false;
};

// Check the jail state before any function call except chroot and chdir().
void ChrootChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
// Ignore chroot and chdir.
if (matchesAny(Call, Chroot, Chdir))
return;

// If jail state is ROOT_CHANGED, generate BugReport.
void *const* k = C.getState()->FindGDM(ChrootChecker::getTag());
if (k)
if (isRootChanged((intptr_t) *k))
if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
constexpr llvm::StringLiteral Msg =
"No call of chdir(\"/\") immediately after chroot";
C.emitReport(
std::make_unique<PathSensitiveBugReport>(BT_BreakJail, Msg, N));
}
}
// If jail state is not ROOT_CHANGED just return.
if (C.getState()->get<ChrootState>() != ROOT_CHANGED)
return;

void ento::registerChrootChecker(CheckerManager &mgr) {
mgr.registerChecker<ChrootChecker>();
// Generate bug report.
ExplodedNode *Err =
C.generateNonFatalErrorNode(C.getState(), C.getPredecessor());
if (!Err)
return;

auto R = std::make_unique<PathSensitiveBugReport>(
BreakJailBug, R"(No call of chdir("/") immediately after chroot)", Err);
R->addVisitor<ChrootInvocationVisitor>(Chroot);
C.emitReport(std::move(R));
}

bool ento::shouldRegisterChrootChecker(const CheckerManager &mgr) {
return true;
} // namespace

void ento::registerChrootChecker(CheckerManager &Mgr) {
Mgr.registerChecker<ChrootChecker>();
}

bool ento::shouldRegisterChrootChecker(const CheckerManager &) { return true; }
Loading