279 changes: 279 additions & 0 deletions clang/include/clang/Basic/arm_sme.td

Large diffs are not rendered by default.

139 changes: 68 additions & 71 deletions clang/include/clang/Basic/arm_sve.td

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -5308,7 +5308,8 @@ def rewrite_objc : Flag<["-"], "rewrite-objc">, Flags<[NoXarchOption]>,
def rewrite_legacy_objc : Flag<["-"], "rewrite-legacy-objc">,
Flags<[NoXarchOption]>,
HelpText<"Rewrite Legacy Objective-C source to C++">;
def rdynamic : Flag<["-"], "rdynamic">, Group<Link_Group>;
def rdynamic : Flag<["-"], "rdynamic">, Group<Link_Group>,
Visibility<[ClangOption, FlangOption]>;
def resource_dir : Separate<["-"], "resource-dir">,
Flags<[NoXarchOption, HelpHidden]>,
Visibility<[ClangOption, CC1Option, CLOption, DXCOption]>,
Expand Down Expand Up @@ -6999,6 +7000,8 @@ def msign_return_address_key_EQ : Joined<["-"], "msign-return-address-key=">,
Values<"a_key,b_key">;
def mbranch_target_enforce : Flag<["-"], "mbranch-target-enforce">,
MarshallingInfoFlag<LangOpts<"BranchTargetEnforcement">>;
def mbranch_protection_pauth_lr : Flag<["-"], "mbranch-protection-pauth-lr">,
MarshallingInfoFlag<LangOpts<"BranchProtectionPAuthLR">>;
def fno_dllexport_inlines : Flag<["-"], "fno-dllexport-inlines">,
MarshallingInfoNegativeFlag<LangOpts<"DllExportInlines">>;
def cfguard_no_checks : Flag<["-"], "cfguard-no-checks">,
Expand Down
33 changes: 1 addition & 32 deletions clang/lib/Analysis/FlowSensitive/RecordOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,19 +66,8 @@ void clang::dataflow::copyRecord(RecordStorageLocation &Src,
}
}

RecordValue *SrcVal = Env.get<RecordValue>(Src);
RecordValue *DstVal = Env.get<RecordValue>(Dst);

DstVal = &Env.create<RecordValue>(Dst);
RecordValue *DstVal = &Env.create<RecordValue>(Dst);
Env.setValue(Dst, *DstVal);

if (SrcVal == nullptr)
return;

for (const auto &[Name, Value] : SrcVal->properties()) {
if (Value != nullptr)
DstVal->setProperty(Name, *Value);
}
}

bool clang::dataflow::recordsEqual(const RecordStorageLocation &Loc1,
Expand Down Expand Up @@ -125,25 +114,5 @@ bool clang::dataflow::recordsEqual(const RecordStorageLocation &Loc1,
}
}

llvm::StringMap<Value *> Props1, Props2;

if (RecordValue *Val1 = Env1.get<RecordValue>(Loc1))
for (const auto &[Name, Value] : Val1->properties())
Props1[Name] = Value;
if (RecordValue *Val2 = Env2.get<RecordValue>(Loc2))
for (const auto &[Name, Value] : Val2->properties())
Props2[Name] = Value;

if (Props1.size() != Props2.size())
return false;

for (const auto &[Name, Value] : Props1) {
auto It = Props2.find(Name);
if (It == Props2.end())
return false;
if (Value != It->second)
return false;
}

return true;
}
1 change: 1 addition & 0 deletions clang/lib/Basic/Targets/AArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ bool AArch64TargetInfo::validateBranchProtection(StringRef Spec, StringRef,
BPI.SignKey = LangOptions::SignReturnAddressKeyKind::BKey;

BPI.BranchTargetEnforcement = PBP.BranchTargetEnforcement;
BPI.BranchProtectionPAuthLR = PBP.BranchProtectionPAuthLR;
return true;
}

Expand Down
1 change: 1 addition & 0 deletions clang/lib/Basic/Targets/ARM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,7 @@ bool ARMTargetInfo::validateBranchProtection(StringRef Spec, StringRef Arch,
BPI.SignKey = LangOptions::SignReturnAddressKeyKind::AKey;

BPI.BranchTargetEnforcement = PBP.BranchTargetEnforcement;
BPI.BranchProtectionPAuthLR = PBP.BranchProtectionPAuthLR;
return true;
}

Expand Down
27 changes: 27 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10318,6 +10318,30 @@ Value *CodeGenFunction::EmitAArch64SVEBuiltinExpr(unsigned BuiltinID,
return nullptr;
}

static void swapCommutativeSMEOperands(unsigned BuiltinID,
SmallVectorImpl<Value *> &Ops) {
unsigned MultiVec;
switch (BuiltinID) {
default:
return;
case SME::BI__builtin_sme_svsumla_za32_s8_vg4x1:
MultiVec = 1;
break;
case SME::BI__builtin_sme_svsumla_za32_s8_vg4x2:
case SME::BI__builtin_sme_svsudot_za32_s8_vg1x2:
MultiVec = 2;
break;
case SME::BI__builtin_sme_svsudot_za32_s8_vg1x4:
case SME::BI__builtin_sme_svsumla_za32_s8_vg4x4:
MultiVec = 4;
break;
}

if (MultiVec > 0)
for (unsigned I = 0; I < MultiVec; ++I)
std::swap(Ops[I + 1], Ops[I + 1 + MultiVec]);
}

Value *CodeGenFunction::EmitAArch64SMEBuiltinExpr(unsigned BuiltinID,
const CallExpr *E) {
auto *Builtin = findARMVectorIntrinsicInMap(AArch64SMEIntrinsicMap, BuiltinID,
Expand All @@ -10340,6 +10364,9 @@ Value *CodeGenFunction::EmitAArch64SMEBuiltinExpr(unsigned BuiltinID,
BuiltinID == SME::BI__builtin_sme_svstr_za)
return EmitSMELdrStr(TypeFlags, Ops, Builtin->LLVMIntrinsic);

// Handle builtins which require their multi-vector operands to be swapped
swapCommutativeSMEOperands(BuiltinID, Ops);

// Should not happen!
if (Builtin->LLVMIntrinsic == 0)
return nullptr;
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,9 @@ void CodeGenModule::Release() {
if (LangOpts.BranchTargetEnforcement)
getModule().addModuleFlag(llvm::Module::Min, "branch-target-enforcement",
1);
if (LangOpts.BranchProtectionPAuthLR)
getModule().addModuleFlag(llvm::Module::Min, "branch-protection-pauth-lr",
1);
if (LangOpts.hasSignReturnAddress())
getModule().addModuleFlag(llvm::Module::Min, "sign-return-address", 1);
if (LangOpts.isSignReturnAddressScopeAll())
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CodeGen/Targets/AArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ class AArch64TargetCodeGenInfo : public TargetCodeGenInfo {

Fn->addFnAttr("branch-target-enforcement",
BPI.BranchTargetEnforcement ? "true" : "false");
Fn->addFnAttr("branch-protection-pauth-lr",
BPI.BranchProtectionPAuthLR ? "true" : "false");
}

bool isScalarizableAsmOperand(CodeGen::CodeGenFunction &CGF,
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Driver/ToolChains/AIX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,12 @@ void aix::Linker::ConstructJob(Compilation &C, const JobAction &JA,
}
}

if (D.IsFlangMode()) {
addFortranRuntimeLibraryPath(ToolChain, Args, CmdArgs);
addFortranRuntimeLibs(ToolChain, Args, CmdArgs);
CmdArgs.push_back("-lm");
CmdArgs.push_back("-lpthread");
}
const char *Exec = Args.MakeArgString(ToolChain.GetLinkerPath());
C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
Exec, CmdArgs, Inputs, Output));
Expand Down
7 changes: 6 additions & 1 deletion clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1497,7 +1497,7 @@ static void CollectARMPACBTIOptions(const ToolChain &TC, const ArgList &Args,
<< Triple.getArchName();

StringRef Scope, Key;
bool IndirectBranches;
bool IndirectBranches, BranchProtectionPAuthLR;

if (A->getOption().matches(options::OPT_msign_return_address_EQ)) {
Scope = A->getValue();
Expand All @@ -1506,6 +1506,7 @@ static void CollectARMPACBTIOptions(const ToolChain &TC, const ArgList &Args,
<< A->getSpelling() << Scope;
Key = "a_key";
IndirectBranches = false;
BranchProtectionPAuthLR = false;
} else {
StringRef DiagMsg;
llvm::ARM::ParsedBranchProtection PBP;
Expand All @@ -1517,6 +1518,7 @@ static void CollectARMPACBTIOptions(const ToolChain &TC, const ArgList &Args,
<< "b-key" << A->getAsString(Args);
Scope = PBP.Scope;
Key = PBP.Key;
BranchProtectionPAuthLR = PBP.BranchProtectionPAuthLR;
IndirectBranches = PBP.BranchTargetEnforcement;
}

Expand All @@ -1525,6 +1527,9 @@ static void CollectARMPACBTIOptions(const ToolChain &TC, const ArgList &Args,
if (!Scope.equals("none"))
CmdArgs.push_back(
Args.MakeArgString(Twine("-msign-return-address-key=") + Key));
if (BranchProtectionPAuthLR)
CmdArgs.push_back(
Args.MakeArgString(Twine("-mbranch-protection-pauth-lr")));
if (IndirectBranches)
CmdArgs.push_back("-mbranch-target-enforce");
}
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/Driver/ToolChains/CommonArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1174,8 +1174,9 @@ static void addFortranMain(const ToolChain &TC, const ArgList &Args,
// The --whole-archive option needs to be part of the link line to make
// sure that the main() function from Fortran_main.a is pulled in by the
// linker. However, it shouldn't be used if it's already active.
// TODO: Find an equivalent of `--whole-archive` for Darwin.
if (!isWholeArchivePresent(Args) && !TC.getTriple().isMacOSX()) {
// TODO: Find an equivalent of `--whole-archive` for Darwin and AIX.
if (!isWholeArchivePresent(Args) && !TC.getTriple().isMacOSX() &&
!TC.getTriple().isOSAIX()) {
CmdArgs.push_back("--whole-archive");
CmdArgs.push_back("-lFortran_main");
CmdArgs.push_back("--no-whole-archive");
Expand Down
50 changes: 26 additions & 24 deletions clang/lib/Serialization/ASTReaderDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,18 @@ void ASTDeclReader::Visit(Decl *D) {

void ASTDeclReader::VisitDecl(Decl *D) {
BitsUnpacker DeclBits(Record.readInt());
auto ModuleOwnership =
(Decl::ModuleOwnershipKind)DeclBits.getNextBits(/*Width=*/3);
D->setReferenced(DeclBits.getNextBit());
D->Used = DeclBits.getNextBit();
IsDeclMarkedUsed |= D->Used;
D->setAccess((AccessSpecifier)DeclBits.getNextBits(/*Width=*/2));
D->setImplicit(DeclBits.getNextBit());
bool HasStandaloneLexicalDC = DeclBits.getNextBit();
bool HasAttrs = DeclBits.getNextBit();
D->setTopLevelDeclInObjCContainer(DeclBits.getNextBit());
D->InvalidDecl = DeclBits.getNextBit();
D->FromASTFile = true;

if (D->isTemplateParameter() || D->isTemplateParameterPack() ||
isa<ParmVarDecl, ObjCTypeParamDecl>(D)) {
Expand Down Expand Up @@ -623,20 +634,6 @@ void ASTDeclReader::VisitDecl(Decl *D) {
}
D->setLocation(ThisDeclLoc);

D->InvalidDecl = DeclBits.getNextBit();
bool HasAttrs = DeclBits.getNextBit();
D->setImplicit(DeclBits.getNextBit());
D->Used = DeclBits.getNextBit();
IsDeclMarkedUsed |= D->Used;
D->setReferenced(DeclBits.getNextBit());
D->setTopLevelDeclInObjCContainer(DeclBits.getNextBit());
D->setAccess((AccessSpecifier)DeclBits.getNextBits(/*Width=*/2));
D->FromASTFile = true;
auto ModuleOwnership =
(Decl::ModuleOwnershipKind)DeclBits.getNextBits(/*Width=*/3);
bool ModulePrivate =
(ModuleOwnership == Decl::ModuleOwnershipKind::ModulePrivate);

if (HasAttrs) {
AttrVec Attrs;
Record.readAttributes(Attrs);
Expand All @@ -647,8 +644,9 @@ void ASTDeclReader::VisitDecl(Decl *D) {

// Determine whether this declaration is part of a (sub)module. If so, it
// may not yet be visible.
bool ModulePrivate =
(ModuleOwnership == Decl::ModuleOwnershipKind::ModulePrivate);
if (unsigned SubmoduleID = readSubmoduleID()) {

switch (ModuleOwnership) {
case Decl::ModuleOwnershipKind::Visible:
ModuleOwnership = Decl::ModuleOwnershipKind::VisibleWhenImported;
Expand Down Expand Up @@ -1065,9 +1063,11 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
// after everything else is read.
BitsUnpacker FunctionDeclBits(Record.readInt());

FD->setCachedLinkage((Linkage)FunctionDeclBits.getNextBits(/*Width=*/3));
FD->setStorageClass((StorageClass)FunctionDeclBits.getNextBits(/*Width=*/3));
FD->setInlineSpecified(FunctionDeclBits.getNextBit());
FD->setImplicitlyInline(FunctionDeclBits.getNextBit());
FD->setHasSkippedBody(FunctionDeclBits.getNextBit());
FD->setVirtualAsWritten(FunctionDeclBits.getNextBit());
// We defer calling `FunctionDecl::setPure()` here as for methods of
// `CXXTemplateSpecializationDecl`s, we may not have connected up the
Expand All @@ -1081,16 +1081,14 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
FD->setDefaulted(FunctionDeclBits.getNextBit());
FD->setExplicitlyDefaulted(FunctionDeclBits.getNextBit());
FD->setIneligibleOrNotSelected(FunctionDeclBits.getNextBit());
FD->setHasImplicitReturnZero(FunctionDeclBits.getNextBit());
FD->setConstexprKind(
(ConstexprSpecKind)FunctionDeclBits.getNextBits(/*Width=*/2));
FD->setUsesSEHTry(FunctionDeclBits.getNextBit());
FD->setHasSkippedBody(FunctionDeclBits.getNextBit());
FD->setHasImplicitReturnZero(FunctionDeclBits.getNextBit());
FD->setIsMultiVersion(FunctionDeclBits.getNextBit());
FD->setLateTemplateParsed(FunctionDeclBits.getNextBit());
FD->setFriendConstraintRefersToEnclosingTemplate(
FunctionDeclBits.getNextBit());
FD->setCachedLinkage((Linkage)FunctionDeclBits.getNextBits(/*Width=*/3));
FD->setUsesSEHTry(FunctionDeclBits.getNextBit());

FD->EndRangeLoc = readSourceLocation();
if (FD->isExplicitlyDefaulted())
Expand Down Expand Up @@ -1597,6 +1595,8 @@ ASTDeclReader::RedeclarableResult ASTDeclReader::VisitVarDeclImpl(VarDecl *VD) {
VisitDeclaratorDecl(VD);

BitsUnpacker VarDeclBits(Record.readInt());
auto VarLinkage = Linkage(VarDeclBits.getNextBits(/*Width=*/3));
bool DefGeneratedInModule = VarDeclBits.getNextBit();
VD->VarDeclBits.SClass = (StorageClass)VarDeclBits.getNextBits(/*Width=*/3);
VD->VarDeclBits.TSCSpec = VarDeclBits.getNextBits(/*Width=*/2);
VD->VarDeclBits.InitStyle = VarDeclBits.getNextBits(/*Width=*/2);
Expand All @@ -1608,17 +1608,20 @@ ASTDeclReader::RedeclarableResult ASTDeclReader::VisitVarDeclImpl(VarDecl *VD) {
VD->NonParmVarDeclBits.ExceptionVar = VarDeclBits.getNextBit();
VD->NonParmVarDeclBits.NRVOVariable = VarDeclBits.getNextBit();
VD->NonParmVarDeclBits.CXXForRangeDecl = VarDeclBits.getNextBit();
VD->NonParmVarDeclBits.ObjCForDecl = VarDeclBits.getNextBit();

VD->NonParmVarDeclBits.IsInline = VarDeclBits.getNextBit();
VD->NonParmVarDeclBits.IsInlineSpecified = VarDeclBits.getNextBit();
VD->NonParmVarDeclBits.IsConstexpr = VarDeclBits.getNextBit();
VD->NonParmVarDeclBits.IsInitCapture = VarDeclBits.getNextBit();
VD->NonParmVarDeclBits.PreviousDeclInSameBlockScope =
VarDeclBits.getNextBit();
VD->NonParmVarDeclBits.ImplicitParamKind =
VarDeclBits.getNextBits(/*Width*/ 3);

VD->NonParmVarDeclBits.EscapingByref = VarDeclBits.getNextBit();
HasDeducedType = VarDeclBits.getNextBit();
VD->NonParmVarDeclBits.ImplicitParamKind =
VarDeclBits.getNextBits(/*Width*/ 3);

VD->NonParmVarDeclBits.ObjCForDecl = VarDeclBits.getNextBit();
}

// If this variable has a deduced type, defer reading that type until we are
Expand All @@ -1630,15 +1633,14 @@ ASTDeclReader::RedeclarableResult ASTDeclReader::VisitVarDeclImpl(VarDecl *VD) {
VD->setType(Reader.GetType(DeferredTypeID));
DeferredTypeID = 0;

auto VarLinkage = Linkage(VarDeclBits.getNextBits(/*Width=*/3));
VD->setCachedLinkage(VarLinkage);

// Reconstruct the one piece of the IdentifierNamespace that we need.
if (VD->getStorageClass() == SC_Extern && VarLinkage != Linkage::None &&
VD->getLexicalDeclContext()->isFunctionOrMethod())
VD->setLocalExternDecl();

if (VarDeclBits.getNextBit()) {
if (DefGeneratedInModule) {
Reader.DefinitionSource[VD] =
Loc.F->Kind == ModuleKind::MK_MainFile ||
Reader.getContext().getLangOpts().BuildingPCHWithObjectFile;
Expand Down
138 changes: 70 additions & 68 deletions clang/lib/Serialization/ASTReaderStmt.cpp

Large diffs are not rendered by default.

261 changes: 149 additions & 112 deletions clang/lib/Serialization/ASTWriterDecl.cpp

Large diffs are not rendered by default.

67 changes: 35 additions & 32 deletions clang/lib/Serialization/ASTWriterStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,8 @@ void ASTStmtWriter::VisitNullStmt(NullStmt *S) {
void ASTStmtWriter::VisitCompoundStmt(CompoundStmt *S) {
VisitStmt(S);

CurrentPackingBits.updateBits();
// 20 bits should be enough to store the size of stmts.
CurrentPackingBits.addBits(S->size(), /*Width=*/20);
CurrentPackingBits.addBit(S->hasStoredFPFeatures());
Record.push_back(S->size());
Record.push_back(S->hasStoredFPFeatures());

for (auto *CS : S->body())
Record.AddStmt(CS);
Expand Down Expand Up @@ -675,25 +673,26 @@ void ASTStmtWriter::VisitPredefinedExpr(PredefinedExpr *E) {
void ASTStmtWriter::VisitDeclRefExpr(DeclRefExpr *E) {
VisitExpr(E);

CurrentPackingBits.addBit(E->hasQualifier());
CurrentPackingBits.addBit(E->getDecl() != E->getFoundDecl());
CurrentPackingBits.addBit(E->hasTemplateKWAndArgsInfo());
CurrentPackingBits.updateBits();

CurrentPackingBits.addBit(E->hadMultipleCandidates());
CurrentPackingBits.addBit(E->refersToEnclosingVariableOrCapture());
CurrentPackingBits.addBits(E->isNonOdrUse(), /*Width=*/2);
CurrentPackingBits.addBit(E->isImmediateEscalating());
CurrentPackingBits.addBit(E->getDecl() != E->getFoundDecl());
CurrentPackingBits.addBit(E->hasQualifier());
CurrentPackingBits.addBit(E->hasTemplateKWAndArgsInfo());

if (E->hasTemplateKWAndArgsInfo()) {
unsigned NumTemplateArgs = E->getNumTemplateArgs();
// 12 bits should be sufficient to store the number of template args.
CurrentPackingBits.addBits(NumTemplateArgs, /*Width=*/12);
Record.push_back(NumTemplateArgs);
}

DeclarationName::NameKind nk = (E->getDecl()->getDeclName().getNameKind());

if ((!E->hasTemplateKWAndArgsInfo()) && (!E->hasQualifier()) &&
(E->getDecl() == E->getFoundDecl()) &&
nk == DeclarationName::Identifier) {
nk == DeclarationName::Identifier && E->getObjectKind() == OK_Ordinary) {
AbbrevToUse = Writer.getDeclRefExprAbbrev();
}

Expand Down Expand Up @@ -936,10 +935,10 @@ void ASTStmtWriter::VisitOMPIteratorExpr(OMPIteratorExpr *E) {
void ASTStmtWriter::VisitCallExpr(CallExpr *E) {
VisitExpr(E);

// 13 bits should be sufficient to store the number args;
CurrentPackingBits.addBits(E->getNumArgs(), /*BitsWidth=*/13);
CurrentPackingBits.addBit(E->hasStoredFPFeatures());
Record.push_back(E->getNumArgs());
CurrentPackingBits.updateBits();
CurrentPackingBits.addBit(static_cast<bool>(E->getADLCallKind()));
CurrentPackingBits.addBit(E->hasStoredFPFeatures());

Record.AddSourceLocation(E->getRParenLoc());
Record.AddStmt(E->getCallee());
Expand All @@ -950,7 +949,8 @@ void ASTStmtWriter::VisitCallExpr(CallExpr *E) {
if (E->hasStoredFPFeatures())
Record.push_back(E->getFPFeatures().getAsOpaqueInt());

if (!E->hasStoredFPFeatures() && E->getStmtClass() == Stmt::CallExprClass)
if (!E->hasStoredFPFeatures() && !static_cast<bool>(E->getADLCallKind()) &&
E->getStmtClass() == Stmt::CallExprClass)
AbbrevToUse = Writer.getCallExprAbbrev();

Code = serialization::EXPR_CALL;
Expand Down Expand Up @@ -979,12 +979,11 @@ void ASTStmtWriter::VisitMemberExpr(MemberExpr *E) {

// Write these first for easy access when deserializing, as they affect the
// size of the MemberExpr.

CurrentPackingBits.updateBits();
CurrentPackingBits.addBit(HasQualifier);
CurrentPackingBits.addBit(HasFoundDecl);
CurrentPackingBits.addBit(HasTemplateInfo);
// 12 bits should be enough to store the number of args
CurrentPackingBits.addBits(NumTemplateArgs, /*Width=*/12);
Record.push_back(NumTemplateArgs);

Record.AddStmt(E->getBase());
Record.AddDeclRef(E->getMemberDecl());
Expand Down Expand Up @@ -1041,9 +1040,10 @@ void ASTStmtWriter::VisitCastExpr(CastExpr *E) {
VisitExpr(E);

Record.push_back(E->path_size());
CurrentPackingBits.addBit(E->hasStoredFPFeatures());
CurrentPackingBits.updateBits();
// 7 bits should be enough to store the casting kinds.
CurrentPackingBits.addBits(E->getCastKind(), /*Width=*/7);
CurrentPackingBits.addBit(E->hasStoredFPFeatures());
Record.AddStmt(E->getSubExpr());

for (CastExpr::path_iterator
Expand All @@ -1056,18 +1056,21 @@ void ASTStmtWriter::VisitCastExpr(CastExpr *E) {

void ASTStmtWriter::VisitBinaryOperator(BinaryOperator *E) {
VisitExpr(E);
bool HasFPFeatures = E->hasStoredFPFeatures();

// Write this first for easy access when deserializing, as they affect the
// size of the UnaryOperator.
CurrentPackingBits.addBit(HasFPFeatures);
CurrentPackingBits.updateBits();
CurrentPackingBits.addBits(E->getOpcode(), /*Width=*/6);
bool HasFPFeatures = E->hasStoredFPFeatures();
CurrentPackingBits.addBit(HasFPFeatures);
Record.AddStmt(E->getLHS());
Record.AddStmt(E->getRHS());
Record.AddSourceLocation(E->getOperatorLoc());
if (HasFPFeatures)
Record.push_back(E->getStoredFPFeatures().getAsOpaqueInt());

if (!HasFPFeatures)
if (!HasFPFeatures && E->getValueKind() == VK_PRValue &&
E->getObjectKind() == OK_Ordinary)
AbbrevToUse = Writer.getBinaryOperatorAbbrev();

Code = serialization::EXPR_BINARY_OPERATOR;
Expand All @@ -1078,7 +1081,8 @@ void ASTStmtWriter::VisitCompoundAssignOperator(CompoundAssignOperator *E) {
Record.AddTypeRef(E->getComputationLHSType());
Record.AddTypeRef(E->getComputationResultType());

if (!E->hasStoredFPFeatures())
if (!E->hasStoredFPFeatures() && E->getValueKind() == VK_PRValue &&
E->getObjectKind() == OK_Ordinary)
AbbrevToUse = Writer.getCompoundAssignOperatorAbbrev();

Code = serialization::EXPR_COMPOUND_ASSIGN_OPERATOR;
Expand Down Expand Up @@ -1664,10 +1668,10 @@ void ASTStmtWriter::VisitMSDependentExistsStmt(MSDependentExistsStmt *S) {

void ASTStmtWriter::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E) {
VisitCallExpr(E);
CurrentPackingBits.addBits(E->getOperator(), /*Width=*/6);
Record.push_back(E->getOperator());
Record.AddSourceRange(E->Range);

if (!E->hasStoredFPFeatures())
if (!E->hasStoredFPFeatures() && !static_cast<bool>(E->getADLCallKind()))
AbbrevToUse = Writer.getCXXOperatorCallExprAbbrev();

Code = serialization::EXPR_CXX_OPERATOR_CALL;
Expand All @@ -1676,7 +1680,7 @@ void ASTStmtWriter::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E) {
void ASTStmtWriter::VisitCXXMemberCallExpr(CXXMemberCallExpr *E) {
VisitCallExpr(E);

if (!E->hasStoredFPFeatures())
if (!E->hasStoredFPFeatures() && !static_cast<bool>(E->getADLCallKind()))
AbbrevToUse = Writer.getCXXMemberCallExprAbbrev();

Code = serialization::EXPR_CXX_MEMBER_CALL;
Expand Down Expand Up @@ -1838,6 +1842,7 @@ void ASTStmtWriter::VisitCXXThisExpr(CXXThisExpr *E) {
VisitExpr(E);
Record.AddSourceLocation(E->getLocation());
Record.push_back(E->isImplicit());

Code = serialization::EXPR_CXX_THIS;
}

Expand Down Expand Up @@ -1971,10 +1976,9 @@ void ASTStmtWriter::VisitCXXDependentScopeMemberExpr(

// Don't emit anything here (or if you do you will have to update
// the corresponding deserialization function).

Record.push_back(E->getNumTemplateArgs());
CurrentPackingBits.updateBits();
CurrentPackingBits.addBit(E->hasTemplateKWAndArgsInfo());
// 16 bits should be enough to store the number of template args.
CurrentPackingBits.addBits(E->getNumTemplateArgs(), /*Width=*/16);
CurrentPackingBits.addBit(E->hasFirstQualifierFoundInScope());

if (E->hasTemplateKWAndArgsInfo()) {
Expand Down Expand Up @@ -2041,15 +2045,14 @@ ASTStmtWriter::VisitCXXUnresolvedConstructExpr(CXXUnresolvedConstructExpr *E) {
void ASTStmtWriter::VisitOverloadExpr(OverloadExpr *E) {
VisitExpr(E);

Record.push_back(E->getNumDecls());

CurrentPackingBits.updateBits();
// 12 Bits should enough to store the number of decls.
CurrentPackingBits.addBits(E->getNumDecls(), /*BitWidth=*/12);
CurrentPackingBits.addBit(E->hasTemplateKWAndArgsInfo());
if (E->hasTemplateKWAndArgsInfo()) {
const ASTTemplateKWAndArgsInfo &ArgInfo =
*E->getTrailingASTTemplateKWAndArgsInfo();
// 12 Bits should enough to store the number of template args.
CurrentPackingBits.addBits(ArgInfo.NumTemplateArgs, /*BitWidth=*/12);
Record.push_back(ArgInfo.NumTemplateArgs);
AddTemplateKWAndArgsInfo(ArgInfo, E->getTrailingTemplateArgumentLoc());
}

Expand Down
86 changes: 86 additions & 0 deletions clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,8 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
{&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
{{{"ftell"}, 1},
{&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}},
{{{"fflush"}, 1},
{&StreamChecker::preFflush, &StreamChecker::evalFflush, 0}},
{{{"rewind"}, 1},
{&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}},
{{{"fgetpos"}, 2},
Expand Down Expand Up @@ -360,6 +362,12 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
CheckerContext &C,
const StreamErrorState &ErrorKind) const;

void preFflush(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;

void evalFflush(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;

/// Check that the stream (in StreamVal) is not NULL.
/// If it can only be NULL a fatal error is emitted and nullptr returned.
/// Otherwise the return value is a new state where the stream is constrained
Expand Down Expand Up @@ -1191,6 +1199,84 @@ void StreamChecker::evalSetFeofFerror(const FnDescription *Desc,
C.addTransition(State);
}

void StreamChecker::preFflush(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
SVal StreamVal = getStreamArg(Desc, Call);
std::optional<DefinedSVal> Stream = StreamVal.getAs<DefinedSVal>();
if (!Stream)
return;

ProgramStateRef StateNotNull, StateNull;
std::tie(StateNotNull, StateNull) =
C.getConstraintManager().assumeDual(State, *Stream);
if (StateNotNull && !StateNull)
ensureStreamOpened(StreamVal, C, StateNotNull);
}

void StreamChecker::evalFflush(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
SVal StreamVal = getStreamArg(Desc, Call);
std::optional<DefinedSVal> Stream = StreamVal.getAs<DefinedSVal>();
if (!Stream)
return;

// Skip if the stream can be both NULL and non-NULL.
ProgramStateRef StateNotNull, StateNull;
std::tie(StateNotNull, StateNull) =
C.getConstraintManager().assumeDual(State, *Stream);
if (StateNotNull && StateNull)
return;
if (StateNotNull && !StateNull)
State = StateNotNull;
else
State = StateNull;

const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
if (!CE)
return;

// `fflush` returns EOF on failure, otherwise returns 0.
ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE);
ProgramStateRef StateNotFailed = bindInt(0, State, C, CE);

// Clear error states if `fflush` returns 0, but retain their EOF flags.
auto ClearErrorInNotFailed = [&StateNotFailed, Desc](SymbolRef Sym,
const StreamState *SS) {
if (SS->ErrorState & ErrorFError) {
StreamErrorState NewES =
(SS->ErrorState & ErrorFEof) ? ErrorFEof : ErrorNone;
StreamState NewSS = StreamState::getOpened(Desc, NewES, false);
StateNotFailed = StateNotFailed->set<StreamMap>(Sym, NewSS);
}
};

if (StateNotNull && !StateNull) {
// Skip if the input stream's state is unknown, open-failed or closed.
if (SymbolRef StreamSym = StreamVal.getAsSymbol()) {
const StreamState *SS = State->get<StreamMap>(StreamSym);
if (SS) {
assert(SS->isOpened() && "Stream is expected to be opened");
ClearErrorInNotFailed(StreamSym, SS);
} else
return;
}
} else {
// Clear error states for all streams.
const StreamMapTy &Map = StateNotFailed->get<StreamMap>();
for (const auto &I : Map) {
SymbolRef Sym = I.first;
const StreamState &SS = I.second;
if (SS.isOpened())
ClearErrorInNotFailed(Sym, &SS);
}
}

C.addTransition(StateNotFailed);
C.addTransition(StateFailed);
}

ProgramStateRef
StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
CheckerContext &C,
Expand Down
1 change: 1 addition & 0 deletions clang/test/Analysis/Inputs/system-header-simulator.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ void clearerr(FILE *stream);
int feof(FILE *stream);
int ferror(FILE *stream);
int fileno(FILE *stream);
int fflush(FILE *stream);

size_t strlen(const char *);

Expand Down
67 changes: 67 additions & 0 deletions clang/test/Analysis/stream-error.c
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,73 @@ void error_fseek_0(void) {
fclose(F);
}

void error_fflush_after_fclose(void) {
FILE *F = tmpfile();
int Ret;
fflush(NULL); // no-warning
if (!F)
return;
if ((Ret = fflush(F)) != 0)
clang_analyzer_eval(Ret == EOF); // expected-warning {{TRUE}}
fclose(F);
fflush(F); // expected-warning {{Stream might be already closed}}
}

void error_fflush_on_open_failed_stream(void) {
FILE *F = tmpfile();
if (!F) {
fflush(F); // no-warning
return;
}
fclose(F);
}

void error_fflush_on_unknown_stream(FILE *F) {
fflush(F); // no-warning
fclose(F); // no-warning
}

void error_fflush_on_non_null_stream_clear_error_states(void) {
FILE *F0 = tmpfile(), *F1 = tmpfile();
// `fflush` clears a non-EOF stream's error state.
if (F0) {
StreamTesterChecker_make_ferror_stream(F0);
if (fflush(F0) == 0) { // no-warning
clang_analyzer_eval(ferror(F0)); // expected-warning {{FALSE}}
clang_analyzer_eval(feof(F0)); // expected-warning {{FALSE}}
}
fclose(F0);
}
// `fflush` clears an EOF stream's error state.
if (F1) {
StreamTesterChecker_make_feof_stream(F1);
if (fflush(F1) == 0) { // no-warning
clang_analyzer_eval(ferror(F1)); // expected-warning {{FALSE}}
clang_analyzer_eval(feof(F1)); // expected-warning {{TRUE}}
}
fclose(F1);
}
}

void error_fflush_on_null_stream_clear_error_states(void) {
FILE *F0 = tmpfile(), *F1 = tmpfile();
// `fflush` clears all stream's error states, while retains their EOF states.
if (F0 && F1) {
StreamTesterChecker_make_ferror_stream(F0);
StreamTesterChecker_make_feof_stream(F1);
if (fflush(NULL) == 0) { // no-warning
clang_analyzer_eval(ferror(F0)); // expected-warning {{FALSE}}
clang_analyzer_eval(feof(F0)); // expected-warning {{FALSE}}
clang_analyzer_eval(ferror(F1)); // expected-warning {{FALSE}}
clang_analyzer_eval(feof(F1)); // expected-warning {{TRUE}}
}
}
if (F0)
fclose(F0);
if (F1)
fclose(F1);
}

void error_indeterminate(void) {
FILE *F = fopen("file", "r+");
if (!F)
Expand Down
28 changes: 28 additions & 0 deletions clang/test/CodeGen/aarch64-branch-protection-attr.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,24 @@ __attribute__ ((target("branch-protection=pac-ret+leaf+bti")))
void btileaf() {}
// CHECK: define{{.*}} void @btileaf() #[[#BTIPACLEAF:]]


__attribute__ ((target("branch-protection=pac-ret+pc")))
void pauthlr() {}
// CHECK: define{{.*}} void @pauthlr() #[[#PAUTHLR:]]

__attribute__ ((target("branch-protection=pac-ret+pc+b-key")))
void pauthlr_bkey() {}
// CHECK: define{{.*}} void @pauthlr_bkey() #[[#PAUTHLR_BKEY:]]

__attribute__ ((target("branch-protection=pac-ret+pc+leaf")))
void pauthlr_leaf() {}
// CHECK: define{{.*}} void @pauthlr_leaf() #[[#PAUTHLR_LEAF:]]

__attribute__ ((target("branch-protection=pac-ret+pc+bti")))
void pauthlr_bti() {}
// CHECK: define{{.*}} void @pauthlr_bti() #[[#PAUTHLR_BTI:]]


// CHECK-DAG: attributes #[[#NONE]] = { {{.*}} "branch-target-enforcement"="false" {{.*}} "sign-return-address"="none"

// CHECK-DAG: attributes #[[#STD]] = { {{.*}} "branch-target-enforcement"="true" {{.*}} "sign-return-address"="non-leaf" "sign-return-address-key"="a_key"
Expand All @@ -61,3 +79,13 @@ void btileaf() {}
// CHECK-DAG: attributes #[[#PACBKEYLEAF]] = { {{.*}} "branch-target-enforcement"="false" {{.*}}"sign-return-address"="all" "sign-return-address-key"="b_key"

// CHECK-DAG: attributes #[[#BTIPACLEAF]] = { {{.*}}"branch-target-enforcement"="true" {{.*}} "sign-return-address"="all" "sign-return-address-key"="a_key"


// CHECK-DAG: attributes #[[#PAUTHLR]] = { {{.*}}"branch-protection-pauth-lr"="true" {{.*}}"branch-target-enforcement"="false" {{.*}}"sign-return-address"="non-leaf" "sign-return-address-key"="a_key"

// CHECK-DAG: attributes #[[#PAUTHLR_BKEY]] = { {{.*}}"branch-protection-pauth-lr"="true" {{.*}}"branch-target-enforcement"="false" {{.*}}"sign-return-address"="non-leaf" "sign-return-address-key"="b_key"

// CHECK-DAG: attributes #[[#PAUTHLR_LEAF]] = { {{.*}}"branch-protection-pauth-lr"="true" {{.*}}"branch-target-enforcement"="false" {{.*}}"sign-return-address"="all" "sign-return-address-key"="a_key"

// CHECK-DAG: attributes #[[#PAUTHLR_BTI]] = { {{.*}}"branch-protection-pauth-lr"="true" {{.*}}"branch-target-enforcement"="true" {{.*}}"sign-return-address"="non-leaf" "sign-return-address-key"="a_key"

297 changes: 297 additions & 0 deletions clang/test/CodeGen/aarch64-sme2-intrinsics/acle_sme2_fp_dots.c

Large diffs are not rendered by default.

282 changes: 282 additions & 0 deletions clang/test/CodeGen/aarch64-sme2-intrinsics/acle_sme2_frint.c

Large diffs are not rendered by default.

1,103 changes: 1,103 additions & 0 deletions clang/test/CodeGen/aarch64-sme2-intrinsics/acle_sme2_int_dots.c

Large diffs are not rendered by default.

292 changes: 292 additions & 0 deletions clang/test/CodeGen/aarch64-sme2-intrinsics/acle_sme2_mla.c

Large diffs are not rendered by default.

696 changes: 696 additions & 0 deletions clang/test/CodeGen/aarch64-sme2-intrinsics/acle_sme2_mlal.c

Large diffs are not rendered by default.

1,790 changes: 1,790 additions & 0 deletions clang/test/CodeGen/aarch64-sme2-intrinsics/acle_sme2_mlall.c

Large diffs are not rendered by default.

292 changes: 292 additions & 0 deletions clang/test/CodeGen/aarch64-sme2-intrinsics/acle_sme2_mls.c

Large diffs are not rendered by default.

696 changes: 696 additions & 0 deletions clang/test/CodeGen/aarch64-sme2-intrinsics/acle_sme2_mlsl.c

Large diffs are not rendered by default.

222 changes: 222 additions & 0 deletions clang/test/CodeGen/aarch64-sme2-intrinsics/acle_sme2_vdot.c

Large diffs are not rendered by default.

99 changes: 54 additions & 45 deletions clang/test/CodeGen/aarch64-sve2p1-intrinsics/acle_sve2p1_ld1.c

Large diffs are not rendered by default.

99 changes: 54 additions & 45 deletions clang/test/CodeGen/aarch64-sve2p1-intrinsics/acle_sve2p1_ldnt1.c

Large diffs are not rendered by default.

98 changes: 53 additions & 45 deletions clang/test/CodeGen/aarch64-sve2p1-intrinsics/acle_sve2p1_st1.c

Large diffs are not rendered by default.

98 changes: 53 additions & 45 deletions clang/test/CodeGen/aarch64-sve2p1-intrinsics/acle_sve2p1_stnt1.c

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions clang/test/Driver/aarch64-pauth-lr.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Check the -cc1 flags for the various forms of -mbranch-protection=pac-ret+pc.

// RUN: %clang -target aarch64-arm-none-eabi -c %s -### -mbranch-protection=pac-ret+pc 2>&1 | FileCheck %s --check-prefixes=PAUTH-LR
// RUN: %clang -target aarch64-arm-none-eabi -c %s -### -mbranch-protection=pac-ret+pc+b-key 2>&1 | FileCheck %s --check-prefixes=PAUTH-LR-B-KEY
// RUN: %clang -target aarch64-arm-none-eabi -c %s -### -mbranch-protection=pac-ret+pc+leaf 2>&1 | FileCheck %s --check-prefixes=PAUTH-LR-LEAF
// RUN: %clang -target aarch64-arm-none-eabi -c %s -### -mbranch-protection=pac-ret+pc+bti 2>&1 | FileCheck %s --check-prefixes=PAUTH-LR-BTI
// RUN: %clang -target aarch64-arm-none-eabi -c %s -### -mbranch-protection=pac-ret+pc+leaf+b-key+bti 2>&1 | FileCheck %s --check-prefixes=PAUTH-LR-LEAF-B-KEY-BTI
// RUN: %clang -target aarch64-arm-none-eabi -c %s -### -mbranch-protection=pac-ret+pc -march=armv9.5-a 2>&1 | FileCheck %s --check-prefixes=PAUTH-LR
// RUN: %clang -target aarch64-arm-none-eabi -c %s -### -mbranch-protection=pac-ret+pc+b-key -march=armv9.5-a 2>&1 | FileCheck %s --check-prefixes=PAUTH-LR-B-KEY
// RUN: %clang -target aarch64-arm-none-eabi -c %s -### -mbranch-protection=pac-ret+pc+leaf -march=armv9.5-a 2>&1 | FileCheck %s --check-prefixes=PAUTH-LR-LEAF
// RUN: %clang -target aarch64-arm-none-eabi -c %s -### -mbranch-protection=pac-ret+pc+bti -march=armv9.5-a 2>&1 | FileCheck %s --check-prefixes=PAUTH-LR-BTI
// RUN: %clang -target aarch64-arm-none-eabi -c %s -### -mbranch-protection=pac-ret+pc+leaf+b-key+bti -march=armv9.5-a 2>&1 | FileCheck %s --check-prefixes=PAUTH-LR-LEAF-B-KEY-BTI

// PAUTH-LR: "-msign-return-address=non-leaf" "-msign-return-address-key=a_key" "-mbranch-protection-pauth-lr"
// PAUTH-LR-B-KEY: "-msign-return-address=non-leaf" "-msign-return-address-key=b_key" "-mbranch-protection-pauth-lr"
// PAUTH-LR-LEAF: "-msign-return-address=all" "-msign-return-address-key=a_key" "-mbranch-protection-pauth-lr"
// PAUTH-LR-BTI: "-msign-return-address=non-leaf" "-msign-return-address-key=a_key" "-mbranch-protection-pauth-lr"
// PAUTH-LR-LEAF-B-KEY-BTI: "-msign-return-address=all" "-msign-return-address-key=b_key" "-mbranch-protection-pauth-lr" "-mbranch-target-enforce"

// NOT-PAUTH-LR: "-mbranch-target-enforce"
// NOT-PAUTH-LR-B-KEY: "-mbranch-target-enforce"
// NOT-PAUTH-LR-LEAF: "-mbranch-target-enforce"
// NOT-PAUTH-LR-BTI: "-mbranch-target-enforce"
7 changes: 7 additions & 0 deletions clang/test/Driver/aarch64-v95a.c
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
// ===== Base v9.5a architecture =====

// RUN: %clang -target aarch64 -march=armv9.5a -### -c %s 2>&1 | FileCheck -check-prefix=GENERICV95A %s
// RUN: %clang -target aarch64 -march=armv9.5-a -### -c %s 2>&1 | FileCheck -check-prefix=GENERICV95A %s
// RUN: %clang -target aarch64 -mlittle-endian -march=armv9.5a -### -c %s 2>&1 | FileCheck -check-prefix=GENERICV95A %s
// RUN: %clang -target aarch64 -mlittle-endian -march=armv9.5-a -### -c %s 2>&1 | FileCheck -check-prefix=GENERICV95A %s
// RUN: %clang -target aarch64_be -mlittle-endian -march=armv9.5a -### -c %s 2>&1 | FileCheck -check-prefix=GENERICV95A %s
// RUN: %clang -target aarch64_be -mlittle-endian -march=armv9.5-a -### -c %s 2>&1 | FileCheck -check-prefix=GENERICV95A %s
// GENERICV95A: "-cc1"{{.*}} "-triple" "aarch64{{.*}}" "-target-cpu" "generic" "-target-feature" "+neon" "-target-feature" "+v9.5a"

// RUN: %clang -target aarch64_be -march=armv9.5a -### -c %s 2>&1 | FileCheck -check-prefix=GENERICV95A-BE %s
// RUN: %clang -target aarch64_be -march=armv9.5-a -### -c %s 2>&1 | FileCheck -check-prefix=GENERICV95A-BE %s
// RUN: %clang -target aarch64 -mbig-endian -march=armv9.5a -### -c %s 2>&1 | FileCheck -check-prefix=GENERICV95A-BE %s
Expand All @@ -18,3 +21,7 @@
// RUN: %clang -target aarch64 -march=armv9.5a+cpa -### -c %s 2>&1 | FileCheck -check-prefix=V95A-CPA %s
// RUN: %clang -target aarch64 -march=armv9.5-a+cpa -### -c %s 2>&1 | FileCheck -check-prefix=V95A-CPA %s
// V95A-CPA: "-cc1"{{.*}} "-triple" "aarch64{{.*}}" "-target-cpu" "generic" "-target-feature" "+neon" "-target-feature" "+v9.5a" "-target-feature" "+cpa"

// RUN: %clang -target aarch64 -march=armv9.5a+pauth-lr -### -c %s 2>&1 | FileCheck -check-prefix=V95A-PAUTHLR %s
// RUN: %clang -target aarch64 -march=armv9.5-a+pauth-lr -### -c %s 2>&1 | FileCheck -check-prefix=V95A-PAUTHLR %s
// V95A-PAUTHLR: "-cc1"{{.*}} "-triple" "aarch64{{.*}}" "-target-cpu" "generic" "-target-feature" "+neon" "-target-feature" "+v9.5a" "-target-feature" "+pauth-lr"
16 changes: 15 additions & 1 deletion clang/test/Driver/modules.m
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,21 @@
// CHECK-MODULE-MAP-FILES: "-fmodule-map-file=foo.map"
// CHECK-MODULE-MAP-FILES: "-fmodule-map-file=bar.map"

// RUN: %clang -fmodules -fbuiltin-module-map -### %s 2>&1 | FileCheck -check-prefix=CHECK-BUILTIN-MODULE-MAP %s
// Verify that the driver propagates -fmodule-name and -fmodule-map-file flags when
// -fmodules-decluse or -fmodules-strict-decluse, as used for layering check.
// RUN: %clang -fmodules-decluse -fmodule-name=foo -c -### %s 2>&1 | FileCheck -check-prefix=CHECK-DECLUSE-PROPAGATE-MODULE-NAME %s
// CHECK-DECLUSE-PROPAGATE-MODULE-NAME: -fmodule-name=foo

// RUN: %clang -fmodules-decluse -fmodule-map-file=foo.map -c -### %s 2>&1 | FileCheck -check-prefix=CHECK-DECLUSE-PROPAGATE-MODULE-MAPS %s
// CHECK-DECLUSE-PROPAGATE-MODULE-MAPS: -fmodule-map-file=foo.map

// RUN: %clang -fmodules-strict-decluse -fmodule-name=foo -c -### %s 2>&1 | FileCheck -check-prefix=CHECK-STRICT-DECLUSE-PROPAGATE-MODULE-NAME %s
// CHECK-STRICT-DECLUSE-PROPAGATE-MODULE-NAME: -fmodule-name=foo

// RUN: %clang -fmodules-strict-decluse -fmodule-map-file=foo.map -c -### %s 2>&1 | FileCheck -check-prefix=CHECK-STRICT-DECLUSE-PROPAGATE-MODULE-MAPS %s
// CHECK-STRICT-DECLUSE-PROPAGATE-MODULE-MAPS: -fmodule-map-file=foo.map

// RUN: %clang -fmodules -fbuiltin-module-map -### %s 2>&1 | FileCheck -check-prefix=CHECK-BUILTIN-MODULE-MAP %s
// CHECK-BUILTIN-MODULE-MAP: "-fmodules"
// CHECK-BUILTIN-MODULE-MAP: "-fmodule-map-file={{.*}}include{{/|\\\\}}module.modulemap"

Expand Down
1 change: 1 addition & 0 deletions clang/test/Preprocessor/aarch64-target-features.c
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,7 @@
// RUN: %clang -target aarch64-none-elf -march=armv9.1-a -x c -E -dM %s -o - | FileCheck --check-prefixes=CHECK-V81-OR-LATER,CHECK-V83-OR-LATER,CHECK-V85-OR-LATER %s
// RUN: %clang -target aarch64-none-elf -march=armv9.2-a -x c -E -dM %s -o - | FileCheck --check-prefixes=CHECK-V81-OR-LATER,CHECK-V83-OR-LATER,CHECK-V85-OR-LATER %s
// RUN: %clang -target aarch64-none-elf -march=armv9.3-a -x c -E -dM %s -o - | FileCheck --check-prefixes=CHECK-V81-OR-LATER,CHECK-V83-OR-LATER,CHECK-V85-OR-LATER %s
// RUN: %clang -target aarch64-none-elf -march=armv9.4-a -x c -E -dM %s -o - | FileCheck --check-prefixes=CHECK-V81-OR-LATER,CHECK-V83-OR-LATER,CHECK-V85-OR-LATER %s
// RUN: %clang -target aarch64-none-elf -march=armv9.5-a -x c -E -dM %s -o - | FileCheck --check-prefixes=CHECK-V81-OR-LATER,CHECK-V83-OR-LATER,CHECK-V85-OR-LATER %s
// CHECK-V81-OR-LATER: __ARM_FEATURE_ATOMICS 1
// CHECK-V85-OR-LATER: __ARM_FEATURE_BTI 1
Expand Down
109 changes: 109 additions & 0 deletions clang/test/Sema/aarch64-sme2-intrinsics/acle_sme2_imm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,3 +241,112 @@ void test_bfmlslb_bad_lane(svfloat32_t zda, svbfloat16_t zn, svbfloat16_t zm) __
svbfmlslb_lane_f32(zda, zn, zm, 8); // expected-error {{argument value 8 is outside the valid range [0, 7]}}
svbfmlslt_lane_f32(zda, zn, zm, 8); // expected-error {{argument value 8 is outside the valid range [0, 7]}}
}

void test_multiply_add_sub_long(uint32_t base, svint8_t s8, svuint8_t u8,
svint16_t s16, svuint16_t u16, svint8x2_t s8x2,
svuint8x2_t u8x2, svint16x2_t s16x2, svuint16x2_t u16x2,
svint8x4_t s8x4, svuint8x4_t u8x4, svint16x4_t s16x4, svuint16x4_t u16x4) __arm_streaming __arm_shared_za {

svmla_lane_za32_s8_vg4x1(base, s8, s8, 16); // expected-error {{argument value 16 is outside the valid range [0, 15]}}
svmla_lane_za32_u8_vg4x1(base, u8, u8, 16); // expected-error {{argument value 16 is outside the valid range [0, 15]}}
svmla_lane_za64_s16_vg4x1(base, s16, s16, 8); // expected-error {{argument value 8 is outside the valid range [0, 7]}}
svmla_lane_za64_u16_vg4x1(base, u16, u16, 8); // expected-error {{argument value 8 is outside the valid range [0, 7]}}

svmla_lane_za32_s8_vg4x2(base, s8x2, s8, 16); // expected-error {{argument value 16 is outside the valid range [0, 15]}}
svmla_lane_za32_u8_vg4x2(base, u8x2, u8, 16); // expected-error {{argument value 16 is outside the valid range [0, 15]}}
svmla_lane_za64_s16_vg4x2(base, s16x2, s16, 8); // expected-error {{argument value 8 is outside the valid range [0, 7]}}
svmla_lane_za64_u16_vg4x2(base, u16x2, u16, 8); // expected-error {{argument value 8 is outside the valid range [0, 7]}}

svmla_lane_za32_s8_vg4x4(base, s8x4, s8, 16); // expected-error {{argument value 16 is outside the valid range [0, 15]}}
svmla_lane_za32_u8_vg4x4(base, u8x4, u8, 16); // expected-error {{argument value 16 is outside the valid range [0, 15]}}
svmla_lane_za64_s16_vg4x4(base, s16x4, s16, 8); // expected-error {{argument value 8 is outside the valid range [0, 7]}}
svmla_lane_za64_u16_vg4x4(base, u16x4, u16, 8); // expected-error {{argument value 8 is outside the valid range [0, 7]}}

svmls_lane_za32_s8_vg4x1(base, s8, s8, 16); // expected-error {{argument value 16 is outside the valid range [0, 15]}}
svmls_lane_za32_u8_vg4x1(base, u8, u8, 16); // expected-error {{argument value 16 is outside the valid range [0, 15]}}
svmls_lane_za64_s16_vg4x1(base, s16, s16, 8); // expected-error {{argument value 8 is outside the valid range [0, 7]}}
svmls_lane_za64_u16_vg4x1(base, u16, u16, 8); // expected-error {{argument value 8 is outside the valid range [0, 7]}}

svmls_lane_za32_s8_vg4x2(base, s8x2, s8, 16); // expected-error {{argument value 16 is outside the valid range [0, 15]}}
svmls_lane_za32_u8_vg4x2(base, u8x2, u8, 16); // expected-error {{argument value 16 is outside the valid range [0, 15]}}
svmls_lane_za64_s16_vg4x2(base, s16x2, s16, 8); // expected-error {{argument value 8 is outside the valid range [0, 7]}}
svmls_lane_za64_u16_vg4x2(base, u16x2, u16, 8); // expected-error {{argument value 8 is outside the valid range [0, 7]}}

svmls_lane_za32_s8_vg4x4(base, s8x4, s8, 16); // expected-error {{argument value 16 is outside the valid range [0, 15]}}
svmls_lane_za32_u8_vg4x4(base, u8x4, u8, 16); // expected-error {{argument value 16 is outside the valid range [0, 15]}}
svmls_lane_za64_s16_vg4x4(base, s16x4, s16, 8); // expected-error {{argument value 8 is outside the valid range [0, 7]}}
svmls_lane_za64_u16_vg4x4(base, u16x4, u16, 8); // expected-error {{argument value 8 is outside the valid range [0, 7]}}

svsumla_lane_za32_s8_vg4x1(base, s8, u8, 16); // expected-error {{argument value 16 is outside the valid range [0, 15]}}
svsumla_lane_za32_s8_vg4x2(base, s8x2, u8, 16); // expected-error {{argument value 16 is outside the valid range [0, 15]}}
svsumla_lane_za32_s8_vg4x4(base, s8x4, u8, 16); // expected-error {{argument value 16 is outside the valid range [0, 15]}}

svusmla_lane_za32_u8_vg4x1(base, u8, s8, 16); // expected-error {{argument value 16 is outside the valid range [0, 15]}}
svusmla_lane_za32_u8_vg4x2(base, u8x2, s8, 16); // expected-error {{argument value 16 is outside the valid range [0, 15]}}
svusmla_lane_za32_u8_vg4x4(base, u8x4, s8, 16); // expected-error {{argument value 16 is outside the valid range [0, 15]}}
}

void test_vertical_dot_product(uint32_t base, svint16x2_t s16x2, svuint16x2_t u16x2,
svint8x4_t s8x4, svuint8x4_t u8x4,
svint16x4_t s16x4, svuint16x4_t u16x4,
svfloat16x2_t f16x2, svbfloat16x2_t bf16x2,
svint16_t s16, svuint16_t u16,
svint8_t s8, svuint8_t u8,
svfloat16_t f16, svbfloat16_t b16) __arm_streaming __arm_shared_za {
// Test lane indices.
svvdot_lane_za32_s16_vg1x2(base, s16x2, s16, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
svvdot_lane_za32_u16_vg1x2(base, u16x2, u16, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
svvdot_lane_za32_s8_vg1x4(base, s8x4, s8, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
svvdot_lane_za32_u8_vg1x4(base, u8x4, u8, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
svvdot_lane_za64_s16_vg1x4(base, s16x4, s16, 2); // expected-error {{argument value 2 is outside the valid range [0, 1]}}
svvdot_lane_za64_u16_vg1x4(base, u16x4, u16, 2); // expected-error {{argument value 2 is outside the valid range [0, 1]}}
svvdot_lane_za32_f16_vg1x2(base, f16x2, f16, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
svvdot_lane_za32_bf16_vg1x2(base, bf16x2, b16, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
svsuvdot_lane_za32_s8_vg1x4(base, s8x4, s8, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
svusvdot_lane_za32_u8_vg1x4(base, u8x4, u8, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
}

void test_fdot_za32_bad_lane(uint32_t slice_base, svfloat16_t z_f16,
svfloat16x2_t z_f16x2, svfloat16x4_t z_f16x4,
svbfloat16_t z_bf16, svbfloat16x2_t z_bf16x2,
svbfloat16x4_t z_bf16x4) __arm_streaming __arm_shared_za {
// 16-bit float
svdot_lane_za32_f16_vg1x2(slice_base, z_f16x2, z_f16, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
svdot_lane_za32_f16_vg1x4(slice_base, z_f16x4, z_f16, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}

// 16-bit binary float
svdot_lane_za32_bf16_vg1x2(slice_base, z_bf16x2, z_bf16, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
svdot_lane_za32_bf16_vg1x4(slice_base, z_bf16x4, z_bf16, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
}

void test_svdot_multi_za32_bad_lane(uint32_t slice_base, svuint16_t z_u16,
svuint16x2_t z_u16x2, svuint16x4_t z_u16x4,
svint16_t z_s16, svint16x2_t z_s16x2,
svint16x4_t z_s16x4, svuint8_t z_u8,
svuint8x2_t z_u8x2, svuint8x4_t z_u8x4,
svint8_t z_s8, svint8x2_t z_s8x2,
svint8x4_t z_s8x4) __arm_streaming __arm_shared_za {
// Multi, indexed (unsigned)
svdot_lane_za32_u16_vg1x2(slice_base, z_u16x2, z_u16, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
svdot_lane_za32_u16_vg1x4(slice_base, z_u16x4, z_u16, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
svdot_lane_za32_u8_vg1x2(slice_base, z_u8x2, z_u8, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
svdot_lane_za32_u8_vg1x4(slice_base, z_u8x4, z_u8, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
svdot_lane_za64_u16_vg1x2(slice_base, z_u16x2, z_u16, 2); // expected-error {{argument value 2 is outside the valid range [0, 1]}}
svdot_lane_za64_u16_vg1x4(slice_base, z_u16x4, z_u16, 2); // expected-error {{argument value 2 is outside the valid range [0, 1]}}

// Multi, indexed (signed)
svdot_lane_za32_s16_vg1x2(slice_base, z_s16x2, z_s16, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
svdot_lane_za32_s16_vg1x4(slice_base, z_s16x4, z_s16, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
svdot_lane_za32_s8_vg1x2(slice_base, z_s8x2, z_s8, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
svdot_lane_za32_s8_vg1x4(slice_base, z_s8x4, z_s8, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
svdot_lane_za64_s16_vg1x2(slice_base, z_s16x2, z_s16, 2); // expected-error {{argument value 2 is outside the valid range [0, 1]}}
svdot_lane_za64_s16_vg1x4(slice_base, z_s16x4, z_s16, 2); // expected-error {{argument value 2 is outside the valid range [0, 1]}}

// Multi, indexed (unsigned by signed)
svusdot_lane_za32_u8_vg1x2(slice_base, z_u8x2, z_s8, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
svusdot_lane_za32_u8_vg1x4(slice_base, z_u8x4, z_s8, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}

// Multi, indexed (unsigned by signed)
svsudot_lane_za32_s8_vg1x2(slice_base, z_s8x2, z_u8, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
svsudot_lane_za32_s8_vg1x4(slice_base, z_s8x4, z_u8, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
}
37 changes: 0 additions & 37 deletions clang/unittests/Analysis/FlowSensitive/RecordOpsTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,6 @@ TEST(RecordOpsTest, CopyRecord) {
auto *S2Val = cast<RecordValue>(Env.getValue(S2));
EXPECT_NE(S1Val, S2Val);

S1Val->setProperty("prop", Env.getBoolLiteralValue(true));

copyRecord(S1, S2, Env);

EXPECT_EQ(getFieldValue(&S1, *OuterIntDecl, Env),
Expand All @@ -104,8 +102,6 @@ TEST(RecordOpsTest, CopyRecord) {
S1Val = cast<RecordValue>(Env.getValue(S1));
S2Val = cast<RecordValue>(Env.getValue(S2));
EXPECT_NE(S1Val, S2Val);

EXPECT_EQ(S2Val->getProperty("prop"), &Env.getBoolLiteralValue(true));
});
}

Expand Down Expand Up @@ -150,9 +146,6 @@ TEST(RecordOpsTest, RecordsEqual) {
Env.setValue(S1.getSyntheticField("synth_int"),
Env.create<IntegerValue>());

cast<RecordValue>(Env.getValue(S1))
->setProperty("prop", Env.getBoolLiteralValue(true));

// Strategy: Create two equal records, then verify each of the various
// ways in which records can differ causes recordsEqual to return false.
// changes we can make to the record.
Expand Down Expand Up @@ -202,36 +195,6 @@ TEST(RecordOpsTest, RecordsEqual) {
EXPECT_FALSE(recordsEqual(S1, S2, Env));
copyRecord(S1, S2, Env);
EXPECT_TRUE(recordsEqual(S1, S2, Env));

// S1 and S2 have the same property with different values.
cast<RecordValue>(Env.getValue(S2))
->setProperty("prop", Env.getBoolLiteralValue(false));
EXPECT_FALSE(recordsEqual(S1, S2, Env));
copyRecord(S1, S2, Env);
EXPECT_TRUE(recordsEqual(S1, S2, Env));

// S1 has a property that S2 doesn't have.
cast<RecordValue>(Env.getValue(S1))
->setProperty("other_prop", Env.getBoolLiteralValue(false));
EXPECT_FALSE(recordsEqual(S1, S2, Env));
// We modified S1 this time, so need to copy back the other way.
copyRecord(S2, S1, Env);
EXPECT_TRUE(recordsEqual(S1, S2, Env));

// S2 has a property that S1 doesn't have.
cast<RecordValue>(Env.getValue(S2))
->setProperty("other_prop", Env.getBoolLiteralValue(false));
EXPECT_FALSE(recordsEqual(S1, S2, Env));
copyRecord(S1, S2, Env);
EXPECT_TRUE(recordsEqual(S1, S2, Env));

// S1 and S2 have the same number of properties, but with different
// names.
cast<RecordValue>(Env.getValue(S1))
->setProperty("prop1", Env.getBoolLiteralValue(false));
cast<RecordValue>(Env.getValue(S2))
->setProperty("prop2", Env.getBoolLiteralValue(false));
EXPECT_FALSE(recordsEqual(S1, S2, Env));
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -623,11 +623,11 @@ TEST_F(JoinFlowConditionsTest, JoinDistinctButProvablyEquivalentValues) {
});
}

class OptionalIntAnalysis final
: public DataflowAnalysis<OptionalIntAnalysis, NoopLattice> {
class NullPointerAnalysis final
: public DataflowAnalysis<NullPointerAnalysis, NoopLattice> {
public:
explicit OptionalIntAnalysis(ASTContext &Context)
: DataflowAnalysis<OptionalIntAnalysis, NoopLattice>(Context) {}
explicit NullPointerAnalysis(ASTContext &Context)
: DataflowAnalysis<NullPointerAnalysis, NoopLattice>(Context) {}

static NoopLattice initialElement() { return {}; }

Expand All @@ -636,40 +636,37 @@ class OptionalIntAnalysis final
if (!CS)
return;
const Stmt *S = CS->getStmt();
auto OptionalIntRecordDecl = recordDecl(hasName("OptionalInt"));
auto HasOptionalIntType = hasType(OptionalIntRecordDecl);

SmallVector<BoundNodes, 1> Matches = match(
stmt(anyOf(cxxConstructExpr(HasOptionalIntType).bind("construct"),
cxxOperatorCallExpr(
callee(cxxMethodDecl(ofClass(OptionalIntRecordDecl))))
.bind("operator"))),
*S, getASTContext());
if (const auto *E = selectFirst<CXXConstructExpr>(
"construct", Matches)) {
cast<RecordValue>(Env.getValue(*E))
->setProperty("has_value", Env.getBoolLiteralValue(false));
} else if (const auto *E =
selectFirst<CXXOperatorCallExpr>("operator", Matches)) {
assert(E->getNumArgs() > 0);
auto *Object = E->getArg(0);
assert(Object != nullptr);

refreshRecordValue(*Object, Env)
.setProperty("has_value", Env.getBoolLiteralValue(true));
const Expr *E = dyn_cast<Expr>(S);
if (!E)
return;

if (!E->getType()->isPointerType())
return;

// Make sure we have a `PointerValue` for `E`.
auto *PtrVal = cast_or_null<PointerValue>(Env.getValue(*E));
if (PtrVal == nullptr) {
PtrVal = cast<PointerValue>(Env.createValue(E->getType()));
Env.setValue(*E, *PtrVal);
}

if (auto *Cast = dyn_cast<ImplicitCastExpr>(E);
Cast && Cast->getCastKind() == CK_NullToPointer)
PtrVal->setProperty("is_null", Env.getBoolLiteralValue(true));
else if (auto *Op = dyn_cast<UnaryOperator>(E);
Op && Op->getOpcode() == UO_AddrOf)
PtrVal->setProperty("is_null", Env.getBoolLiteralValue(false));
}

ComparisonResult compare(QualType Type, const Value &Val1,
const Environment &Env1, const Value &Val2,
const Environment &Env2) override {
// Nothing to say about a value that does not model an `OptionalInt`.
if (!Type->isRecordType() ||
Type->getAsCXXRecordDecl()->getQualifiedNameAsString() != "OptionalInt")
// Nothing to say about a value that is not a pointer.
if (!Type->isPointerType())
return ComparisonResult::Unknown;

auto *Prop1 = Val1.getProperty("has_value");
auto *Prop2 = Val2.getProperty("has_value");
auto *Prop1 = Val1.getProperty("is_null");
auto *Prop2 = Val2.getProperty("is_null");
assert(Prop1 != nullptr && Prop2 != nullptr);
return areEquivalentValues(*Prop1, *Prop2) ? ComparisonResult::Same
: ComparisonResult::Different;
Expand All @@ -678,23 +675,22 @@ class OptionalIntAnalysis final
bool merge(QualType Type, const Value &Val1, const Environment &Env1,
const Value &Val2, const Environment &Env2, Value &MergedVal,
Environment &MergedEnv) override {
// Nothing to say about a value that does not model an `OptionalInt`.
if (!Type->isRecordType() ||
Type->getAsCXXRecordDecl()->getQualifiedNameAsString() != "OptionalInt")
// Nothing to say about a value that is not a pointer.
if (!Type->isPointerType())
return false;

auto *HasValue1 = cast_or_null<BoolValue>(Val1.getProperty("has_value"));
if (HasValue1 == nullptr)
auto *IsNull1 = cast_or_null<BoolValue>(Val1.getProperty("is_null"));
if (IsNull1 == nullptr)
return false;

auto *HasValue2 = cast_or_null<BoolValue>(Val2.getProperty("has_value"));
if (HasValue2 == nullptr)
auto *IsNull2 = cast_or_null<BoolValue>(Val2.getProperty("is_null"));
if (IsNull2 == nullptr)
return false;

if (HasValue1 == HasValue2)
MergedVal.setProperty("has_value", *HasValue1);
if (IsNull1 == IsNull2)
MergedVal.setProperty("is_null", *IsNull1);
else
MergedVal.setProperty("has_value", MergedEnv.makeTopBoolValue());
MergedVal.setProperty("is_null", MergedEnv.makeTopBoolValue());
return true;
}
};
Expand All @@ -703,23 +699,14 @@ class WideningTest : public Test {
protected:
template <typename Matcher>
void runDataflow(llvm::StringRef Code, Matcher Match) {
tooling::FileContentMappings FilesContents;
FilesContents.push_back(
std::make_pair<std::string, std::string>("widening_test_defs.h", R"(
struct OptionalInt {
OptionalInt() = default;
OptionalInt& operator=(int);
};
)"));
ASSERT_THAT_ERROR(
checkDataflow<OptionalIntAnalysis>(
AnalysisInputs<OptionalIntAnalysis>(
checkDataflow<NullPointerAnalysis>(
AnalysisInputs<NullPointerAnalysis>(
Code, ast_matchers::hasName("target"),
[](ASTContext &Context, Environment &Env) {
return OptionalIntAnalysis(Context);
return NullPointerAnalysis(Context);
})
.withASTBuildArgs({"-fsyntax-only", "-std=c++17"})
.withASTBuildVirtualMappedFiles(std::move(FilesContents)),
.withASTBuildArgs({"-fsyntax-only", "-std=c++17"}),
/*VerifyResults=*/[&Match](const llvm::StringMap<
DataflowAnalysisState<NoopLattice>>
&Results,
Expand All @@ -731,13 +718,12 @@ class WideningTest : public Test {

TEST_F(WideningTest, JoinDistinctValuesWithDistinctProperties) {
std::string Code = R"(
#include "widening_test_defs.h"

void target(bool Cond) {
OptionalInt Foo;
int *Foo = nullptr;
int i = 0;
/*[[p1]]*/
if (Cond) {
Foo = 1;
Foo = &i;
/*[[p2]]*/
}
(void)0;
Expand All @@ -760,27 +746,27 @@ TEST_F(WideningTest, JoinDistinctValuesWithDistinctProperties) {
return Env.getValue(*FooDecl);
};

EXPECT_EQ(GetFooValue(Env1)->getProperty("has_value"),
&Env1.getBoolLiteralValue(false));
EXPECT_EQ(GetFooValue(Env2)->getProperty("has_value"),
&Env2.getBoolLiteralValue(true));
EXPECT_EQ(GetFooValue(Env1)->getProperty("is_null"),
&Env1.getBoolLiteralValue(true));
EXPECT_EQ(GetFooValue(Env2)->getProperty("is_null"),
&Env2.getBoolLiteralValue(false));
EXPECT_TRUE(
isa<TopBoolValue>(GetFooValue(Env3)->getProperty("has_value")));
isa<TopBoolValue>(GetFooValue(Env3)->getProperty("is_null")));
});
}

TEST_F(WideningTest, JoinDistinctValuesWithSameProperties) {
std::string Code = R"(
#include "widening_test_defs.h"

void target(bool Cond) {
OptionalInt Foo;
int *Foo = nullptr;
int i1 = 0;
int i2 = 0;
/*[[p1]]*/
if (Cond) {
Foo = 1;
Foo = &i1;
/*[[p2]]*/
} else {
Foo = 2;
Foo = &i2;
/*[[p3]]*/
}
(void)0;
Expand All @@ -805,14 +791,14 @@ TEST_F(WideningTest, JoinDistinctValuesWithSameProperties) {
return Env.getValue(*FooDecl);
};

EXPECT_EQ(GetFooValue(Env1)->getProperty("has_value"),
&Env1.getBoolLiteralValue(false));
EXPECT_EQ(GetFooValue(Env2)->getProperty("has_value"),
&Env2.getBoolLiteralValue(true));
EXPECT_EQ(GetFooValue(Env3)->getProperty("has_value"),
&Env3.getBoolLiteralValue(true));
EXPECT_EQ(GetFooValue(Env4)->getProperty("has_value"),
&Env4.getBoolLiteralValue(true));
EXPECT_EQ(GetFooValue(Env1)->getProperty("is_null"),
&Env1.getBoolLiteralValue(true));
EXPECT_EQ(GetFooValue(Env2)->getProperty("is_null"),
&Env2.getBoolLiteralValue(false));
EXPECT_EQ(GetFooValue(Env3)->getProperty("is_null"),
&Env3.getBoolLiteralValue(false));
EXPECT_EQ(GetFooValue(Env4)->getProperty("is_null"),
&Env4.getBoolLiteralValue(false));
});
}

Expand Down Expand Up @@ -849,13 +835,13 @@ TEST_F(WideningTest, DistinctPointersToTheSameLocationAreEquivalent) {

TEST_F(WideningTest, DistinctValuesWithSamePropertiesAreEquivalent) {
std::string Code = R"(
#include "widening_test_defs.h"

void target(bool Cond) {
OptionalInt Foo;
Foo = 1;
int *Foo;
int i1 = 0;
int i2 = 0;
Foo = &i1;
while (Cond) {
Foo = 2;
Foo = &i2;
}
(void)0;
/*[[p]]*/
Expand All @@ -872,8 +858,8 @@ TEST_F(WideningTest, DistinctValuesWithSamePropertiesAreEquivalent) {
ASSERT_THAT(FooDecl, NotNull());

const auto *FooVal = Env.getValue(*FooDecl);
EXPECT_EQ(FooVal->getProperty("has_value"),
&Env.getBoolLiteralValue(true));
EXPECT_EQ(FooVal->getProperty("is_null"),
&Env.getBoolLiteralValue(false));
});
}

Expand Down
14 changes: 5 additions & 9 deletions compiler-rt/lib/hwasan/hwasan_report.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ static void PrintStackAllocations(const StackAllocationsRingBuffer *sa,
continue;
if (!(local.name && internal_strlen(local.name)) &&
!(local.function_name && internal_strlen(local.name)) &&
!local.decl_file)
!(local.decl_file && internal_strlen(local.decl_file)))
continue;
tag_t obj_tag = base_tag ^ local.tag_offset;
if (obj_tag != addr_tag)
Expand Down Expand Up @@ -251,7 +251,7 @@ static void PrintStackAllocations(const StackAllocationsRingBuffer *sa,
} else {
offset = local_beg - untagged_addr;
whence = "before";
cause = "stack-buffer-underflow";
cause = "stack-buffer-overflow";
}
Decorator d;
Printf("%s", d.Error());
Expand All @@ -263,10 +263,10 @@ static void PrintStackAllocations(const StackAllocationsRingBuffer *sa,
local_end);
Printf("%s", d.Allocation());
StackTracePrinter::GetOrInit()->RenderSourceLocation(
&location, local.decl_file, local.decl_line, 0,
&location, local.decl_file, local.decl_line, /* column= */ 0,
common_flags()->symbolize_vs_style,
common_flags()->strip_path_prefix);
Printf("declared as %s in %s %s\n", local.name, local.function_name,
Printf(" %s in %s %s\n", local.name, local.function_name,
location.data());
location.clear();
Printf("%s\n", d.Default());
Expand Down Expand Up @@ -680,23 +680,19 @@ void BaseReport::PrintHeapOrGlobalCandidate() const {
if (candidate.heap.is_allocated) {
uptr offset;
const char *whence;
const char *cause;
if (candidate.heap.begin <= untagged_addr &&
untagged_addr < candidate.heap.end) {
offset = untagged_addr - candidate.heap.begin;
whence = "inside";
cause = "heap-use-after-free";
} else if (candidate.after) {
offset = untagged_addr - candidate.heap.end;
whence = "after";
cause = "heap-buffer-overflow";
} else {
offset = candidate.heap.begin - untagged_addr;
whence = "before";
cause = "heap-buffer-underflow";
}
Printf("%s", d.Error());
Printf("\nCause: %s\n", cause);
Printf("\nCause: heap-buffer-overflow\n");
Printf("%s", d.Default());
Printf("%s", d.Location());
Printf("%p is located %zd bytes %s a %zd-byte region [%p,%p)\n",
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/test/hwasan/TestCases/Linux/syscalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ int main(int argc, char *argv[]) {

__sanitizer_syscall_pre_recvmsg(0, buf - 1, 0);
// CHECK: HWAddressSanitizer: tag-mismatch on address [[PTR:0x[a-f0-9]+]]
// CHECK: Cause: heap-buffer-underflow
// CHECK: Cause: heap-buffer-overflow
// CHECK: [[PTR]] is located 1 bytes before a 1000-byte region

free(buf);
Expand Down
7 changes: 3 additions & 4 deletions compiler-rt/test/hwasan/TestCases/heap-buffer-overflow.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ int main(int argc, char **argv) {
if (size == 1000000) {
fprintf(stderr, "is a large allocated heap chunk; size: 1003520 offset: %d\n",
offset);
fprintf(stderr, "Cause: heap-buffer-%s\n",
offset == -30 ? "underflow" : "overflow");
fprintf(stderr, "Cause: heap-buffer-overflow\n");
fprintf(stderr, "is located %s a 1000000-byte region\n",
offset == -30 ? "30 bytes before" : "0 bytes after");
return -1;
Expand All @@ -45,11 +44,11 @@ int main(int argc, char **argv) {
// CHECK80: Cause: heap-buffer-overflow
// CHECK80: is located 50 bytes after a 30-byte region
//
// CHECKm30: Cause: heap-buffer-underflow
// CHECKm30: Cause: heap-buffer-overflow
// CHECKm30: is located 30 bytes before a 30-byte region
//
// CHECKMm30: is a large allocated heap chunk; size: 1003520 offset: -30
// CHECKMm30: Cause: heap-buffer-underflow
// CHECKMm30: Cause: heap-buffer-overflow
// CHECKMm30: is located 30 bytes before a 1000000-byte region
//
// CHECKM: is a large allocated heap chunk; size: 1003520 offset: 1000000
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/test/hwasan/TestCases/stack-overflow.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ int main() {
// CHECK: Potentially referenced stack objects:
// CHECK: Cause: stack-buffer-overflow
// CHECK-NEXT: 0x{{.*}} is located 1 bytes after a 64-byte region
// CHECK-NEXT: declared as c in buggy {{.*}}stack-overflow.c:
// CHECK-NEXT: c in buggy {{.*}}stack-overflow.c:
// CHECK: Memory tags around the buggy address

// CHECK: SUMMARY: HWAddressSanitizer: tag-mismatch {{.*}} in buggy
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/test/hwasan/TestCases/stack-uar.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ int main() {
// CHECK: Potentially referenced stack objects:
// CHECK: Cause: use-after-scope
// CHECK-NEXT: 0x{{.*}} is located 0 bytes inside a 2048-byte region
// CHECK-NEXT: declared as {{zzz|yyy}} in buggy {{.*}}stack-uar.c:
// CHECK-NEXT: {{zzz|yyy}} in buggy {{.*}}stack-uar.c:
// CHECK: Memory tags around the buggy address

// NOSYM: Previously allocated frames:
Expand Down
4 changes: 2 additions & 2 deletions compiler-rt/test/hwasan/TestCases/stack-uas.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,14 @@ int main() {
// CHECK: is located in stack of thread
// CHECK: Potentially referenced stack objects:
// CHECK-NEXT: {{zzz|yyy}} in buggy {{.*}}stack-uas.c:
// CHECK-NEXT: Memory tags around the buggy address
// CHECK: Memory tags around the buggy address

// NOSYM: Previously allocated frames:
// NOSYM-NEXT: record_addr:0x{{.*}} record:0x{{.*}} ({{.*}}/stack-uas.c.tmp+0x{{.*}}){{$}}
// NOSYM-NEXT: record_addr:0x{{.*}} record:0x{{.*}} ({{.*}}/stack-uas.c.tmp+0x{{.*}}){{$}}
// NOSYM-NEXT: record_addr:0x{{.*}} record:0x{{.*}} ({{.*}}/stack-uas.c.tmp+0x{{.*}}){{$}}
// NOSYM-NEXT: record_addr:0x{{.*}} record:0x{{.*}} ({{.*}}/stack-uas.c.tmp+0x{{.*}}){{$}}
// NOSYM-NEXT: Memory tags around the buggy address
// NOSYM: Memory tags around the buggy address

// CHECK: SUMMARY: HWAddressSanitizer: tag-mismatch {{.*}} in buggy
}
4 changes: 2 additions & 2 deletions compiler-rt/test/hwasan/TestCases/stack-underflow.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ int main() {
// CHECK: Cause: stack tag-mismatch
// CHECK: is located in stack of thread
// CHECK: Potentially referenced stack objects:
// CHECK: Cause: stack-buffer-underflow
// CHECK: Cause: stack-buffer-overflow
// CHECK-NEXT: 0x{{.*}} is located 2 bytes before a 64-byte region
// CHECK-NEXT: declared as c in buggy {{.*}}stack-underflow.c:
// CHECK-NEXT: c in buggy {{.*}}stack-underflow.c:
// CHECK: Memory tags around the buggy address

// CHECK: SUMMARY: HWAddressSanitizer: tag-mismatch {{.*}} in buggy
Expand Down
27 changes: 27 additions & 0 deletions compiler-rt/test/hwasan/TestCases/strip_path_prefix.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// RUN: %clang_hwasan -O0 %s -o %t && %env_hwasan_opts=strip_path_prefix='"%S/"' not %run %t 2>&1 | FileCheck %s

// Stack histories currently are not recorded on x86.
// XFAIL: target=x86_64{{.*}}

#include <assert.h>
#include <sanitizer/hwasan_interface.h>
#include <stdio.h>

int t;

__attribute__((noinline)) char *buggy() {
char *volatile p;
char zzz = {};
char yyy = {};
p = t ? &yyy : &zzz;
return p;
}

int main() {
char *p = buggy();
return *p;
// CHECK: READ of size 1 at
// CHECK: #0 {{.*}} in main strip_path_prefix.c:[[@LINE-2]]
// CHECK: Potentially referenced stack objects:
// CHECK: zzz in buggy strip_path_prefix.c:[[@LINE-12]]
}
6 changes: 6 additions & 0 deletions flang/docs/Intrinsics.md
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,11 @@ CACHESIZE, EOF, FP_CLASS, INT_PTR_KIND, ISNAN, LOC
MALLOC
```

### Library subroutine
```
CALL GETLOG(USRNAME)
```

## Intrinsic Procedure Name Resolution

When the name of a procedure in a program is the same as the one of an intrinsic
Expand Down Expand Up @@ -754,6 +759,7 @@ This phase currently supports all the intrinsic procedures listed above but the
| Intrinsic subroutines |MVBITS (elemental), CPU_TIME, DATE_AND_TIME, EVENT_QUERY, EXECUTE_COMMAND_LINE, GET_COMMAND, GET_COMMAND_ARGUMENT, GET_ENVIRONMENT_VARIABLE, MOVE_ALLOC, RANDOM_INIT, RANDOM_NUMBER, RANDOM_SEED, SYSTEM_CLOCK |
| Atomic intrinsic subroutines | ATOMIC_ADD |
| Collective intrinsic subroutines | CO_REDUCE |
| Library subroutines | GETLOG|


### Intrinsic Function Folding
Expand Down
4 changes: 4 additions & 0 deletions flang/include/flang/Runtime/extensions.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#define FORTRAN_PROCEDURE_NAME(name) name##_

#include <cstddef>
#include <cstdint>

extern "C" {
Expand All @@ -28,5 +29,8 @@ std::int32_t FORTRAN_PROCEDURE_NAME(iargc)();
void FORTRAN_PROCEDURE_NAME(getarg)(
std::int32_t &n, std::int8_t *arg, std::int64_t length);

// GNU extension subroutine GETLOG(C).
void FORTRAN_PROCEDURE_NAME(getlog)(std::byte *name, std::int64_t length);

} // extern "C"
#endif // FORTRAN_RUNTIME_EXTENSIONS_H_
6 changes: 4 additions & 2 deletions flang/lib/Lower/OpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2197,10 +2197,12 @@ static void genBodyOfTargetDataOp(
if (fir::isa_builtin_cptr_type(refType.getElementType())) {
converter.bindSymbol(*argSymbol, arg);
} else {
// Avoid capture of a reference to a structured binding.
const Fortran::semantics::Symbol *sym = argSymbol;
extVal.match(
[&](const fir::MutableBoxValue &mbv) {
converter.bindSymbol(
*argSymbol,
*sym,
fir::MutableBoxValue(
arg, fir::factory::getNonDeferredLenParams(extVal), {}));
},
Expand Down Expand Up @@ -2489,7 +2491,7 @@ static void genBodyOfTargetOp(
// Bind the symbols to their corresponding block arguments.
for (auto [argIndex, argSymbol] : llvm::enumerate(mapSymbols)) {
const mlir::BlockArgument &arg = region.getArgument(argIndex);
// Avoid capture of reference to a structured binding.
// Avoid capture of a reference to a structured binding.
const Fortran::semantics::Symbol *sym = argSymbol;
fir::ExtendedValue extVal = converter.getSymbolExtendedValue(*sym);
extVal.match(
Expand Down
13 changes: 6 additions & 7 deletions flang/lib/Optimizer/Transforms/SimplifyIntrinsics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1162,14 +1162,11 @@ void SimplifyIntrinsicsPass::simplifyMinMaxlocReduction(

mlir::Operation::operand_range args = call.getArgs();

mlir::SymbolRefAttr callee = call.getCalleeAttr();
mlir::StringRef funcNameBase = callee.getLeafReference().getValue();
bool isDim = funcNameBase.ends_with("Dim");
mlir::Value back = args[isDim ? 7 : 6];
mlir::Value back = args[6];
if (isTrueOrNotConstant(back))
return;

mlir::Value mask = args[isDim ? 6 : 5];
mlir::Value mask = args[5];
mlir::Value maskDef = findMaskDef(mask);

// maskDef is set to NULL when the defining op is not one we accept.
Expand All @@ -1178,8 +1175,10 @@ void SimplifyIntrinsicsPass::simplifyMinMaxlocReduction(
if (maskDef == NULL)
return;

mlir::SymbolRefAttr callee = call.getCalleeAttr();
mlir::StringRef funcNameBase = callee.getLeafReference().getValue();
unsigned rank = getDimCount(args[1]);
if ((isDim && rank != 1) || !(rank > 0))
if (funcNameBase.ends_with("Dim") || !(rank > 0))
return;

fir::FirOpBuilder builder{getSimplificationBuilder(call, kindMap)};
Expand Down Expand Up @@ -1235,7 +1234,7 @@ void SimplifyIntrinsicsPass::simplifyMinMaxlocReduction(
mlir::func::FuncOp newFunc =
getOrCreateFunction(builder, funcName, typeGenerator, bodyGenerator);
builder.create<fir::CallOp>(loc, newFunc,
mlir::ValueRange{args[0], args[1], mask});
mlir::ValueRange{args[0], args[1], args[5]});
call->dropAllReferences();
call->erase();
}
Expand Down
2 changes: 1 addition & 1 deletion flang/lib/Optimizer/Transforms/StackArrays.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ StackArraysAnalysisWrapper::analyseFunction(mlir::Operation *func) {
const LatticePoint *lattice = solver.lookupState<LatticePoint>(op);
// there will be no lattice for an unreachable block
if (lattice)
point.join(*lattice);
(void)point.join(*lattice);
};
func->walk([&](mlir::func::ReturnOp child) { joinOperationLattice(child); });
func->walk([&](fir::UnreachableOp child) { joinOperationLattice(child); });
Expand Down
22 changes: 1 addition & 21 deletions flang/runtime/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "tools.h"
#include "flang/Common/bit-population-count.h"
#include "flang/Common/uint128.h"
#include "flang/Runtime/character.h"
#include "flang/Runtime/cpp-type.h"
#include "flang/Runtime/descriptor.h"
#include <algorithm>
Expand Down Expand Up @@ -464,27 +465,6 @@ static void GeneralCharFuncKind(Descriptor &result, const Descriptor &string,
}
}

template <typename TO, typename FROM>
static void CopyAndPad(
TO *to, const FROM *from, std::size_t toChars, std::size_t fromChars) {
if constexpr (sizeof(TO) != sizeof(FROM)) {
std::size_t copyChars{std::min(toChars, fromChars)};
for (std::size_t j{0}; j < copyChars; ++j) {
to[j] = from[j];
}
for (std::size_t j{copyChars}; j < toChars; ++j) {
to[j] = static_cast<TO>(' ');
}
} else if (toChars <= fromChars) {
std::memcpy(to, from, toChars * sizeof(TO));
} else {
std::memcpy(to, from, fromChars * sizeof(TO));
for (std::size_t j{fromChars}; j < toChars; ++j) {
to[j] = static_cast<TO>(' ');
}
}
}

template <typename CHAR, bool ISMIN>
static void MaxMinHelper(Descriptor &accumulator, const Descriptor &x,
const Terminator &terminator) {
Expand Down
39 changes: 39 additions & 0 deletions flang/runtime/extensions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,29 @@
// extensions that will eventually be implemented in Fortran.

#include "flang/Runtime/extensions.h"
#include "tools.h"
#include "flang/Runtime/command.h"
#include "flang/Runtime/descriptor.h"
#include "flang/Runtime/io-api.h"

#if _REENTRANT || _POSIX_C_SOURCE >= 199506L
// System is posix-compliant and has getlogin_r
#include <unistd.h>
#endif

extern "C" {

namespace Fortran::runtime {

void GetUsernameEnvVar(
const char *envName, std::byte *arg, std::int64_t length) {
Descriptor name{*Descriptor::Create(
1, std::strlen(envName) + 1, const_cast<char *>(envName), 0)};
Descriptor value{*Descriptor::Create(1, length, arg, 0)};

RTNAME(GetEnvVariable)
(name, &value, nullptr, false, nullptr, __FILE__, __LINE__);
}
namespace io {
// SUBROUTINE FLUSH(N)
// FLUSH N
Expand All @@ -37,5 +53,28 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
(void)RTNAME(GetCommandArgument)(
n, &value, nullptr, nullptr, __FILE__, __LINE__);
}

// CALL GETLOG(USRNAME)
void FORTRAN_PROCEDURE_NAME(getlog)(std::byte *arg, std::int64_t length) {
#if _REENTRANT || _POSIX_C_SOURCE >= 199506L
const int nameMaxLen{LOGIN_NAME_MAX + 1};
char str[nameMaxLen];

int error{getlogin_r(str, nameMaxLen)};
if (error == 0) {
// no error: find first \0 in string then pad from there
CopyAndPad(reinterpret_cast<char *>(arg), str, length, std::strlen(str));
} else {
// error occur: get username from environment variable
GetUsernameEnvVar("LOGNAME", arg, length);
}
#elif _WIN32
// Get username from environment to avoid link to Advapi32.lib
GetUsernameEnvVar("USERNAME", arg, length);
#else
GetUsernameEnvVar("LOGNAME", arg, length);
#endif
}

} // namespace Fortran::runtime
} // extern "C"
22 changes: 22 additions & 0 deletions flang/runtime/tools.h
Original file line number Diff line number Diff line change
Expand Up @@ -411,5 +411,27 @@ RT_API_ATTRS void ShallowCopy(const Descriptor &to, const Descriptor &from,
bool toIsContiguous, bool fromIsContiguous);
RT_API_ATTRS void ShallowCopy(const Descriptor &to, const Descriptor &from);

// Defines a utility function for copying and padding characters
template <typename TO, typename FROM>
RT_API_ATTRS void CopyAndPad(
TO *to, const FROM *from, std::size_t toChars, std::size_t fromChars) {
if constexpr (sizeof(TO) != sizeof(FROM)) {
std::size_t copyChars{std::min(toChars, fromChars)};
for (std::size_t j{0}; j < copyChars; ++j) {
to[j] = from[j];
}
for (std::size_t j{copyChars}; j < toChars; ++j) {
to[j] = static_cast<TO>(' ');
}
} else if (toChars <= fromChars) {
std::memcpy(to, from, toChars * sizeof(TO));
} else {
std::memcpy(to, from, std::min(toChars, fromChars) * sizeof(TO));
for (std::size_t j{fromChars}; j < toChars; ++j) {
to[j] = static_cast<TO>(' ');
}
}
}

} // namespace Fortran::runtime
#endif // FORTRAN_RUNTIME_TOOLS_H_
4 changes: 4 additions & 0 deletions flang/test/Driver/dynamic-linker.f90
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@
! RUN: %flang -### --target=x86_64-windows-msvc -rpath /path/to/dir -shared \
! RUN: -static %s 2>&1 | FileCheck \
! RUN: --check-prefixes=MSVC-LINKER-OPTIONS %s
! RUN: %flang -### --target=aarch64-linux-none -rdynamic %s 2>&1 | FileCheck --check-prefixes=RDYNAMIC-LINKER-OPTION %s

! TODO: Could the linker have an extension or a suffix?
! GNU-LINKER-OPTIONS: "{{.*}}ld{{(.exe)?}}"
! GNU-LINKER-OPTIONS-SAME: "-shared"
! GNU-LINKER-OPTIONS-SAME: "-static"
! GNU-LINKER-OPTIONS-SAME: "-rpath" "/path/to/dir"

! RDYNAMIC-LINKER-OPTION: "{{.*}}ld"
! RDYNAMIC-LINKER-OPTION-SAME: "-export-dynamic"

! For MSVC, adding -static does not add any additional linker options.
! MSVC-LINKER-OPTIONS: "{{.*}}link{{(.exe)?}}"
! MSVC-LINKER-OPTIONS-SAME: "-dll"
Expand Down
2 changes: 1 addition & 1 deletion flang/test/Driver/no-duplicate-main.f90
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
! UNSUPPORTED: system-windows, system-darwin
! UNSUPPORTED: system-windows, system-darwin, system-aix

! RUN: %flang -x ir -o %t.c-object -c %S/Inputs/no_duplicate_main.ll
! RUN: %flang -o %t -c %s
Expand Down
5 changes: 4 additions & 1 deletion flang/test/Runtime/no-cpp-dep.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ a C compiler.

REQUIRES: c-compiler

RUN: %cc -std=c99 %s -I%include %libruntime %libdecimal -lm -o /dev/null
RUN: %if system-aix %{ export OBJECT_MODE=64 %}
RUN: %cc -std=c99 %s -I%include %libruntime %libdecimal -lm \
RUN: %if system-aix %{-lpthread %}
RUN: rm a.out
*/

#include "flang/Runtime/entry-names.h"
Expand Down
68 changes: 7 additions & 61 deletions flang/test/Transforms/simplifyintrinsics.fir
Original file line number Diff line number Diff line change
Expand Up @@ -2115,13 +2115,13 @@ func.func @_QPtestminloc_doesntwork1d_back(%arg0: !fir.ref<!fir.array<10xi32>> {
// CHECK-NOT: fir.call @_FortranAMinlocInteger4x1_i32_contract_simplified({{.*}}) fastmath<contract> : (!fir.ref<!fir.box<none>>, !fir.box<none>, !fir.box<none>) -> ()

// -----
// Check Minloc is simplified when DIM arg is set so long as the result is scalar
// Check Minloc is not simplified when DIM arg is set

func.func @_QPtestminloc_1d_dim(%arg0: !fir.ref<!fir.array<10xi32>> {fir.bindc_name = "a"}) -> !fir.array<1xi32> {
func.func @_QPtestminloc_doesntwork1d_dim(%arg0: !fir.ref<!fir.array<10xi32>> {fir.bindc_name = "a"}) -> !fir.array<1xi32> {
%0 = fir.alloca !fir.box<!fir.heap<i32>>
%c10 = arith.constant 10 : index
%c1 = arith.constant 1 : index
%1 = fir.alloca !fir.array<1xi32> {bindc_name = "testminloc_1d_dim", uniq_name = "_QFtestminloc_1d_dimEtestminloc_1d_dim"}
%1 = fir.alloca !fir.array<1xi32> {bindc_name = "testminloc_doesntwork1d_dim", uniq_name = "_QFtestminloc_doesntwork1d_dimEtestminloc_doesntwork1d_dim"}
%2 = fir.shape %c1 : (index) -> !fir.shape<1>
%3 = fir.array_load %1(%2) : (!fir.ref<!fir.array<1xi32>>, !fir.shape<1>) -> !fir.array<1xi32>
%4 = fir.shape %c10 : (index) -> !fir.shape<1>
Expand Down Expand Up @@ -2156,65 +2156,11 @@ func.func @_QPtestminloc_1d_dim(%arg0: !fir.ref<!fir.array<10xi32>> {fir.bindc_n
%21 = fir.load %1 : !fir.ref<!fir.array<1xi32>>
return %21 : !fir.array<1xi32>
}
// CHECK-LABEL: func.func @_QPtestminloc_1d_dim(
// CHECK-LABEL: func.func @_QPtestminloc_doesntwork1d_dim(
// CHECK-SAME: %[[ARR:.*]]: !fir.ref<!fir.array<10xi32>> {fir.bindc_name = "a"}) -> !fir.array<1xi32> {
// CHECK: fir.call @_FortranAMinlocDimx1_i32_contract_simplified({{.*}}) fastmath<contract> : (!fir.ref<!fir.box<none>>, !fir.box<none>, !fir.box<none>) -> ()

// CHECK-LABEL: func.func private @_FortranAMinlocDimx1_i32_contract_simplified(%arg0: !fir.ref<!fir.box<none>>, %arg1: !fir.box<none>, %arg2: !fir.box<none>) attributes {llvm.linkage = #llvm.linkage<linkonce_odr>} {
// CHECK-NEXT: %[[V0:.*]] = fir.alloca i32
// CHECK-NEXT: %c0_i32 = arith.constant 0 : i32
// CHECK-NEXT: %c1 = arith.constant 1 : index
// CHECK-NEXT: %[[V1:.*]] = fir.allocmem !fir.array<1xi32>
// CHECK-NEXT: %[[V2:.*]] = fir.shape %c1 : (index) -> !fir.shape<1>
// CHECK-NEXT: %[[V3:.*]] = fir.embox %[[V1]](%[[V2]]) : (!fir.heap<!fir.array<1xi32>>, !fir.shape<1>) -> !fir.box<!fir.heap<!fir.array<1xi32>>>
// CHECK-NEXT: %c0 = arith.constant 0 : index
// CHECK-NEXT: %[[V4:.*]] = fir.coordinate_of %[[V3]], %c0 : (!fir.box<!fir.heap<!fir.array<1xi32>>>, index) -> !fir.ref<i32>
// CHECK-NEXT: fir.store %c0_i32 to %[[V4]] : !fir.ref<i32>
// CHECK-NEXT: %c0_0 = arith.constant 0 : index
// CHECK-NEXT: %[[V5:.*]] = fir.convert %arg1 : (!fir.box<none>) -> !fir.box<!fir.array<?xi32>>
// CHECK-NEXT: %c1_i32 = arith.constant 1 : i32
// CHECK-NEXT: %c0_i32_1 = arith.constant 0 : i32
// CHECK-NEXT: fir.store %c0_i32_1 to %[[V0]] : !fir.ref<i32>
// CHECK-NEXT: %c2147483647_i32 = arith.constant 2147483647 : i32
// CHECK-NEXT: %c1_2 = arith.constant 1 : index
// CHECK-NEXT: %c0_3 = arith.constant 0 : index
// CHECK-NEXT: %[[V6:.*]]:3 = fir.box_dims %[[V5]], %c0_3 : (!fir.box<!fir.array<?xi32>>, index) -> (index, index, index)
// CHECK-NEXT: %[[V7:.*]] = arith.subi %[[V6]]#1, %c1_2 : index
// CHECK-NEXT: %[[V8:.*]] = fir.do_loop %arg3 = %c0_0 to %[[V7]] step %c1_2 iter_args(%arg4 = %c2147483647_i32) -> (i32) {
// CHECK-NEXT: fir.store %c1_i32 to %[[V0]] : !fir.ref<i32>
// CHECK-NEXT: %[[V12:.*]] = fir.coordinate_of %[[V5]], %arg3 : (!fir.box<!fir.array<?xi32>>, index) -> !fir.ref<i32>
// CHECK-NEXT: %[[V13:.*]] = fir.load %[[V12]] : !fir.ref<i32>
// CHECK-NEXT: %[[V14:.*]] = arith.cmpi slt, %[[V13]], %arg4 : i32
// CHECK-NEXT: %[[V15:.*]] = fir.if %[[V14]] -> (i32) {
// CHECK-NEXT: %c1_i32_4 = arith.constant 1 : i32
// CHECK-NEXT: %c0_5 = arith.constant 0 : index
// CHECK-NEXT: %[[V16:.*]] = fir.coordinate_of %[[V3]], %c0_5 : (!fir.box<!fir.heap<!fir.array<1xi32>>>, index) -> !fir.ref<i32>
// CHECK-NEXT: %[[V17:.*]] = fir.convert %arg3 : (index) -> i32
// CHECK-NEXT: %[[V18:.*]] = arith.addi %[[V17]], %c1_i32_4 : i32
// CHECK-NEXT: fir.store %[[V18]] to %[[V16]] : !fir.ref<i32>
// CHECK-NEXT: fir.result %[[V13]] : i32
// CHECK-NEXT: } else {
// CHECK-NEXT: fir.result %arg4 : i32
// CHECK-NEXT: }
// CHECK-NEXT: fir.result %[[V15]] : i32
// CHECK-NEXT: }
// CHECK-NEXT: %[[V9:.*]] = fir.load %[[V0]] : !fir.ref<i32>
// CHECK-NEXT: %[[V10:.*]] = arith.cmpi eq, %[[V9]], %c1_i32 : i32
// CHECK-NEXT: fir.if %[[V10]] {
// CHECK-NEXT: %c2147483647_i32_4 = arith.constant 2147483647 : i32
// CHECK-NEXT: %[[V12]] = arith.cmpi eq, %c2147483647_i32_4, %[[V8]] : i32
// CHECK-NEXT: fir.if %[[V12]] {
// CHECK-NEXT: %c0_5 = arith.constant 0 : index
// CHECK-NEXT: %[[V13]] = fir.coordinate_of %[[V3]], %c0_5 : (!fir.box<!fir.heap<!fir.array<1xi32>>>, index) -> !fir.ref<i32>
// CHECK-NEXT: fir.store %c1_i32 to %[[V13]] : !fir.ref<i32>
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: %[[V11:.*]] = fir.convert %arg0 : (!fir.ref<!fir.box<none>>) -> !fir.ref<!fir.box<!fir.heap<!fir.array<1xi32>>>>
// CHECK-NEXT: fir.store %[[V3]] to %[[V11]] : !fir.ref<!fir.box<!fir.heap<!fir.array<1xi32>>>>
// CHECK-NEXT: return
// CHECK-NEXT: }


// CHECK-NOT: fir.call @_FortranAMinlocDimx1_i32_contract_simplified({{.*}}) fastmath<contract> : (!fir.ref<!fir.box<none>>, !fir.box<none>, !fir.box<none>) -> ()
// CHECK: fir.call @_FortranAMinlocDim({{.*}}) fastmath<contract> : (!fir.ref<!fir.box<none>>, !fir.box<none>, i32, i32, !fir.ref<i8>, i32, !fir.box<none>, i1) -> none
// CHECK-NOT: fir.call @_FortranAMinlocDimx1_i32_contract_simplified({{.*}}) fastmath<contract> : (!fir.ref<!fir.box<none>>, !fir.box<none>, !fir.box<none>) -> ()

// -----
// Check Minloc is not simplified when dimension of inputArr is unknown
Expand Down
83 changes: 83 additions & 0 deletions flang/unittests/Runtime/CommandTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,15 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "flang/Runtime/descriptor.h"
#include "flang/Runtime/extensions.h"
#include "flang/Runtime/main.h"
#include <cstddef>
#include <cstdlib>

#if _REENTRANT || _POSIX_C_SOURCE >= 199506L
#include <limits.h> // LOGIN_NAME_MAX used in getlog test
#endif

using namespace Fortran::runtime;

template <std::size_t n = 64>
Expand Down Expand Up @@ -59,6 +65,13 @@ class CommandFixture : public ::testing::Test {
return res;
}

void CheckCharEqStr(const char *value, const std::string &expected) const {
ASSERT_NE(value, nullptr);
EXPECT_EQ(std::strncmp(value, expected.c_str(), expected.size()), 0)
<< "expected: " << expected << "\n"
<< "value: " << value;
}

void CheckDescriptorEqStr(
const Descriptor *value, const std::string &expected) const {
ASSERT_NE(value, nullptr);
Expand Down Expand Up @@ -397,6 +410,11 @@ class EnvironmentVariables : public CommandFixture {
protected:
EnvironmentVariables() : CommandFixture(0, nullptr) {
SetEnv("NAME", "VALUE");
#ifdef _WIN32
SetEnv("USERNAME", "loginName");
#else
SetEnv("LOGNAME", "loginName");
#endif
SetEnv("EMPTY", "");
}

Expand Down Expand Up @@ -494,3 +512,68 @@ TEST_F(EnvironmentVariables, ErrMsgTooShort) {
1);
CheckDescriptorEqStr(errMsg.get(), "Mis");
}

// username first char must not be null
TEST_F(EnvironmentVariables, GetlogGetName) {
const int charLen{3};
char input[charLen]{"\0\0"};

FORTRAN_PROCEDURE_NAME(getlog)
(reinterpret_cast<std::byte *>(input), charLen);

EXPECT_NE(input[0], '\0');
}

#if _REENTRANT || _POSIX_C_SOURCE >= 199506L
TEST_F(EnvironmentVariables, GetlogPadSpace) {
// guarantee 1 char longer than max, last char should be pad space
const int charLen{LOGIN_NAME_MAX + 2};
char input[charLen];

FORTRAN_PROCEDURE_NAME(getlog)
(reinterpret_cast<std::byte *>(input), charLen);

EXPECT_EQ(input[charLen - 1], ' ');
}
#endif

#ifdef _WIN32 // Test ability to get name from environment variable
TEST_F(EnvironmentVariables, GetlogEnvGetName) {
if (EnableFineGrainedTests()) {
ASSERT_NE(std::getenv("USERNAME"), nullptr)
<< "Environment variable USERNAME does not exist";

char input[]{"XXXXXXXXX"};
FORTRAN_PROCEDURE_NAME(getlog)
(reinterpret_cast<std::byte *>(input), sizeof(input));

CheckCharEqStr(input, "loginName");
}
}

TEST_F(EnvironmentVariables, GetlogEnvBufferShort) {
if (EnableFineGrainedTests()) {
ASSERT_NE(std::getenv("USERNAME"), nullptr)
<< "Environment variable USERNAME does not exist";

char input[]{"XXXXXX"};
FORTRAN_PROCEDURE_NAME(getlog)
(reinterpret_cast<std::byte *>(input), sizeof(input));

CheckCharEqStr(input, "loginN");
}
}

TEST_F(EnvironmentVariables, GetlogEnvPadSpace) {
if (EnableFineGrainedTests()) {
ASSERT_NE(std::getenv("USERNAME"), nullptr)
<< "Environment variable USERNAME does not exist";

char input[]{"XXXXXXXXXX"};
FORTRAN_PROCEDURE_NAME(getlog)
(reinterpret_cast<std::byte *>(input), sizeof(input));

CheckCharEqStr(input, "loginName ");
}
}
#endif
8 changes: 4 additions & 4 deletions libc/src/__support/CPP/bit.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ namespace LIBC_NAMESPACE::cpp {
// UB in the implementation.
template <
typename To, typename From,
typename = cpp::enable_if_t<sizeof(To) == sizeof(From)>,
typename = cpp::enable_if_t<cpp::is_trivially_constructible<To>::value>,
typename = cpp::enable_if_t<cpp::is_trivially_copyable<To>::value>,
typename = cpp::enable_if_t<cpp::is_trivially_copyable<From>::value>>
typename = cpp::enable_if_t<sizeof(To) == sizeof(From) &&
cpp::is_trivially_constructible<To>::value &&
cpp::is_trivially_copyable<To>::value &&
cpp::is_trivially_copyable<From>::value>>
LIBC_INLINE constexpr To bit_cast(const From &from) {
MSAN_UNPOISON(&from, sizeof(From));
#if LIBC_HAS_BUILTIN(__builtin_bit_cast)
Expand Down
179 changes: 104 additions & 75 deletions libc/src/__support/FPUtil/FPBits.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include "src/__support/CPP/bit.h"
#include "src/__support/CPP/type_traits.h"
#include "src/__support/UInt128.h"
#include "src/__support/common.h"
#include "src/__support/macros/attributes.h" // LIBC_INLINE

Expand All @@ -20,41 +21,37 @@
namespace LIBC_NAMESPACE {
namespace fputil {

// A generic class to represent single precision, double precision, and quad
// precision IEEE 754 floating point formats.
// On most platforms, the 'float' type corresponds to single precision floating
// point numbers, the 'double' type corresponds to double precision floating
// point numers, and the 'long double' type corresponds to the quad precision
// floating numbers. On x86 platforms however, the 'long double' type maps to
// an x87 floating point format. This format is an IEEE 754 extension format.
// It is handled as an explicit specialization of this class.
template <typename T> struct FPBits : private FloatProperties<T> {
static_assert(cpp::is_floating_point_v<T>,
"FPBits instantiated with invalid type.");
using typename FloatProperties<T>::StorageType;
using FloatProperties<T>::TOTAL_LEN;
namespace internal {

private:
using FloatProperties<T>::EXP_SIG_MASK;

public:
using FloatProperties<T>::EXP_MASK;
using FloatProperties<T>::EXP_BIAS;
using FloatProperties<T>::EXP_LEN;
using FloatProperties<T>::FRACTION_MASK;
using FloatProperties<T>::FRACTION_LEN;
// This is a temporary class to unify common methods and properties between
// FPBits and FPBits<long double>.
template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {
using UP = FPProperties<fp_type>;
using typename UP::StorageType;
using UP::TOTAL_LEN;

private:
using FloatProperties<T>::QUIET_NAN_MASK;
protected:
using UP::EXP_SIG_MASK;
using UP::QUIET_NAN_MASK;

public:
using FloatProperties<T>::SIGN_MASK;
using UP::EXP_BIAS;
using UP::EXP_LEN;
using UP::EXP_MASK;
using UP::EXP_MASK_SHIFT;
using UP::FP_MASK;
using UP::FRACTION_LEN;
using UP::FRACTION_MASK;
using UP::SIGN_MASK;

// Reinterpreting bits as an integer value and interpreting the bits of an
// integer value as a floating point value is used in tests. So, a convenient
// type is provided for such reinterpretations.
StorageType bits;

LIBC_INLINE constexpr FPBitsCommon() : bits(0) {}
LIBC_INLINE explicit constexpr FPBitsCommon(StorageType bits) : bits(bits) {}

LIBC_INLINE constexpr void set_mantissa(StorageType mantVal) {
mantVal &= FRACTION_MASK;
bits &= ~FRACTION_MASK;
Expand All @@ -65,16 +62,89 @@ template <typename T> struct FPBits : private FloatProperties<T> {
return bits & FRACTION_MASK;
}

LIBC_INLINE constexpr void set_biased_exponent(StorageType expVal) {
expVal = (expVal << FRACTION_LEN) & EXP_MASK;
LIBC_INLINE constexpr void set_sign(bool signVal) {
if (get_sign() != signVal)
bits ^= SIGN_MASK;
}

LIBC_INLINE constexpr bool get_sign() const {
return (bits & SIGN_MASK) != 0;
}

LIBC_INLINE constexpr void set_biased_exponent(StorageType biased) {
// clear exponent bits
bits &= ~EXP_MASK;
bits |= expVal;
// set exponent bits
bits |= (biased << EXP_MASK_SHIFT) & EXP_MASK;
}

LIBC_INLINE constexpr uint16_t get_biased_exponent() const {
return uint16_t((bits & EXP_MASK) >> FRACTION_LEN);
return uint16_t((bits & EXP_MASK) >> EXP_MASK_SHIFT);
}

LIBC_INLINE constexpr int get_exponent() const {
return int(get_biased_exponent()) - EXP_BIAS;
}

// If the number is subnormal, the exponent is treated as if it were the
// minimum exponent for a normal number. This is to keep continuity between
// the normal and subnormal ranges, but it causes problems for functions where
// values are calculated from the exponent, since just subtracting the bias
// will give a slightly incorrect result. Additionally, zero has an exponent
// of zero, and that should actually be treated as zero.
LIBC_INLINE constexpr int get_explicit_exponent() const {
const int biased_exp = int(get_biased_exponent());
if (is_zero()) {
return 0;
} else if (biased_exp == 0) {
return 1 - EXP_BIAS;
} else {
return biased_exp - EXP_BIAS;
}
}

LIBC_INLINE constexpr StorageType uintval() const { return bits & FP_MASK; }

LIBC_INLINE constexpr bool is_zero() const {
return (bits & EXP_SIG_MASK) == 0;
}
};

} // namespace internal

// A generic class to represent single precision, double precision, and quad
// precision IEEE 754 floating point formats.
// On most platforms, the 'float' type corresponds to single precision floating
// point numbers, the 'double' type corresponds to double precision floating
// point numers, and the 'long double' type corresponds to the quad precision
// floating numbers. On x86 platforms however, the 'long double' type maps to
// an x87 floating point format. This format is an IEEE 754 extension format.
// It is handled as an explicit specialization of this class.
template <typename T>
struct FPBits : public internal::FPBitsCommon<get_fp_type<T>()> {
static_assert(cpp::is_floating_point_v<T>,
"FPBits instantiated with invalid type.");
using UP = internal::FPBitsCommon<get_fp_type<T>()>;
using StorageType = typename UP::StorageType;
using UP::bits;

private:
using UP::EXP_SIG_MASK;
using UP::QUIET_NAN_MASK;

public:
using UP::EXP_BIAS;
using UP::EXP_LEN;
using UP::EXP_MASK;
using UP::EXP_MASK_SHIFT;
using UP::FRACTION_LEN;
using UP::FRACTION_MASK;
using UP::SIGN_MASK;
using UP::TOTAL_LEN;

using UP::get_biased_exponent;
using UP::is_zero;

// The function return mantissa with the implicit bit set iff the current
// value is a valid normal number.
LIBC_INLINE constexpr StorageType get_explicit_mantissa() {
Expand All @@ -84,19 +154,6 @@ template <typename T> struct FPBits : private FloatProperties<T> {
(FRACTION_MASK & bits);
}

LIBC_INLINE constexpr void set_sign(bool signVal) {
bits |= SIGN_MASK;
if (!signVal)
bits -= SIGN_MASK;
}

LIBC_INLINE constexpr bool get_sign() const {
return (bits & SIGN_MASK) != 0;
}

static_assert(sizeof(T) == sizeof(StorageType),
"Data type and integral representation have different sizes.");

static constexpr int MAX_BIASED_EXPONENT = (1 << EXP_LEN) - 1;
static constexpr StorageType MIN_SUBNORMAL = StorageType(1);
static constexpr StorageType MAX_SUBNORMAL = FRACTION_MASK;
Expand All @@ -108,49 +165,21 @@ template <typename T> struct FPBits : private FloatProperties<T> {
// type match.
template <typename XType, cpp::enable_if_t<cpp::is_same_v<T, XType>, int> = 0>
LIBC_INLINE constexpr explicit FPBits(XType x)
: bits(cpp::bit_cast<StorageType>(x)) {}
: UP(cpp::bit_cast<StorageType>(x)) {}

template <typename XType,
cpp::enable_if_t<cpp::is_same_v<XType, StorageType>, int> = 0>
LIBC_INLINE constexpr explicit FPBits(XType x) : bits(x) {}

LIBC_INLINE constexpr FPBits() : bits(0) {}
LIBC_INLINE constexpr explicit FPBits(XType x) : UP(x) {}

LIBC_INLINE constexpr T get_val() const { return cpp::bit_cast<T>(bits); }
LIBC_INLINE constexpr FPBits() : UP() {}

LIBC_INLINE constexpr void set_val(T value) {
bits = cpp::bit_cast<StorageType>(value);
}

LIBC_INLINE constexpr explicit operator T() const { return get_val(); }

LIBC_INLINE constexpr StorageType uintval() const { return bits; }

LIBC_INLINE constexpr int get_exponent() const {
return int(get_biased_exponent()) - EXP_BIAS;
}

// If the number is subnormal, the exponent is treated as if it were the
// minimum exponent for a normal number. This is to keep continuity between
// the normal and subnormal ranges, but it causes problems for functions where
// values are calculated from the exponent, since just subtracting the bias
// will give a slightly incorrect result. Additionally, zero has an exponent
// of zero, and that should actually be treated as zero.
LIBC_INLINE constexpr int get_explicit_exponent() const {
const int biased_exp = int(get_biased_exponent());
if (is_zero()) {
return 0;
} else if (biased_exp == 0) {
return 1 - EXP_BIAS;
} else {
return biased_exp - EXP_BIAS;
}
}
LIBC_INLINE constexpr T get_val() const { return cpp::bit_cast<T>(bits); }

LIBC_INLINE constexpr bool is_zero() const {
// Remove sign bit by shift
return (bits << 1) == 0;
}
LIBC_INLINE constexpr explicit operator T() const { return get_val(); }

LIBC_INLINE constexpr bool is_inf() const {
return (bits & EXP_SIG_MASK) == EXP_MASK;
Expand Down
2 changes: 1 addition & 1 deletion libc/src/__support/FPUtil/FloatProperties.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ struct FPProperties : public internal::FPBaseProperties<fp_type> {
(1U << (EXP_LEN - 1U)) - 1U;
static_assert(EXP_BIAS > 0);

private:
protected:
// The shift amount to get the *significand* part to the least significant
// bit. Always `0` but kept for consistency.
LIBC_INLINE_VAR static constexpr int SIG_MASK_SHIFT = 0;
Expand Down
Loading