9 changes: 5 additions & 4 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -1569,9 +1569,7 @@ bool OffsetHelper(InterpState &S, CodePtr OpPC, const T &Offset,
APSInt NewIndex =
(Op == ArithOp::Add) ? (APIndex + APOffset) : (APIndex - APOffset);
S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
<< NewIndex
<< /*array*/ static_cast<int>(!Ptr.inArray())
<< static_cast<unsigned>(MaxIndex);
<< NewIndex << /*array*/ static_cast<int>(!Ptr.inArray()) << MaxIndex;
Invalid = true;
};

Expand All @@ -1598,7 +1596,7 @@ bool OffsetHelper(InterpState &S, CodePtr OpPC, const T &Offset,
}
}

if (Invalid && !Ptr.isDummy() && S.getLangOpts().CPlusPlus)
if (Invalid && S.getLangOpts().CPlusPlus)
return false;

// Offset is valid - compute it on unsigned.
Expand Down Expand Up @@ -2110,6 +2108,9 @@ inline bool ArrayDecay(InterpState &S, CodePtr OpPC) {
return true;
}

if (!CheckRange(S, OpPC, Ptr, CSK_ArrayToPointer))
return false;

if (!Ptr.isUnknownSizeArray() || Ptr.isDummy()) {
S.Stk.push<Pointer>(Ptr.atIndex(0));
return true;
Expand Down
8 changes: 6 additions & 2 deletions clang/lib/AST/Interp/InterpBlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,15 @@ class Block final {
/// Creates a new block.
Block(const std::optional<unsigned> &DeclID, const Descriptor *Desc,
bool IsStatic = false, bool IsExtern = false)
: DeclID(DeclID), IsStatic(IsStatic), IsExtern(IsExtern), Desc(Desc) {}
: DeclID(DeclID), IsStatic(IsStatic), IsExtern(IsExtern), Desc(Desc) {
assert(Desc);
}

Block(const Descriptor *Desc, bool IsStatic = false, bool IsExtern = false)
: DeclID((unsigned)-1), IsStatic(IsStatic), IsExtern(IsExtern),
Desc(Desc) {}
Desc(Desc) {
assert(Desc);
}

/// Returns the block's descriptor.
const Descriptor *getDescriptor() const { return Desc; }
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/AST/Interp/Pointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,12 +181,12 @@ void Pointer::print(llvm::raw_ostream &OS) const {
if (isBlockPointer()) {
OS << "Block) {";

if (PointeeStorage.BS.Base == RootPtrMark)
OS << "rootptr, ";
if (isRoot())
OS << "rootptr(" << PointeeStorage.BS.Base << "), ";
else
OS << PointeeStorage.BS.Base << ", ";

if (Offset == PastEndMark)
if (isElementPastEnd())
OS << "pastend, ";
else
OS << Offset << ", ";
Expand Down
7 changes: 0 additions & 7 deletions clang/lib/AST/Interp/Pointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -384,11 +384,6 @@ class Pointer {
bool isUnknownSizeArray() const {
if (!isBlockPointer())
return false;
// If this points inside a dummy block, return true.
// FIXME: This might change in the future. If it does, we need
// to set the proper Ctor/Dtor functions for dummy Descriptors.
if (!isRoot() && isDummy())
return true;
return getFieldDesc()->isUnknownSizeArray();
}
/// Checks if the pointer points to an array.
Expand Down Expand Up @@ -560,8 +555,6 @@ class Pointer {

if (!asBlockPointer().Pointee)
return false;
if (isDummy())
return false;

return isElementPastEnd() || getSize() == getOffset();
}
Expand Down
8 changes: 6 additions & 2 deletions clang/lib/AST/Interp/Program.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,12 @@ std::optional<unsigned> Program::getOrCreateDummy(const ValueDecl *VD) {
if (auto It = DummyVariables.find(VD); It != DummyVariables.end())
return It->second;

QualType QT = VD->getType();
if (const auto *RT = QT->getAs<ReferenceType>())
QT = RT->getPointeeType();

Descriptor *Desc;
if (std::optional<PrimType> T = Ctx.classify(VD->getType()))
if (std::optional<PrimType> T = Ctx.classify(QT))
Desc = createDescriptor(VD, *T, std::nullopt, true, false);
else
Desc = createDescriptor(VD, VD->getType().getTypePtr(), std::nullopt, true,
Expand Down Expand Up @@ -372,7 +376,7 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
// Arrays of composites. In this case, the array is a list of pointers,
// followed by the actual elements.
const Descriptor *ElemDesc = createDescriptor(
D, ElemTy.getTypePtr(), MDSize, IsConst, IsTemporary);
D, ElemTy.getTypePtr(), std::nullopt, IsConst, IsTemporary);
if (!ElemDesc)
return nullptr;
unsigned ElemSize =
Expand Down
22 changes: 19 additions & 3 deletions clang/lib/Driver/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,6 @@ Driver::Driver(StringRef ClangExecutable, StringRef TargetTriple,
UserConfigDir = static_cast<std::string>(P);
}
#endif

// Compute the path to the resource directory.
ResourceDir = GetResourcesPath(ClangExecutable, CLANG_RESOURCE_DIR);
}

void Driver::setDriverMode(StringRef Value) {
Expand All @@ -250,6 +247,24 @@ void Driver::setDriverMode(StringRef Value) {
Diag(diag::err_drv_unsupported_option_argument) << OptName << Value;
}

void Driver::setResourceDirectory() {
// Compute the path to the resource directory, depending on the driver mode.
switch (Mode) {
case GCCMode:
case GXXMode:
case CPPMode:
case CLMode:
case DXCMode:
ResourceDir = GetResourcesPath(ClangExecutable, CLANG_RESOURCE_DIR);
break;
case FlangMode:
SmallString<64> customResourcePathRelativeToDriver{".."};
ResourceDir =
GetResourcesPath(ClangExecutable, customResourcePathRelativeToDriver);
break;
}
}

InputArgList Driver::ParseArgStrings(ArrayRef<const char *> ArgStrings,
bool UseDriverMode, bool &ContainsError) {
llvm::PrettyStackTraceString CrashInfo("Command line argument parsing");
Expand Down Expand Up @@ -1202,6 +1217,7 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
if (!DriverMode.empty())
setDriverMode(DriverMode);

setResourceDirectory();
// FIXME: What are we going to do with -V and -b?

// Arguments specified in command line.
Expand Down
3 changes: 1 addition & 2 deletions clang/lib/Format/Format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3858,8 +3858,7 @@ LangOptions getFormattingLangOpts(const FormatStyle &Style) {
LangOpts.Digraphs = LexingStd >= FormatStyle::LS_Cpp11;

LangOpts.LineComment = 1;
bool AlternativeOperators = Style.isCpp();
LangOpts.CXXOperatorNames = AlternativeOperators ? 1 : 0;
LangOpts.CXXOperatorNames = Style.isCpp();
LangOpts.Bool = 1;
LangOpts.ObjC = 1;
LangOpts.MicrosoftExt = 1; // To get kw___try, kw___finally.
Expand Down
46 changes: 5 additions & 41 deletions clang/lib/Format/FormatToken.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,59 +34,23 @@ const char *getTokenTypeName(TokenType Type) {
return nullptr;
}

// FIXME: This is copy&pasted from Sema. Put it in a common place and remove
// duplication.
bool FormatToken::isSimpleTypeSpecifier() const {
switch (Tok.getKind()) {
case tok::kw_short:
case tok::kw_long:
case tok::kw___int64:
case tok::kw___int128:
case tok::kw_signed:
case tok::kw_unsigned:
case tok::kw_void:
case tok::kw_char:
case tok::kw_int:
case tok::kw_half:
case tok::kw_float:
case tok::kw_double:
case tok::kw___bf16:
case tok::kw__Float16:
case tok::kw___float128:
case tok::kw___ibm128:
case tok::kw_wchar_t:
case tok::kw_bool:
#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case tok::kw___##Trait:
#include "clang/Basic/TransformTypeTraits.def"
case tok::annot_typename:
case tok::kw_char8_t:
case tok::kw_char16_t:
case tok::kw_char32_t:
case tok::kw_typeof:
case tok::kw_decltype:
case tok::kw__Atomic:
return true;
default:
return false;
}
}

// Sorted common C++ non-keyword types.
static SmallVector<StringRef> CppNonKeywordTypes = {
"clock_t", "int16_t", "int32_t", "int64_t", "int8_t",
"intptr_t", "ptrdiff_t", "size_t", "time_t", "uint16_t",
"uint32_t", "uint64_t", "uint8_t", "uintptr_t",
};

bool FormatToken::isTypeName(bool IsCpp) const {
return is(TT_TypeName) || isSimpleTypeSpecifier() ||
bool FormatToken::isTypeName(const LangOptions &LangOpts) const {
const bool IsCpp = LangOpts.CXXOperatorNames;
return is(TT_TypeName) || Tok.isSimpleTypeSpecifier(LangOpts) ||
(IsCpp && is(tok::identifier) &&
std::binary_search(CppNonKeywordTypes.begin(),
CppNonKeywordTypes.end(), TokenText));
}

bool FormatToken::isTypeOrIdentifier(bool IsCpp) const {
return isTypeName(IsCpp) || isOneOf(tok::kw_auto, tok::identifier);
bool FormatToken::isTypeOrIdentifier(const LangOptions &LangOpts) const {
return isTypeName(LangOpts) || isOneOf(tok::kw_auto, tok::identifier);
}

bool FormatToken::isBlockIndentedInitRBrace(const FormatStyle &Style) const {
Expand Down
8 changes: 2 additions & 6 deletions clang/lib/Format/FormatToken.h
Original file line number Diff line number Diff line change
Expand Up @@ -684,12 +684,8 @@ struct FormatToken {
isAttribute();
}

/// Determine whether the token is a simple-type-specifier.
[[nodiscard]] bool isSimpleTypeSpecifier() const;

[[nodiscard]] bool isTypeName(bool IsCpp) const;

[[nodiscard]] bool isTypeOrIdentifier(bool IsCpp) const;
[[nodiscard]] bool isTypeName(const LangOptions &LangOpts) const;
[[nodiscard]] bool isTypeOrIdentifier(const LangOptions &LangOpts) const;

bool isObjCAccessSpecifier() const {
return is(tok::at) && Next &&
Expand Down
1 change: 0 additions & 1 deletion clang/lib/Format/FormatTokenLexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1442,7 +1442,6 @@ void FormatTokenLexer::readRawToken(FormatToken &Tok) {

void FormatTokenLexer::resetLexer(unsigned Offset) {
StringRef Buffer = SourceMgr.getBufferData(ID);
LangOpts = getFormattingLangOpts(Style);
Lex.reset(new Lexer(SourceMgr.getLocForStartOfFile(ID), LangOpts,
Buffer.begin(), Buffer.begin() + Offset, Buffer.end()));
Lex->SetKeepWhitespaceMode(true);
Expand Down
29 changes: 13 additions & 16 deletions clang/lib/Format/QualifierAlignmentFixer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,13 +268,11 @@ const FormatToken *LeftRightQualifierAlignmentFixer::analyzeRight(
if (isPossibleMacro(TypeToken))
return Tok;

const bool IsCpp = Style.isCpp();

// The case `const long long int volatile` -> `long long int const volatile`
// The case `long const long int volatile` -> `long long int const volatile`
// The case `long long volatile int const` -> `long long int const volatile`
// The case `const long long volatile int` -> `long long int const volatile`
if (TypeToken->isTypeName(IsCpp)) {
if (TypeToken->isTypeName(LangOpts)) {
// The case `const decltype(foo)` -> `const decltype(foo)`
// The case `const typeof(foo)` -> `const typeof(foo)`
// The case `const _Atomic(foo)` -> `const _Atomic(foo)`
Expand All @@ -283,7 +281,7 @@ const FormatToken *LeftRightQualifierAlignmentFixer::analyzeRight(

const FormatToken *LastSimpleTypeSpecifier = TypeToken;
while (isQualifierOrType(LastSimpleTypeSpecifier->getNextNonComment(),
IsCpp)) {
LangOpts)) {
LastSimpleTypeSpecifier = LastSimpleTypeSpecifier->getNextNonComment();
}

Expand All @@ -295,7 +293,7 @@ const FormatToken *LeftRightQualifierAlignmentFixer::analyzeRight(
// The case `unsigned short const` -> `unsigned short const`
// The case:
// `unsigned short volatile const` -> `unsigned short const volatile`
if (PreviousCheck && PreviousCheck->isTypeName(IsCpp)) {
if (PreviousCheck && PreviousCheck->isTypeName(LangOpts)) {
if (LastQual != Tok)
rotateTokens(SourceMgr, Fixes, Tok, LastQual, /*Left=*/false);
return Tok;
Expand Down Expand Up @@ -412,11 +410,11 @@ const FormatToken *LeftRightQualifierAlignmentFixer::analyzeLeft(
// The case `volatile long long const int` -> `const volatile long long int`
// The case `const long long volatile int` -> `const volatile long long int`
// The case `long volatile long int const` -> `const volatile long long int`
if (const bool IsCpp = Style.isCpp(); TypeToken->isTypeName(IsCpp)) {
if (TypeToken->isTypeName(LangOpts)) {
const FormatToken *LastSimpleTypeSpecifier = TypeToken;
while (isConfiguredQualifierOrType(
LastSimpleTypeSpecifier->getPreviousNonComment(),
ConfiguredQualifierTokens, IsCpp)) {
ConfiguredQualifierTokens, LangOpts)) {
LastSimpleTypeSpecifier =
LastSimpleTypeSpecifier->getPreviousNonComment();
}
Expand Down Expand Up @@ -614,22 +612,21 @@ void prepareLeftRightOrderingForQualifierAlignmentFixer(
}
}

bool LeftRightQualifierAlignmentFixer::isQualifierOrType(const FormatToken *Tok,
bool IsCpp) {
return Tok &&
(Tok->isTypeName(IsCpp) || Tok->is(tok::kw_auto) || isQualifier(Tok));
bool isQualifierOrType(const FormatToken *Tok, const LangOptions &LangOpts) {
return Tok && (Tok->isTypeName(LangOpts) || Tok->is(tok::kw_auto) ||
isQualifier(Tok));
}

bool LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
const FormatToken *Tok, const std::vector<tok::TokenKind> &Qualifiers,
bool IsCpp) {
return Tok && (Tok->isTypeName(IsCpp) || Tok->is(tok::kw_auto) ||
bool isConfiguredQualifierOrType(const FormatToken *Tok,
const std::vector<tok::TokenKind> &Qualifiers,
const LangOptions &LangOpts) {
return Tok && (Tok->isTypeName(LangOpts) || Tok->is(tok::kw_auto) ||
isConfiguredQualifier(Tok, Qualifiers));
}

// If a token is an identifier and it's upper case, it could
// be a macro and hence we need to be able to ignore it.
bool LeftRightQualifierAlignmentFixer::isPossibleMacro(const FormatToken *Tok) {
bool isPossibleMacro(const FormatToken *Tok) {
if (!Tok)
return false;
if (Tok->isNot(tok::identifier))
Expand Down
19 changes: 9 additions & 10 deletions clang/lib/Format/QualifierAlignmentFixer.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ void prepareLeftRightOrderingForQualifierAlignmentFixer(
std::vector<std::string> &RightOrder,
std::vector<tok::TokenKind> &Qualifiers);

// Is the Token a simple or qualifier type
bool isQualifierOrType(const FormatToken *Tok, const LangOptions &LangOpts);
bool isConfiguredQualifierOrType(const FormatToken *Tok,
const std::vector<tok::TokenKind> &Qualifiers,
const LangOptions &LangOpts);

// Is the Token likely a Macro
bool isPossibleMacro(const FormatToken *Tok);

class LeftRightQualifierAlignmentFixer : public TokenAnalyzer {
std::string Qualifier;
bool RightAlign;
Expand Down Expand Up @@ -69,16 +78,6 @@ class LeftRightQualifierAlignmentFixer : public TokenAnalyzer {
const FormatToken *Tok,
const std::string &Qualifier,
tok::TokenKind QualifierType);

// Is the Token a simple or qualifier type
static bool isQualifierOrType(const FormatToken *Tok, bool IsCpp = true);
static bool
isConfiguredQualifierOrType(const FormatToken *Tok,
const std::vector<tok::TokenKind> &Qualifiers,
bool IsCpp = true);

// Is the Token likely a Macro
static bool isPossibleMacro(const FormatToken *Tok);
};

} // end namespace format
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Format/TokenAnalyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ Environment::Environment(StringRef Code, StringRef FileName,
NextStartColumn(NextStartColumn), LastStartColumn(LastStartColumn) {}

TokenAnalyzer::TokenAnalyzer(const Environment &Env, const FormatStyle &Style)
: Style(Style), Env(Env),
: Style(Style), LangOpts(getFormattingLangOpts(Style)), Env(Env),
AffectedRangeMgr(Env.getSourceManager(), Env.getCharRanges()),
UnwrappedLines(1),
Encoding(encoding::detectEncoding(
Expand All @@ -101,7 +101,7 @@ std::pair<tooling::Replacements, unsigned>
TokenAnalyzer::process(bool SkipAnnotation) {
tooling::Replacements Result;
llvm::SpecificBumpPtrAllocator<FormatToken> Allocator;
IdentifierTable IdentTable(getFormattingLangOpts(Style));
IdentifierTable IdentTable(LangOpts);
FormatTokenLexer Lex(Env.getSourceManager(), Env.getFileID(),
Env.getFirstStartColumn(), Style, Encoding, Allocator,
IdentTable);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Format/TokenAnalyzer.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ class TokenAnalyzer : public UnwrappedLineConsumer {
void finishRun() override;

FormatStyle Style;
LangOptions LangOpts;
// Stores Style, FileID and SourceManager etc.
const Environment &Env;
// AffectedRangeMgr stores ranges to be fixed.
Expand Down
43 changes: 25 additions & 18 deletions clang/lib/Format/TokenAnnotator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,9 @@ class AnnotatingParser {
const AdditionalKeywords &Keywords,
SmallVector<ScopeType> &Scopes)
: Style(Style), Line(Line), CurrentToken(Line.First), AutoFound(false),
IsCpp(Style.isCpp()), Keywords(Keywords), Scopes(Scopes) {
IsCpp(Style.isCpp()), LangOpts(getFormattingLangOpts(Style)),
Keywords(Keywords), Scopes(Scopes) {
assert(IsCpp == LangOpts.CXXOperatorNames);
Contexts.push_back(Context(tok::unknown, 1, /*IsExpression=*/false));
resetTokenMetadata();
}
Expand Down Expand Up @@ -562,7 +564,7 @@ class AnnotatingParser {
(CurrentToken->is(tok::l_paren) && CurrentToken->Next &&
CurrentToken->Next->isOneOf(tok::star, tok::amp, tok::caret));
if ((CurrentToken->Previous->isOneOf(tok::kw_const, tok::kw_auto) ||
CurrentToken->Previous->isTypeName(IsCpp)) &&
CurrentToken->Previous->isTypeName(LangOpts)) &&
!(CurrentToken->is(tok::l_brace) ||
(CurrentToken->is(tok::l_paren) && !ProbablyFunctionTypeLParen))) {
Contexts.back().IsExpression = false;
Expand Down Expand Up @@ -2624,7 +2626,7 @@ class AnnotatingParser {
return true;

// MyClass a;
if (PreviousNotConst->isTypeName(IsCpp))
if (PreviousNotConst->isTypeName(LangOpts))
return true;

// type[] a in Java
Expand Down Expand Up @@ -2728,7 +2730,7 @@ class AnnotatingParser {
}

if (Tok.Next->is(tok::question) ||
(Tok.Next->is(tok::ampamp) && !Tok.Previous->isTypeName(IsCpp))) {
(Tok.Next->is(tok::ampamp) && !Tok.Previous->isTypeName(LangOpts))) {
return false;
}

Expand Down Expand Up @@ -2757,9 +2759,10 @@ class AnnotatingParser {
}

// Heuristically try to determine whether the parentheses contain a type.
auto IsQualifiedPointerOrReference = [](FormatToken *T, bool IsCpp) {
auto IsQualifiedPointerOrReference = [](FormatToken *T,
const LangOptions &LangOpts) {
// This is used to handle cases such as x = (foo *const)&y;
assert(!T->isTypeName(IsCpp) && "Should have already been checked");
assert(!T->isTypeName(LangOpts) && "Should have already been checked");
// Strip trailing qualifiers such as const or volatile when checking
// whether the parens could be a cast to a pointer/reference type.
while (T) {
Expand Down Expand Up @@ -2791,8 +2794,8 @@ class AnnotatingParser {
bool ParensAreType =
!Tok.Previous ||
Tok.Previous->isOneOf(TT_TemplateCloser, TT_TypeDeclarationParen) ||
Tok.Previous->isTypeName(IsCpp) ||
IsQualifiedPointerOrReference(Tok.Previous, IsCpp);
Tok.Previous->isTypeName(LangOpts) ||
IsQualifiedPointerOrReference(Tok.Previous, LangOpts);
bool ParensCouldEndDecl =
Tok.Next->isOneOf(tok::equal, tok::semi, tok::l_brace, tok::greater);
if (ParensAreType && !ParensCouldEndDecl)
Expand Down Expand Up @@ -3065,6 +3068,7 @@ class AnnotatingParser {
FormatToken *CurrentToken;
bool AutoFound;
bool IsCpp;
LangOptions LangOpts;
const AdditionalKeywords &Keywords;

SmallVector<ScopeType> &Scopes;
Expand Down Expand Up @@ -3639,7 +3643,8 @@ void TokenAnnotator::annotate(AnnotatedLine &Line) {

// This function heuristically determines whether 'Current' starts the name of a
// function declaration.
static bool isFunctionDeclarationName(bool IsCpp, const FormatToken &Current,
static bool isFunctionDeclarationName(const LangOptions &LangOpts,
const FormatToken &Current,
const AnnotatedLine &Line,
FormatToken *&ClosingParen) {
assert(Current.Previous);
Expand All @@ -3658,7 +3663,7 @@ static bool isFunctionDeclarationName(bool IsCpp, const FormatToken &Current,
}

auto skipOperatorName =
[IsCpp](const FormatToken *Next) -> const FormatToken * {
[&LangOpts](const FormatToken *Next) -> const FormatToken * {
for (; Next; Next = Next->Next) {
if (Next->is(TT_OverloadedOperatorLParen))
return Next;
Expand All @@ -3677,7 +3682,7 @@ static bool isFunctionDeclarationName(bool IsCpp, const FormatToken &Current,
Next = Next->Next;
continue;
}
if ((Next->isTypeName(IsCpp) || Next->is(tok::identifier)) &&
if ((Next->isTypeName(LangOpts) || Next->is(tok::identifier)) &&
Next->Next && Next->Next->isPointerOrReference()) {
// For operator void*(), operator char*(), operator Foo*().
Next = Next->Next;
Expand All @@ -3693,8 +3698,10 @@ static bool isFunctionDeclarationName(bool IsCpp, const FormatToken &Current,
return nullptr;
};

const auto *Next = Current.Next;
const bool IsCpp = LangOpts.CXXOperatorNames;

// Find parentheses of parameter list.
const FormatToken *Next = Current.Next;
if (Current.is(tok::kw_operator)) {
if (Previous.Tok.getIdentifierInfo() &&
!Previous.isOneOf(tok::kw_return, tok::kw_co_return)) {
Expand Down Expand Up @@ -3774,7 +3781,7 @@ static bool isFunctionDeclarationName(bool IsCpp, const FormatToken &Current,
Tok = Tok->MatchingParen;
continue;
}
if (Tok->is(tok::kw_const) || Tok->isTypeName(IsCpp) ||
if (Tok->is(tok::kw_const) || Tok->isTypeName(LangOpts) ||
Tok->isOneOf(TT_PointerOrReference, TT_StartOfName, tok::ellipsis)) {
return true;
}
Expand Down Expand Up @@ -3837,7 +3844,7 @@ void TokenAnnotator::calculateFormattingInformation(AnnotatedLine &Line) const {
AfterLastAttribute = Tok;
if (const bool IsCtorOrDtor = Tok->is(TT_CtorDtorDeclName);
IsCtorOrDtor ||
isFunctionDeclarationName(IsCpp, *Tok, Line, ClosingParen)) {
isFunctionDeclarationName(LangOpts, *Tok, Line, ClosingParen)) {
if (!IsCtorOrDtor)
Tok->setFinalizedType(TT_FunctionDeclarationName);
LineIsFunctionDeclaration = true;
Expand Down Expand Up @@ -4447,7 +4454,7 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
if (Left.Tok.isLiteral())
return true;
// for (auto a = 0, b = 0; const auto & c : {1, 2, 3})
if (Left.isTypeOrIdentifier(IsCpp) && Right.Next && Right.Next->Next &&
if (Left.isTypeOrIdentifier(LangOpts) && Right.Next && Right.Next->Next &&
Right.Next->Next->is(TT_RangeBasedForLoopColon)) {
return getTokenPointerOrReferenceAlignment(Right) !=
FormatStyle::PAS_Left;
Expand Down Expand Up @@ -4490,7 +4497,7 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
if (Right.is(tok::l_brace) && Right.is(BK_Block))
return true;
// for (auto a = 0, b = 0; const auto& c : {1, 2, 3})
if (BeforeLeft && BeforeLeft->isTypeOrIdentifier(IsCpp) && Right.Next &&
if (BeforeLeft && BeforeLeft->isTypeOrIdentifier(LangOpts) && Right.Next &&
Right.Next->is(TT_RangeBasedForLoopColon)) {
return getTokenPointerOrReferenceAlignment(Left) !=
FormatStyle::PAS_Right;
Expand Down Expand Up @@ -4534,7 +4541,7 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
if (Right.isPointerOrReference()) {
const FormatToken *Previous = &Left;
while (Previous && Previous->isNot(tok::kw_operator)) {
if (Previous->is(tok::identifier) || Previous->isTypeName(IsCpp)) {
if (Previous->is(tok::identifier) || Previous->isTypeName(LangOpts)) {
Previous = Previous->getPreviousNonComment();
continue;
}
Expand Down Expand Up @@ -4723,7 +4730,7 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
if (!Style.isVerilog() &&
(Left.isOneOf(tok::identifier, tok::greater, tok::r_square,
tok::r_paren) ||
Left.isTypeName(IsCpp)) &&
Left.isTypeName(LangOpts)) &&
Right.is(tok::l_brace) && Right.getNextNonComment() &&
Right.isNot(BK_Block)) {
return false;
Expand Down
6 changes: 5 additions & 1 deletion clang/lib/Format/TokenAnnotator.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,10 @@ class AnnotatedLine {
class TokenAnnotator {
public:
TokenAnnotator(const FormatStyle &Style, const AdditionalKeywords &Keywords)
: Style(Style), IsCpp(Style.isCpp()), Keywords(Keywords) {}
: Style(Style), IsCpp(Style.isCpp()),
LangOpts(getFormattingLangOpts(Style)), Keywords(Keywords) {
assert(IsCpp == LangOpts.CXXOperatorNames);
}

/// Adapts the indent levels of comment lines to the indent of the
/// subsequent line.
Expand Down Expand Up @@ -260,6 +263,7 @@ class TokenAnnotator {
const FormatStyle &Style;

bool IsCpp;
LangOptions LangOpts;

const AdditionalKeywords &Keywords;

Expand Down
19 changes: 11 additions & 8 deletions clang/lib/Format/UnwrappedLineParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,16 @@ UnwrappedLineParser::UnwrappedLineParser(
IdentifierTable &IdentTable)
: Line(new UnwrappedLine), MustBreakBeforeNextToken(false),
CurrentLines(&Lines), Style(Style), IsCpp(Style.isCpp()),
Keywords(Keywords), CommentPragmasRegex(Style.CommentPragmas),
Tokens(nullptr), Callback(Callback), AllTokens(Tokens), PPBranchLevel(-1),
LangOpts(getFormattingLangOpts(Style)), Keywords(Keywords),
CommentPragmasRegex(Style.CommentPragmas), Tokens(nullptr),
Callback(Callback), AllTokens(Tokens), PPBranchLevel(-1),
IncludeGuard(Style.IndentPPDirectives == FormatStyle::PPDIS_None
? IG_Rejected
: IG_Inited),
IncludeGuardToken(nullptr), FirstStartColumn(FirstStartColumn),
Macros(Style.Macros, SourceMgr, Style, Allocator, IdentTable) {}
Macros(Style.Macros, SourceMgr, Style, Allocator, IdentTable) {
assert(IsCpp == LangOpts.CXXOperatorNames);
}

void UnwrappedLineParser::reset() {
PPBranchLevel = -1;
Expand Down Expand Up @@ -1870,7 +1873,7 @@ void UnwrappedLineParser::parseStructuralElement(
case tok::caret:
nextToken();
// Block return type.
if (FormatTok->Tok.isAnyIdentifier() || FormatTok->isTypeName(IsCpp)) {
if (FormatTok->Tok.isAnyIdentifier() || FormatTok->isTypeName(LangOpts)) {
nextToken();
// Return types: pointers are ok too.
while (FormatTok->is(tok::star))
Expand Down Expand Up @@ -2231,7 +2234,7 @@ bool UnwrappedLineParser::tryToParseLambda() {
bool InTemplateParameterList = false;

while (FormatTok->isNot(tok::l_brace)) {
if (FormatTok->isTypeName(IsCpp)) {
if (FormatTok->isTypeName(LangOpts)) {
nextToken();
continue;
}
Expand Down Expand Up @@ -3448,7 +3451,7 @@ bool UnwrappedLineParser::parseRequires() {
break;
}
default:
if (PreviousNonComment->isTypeOrIdentifier(IsCpp)) {
if (PreviousNonComment->isTypeOrIdentifier(LangOpts)) {
// This is a requires clause.
parseRequiresClause(RequiresToken);
return true;
Expand Down Expand Up @@ -3511,7 +3514,7 @@ bool UnwrappedLineParser::parseRequires() {
--OpenAngles;
break;
default:
if (NextToken->isTypeName(IsCpp)) {
if (NextToken->isTypeName(LangOpts)) {
FormatTok = Tokens->setPosition(StoredPosition);
parseRequiresExpression(RequiresToken);
return false;
Expand Down Expand Up @@ -4027,7 +4030,7 @@ void UnwrappedLineParser::parseRecord(bool ParseAsExpr) {
if (FormatTok->is(tok::l_square)) {
FormatToken *Previous = FormatTok->Previous;
if (!Previous || (Previous->isNot(tok::r_paren) &&
!Previous->isTypeOrIdentifier(IsCpp))) {
!Previous->isTypeOrIdentifier(LangOpts))) {
// Don't try parsing a lambda if we had a closing parenthesis before,
// it was probably a pointer to an array: int (*)[].
if (!tryToParseLambda())
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Format/UnwrappedLineParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ class UnwrappedLineParser {

const FormatStyle &Style;
bool IsCpp;
LangOptions LangOpts;
const AdditionalKeywords &Keywords;

llvm::Regex CommentPragmasRegex;
Expand Down
13 changes: 12 additions & 1 deletion clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -2874,10 +2874,21 @@ class TreeTransform {
return ExprError();
Base = BaseResult.get();

// `TranformMaterializeTemporaryExpr()` removes materialized temporaries
// from the AST, so we need to re-insert them if needed (since
// `BuildFieldRefereneExpr()` doesn't do this).
if (!isArrow && Base->isPRValue()) {
BaseResult = getSema().TemporaryMaterializationConversion(Base);
if (BaseResult.isInvalid())
return ExprError();
Base = BaseResult.get();
}

CXXScopeSpec EmptySS;
return getSema().BuildFieldReferenceExpr(
Base, isArrow, OpLoc, EmptySS, cast<FieldDecl>(Member),
DeclAccessPair::make(FoundDecl, FoundDecl->getAccess()), MemberNameInfo);
DeclAccessPair::make(FoundDecl, FoundDecl->getAccess()),
MemberNameInfo);
}

CXXScopeSpec SS;
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G,
SL = DL.asLocation();
if (SR.isInvalid() || !SL.isValid())
continue;
if (isa<CXXTryStmt>(S))
continue;
}
else
continue;
Expand Down Expand Up @@ -254,4 +256,4 @@ void ento::registerUnreachableCodeChecker(CheckerManager &mgr) {

bool ento::shouldRegisterUnreachableCodeChecker(const CheckerManager &mgr) {
return true;
}
}
7 changes: 7 additions & 0 deletions clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ bool tryToFindPtrOrigin(
E = tempExpr->getSubExpr();
continue;
}
if (auto *tempExpr = dyn_cast<CXXTemporaryObjectExpr>(E)) {
if (auto *C = tempExpr->getConstructor()) {
if (auto *Class = C->getParent(); Class && isRefCounted(Class))
return callback(E, true);
break;
}
}
if (auto *tempExpr = dyn_cast<ParenExpr>(E)) {
E = tempExpr->getSubExpr();
continue;
Expand Down
15 changes: 15 additions & 0 deletions clang/test/AST/Interp/arrays.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -580,3 +580,18 @@ constexpr ptrdiff_t d3 = &melchizedek[0] - &melchizedek[1]; // ok
/// GH#88018
const int SZA[] = {};
void testZeroSizedArrayAccess() { unsigned c = SZA[4]; }

#if __cplusplus >= 202002L
constexpr int test_multiarray2() { // both-error {{never produces a constant expression}}
int multi2[2][1]; // both-note {{declared here}}
return multi2[2][0]; // both-note {{cannot access array element of pointer past the end of object}} \
// both-warning {{array index 2 is past the end of the array (that has type 'int[2][1]')}}
}

/// Same but with a dummy pointer.
int multi22[2][2]; // both-note {{declared here}}
int test_multiarray22() {
return multi22[2][0]; // both-warning {{array index 2 is past the end of the array (that has type 'int[2][2]')}}
}

#endif
49 changes: 49 additions & 0 deletions clang/test/AST/ast-dump-anonymous-class.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown -ast-dump %s \
// RUN: | FileCheck -strict-whitespace %s

struct S {
struct {
int i;
};
};

int accessInRegularFunction() {
return S().i;
// CHECK: FunctionDecl {{.*}} accessInRegularFunction 'int ()'
// CHECK: | `-ReturnStmt {{.*}}
// CHECK-NEXT: | `-ExprWithCleanups {{.*}} 'int'
// CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
// CHECK-NEXT: | `-MemberExpr {{.*}} 'int' xvalue .i
// CHECK-NEXT: | `-MemberExpr {{.*}} 'S::(anonymous struct at {{.*}})
// CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'S' xvalue
// CHECK-NEXT: | `-CXXTemporaryObjectExpr {{.*}} 'S' 'void () noexcept' zeroing
}

// AST should look the same in a function template with an unused template
// parameter.
template <class>
int accessInFunctionTemplate() {
return S().i;
// CHECK: FunctionDecl {{.*}} accessInFunctionTemplate 'int ()'
// CHECK: | `-ReturnStmt {{.*}}
// CHECK-NEXT: | `-ExprWithCleanups {{.*}} 'int'
// CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
// CHECK-NEXT: | `-MemberExpr {{.*}} 'int' xvalue .i
// CHECK-NEXT: | `-MemberExpr {{.*}} 'S::(anonymous struct at {{.*}})
// CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'S' xvalue
// CHECK-NEXT: | `-CXXTemporaryObjectExpr {{.*}} 'S' 'void () noexcept' zeroing
}

// AST should look the same in an instantiation of the function template.
// This is a regression test: The AST used to contain the
// `MaterializeTemporaryExpr` in the wrong place, causing a `MemberExpr` to have
// a prvalue base (which is not allowed in C++).
template int accessInFunctionTemplate<int>();
// CHECK: FunctionDecl {{.*}} accessInFunctionTemplate 'int ()' explicit_instantiation_definition
// CHECK: `-ReturnStmt {{.*}}
// CHECK-NEXT: `-ExprWithCleanups {{.*}} 'int'
// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
// CHECK-NEXT: `-MemberExpr {{.*}} 'int' xvalue .i
// CHECK-NEXT: `-MemberExpr {{.*}} 'S::(anonymous struct at {{.*}})
// CHECK-NEXT: `-MaterializeTemporaryExpr {{.*}} 'S' xvalue
// CHECK-NEXT: `-CXXTemporaryObjectExpr {{.*}} 'S' 'void () noexcept' zeroing
7 changes: 7 additions & 0 deletions clang/test/Analysis/Checkers/WebKit/call-args.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -358,3 +358,10 @@ namespace call_with_ptr_on_ref {
// expected-warning@-1{{Call argument for parameter 'bad' is uncounted and unsafe}}
}
}

namespace call_with_explicit_temporary_obj {
void foo() {
Ref { *provide() }->method();
RefPtr { provide() }->method();
}
}
13 changes: 13 additions & 0 deletions clang/test/Analysis/unreachable-code-exceptions.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// RUN: %clang_analyze_cc1 -verify %s -fcxx-exceptions -fexceptions -analyzer-checker=core,alpha.deadcode.UnreachableCode

// expected-no-diagnostics

void foo();

void fp_90162() {
try { // no-warning: The TryStmt shouldn't be unreachable.
foo();
} catch (int) {
foo(); // We assume that catch handlers are reachable.
}
}
20 changes: 20 additions & 0 deletions clang/test/SemaCXX/cxx2c-attributes.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple x86_64-pc-linux -fsyntax-only -verify -Wno-c++17-extensions
// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple x86_64-windows-msvc -fsyntax-only -verify=msvc -Wno-c++17-extensions
// expected-no-diagnostics

// Check we return non-zero values for supported attributes as per
// wg21.link/P2552
static_assert(__has_cpp_attribute(assume));

// The standard does not prescribe a behavior for [[carries_dependency]]

static_assert(__has_cpp_attribute(deprecated));
static_assert(__has_cpp_attribute(fallthrough));
static_assert(__has_cpp_attribute(likely));
static_assert(__has_cpp_attribute(unlikely));
static_assert(__has_cpp_attribute(maybe_unused));
static_assert(__has_cpp_attribute(nodiscard));
static_assert(__has_cpp_attribute(noreturn));

// We do not support [[no_unique_address]] in MSVC emulation mode
static_assert(__has_cpp_attribute(no_unique_address)); // msvc-error {{static assertion failed}}
35 changes: 35 additions & 0 deletions clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@
#include <string>
#include <utility>

namespace clang {
namespace dataflow {
namespace {
AST_MATCHER(FunctionDecl, isTemplated) { return Node.isTemplated(); }
} // namespace
} // namespace dataflow
} // namespace clang

namespace {

using namespace clang;
Expand Down Expand Up @@ -7416,4 +7424,31 @@ TEST(TransferTest, ConditionalRelation) {
});
}

// This is a crash repro.
// We used to crash while transferring `S().i` because Clang contained a bug
// causing the AST to be malformed.
TEST(TransferTest, AnonymousUnionMemberExprInTemplate) {
using ast_matchers::functionDecl;
using ast_matchers::hasName;
using ast_matchers::unless;

std::string Code = R"cc(
struct S {
struct {
int i;
};
};

template <class>
void target() {
S().i;
}

template void target<int>();
)cc";
auto Matcher = functionDecl(hasName("target"), unless(isTemplated()));
ASSERT_THAT_ERROR(checkDataflowWithNoopAnalysis(Code, Matcher),
llvm::Succeeded());
}

} // namespace
111 changes: 54 additions & 57 deletions clang/unittests/Format/QualifierFixerTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1055,81 +1055,78 @@ TEST_F(QualifierFixerTest, IsQualifierType) {
ConfiguredTokens.push_back(tok::kw_constexpr);
ConfiguredTokens.push_back(tok::kw_friend);

auto Tokens = annotate(
TestLexer lexer{Allocator, Buffers};
const auto LangOpts = getFormattingLangOpts();

auto Tokens = lexer.lex(
"const static inline auto restrict int double long constexpr friend");
ASSERT_EQ(Tokens.size(), 11u) << Tokens;

EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
Tokens[0], ConfiguredTokens));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
Tokens[1], ConfiguredTokens));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
Tokens[2], ConfiguredTokens));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
Tokens[3], ConfiguredTokens));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
Tokens[4], ConfiguredTokens));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
Tokens[5], ConfiguredTokens));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
Tokens[6], ConfiguredTokens));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
Tokens[7], ConfiguredTokens));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
Tokens[8], ConfiguredTokens));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
Tokens[9], ConfiguredTokens));

EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(Tokens[0]));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(Tokens[1]));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(Tokens[2]));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(Tokens[3]));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(Tokens[4]));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(Tokens[5]));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(Tokens[6]));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(Tokens[7]));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(Tokens[8]));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(Tokens[9]));

auto NotTokens = annotate("for while do Foo Bar ");
EXPECT_TRUE(
isConfiguredQualifierOrType(Tokens[0], ConfiguredTokens, LangOpts));
EXPECT_TRUE(
isConfiguredQualifierOrType(Tokens[1], ConfiguredTokens, LangOpts));
EXPECT_TRUE(
isConfiguredQualifierOrType(Tokens[2], ConfiguredTokens, LangOpts));
EXPECT_TRUE(
isConfiguredQualifierOrType(Tokens[3], ConfiguredTokens, LangOpts));
EXPECT_TRUE(
isConfiguredQualifierOrType(Tokens[4], ConfiguredTokens, LangOpts));
EXPECT_TRUE(
isConfiguredQualifierOrType(Tokens[5], ConfiguredTokens, LangOpts));
EXPECT_TRUE(
isConfiguredQualifierOrType(Tokens[6], ConfiguredTokens, LangOpts));
EXPECT_TRUE(
isConfiguredQualifierOrType(Tokens[7], ConfiguredTokens, LangOpts));
EXPECT_TRUE(
isConfiguredQualifierOrType(Tokens[8], ConfiguredTokens, LangOpts));
EXPECT_TRUE(
isConfiguredQualifierOrType(Tokens[9], ConfiguredTokens, LangOpts));

EXPECT_TRUE(isQualifierOrType(Tokens[0], LangOpts));
EXPECT_TRUE(isQualifierOrType(Tokens[1], LangOpts));
EXPECT_TRUE(isQualifierOrType(Tokens[2], LangOpts));
EXPECT_TRUE(isQualifierOrType(Tokens[3], LangOpts));
EXPECT_TRUE(isQualifierOrType(Tokens[4], LangOpts));
EXPECT_TRUE(isQualifierOrType(Tokens[5], LangOpts));
EXPECT_TRUE(isQualifierOrType(Tokens[6], LangOpts));
EXPECT_TRUE(isQualifierOrType(Tokens[7], LangOpts));
EXPECT_TRUE(isQualifierOrType(Tokens[8], LangOpts));
EXPECT_TRUE(isQualifierOrType(Tokens[9], LangOpts));

auto NotTokens = lexer.lex("for while do Foo Bar ");
ASSERT_EQ(NotTokens.size(), 6u) << Tokens;

EXPECT_FALSE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
NotTokens[0], ConfiguredTokens));
EXPECT_FALSE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
NotTokens[1], ConfiguredTokens));
EXPECT_FALSE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
NotTokens[2], ConfiguredTokens));
EXPECT_FALSE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
NotTokens[3], ConfiguredTokens));
EXPECT_FALSE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
NotTokens[4], ConfiguredTokens));
EXPECT_FALSE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
NotTokens[5], ConfiguredTokens));

EXPECT_FALSE(
LeftRightQualifierAlignmentFixer::isQualifierOrType(NotTokens[0]));
isConfiguredQualifierOrType(NotTokens[0], ConfiguredTokens, LangOpts));
EXPECT_FALSE(
LeftRightQualifierAlignmentFixer::isQualifierOrType(NotTokens[1]));
isConfiguredQualifierOrType(NotTokens[1], ConfiguredTokens, LangOpts));
EXPECT_FALSE(
LeftRightQualifierAlignmentFixer::isQualifierOrType(NotTokens[2]));
isConfiguredQualifierOrType(NotTokens[2], ConfiguredTokens, LangOpts));
EXPECT_FALSE(
LeftRightQualifierAlignmentFixer::isQualifierOrType(NotTokens[3]));
isConfiguredQualifierOrType(NotTokens[3], ConfiguredTokens, LangOpts));
EXPECT_FALSE(
LeftRightQualifierAlignmentFixer::isQualifierOrType(NotTokens[4]));
isConfiguredQualifierOrType(NotTokens[4], ConfiguredTokens, LangOpts));
EXPECT_FALSE(
LeftRightQualifierAlignmentFixer::isQualifierOrType(NotTokens[5]));
isConfiguredQualifierOrType(NotTokens[5], ConfiguredTokens, LangOpts));

EXPECT_FALSE(isQualifierOrType(NotTokens[0], LangOpts));
EXPECT_FALSE(isQualifierOrType(NotTokens[1], LangOpts));
EXPECT_FALSE(isQualifierOrType(NotTokens[2], LangOpts));
EXPECT_FALSE(isQualifierOrType(NotTokens[3], LangOpts));
EXPECT_FALSE(isQualifierOrType(NotTokens[4], LangOpts));
EXPECT_FALSE(isQualifierOrType(NotTokens[5], LangOpts));
}

TEST_F(QualifierFixerTest, IsMacro) {

auto Tokens = annotate("INT INTPR Foo int");
ASSERT_EQ(Tokens.size(), 5u) << Tokens;

EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isPossibleMacro(Tokens[0]));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isPossibleMacro(Tokens[1]));
EXPECT_FALSE(LeftRightQualifierAlignmentFixer::isPossibleMacro(Tokens[2]));
EXPECT_FALSE(LeftRightQualifierAlignmentFixer::isPossibleMacro(Tokens[3]));
EXPECT_TRUE(isPossibleMacro(Tokens[0]));
EXPECT_TRUE(isPossibleMacro(Tokens[1]));
EXPECT_FALSE(isPossibleMacro(Tokens[2]));
EXPECT_FALSE(isPossibleMacro(Tokens[3]));
}

TEST_F(QualifierFixerTest, OverlappingQualifier) {
Expand Down
2 changes: 1 addition & 1 deletion clang/www/cxx_status.html
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ <h2 id="cxx26">C++2c implementation status</h2>
<tr>
<td>On the ignorability of standard attributes</td>
<td><a href="https://wg21.link/P2552R3">P2552R3</a> (<a href="#dr">DR</a>)</td>
<td class="none" align="center">No</td>
<td class="full" align="center">Yes</td>
</tr>
<tr>
<td>Static storage for braced initializers</td>
Expand Down
14 changes: 14 additions & 0 deletions flang/include/flang/Lower/StatementContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@
#include <functional>
#include <optional>

namespace mlir {
class Location;
class Region;
} // namespace mlir

namespace fir {
class FirOpBuilder;
}

namespace Fortran::lower {

/// When lowering a statement, temporaries for intermediate results may be
Expand Down Expand Up @@ -105,6 +114,11 @@ class StatementContext {
llvm::SmallVector<std::optional<CleanupFunction>> cufs;
};

/// If \p context contains any cleanups, ensure \p region has a block, and
/// generate the cleanup inside that block.
void genCleanUpInRegionIfAny(mlir::Location loc, fir::FirOpBuilder &builder,
mlir::Region &region, StatementContext &context);

} // namespace Fortran::lower

#endif // FORTRAN_LOWER_STATEMENTCONTEXT_H
24 changes: 23 additions & 1 deletion flang/include/flang/Optimizer/HLFIR/HLFIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -1330,7 +1330,8 @@ def hlfir_RegionAssignOp : hlfir_Op<"region_assign", [hlfir_OrderedAssignmentTre
}

def hlfir_YieldOp : hlfir_Op<"yield", [Terminator, ParentOneOf<["RegionAssignOp",
"ElementalAddrOp", "ForallOp", "ForallMaskOp", "WhereOp", "ElseWhereOp"]>,
"ElementalAddrOp", "ForallOp", "ForallMaskOp", "WhereOp", "ElseWhereOp",
"ExactlyOnceOp"]>,
SingleBlockImplicitTerminator<"fir::FirEndOp">, RecursivelySpeculatable,
RecursiveMemoryEffects]> {

Expand Down Expand Up @@ -1595,6 +1596,27 @@ def hlfir_ForallMaskOp : hlfir_AssignmentMaskOp<"forall_mask"> {
let hasVerifier = 1;
}

def hlfir_ExactlyOnceOp : hlfir_Op<"exactly_once", [RecursiveMemoryEffects]> {
let summary = "Execute exactly once its region in a WhereOp";
let description = [{
Inside a Where assignment, Fortran requires a non elemental call and its
arguments to be executed exactly once, regardless of the mask values.
This operation allows holding these evaluations that cannot be hoisted
until potential parent Forall loops have been created.
It also allows inlining the calls without losing the information that
these calls must be hoisted.
}];

let regions = (region SizedRegion<1>:$body);

let results = (outs AnyFortranEntity:$result);

let assemblyFormat = [{
attr-dict `:` type($result)
$body
}];
}

def hlfir_WhereOp : hlfir_AssignmentMaskOp<"where"> {
let summary = "Represent a Fortran where construct or statement";
let description = [{
Expand Down
45 changes: 23 additions & 22 deletions flang/lib/Lower/Bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3687,22 +3687,6 @@ class FirConverter : public Fortran::lower::AbstractConverter {
return hlfir::Entity{valueAndPair.first};
}

static void
genCleanUpInRegionIfAny(mlir::Location loc, fir::FirOpBuilder &builder,
mlir::Region &region,
Fortran::lower::StatementContext &context) {
if (!context.hasCode())
return;
mlir::OpBuilder::InsertPoint insertPt = builder.saveInsertionPoint();
if (region.empty())
builder.createBlock(&region);
else
builder.setInsertionPointToEnd(&region.front());
context.finalizeAndPop();
hlfir::YieldOp::ensureTerminator(region, builder, loc);
builder.restoreInsertionPoint(insertPt);
}

bool firstDummyIsPointerOrAllocatable(
const Fortran::evaluate::ProcedureRef &userDefinedAssignment) {
using DummyAttr = Fortran::evaluate::characteristics::DummyDataObject::Attr;
Expand Down Expand Up @@ -3928,23 +3912,24 @@ class FirConverter : public Fortran::lower::AbstractConverter {
Fortran::lower::StatementContext rhsContext;
hlfir::Entity rhs = evaluateRhs(rhsContext);
auto rhsYieldOp = builder.create<hlfir::YieldOp>(loc, rhs);
genCleanUpInRegionIfAny(loc, builder, rhsYieldOp.getCleanup(), rhsContext);
Fortran::lower::genCleanUpInRegionIfAny(
loc, builder, rhsYieldOp.getCleanup(), rhsContext);
// Lower LHS in its own region.
builder.createBlock(&regionAssignOp.getLhsRegion());
Fortran::lower::StatementContext lhsContext;
mlir::Value lhsYield = nullptr;
if (!lhsHasVectorSubscripts) {
hlfir::Entity lhs = evaluateLhs(lhsContext);
auto lhsYieldOp = builder.create<hlfir::YieldOp>(loc, lhs);
genCleanUpInRegionIfAny(loc, builder, lhsYieldOp.getCleanup(),
lhsContext);
Fortran::lower::genCleanUpInRegionIfAny(
loc, builder, lhsYieldOp.getCleanup(), lhsContext);
lhsYield = lhs;
} else {
hlfir::ElementalAddrOp elementalAddr =
Fortran::lower::convertVectorSubscriptedExprToElementalAddr(
loc, *this, assign.lhs, localSymbols, lhsContext);
genCleanUpInRegionIfAny(loc, builder, elementalAddr.getCleanup(),
lhsContext);
Fortran::lower::genCleanUpInRegionIfAny(
loc, builder, elementalAddr.getCleanup(), lhsContext);
lhsYield = elementalAddr.getYieldOp().getEntity();
}
assert(lhsYield && "must have been set");
Expand Down Expand Up @@ -4299,7 +4284,8 @@ class FirConverter : public Fortran::lower::AbstractConverter {
loc, *this, *maskExpr, localSymbols, maskContext);
mask = hlfir::loadTrivialScalar(loc, *builder, mask);
auto yieldOp = builder->create<hlfir::YieldOp>(loc, mask);
genCleanUpInRegionIfAny(loc, *builder, yieldOp.getCleanup(), maskContext);
Fortran::lower::genCleanUpInRegionIfAny(loc, *builder, yieldOp.getCleanup(),
maskContext);
}
void genFIR(const Fortran::parser::WhereConstructStmt &stmt) {
const Fortran::semantics::SomeExpr *maskExpr = Fortran::semantics::GetExpr(
Expand Down Expand Up @@ -5599,3 +5585,18 @@ Fortran::lower::LoweringBridge::LoweringBridge(
fir::support::setMLIRDataLayout(*module.get(),
targetMachine.createDataLayout());
}

void Fortran::lower::genCleanUpInRegionIfAny(
mlir::Location loc, fir::FirOpBuilder &builder, mlir::Region &region,
Fortran::lower::StatementContext &context) {
if (!context.hasCode())
return;
mlir::OpBuilder::InsertPoint insertPt = builder.saveInsertionPoint();
if (region.empty())
builder.createBlock(&region);
else
builder.setInsertionPointToEnd(&region.front());
context.finalizeAndPop();
hlfir::YieldOp::ensureTerminator(region, builder, loc);
builder.restoreInsertionPoint(insertPt);
}
38 changes: 38 additions & 0 deletions flang/lib/Lower/ConvertCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2682,10 +2682,48 @@ bool Fortran::lower::isIntrinsicModuleProcRef(
return module && module->attrs().test(Fortran::semantics::Attr::INTRINSIC);
}

static bool isInWhereMaskedExpression(fir::FirOpBuilder &builder) {
// The MASK of the outer WHERE is not masked itself.
mlir::Operation *op = builder.getRegion().getParentOp();
return op && op->getParentOfType<hlfir::WhereOp>();
}

std::optional<hlfir::EntityWithAttributes> Fortran::lower::convertCallToHLFIR(
mlir::Location loc, Fortran::lower::AbstractConverter &converter,
const evaluate::ProcedureRef &procRef, std::optional<mlir::Type> resultType,
Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
auto &builder = converter.getFirOpBuilder();
if (resultType && !procRef.IsElemental() &&
isInWhereMaskedExpression(builder) &&
!builder.getRegion().getParentOfType<hlfir::ExactlyOnceOp>()) {
// Non elemental calls inside a where-assignment-stmt must be executed
// exactly once without mask control. Lower them in a special region so that
// this can be enforced whenscheduling forall/where expression evaluations.
Fortran::lower::StatementContext localStmtCtx;
mlir::Type bogusType = builder.getIndexType();
auto exactlyOnce = builder.create<hlfir::ExactlyOnceOp>(loc, bogusType);
mlir::Block *block = builder.createBlock(&exactlyOnce.getBody());
builder.setInsertionPointToStart(block);
CallContext callContext(procRef, resultType, loc, converter, symMap,
localStmtCtx);
std::optional<hlfir::EntityWithAttributes> res =
genProcedureRef(callContext);
assert(res.has_value() && "must be a function");
auto yield = builder.create<hlfir::YieldOp>(loc, *res);
Fortran::lower::genCleanUpInRegionIfAny(loc, builder, yield.getCleanup(),
localStmtCtx);
builder.setInsertionPointAfter(exactlyOnce);
exactlyOnce->getResult(0).setType(res->getType());
if (hlfir::isFortranValue(exactlyOnce.getResult()))
return hlfir::EntityWithAttributes{exactlyOnce.getResult()};
// Create hlfir.declare for the result to satisfy
// hlfir::EntityWithAttributes requirements.
auto [exv, cleanup] = hlfir::translateToExtendedValue(
loc, builder, hlfir::Entity{exactlyOnce});
assert(!cleanup && "resut is a variable");
return hlfir::genDeclare(loc, builder, exv, ".func.pointer.result",
fir::FortranVariableFlagsAttr{});
}
CallContext callContext(procRef, resultType, loc, converter, symMap, stmtCtx);
return genProcedureRef(callContext);
}
Expand Down
7 changes: 7 additions & 0 deletions flang/lib/Lower/OpenMP/OpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2013,6 +2013,13 @@ static void genOMPDispatch(Fortran::lower::AbstractConverter &converter,
case llvm::omp::Directive::OMPD_taskyield:
genTaskyieldOp(converter, symTable, semaCtx, eval, loc, queue, item);
break;
case llvm::omp::Directive::OMPD_taskwait:
genTaskwaitOp(converter, symTable, semaCtx, eval, loc, clauses, queue,
item);
break;
case llvm::omp::Directive::OMPD_taskyield:
genTaskyieldOp(converter, symTable, semaCtx, eval, loc, queue, item);
break;
case llvm::omp::Directive::OMPD_teams:
genTeamsOp(converter, symTable, semaCtx, eval, loc, queue, item);
break;
Expand Down
130 changes: 108 additions & 22 deletions flang/lib/Optimizer/HLFIR/Transforms/LowerHLFIROrderedAssignments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ namespace {
/// expression and allows splitting the generation of the none elemental part
/// from the elemental part.
struct MaskedArrayExpr {
MaskedArrayExpr(mlir::Location loc, mlir::Region &region);
MaskedArrayExpr(mlir::Location loc, mlir::Region &region,
bool isOuterMaskExpr);

/// Generate the none elemental part. Must be called outside of the
/// loops created for the WHERE construct.
Expand All @@ -79,16 +80,25 @@ struct MaskedArrayExpr {
void generateNoneElementalCleanupIfAny(fir::FirOpBuilder &builder,
mlir::IRMapping &mapper);

/// Helper to clone the clean-ups of the masked expr region terminator.
/// This is called outside of the loops for the initial mask, and inside
/// the loops for the other masked expressions.
mlir::Operation *generateMaskedExprCleanUps(fir::FirOpBuilder &builder,
mlir::IRMapping &mapper);

mlir::Location loc;
mlir::Region &region;
/// Was generateNoneElementalPart called?
bool noneElementalPartWasGenerated = false;
/// Set of operations that form the elemental parts of the
/// expression evaluation. These are the hlfir.elemental and
/// hlfir.elemental_addr that form the elemental tree producing
/// the expression value. hlfir.elemental that produce values
/// used inside transformational operations are not part of this set.
llvm::SmallSet<mlir::Operation *, 4> elementalParts{};
/// Was generateNoneElementalPart called?
bool noneElementalPartWasGenerated = false;
/// Is this expression the mask expression of the outer where statement?
/// It is special because its evaluation is not masked by anything yet.
bool isOuterMaskExpr = false;
};
} // namespace

Expand Down Expand Up @@ -202,7 +212,7 @@ class OrderedAssignmentRewriter {
/// This method returns the scalar element (that may have been previously
/// saved) for the current indices inside the where loop.
mlir::Value generateMaskedEntity(mlir::Location loc, mlir::Region &region) {
MaskedArrayExpr maskedExpr(loc, region);
MaskedArrayExpr maskedExpr(loc, region, /*isOuterMaskExpr=*/!whereLoopNest);
return generateMaskedEntity(maskedExpr);
}
mlir::Value generateMaskedEntity(MaskedArrayExpr &maskedExpr);
Expand Down Expand Up @@ -524,7 +534,8 @@ void OrderedAssignmentRewriter::pre(hlfir::WhereOp whereOp) {
return;
}
// The mask was not evaluated yet or can be safely re-evaluated.
MaskedArrayExpr mask(loc, whereOp.getMaskRegion());
MaskedArrayExpr mask(loc, whereOp.getMaskRegion(),
/*isOuterMaskExpr=*/true);
mask.generateNoneElementalPart(builder, mapper);
mlir::Value shape = mask.generateShape(builder, mapper);
whereLoopNest = hlfir::genLoopNest(loc, builder, shape);
Expand Down Expand Up @@ -628,6 +639,13 @@ OrderedAssignmentRewriter::getIfSaved(mlir::Region &region) {
return std::nullopt;
}

static hlfir::YieldOp getYield(mlir::Region &region) {
auto yield = mlir::dyn_cast_or_null<hlfir::YieldOp>(
region.back().getOperations().back());
assert(yield && "region computing entities must end with a YieldOp");
return yield;
}

OrderedAssignmentRewriter::ValueAndCleanUp
OrderedAssignmentRewriter::generateYieldedEntity(
mlir::Region &region, std::optional<mlir::Type> castToType) {
Expand All @@ -644,9 +662,7 @@ OrderedAssignmentRewriter::generateYieldedEntity(
}

assert(region.hasOneBlock() && "region must contain one block");
auto oldYield = mlir::dyn_cast_or_null<hlfir::YieldOp>(
region.back().getOperations().back());
assert(oldYield && "region computing entities must end with a YieldOp");
auto oldYield = getYield(region);
mlir::Block::OpListType &ops = region.back().getOperations();

// Inside Forall, scalars that do not depend on forall indices can be hoisted
Expand Down Expand Up @@ -792,8 +808,15 @@ OrderedAssignmentRewriter::generateMaskedEntity(MaskedArrayExpr &maskedExpr) {
// at the current insertion point (inside the where loops, and any fir.if
// generated for previous masks).
builder.restoreInsertionPoint(insertionPoint);
return maskedExpr.generateElementalParts(
mlir::Value scalar = maskedExpr.generateElementalParts(
builder, whereLoopNest->oneBasedIndices, mapper);
/// Generate cleanups for the elemental parts inside the loops (setting the
/// location so that the assignment will be generated before the cleanups).
if (!maskedExpr.isOuterMaskExpr)
if (mlir::Operation *firstCleanup =
maskedExpr.generateMaskedExprCleanUps(builder, mapper))
builder.setInsertionPoint(firstCleanup);
return scalar;
}

void OrderedAssignmentRewriter::generateCleanupIfAny(
Expand Down Expand Up @@ -887,8 +910,9 @@ gatherElementalTree(hlfir::ElementalOpInterface elemental,
}
}

MaskedArrayExpr::MaskedArrayExpr(mlir::Location loc, mlir::Region &region)
: loc{loc}, region{region} {
MaskedArrayExpr::MaskedArrayExpr(mlir::Location loc, mlir::Region &region,
bool isOuterMaskExpr)
: loc{loc}, region{region}, isOuterMaskExpr{isOuterMaskExpr} {
mlir::Operation &terminator = region.back().back();
if (auto elementalAddr =
mlir::dyn_cast<hlfir::ElementalOpInterface>(terminator)) {
Expand All @@ -907,13 +931,36 @@ void MaskedArrayExpr::generateNoneElementalPart(fir::FirOpBuilder &builder,
mlir::IRMapping &mapper) {
assert(!noneElementalPartWasGenerated &&
"none elemental parts already generated");
// Clone all operations, except the elemental and the final yield.
mlir::Block::OpListType &ops = region.back().getOperations();
assert(!ops.empty() && "yield block cannot be empty");
auto end = ops.end();
for (auto opIt = ops.begin(); std::next(opIt) != end; ++opIt)
if (!elementalParts.contains(&*opIt))
(void)builder.clone(*opIt, mapper);
if (isOuterMaskExpr) {
// The outer mask expression is actually not masked, it is dealt as
// such so that its elemental part, if any, can be inlined in the WHERE
// loops. But all of the operations outside of hlfir.elemental/
// hlfir.elemental_addr must be emitted now because their value may be
// required to deduce the mask shape and the WHERE loop bounds.
for (mlir::Operation &op : region.back().without_terminator())
if (!elementalParts.contains(&op))
(void)builder.clone(op, mapper);
} else {
// For actual masked expressions, Fortran requires elemental expressions,
// even the scalar ones that are not encoded with hlfir.elemental, to be
// evaluated only when the mask is true. Blindly hoisting all scalar SSA
// tree could be wrong if the scalar computation has side effects and
// would never have been evaluated (e.g. division by zero) if the mask
// is fully false. See F'2023 10.2.3.2 point 10.
// Clone only the bodies of all hlfir.exactly_once operations, which contain
// the evaluation of sub-expression tree whose root was a non elemental
// function call at the Fortran level (the call itself may have been inlined
// since). These must be evaluated only once as per F'2023 10.2.3.2 point 9.
for (mlir::Operation &op : region.back().without_terminator())
if (auto exactlyOnce = mlir::dyn_cast<hlfir::ExactlyOnceOp>(op)) {
for (mlir::Operation &subOp :
exactlyOnce.getBody().back().without_terminator())
(void)builder.clone(subOp, mapper);
mlir::Value oldYield = getYield(exactlyOnce.getBody()).getEntity();
auto newYield = mapper.lookupOrDefault(oldYield);
mapper.map(exactlyOnce.getResult(), newYield);
}
}
noneElementalPartWasGenerated = true;
}

Expand Down Expand Up @@ -942,6 +989,15 @@ MaskedArrayExpr::generateElementalParts(fir::FirOpBuilder &builder,
mlir::IRMapping &mapper) {
assert(noneElementalPartWasGenerated &&
"non elemental part must have been generated");
if (!isOuterMaskExpr) {
// Clone all operations that are not hlfir.exactly_once and that are not
// hlfir.elemental/hlfir.elemental_addr.
for (mlir::Operation &op : region.back().without_terminator())
if (!mlir::isa<hlfir::ExactlyOnceOp>(op) && !elementalParts.contains(&op))
(void)builder.clone(op, mapper);
// For the outer mask, this was already done outside of the loop.
}
// Clone and "index" bodies of hlfir.elemental/hlfir.elemental_addr.
mlir::Operation &terminator = region.back().back();
hlfir::ElementalOpInterface elemental =
mlir::dyn_cast<hlfir::ElementalAddrOp>(terminator);
Expand All @@ -966,8 +1022,11 @@ MaskedArrayExpr::generateElementalParts(fir::FirOpBuilder &builder,
mustRecursivelyInline);
}

void MaskedArrayExpr::generateNoneElementalCleanupIfAny(
fir::FirOpBuilder &builder, mlir::IRMapping &mapper) {
mlir::Operation *
MaskedArrayExpr::generateMaskedExprCleanUps(fir::FirOpBuilder &builder,
mlir::IRMapping &mapper) {
// Clone the clean-ups from the region itself, except for the destroy
// of the hlfir.elemental that have been inlined.
mlir::Operation &terminator = region.back().back();
mlir::Region *cleanupRegion = nullptr;
if (auto elementalAddr = mlir::dyn_cast<hlfir::ElementalAddrOp>(terminator)) {
Expand All @@ -977,12 +1036,39 @@ void MaskedArrayExpr::generateNoneElementalCleanupIfAny(
cleanupRegion = &yieldOp.getCleanup();
}
if (cleanupRegion->empty())
return;
return nullptr;
mlir::Operation *firstNewCleanup = nullptr;
for (mlir::Operation &op : cleanupRegion->front().without_terminator()) {
if (auto destroy = mlir::dyn_cast<hlfir::DestroyOp>(op))
if (elementalParts.contains(destroy.getExpr().getDefiningOp()))
continue;
(void)builder.clone(op, mapper);
mlir::Operation *cleanup = builder.clone(op, mapper);
if (!firstNewCleanup)
firstNewCleanup = cleanup;
}
return firstNewCleanup;
}

void MaskedArrayExpr::generateNoneElementalCleanupIfAny(
fir::FirOpBuilder &builder, mlir::IRMapping &mapper) {
if (!isOuterMaskExpr) {
// Clone clean-ups of hlfir.exactly_once operations (in reverse order
// to properly deal with stack restores).
for (mlir::Operation &op :
llvm::reverse(region.back().without_terminator()))
if (auto exactlyOnce = mlir::dyn_cast<hlfir::ExactlyOnceOp>(op)) {
mlir::Region &cleanupRegion =
getYield(exactlyOnce.getBody()).getCleanup();
if (!cleanupRegion.empty())
for (mlir::Operation &cleanupOp :
cleanupRegion.front().without_terminator())
(void)builder.clone(cleanupOp, mapper);
}
} else {
// For the outer mask, the region clean-ups must be generated
// outside of the loops since the mask non hlfir.elemental part
// is generated before the loops.
generateMaskedExprCleanUps(builder, mapper);
}
}

Expand Down
4 changes: 4 additions & 0 deletions flang/test/Driver/print-resource-dir.F90
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
! DEFINE: %{resource_dir} = %S/Inputs/resource_dir
! RUN: %flang -print-resource-dir -resource-dir=%{resource_dir}.. \
! RUN: | FileCheck -check-prefix=PRINT-RESOURCE-DIR -DFILE=%{resource_dir} %s
! PRINT-RESOURCE-DIR: [[FILE]]
9 changes: 6 additions & 3 deletions flang/test/HLFIR/order_assignments/impure-where.fir
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ func.func @test_elsewhere_impure_mask(%x: !fir.ref<!fir.array<10xi32>>, %y: !fir
hlfir.yield %mask : !fir.ref<!fir.array<10x!fir.logical<4>>>
} do {
hlfir.elsewhere mask {
%mask2 = fir.call @impure() : () -> !fir.heap<!fir.array<10x!fir.logical<4>>>
hlfir.yield %mask2 : !fir.heap<!fir.array<10x!fir.logical<4>>> cleanup {
fir.freemem %mask2 : !fir.heap<!fir.array<10x!fir.logical<4>>>
%mask2 = hlfir.exactly_once : !fir.heap<!fir.array<10x!fir.logical<4>>> {
%imp = fir.call @impure() : () -> !fir.heap<!fir.array<10x!fir.logical<4>>>
hlfir.yield %imp : !fir.heap<!fir.array<10x!fir.logical<4>>> cleanup {
fir.freemem %imp : !fir.heap<!fir.array<10x!fir.logical<4>>>
}
}
hlfir.yield %mask2 : !fir.heap<!fir.array<10x!fir.logical<4>>>
} do {
hlfir.region_assign {
hlfir.yield %y : !fir.ref<!fir.array<10xi32>>
Expand Down
2 changes: 1 addition & 1 deletion flang/test/HLFIR/order_assignments/inlined-stack-temp.fir
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,6 @@ func.func @test_where_rhs_save(%x: !fir.ref<!fir.array<10xi32>>, %mask: !fir.ref
// CHECK: %[[VAL_7:.*]] = arith.constant 10 : index
// CHECK: %[[VAL_8:.*]] = fir.shape %[[VAL_7]] : (index) -> !fir.shape<1>
// CHECK: %[[VAL_9:.*]] = arith.constant 1 : index
// CHECK: %[[VAL_10:.*]] = hlfir.designate %[[VAL_0]] (%[[VAL_5]]:%[[VAL_4]]:%[[VAL_3]]) shape %[[VAL_6]] : (!fir.ref<!fir.array<10xi32>>, index, index, index, !fir.shape<1>) -> !fir.ref<!fir.array<10xi32>>
// CHECK: %[[VAL_11:.*]] = arith.constant 0 : index
// CHECK: %[[VAL_12:.*]] = arith.subi %[[VAL_7]], %[[VAL_9]] : index
// CHECK: %[[VAL_13:.*]] = arith.addi %[[VAL_12]], %[[VAL_9]] : index
Expand All @@ -300,6 +299,7 @@ func.func @test_where_rhs_save(%x: !fir.ref<!fir.array<10xi32>>, %mask: !fir.ref
// CHECK: %[[VAL_24:.*]] = fir.load %[[VAL_23]] : !fir.ref<!fir.logical<4>>
// CHECK: %[[VAL_25:.*]] = fir.convert %[[VAL_24]] : (!fir.logical<4>) -> i1
// CHECK: fir.if %[[VAL_25]] {
// CHECK: %[[VAL_10:.*]] = hlfir.designate %[[VAL_0]] (%[[VAL_5]]:%[[VAL_4]]:%[[VAL_3]]) shape %[[VAL_6]] : (!fir.ref<!fir.array<10xi32>>, index, index, index, !fir.shape<1>) -> !fir.ref<!fir.array<10xi32>>
// CHECK: %[[VAL_26:.*]] = hlfir.designate %[[VAL_10]] (%[[VAL_22]]) : (!fir.ref<!fir.array<10xi32>>, index) -> !fir.ref<i32>
// CHECK: %[[VAL_27:.*]] = fir.load %[[VAL_26]] : !fir.ref<i32>
// CHECK: %[[VAL_28:.*]] = fir.load %[[VAL_2]] : !fir.ref<index>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func.func @_QPtest1() {
%7 = fir.call @_FortranADestroy(%6) fastmath<contract> : (!fir.box<none>) -> none
}
} to {
hlfir.yield %2#0 : !fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
hlfir.yield %2#0 : !fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
} user_defined_assign (%arg0: !fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) to (%arg1: !fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) {
%3 = fir.embox %arg1 : (!fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) -> !fir.box<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
%4 = fir.convert %3 : (!fir.box<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) -> !fir.class<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
Expand Down Expand Up @@ -119,7 +119,7 @@ func.func @_QPtest2() {
fir.call @llvm.stackrestore.p0(%4) fastmath<contract> : (!fir.ref<i8>) -> ()
}
} to {
hlfir.yield %3#0 : !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>
hlfir.yield %3#0 : !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>
} user_defined_assign (%arg0: !fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) to (%arg1: !fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) {
%4 = fir.embox %arg1 : (!fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) -> !fir.box<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
%5 = fir.convert %4 : (!fir.box<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) -> !fir.class<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
Expand Down Expand Up @@ -193,18 +193,22 @@ func.func @_QPtest3(%arg0: !fir.ref<!fir.array<2xi32>> {fir.bindc_name = "y"}) {
}
} do {
hlfir.region_assign {
%5 = fir.call @llvm.stacksave.p0() fastmath<contract> : () -> !fir.ref<i8>
%6 = fir.call @_QPnew_obja() fastmath<contract> : () -> !fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
fir.save_result %6 to %0(%2) : !fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>, !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>, !fir.shape<1>
%7:2 = hlfir.declare %0(%2) {uniq_name = ".tmp.func_result"} : (!fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>, !fir.shape<1>) -> (!fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>, !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>)
hlfir.yield %7#0 : !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>> cleanup {
%8 = fir.embox %0(%2) : (!fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>, !fir.shape<1>) -> !fir.box<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>
%9 = fir.convert %8 : (!fir.box<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>) -> !fir.box<none>
%10 = fir.call @_FortranADestroy(%9) fastmath<contract> : (!fir.box<none>) -> none
fir.call @llvm.stackrestore.p0(%5) fastmath<contract> : (!fir.ref<i8>) -> ()
%5 = hlfir.exactly_once : !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>> {
%7 = fir.call @llvm.stacksave.p0() fastmath<contract> : () -> !fir.ref<i8>
%8 = fir.call @_QPnew_obja() fastmath<contract> : () -> !fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
fir.save_result %8 to %0(%2) : !fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>, !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>, !fir.shape<1>
%9:2 = hlfir.declare %0(%2) {uniq_name = ".tmp.func_result"} : (!fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>, !fir.shape<1>) -> (!fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>, !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>)
hlfir.yield %9#0 : !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>> cleanup {
%10 = fir.embox %0(%2) : (!fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>, !fir.shape<1>) -> !fir.box<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>
%11 = fir.convert %10 : (!fir.box<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>) -> !fir.box<none>
%12 = fir.call @_FortranADestroy(%11) fastmath<contract> : (!fir.box<none>) -> none
fir.call @llvm.stackrestore.p0(%7) fastmath<contract> : (!fir.ref<i8>) -> ()
}
}
%6:2 = hlfir.declare %5(%2) {uniq_name = ".func.pointer.result"} : (!fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>, !fir.shape<1>) -> (!fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>, !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>)
hlfir.yield %6#0 : !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>
} to {
hlfir.yield %3#0 : !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>
hlfir.yield %3#0 : !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>
} user_defined_assign (%arg1: !fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) to (%arg2: !fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) {
%5 = fir.embox %arg2 : (!fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) -> !fir.box<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
%6 = fir.convert %5 : (!fir.box<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) -> !fir.class<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
Expand Down Expand Up @@ -246,7 +250,8 @@ func.func @_QPtest3(%arg0: !fir.ref<!fir.array<2xi32>> {fir.bindc_name = "y"}) {
// CHECK: %[[VAL_30:.*]] = fir.load %[[VAL_29]] : !fir.ref<!fir.logical<4>>
// CHECK: %[[VAL_31:.*]] = fir.convert %[[VAL_30]] : (!fir.logical<4>) -> i1
// CHECK: fir.if %[[VAL_31]] {
// CHECK: %[[VAL_32:.*]] = hlfir.designate %[[VAL_20]]#0 (%[[VAL_28]]) : (!fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>, index) -> !fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
// CHECK: %[[VAL_20B:.*]]:2 = hlfir.declare %[[VAL_20]]#0(%[[VAL_7]]) {uniq_name = ".func.pointer.result"}
// CHECK: %[[VAL_32:.*]] = hlfir.designate %[[VAL_20B]]#0 (%[[VAL_28]]) : (!fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>, index) -> !fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
// CHECK: %[[VAL_33:.*]] = fir.embox %[[VAL_32]] : (!fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) -> !fir.box<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
// CHECK: %[[VAL_34:.*]] = fir.convert %[[VAL_33]] : (!fir.box<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) -> !fir.box<none>
// CHECK: %[[VAL_35:.*]] = fir.call @_FortranAPushValue(%[[VAL_27]], %[[VAL_34]]) : (!fir.llvm_ptr<i8>, !fir.box<none>) -> none
Expand Down
44 changes: 44 additions & 0 deletions flang/test/HLFIR/order_assignments/where-cleanup.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Test hlfir.where masked region cleanup lowering (the freemem in the tests).
// RUN: fir-opt %s --lower-hlfir-ordered-assignments | FileCheck %s

func.func @loop_cleanup(%mask : !fir.ref<!fir.array<2x!fir.logical<4>>>, %x : !fir.ref<!fir.array<2xf32>>, %y : !fir.ref<!fir.array<2xf32>>) {
hlfir.where {
%1 = fir.allocmem !fir.array<10xi32>
hlfir.yield %mask : !fir.ref<!fir.array<2x!fir.logical<4>>> cleanup {
fir.freemem %1 : !fir.heap<!fir.array<10xi32>>
}
} do {
hlfir.region_assign {
%1 = fir.allocmem !fir.array<1xi32>
%2 = fir.allocmem !fir.array<2xi32>
hlfir.yield %x : !fir.ref<!fir.array<2xf32>> cleanup {
fir.freemem %2 : !fir.heap<!fir.array<2xi32>>
fir.freemem %1 : !fir.heap<!fir.array<1xi32>>
}
} to {
%1 = fir.allocmem !fir.array<3xi32>
%2 = fir.allocmem !fir.array<4xi32>
hlfir.yield %y : !fir.ref<!fir.array<2xf32>> cleanup {
fir.freemem %2 : !fir.heap<!fir.array<4xi32>>
fir.freemem %1 : !fir.heap<!fir.array<3xi32>>
}
}
}
return
}
// CHECK-LABEL: func.func @loop_cleanup(
// CHECK: %[[VAL_3:.*]] = fir.allocmem !fir.array<10xi32>
// CHECK: fir.do_loop
// CHECK: fir.if
// CHECK: %[[VAL_11:.*]] = fir.allocmem !fir.array<1xi32>
// CHECK: %[[VAL_12:.*]] = fir.allocmem !fir.array<2xi32>
// CHECK: %[[VAL_14:.*]] = fir.allocmem !fir.array<3xi32>
// CHECK: %[[VAL_15:.*]] = fir.allocmem !fir.array<4xi32>
// CHECK: hlfir.assign
// CHECK: fir.freemem %[[VAL_15]] : !fir.heap<!fir.array<4xi32>>
// CHECK: fir.freemem %[[VAL_14]] : !fir.heap<!fir.array<3xi32>>
// CHECK: fir.freemem %[[VAL_12]] : !fir.heap<!fir.array<2xi32>>
// CHECK: fir.freemem %[[VAL_11]] : !fir.heap<!fir.array<1xi32>>
// CHECK: }
// CHECK: }
// CHECK: fir.freemem %[[VAL_3]] : !fir.heap<!fir.array<10xi32>>
Original file line number Diff line number Diff line change
Expand Up @@ -290,8 +290,6 @@ func.func @inside_forall(%arg0: !fir.ref<!fir.array<10x20xf32>>, %arg1: !fir.ref
// CHECK: fir.do_loop %[[VAL_15:.*]] = %[[VAL_12]] to %[[VAL_13]] step %[[VAL_14]] {
// CHECK: %[[VAL_16:.*]] = fir.convert %[[VAL_15]] : (index) -> i32
// CHECK: %[[VAL_17:.*]] = arith.constant 1 : index
// CHECK: %[[VAL_18:.*]] = fir.convert %[[VAL_16]] : (i32) -> i64
// CHECK: %[[VAL_19:.*]] = hlfir.designate %[[VAL_9]]#0 (%[[VAL_18]], %[[VAL_2]]:%[[VAL_7]]:%[[VAL_2]]) shape %[[VAL_10]] : (!fir.ref<!fir.array<10x20xf32>>, i64, index, index, index, !fir.shape<1>) -> !fir.box<!fir.array<20xf32>>
// CHECK: fir.do_loop %[[VAL_20:.*]] = %[[VAL_17]] to %[[VAL_7]] step %[[VAL_17]] {
// CHECK: %[[VAL_21:.*]] = hlfir.designate %[[VAL_11]]#0 (%[[VAL_20]]) : (!fir.ref<!fir.array<20xf32>>, index) -> !fir.ref<f32>
// CHECK: %[[VAL_22:.*]] = fir.load %[[VAL_21]] : !fir.ref<f32>
Expand All @@ -300,6 +298,8 @@ func.func @inside_forall(%arg0: !fir.ref<!fir.array<10x20xf32>>, %arg1: !fir.ref
// CHECK: %[[VAL_25:.*]] = fir.convert %[[VAL_24]] : (!fir.logical<4>) -> i1
// CHECK: fir.if %[[VAL_25]] {
// CHECK: %[[VAL_26:.*]] = hlfir.designate %[[VAL_11]]#0 (%[[VAL_20]]) : (!fir.ref<!fir.array<20xf32>>, index) -> !fir.ref<f32>
// CHECK: %[[VAL_18:.*]] = fir.convert %[[VAL_16]] : (i32) -> i64
// CHECK: %[[VAL_19:.*]] = hlfir.designate %[[VAL_9]]#0 (%[[VAL_18]], %[[VAL_2]]:%[[VAL_7]]:%[[VAL_2]]) shape %[[VAL_10]] : (!fir.ref<!fir.array<10x20xf32>>, i64, index, index, index, !fir.shape<1>) -> !fir.box<!fir.array<20xf32>>
// CHECK: %[[VAL_27:.*]] = hlfir.designate %[[VAL_19]] (%[[VAL_20]]) : (!fir.box<!fir.array<20xf32>>, index) -> !fir.ref<f32>
// CHECK: hlfir.assign %[[VAL_26]] to %[[VAL_27]] : !fir.ref<f32>, !fir.ref<f32>
// CHECK: }
Expand Down
50 changes: 50 additions & 0 deletions flang/test/HLFIR/order_assignments/where-hoisting.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
! Test that scalar expressions are not hoisted from WHERE loops
! when they do not appear
! RUN: bbc -hlfir -o - -pass-pipeline="builtin.module(lower-hlfir-ordered-assignments)" %s | FileCheck %s

subroutine do_not_hoist_div(n, mask, a)
integer :: a(10), n
logical :: mask(10)
where(mask) a=1/n
end subroutine
! CHECK-LABEL: func.func @_QPdo_not_hoist_div(
! CHECK-NOT: arith.divsi
! CHECK: fir.do_loop {{.*}} {
! CHECK: fir.if {{.*}} {
! CHECK: arith.divsi
! CHECK: }
! CHECK: }

subroutine do_not_hoist_optional(n, mask, a)
integer :: a(10)
integer, optional :: n
logical :: mask(10)
where(mask) a=n
end subroutine
! CHECK-LABEL: func.func @_QPdo_not_hoist_optional(
! CHECK: %[[VAL_9:.*]]:2 = hlfir.declare {{.*}}"_QFdo_not_hoist_optionalEn"
! CHECK-NOT: fir.load %[[VAL_9]]
! CHECK: fir.do_loop {{.*}} {
! CHECK: fir.if {{.*}} {
! CHECK: %[[VAL_15:.*]] = fir.load %[[VAL_9]]#0 : !fir.ref<i32>
! CHECK: }
! CHECK: }

subroutine hoist_function(n, mask, a)
integer :: a(10, 10)
integer, optional :: n
logical :: mask(10, 10)
forall (i=1:10)
where(mask(i, :)) a(i,:)=ihoist_me(i)
end forall
end subroutine
! CHECK-LABEL: func.func @_QPhoist_function(
! CHECK: fir.do_loop {{.*}} {
! CHECK: fir.call @_QPihoist_me
! CHECK: fir.do_loop {{.*}} {
! CHECK: fir.if %{{.*}} {
! CHECK-NOT: fir.call @_QPihoist_me
! CHECK: }
! CHECK: }
! CHECK: }
! CHECK-NOT: fir.call @_QPihoist_me
198 changes: 198 additions & 0 deletions flang/test/Lower/HLFIR/where-nonelemental.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
! Test lowering of non elemental calls and there inputs inside WHERE
! constructs. These must be lowered inside hlfir.exactly_once so that
! they are properly hoisted once the loops are materialized and
! expression evaluations are scheduled.
! RUN: bbc -emit-hlfir -o - %s | FileCheck %s

subroutine test_where(a, b, c)
real, dimension(:) :: a, b, c
interface
function logical_func1()
logical :: logical_func1(100)
end function
function logical_func2()
logical :: logical_func2(100)
end function
real elemental function elem_func(x)
real, intent(in) :: x
end function
end interface
where (logical_func1())
a = b + real_func(a+b+real_func2()) + elem_func(a)
elsewhere(logical_func2())
a(1:ifoo()) = c
end where
end subroutine
! CHECK-LABEL: func.func @_QPtest_where(
! CHECK: hlfir.where {
! CHECK-NOT: hlfir.exactly_once
! CHECK: %[[VAL_17:.*]] = fir.call @llvm.stacksave.p0() fastmath<contract> : () -> !fir.ref<i8>
! CHECK: %[[VAL_19:.*]] = fir.call @_QPlogical_func1() fastmath<contract> : () -> !fir.array<100x!fir.logical<4>>
! CHECK: hlfir.yield %{{.*}} : !hlfir.expr<100x!fir.logical<4>> cleanup {
! CHECK: fir.call @llvm.stackrestore.p0(%[[VAL_17]]) fastmath<contract> : (!fir.ref<i8>) -> ()
! CHECK: }
! CHECK: } do {
! CHECK: hlfir.region_assign {
! CHECK: %[[VAL_24:.*]] = hlfir.exactly_once : f32 {
! CHECK: %[[VAL_28:.*]] = hlfir.elemental %{{.*}} unordered : (!fir.shape<1>) -> !hlfir.expr<?xf32> {
! CHECK: }
! CHECK-NOT: hlfir.exactly_once
! CHECK: %[[VAL_35:.*]] = fir.call @_QPreal_func2() fastmath<contract> : () -> f32
! CHECK: %[[VAL_36:.*]] = hlfir.elemental %{{.*}} unordered : (!fir.shape<1>) -> !hlfir.expr<?xf32> {
! CHECK: ^bb0(%[[VAL_37:.*]]: index):
! CHECK: %[[VAL_38:.*]] = hlfir.apply %[[VAL_28]], %[[VAL_37]] : (!hlfir.expr<?xf32>, index) -> f32
! CHECK: %[[VAL_39:.*]] = arith.addf %[[VAL_38]], %[[VAL_35]] fastmath<contract> : f32
! CHECK: hlfir.yield_element %[[VAL_39]] : f32
! CHECK: }
! CHECK: %[[VAL_41:.*]] = fir.call @_QPreal_func
! CHECK: hlfir.yield %[[VAL_41]] : f32 cleanup {
! CHECK: hlfir.destroy %[[VAL_36]] : !hlfir.expr<?xf32>
! CHECK: hlfir.destroy %[[VAL_28]] : !hlfir.expr<?xf32>
! CHECK: }
! CHECK: }
! CHECK: %[[VAL_45:.*]] = hlfir.elemental %{{.*}} unordered : (!fir.shape<1>) -> !hlfir.expr<?xf32> {
! CHECK: arith.addf
! CHECK-NOT: hlfir.exactly_once
! CHECK: }
! CHECK: %[[VAL_53:.*]] = hlfir.elemental %{{.*}} unordered : (!fir.shape<1>) -> !hlfir.expr<?xf32> {
! CHECK: fir.call @_QPelem_func
! CHECK: }
! CHECK: %[[VAL_57:.*]] = hlfir.elemental %{{.*}} unordered : (!fir.shape<1>) -> !hlfir.expr<?xf32> {
! CHECK: arith.addf
! CHECK: }
! CHECK: hlfir.yield %[[VAL_57]] : !hlfir.expr<?xf32> cleanup {
! CHECK: hlfir.destroy %[[VAL_57]] : !hlfir.expr<?xf32>
! CHECK: hlfir.destroy %[[VAL_53]] : !hlfir.expr<?xf32>
! CHECK: hlfir.destroy %[[VAL_45]] : !hlfir.expr<?xf32>
! CHECK: }
! CHECK: } to {
! CHECK: hlfir.yield %{{.*}} : !fir.box<!fir.array<?xf32>>
! CHECK: }
! CHECK: hlfir.elsewhere mask {
! CHECK: %[[VAL_62:.*]] = hlfir.exactly_once : !hlfir.expr<100x!fir.logical<4>> {
! CHECK: %[[VAL_72:.*]] = fir.call @llvm.stacksave.p0() fastmath<contract> : () -> !fir.ref<i8>
! CHECK: fir.call @_QPlogical_func2() fastmath<contract> : () -> !fir.array<100x!fir.logical<4>>
! CHECK: hlfir.yield %{{.*}} : !hlfir.expr<100x!fir.logical<4>> cleanup {
! CHECK: fir.call @llvm.stackrestore.p0(%[[VAL_72]]) fastmath<contract> : (!fir.ref<i8>) -> ()
! CHECK: }
! CHECK: }
! CHECK: hlfir.yield %[[VAL_62]] : !hlfir.expr<100x!fir.logical<4>>
! CHECK: } do {
! CHECK: hlfir.region_assign {
! CHECK: hlfir.yield %{{.*}} : !fir.box<!fir.array<?xf32>>
! CHECK: } to {
! CHECK: %[[VAL_80:.*]] = hlfir.exactly_once : i32 {
! CHECK: %[[VAL_81:.*]] = fir.call @_QPifoo() fastmath<contract> : () -> i32
! CHECK: hlfir.yield %[[VAL_81]] : i32
! CHECK: }
! CHECK: hlfir.yield %{{.*}} : !fir.box<!fir.array<?xf32>>
! CHECK: }
! CHECK: }
! CHECK: }
! CHECK: return
! CHECK: }

subroutine test_where_in_forall(a, b, c)
real, dimension(:, :) :: a, b, c
interface
pure function pure_logical_func1()
logical :: pure_logical_func1(100)
end function
pure function pure_logical_func2()
logical :: pure_logical_func2(100)
end function
real pure elemental function pure_elem_func(x)
real, intent(in) :: x
end function
integer pure function pure_ifoo()
end function
end interface
forall(i=1:10)
where (pure_logical_func1())
a(2*i, :) = b(i, :) + pure_real_func(a(i,:)+b(i,:)+pure_real_func2()) + pure_elem_func(a(i,:))
elsewhere(pure_logical_func2())
a(2*i, 1:pure_ifoo()) = c(i, :)
end where
end forall
end subroutine
! CHECK-LABEL: func.func @_QPtest_where_in_forall(
! CHECK: hlfir.forall lb {
! CHECK: hlfir.yield %{{.*}} : i32
! CHECK: } ub {
! CHECK: hlfir.yield %{{.*}} : i32
! CHECK: } (%[[VAL_10:.*]]: i32) {
! CHECK: %[[VAL_11:.*]] = hlfir.forall_index "i" %[[VAL_10]] : (i32) -> !fir.ref<i32>
! CHECK: hlfir.where {
! CHECK: %[[VAL_21:.*]] = fir.call @llvm.stacksave.p0() fastmath<contract> : () -> !fir.ref<i8>
! CHECK-NOT: hlfir.exactly_once
! CHECK: %[[VAL_23:.*]] = fir.call @_QPpure_logical_func1() fastmath<contract> : () -> !fir.array<100x!fir.logical<4>>
! CHECK: hlfir.yield %{{.*}} : !hlfir.expr<100x!fir.logical<4>> cleanup {
! CHECK: fir.call @llvm.stackrestore.p0(%[[VAL_21]]) fastmath<contract> : (!fir.ref<i8>) -> ()
! CHECK: }
! CHECK: } do {
! CHECK: hlfir.region_assign {
! CHECK: %[[VAL_41:.*]] = hlfir.designate
! CHECK: %[[VAL_42:.*]] = hlfir.exactly_once : f32 {
! CHECK: hlfir.designate
! CHECK: hlfir.designate
! CHECK: %[[VAL_71:.*]] = hlfir.elemental %{{.*}} unordered : (!fir.shape<1>) -> !hlfir.expr<?xf32> {
! CHECK: arith.addf
! CHECK: }
! CHECK-NOT: hlfir.exactly_once
! CHECK: %[[VAL_78:.*]] = fir.call @_QPpure_real_func2() fastmath<contract> : () -> f32
! CHECK: %[[VAL_79:.*]] = hlfir.elemental %{{.*}} unordered : (!fir.shape<1>) -> !hlfir.expr<?xf32> {
! CHECK: arith.addf
! CHECK: }
! CHECK: %[[VAL_84:.*]] = fir.call @_QPpure_real_func(
! CHECK: hlfir.yield %[[VAL_84]] : f32 cleanup {
! CHECK: hlfir.destroy %[[VAL_79]] : !hlfir.expr<?xf32>
! CHECK: hlfir.destroy %[[VAL_71]] : !hlfir.expr<?xf32>
! CHECK: }
! CHECK: }
! CHECK: %[[VAL_85:.*]] = hlfir.elemental %{{.*}} unordered : (!fir.shape<1>) -> !hlfir.expr<?xf32> {
! CHECK: arith.addf
! CHECK: }
! CHECK-NOT: hlfir.exactly_once
! CHECK: %[[VAL_104:.*]] = hlfir.elemental %{{.*}} unordered : (!fir.shape<1>) -> !hlfir.expr<?xf32> {
! CHECK: ^bb0(%[[VAL_105:.*]]: index):
! CHECK-NOT: hlfir.exactly_once
! CHECK: fir.call @_QPpure_elem_func
! CHECK: }
! CHECK: %[[VAL_108:.*]] = hlfir.elemental %{{.*}} unordered : (!fir.shape<1>) -> !hlfir.expr<?xf32> {
! CHECK: arith.addf
! CHECK: }
! CHECK: hlfir.yield %[[VAL_108]] : !hlfir.expr<?xf32> cleanup {
! CHECK: hlfir.destroy %[[VAL_108]] : !hlfir.expr<?xf32>
! CHECK: hlfir.destroy %[[VAL_104]] : !hlfir.expr<?xf32>
! CHECK: hlfir.destroy %[[VAL_85]] : !hlfir.expr<?xf32>
! CHECK: }
! CHECK: } to {
! CHECK: hlfir.designate
! CHECK: hlfir.yield %{{.*}} : !fir.box<!fir.array<?xf32>>
! CHECK: }
! CHECK: hlfir.elsewhere mask {
! CHECK: %[[VAL_129:.*]] = hlfir.exactly_once : !hlfir.expr<100x!fir.logical<4>> {
! CHECK: %[[VAL_139:.*]] = fir.call @llvm.stacksave.p0() fastmath<contract> : () -> !fir.ref<i8>
! CHECK: %[[VAL_141:.*]] = fir.call @_QPpure_logical_func2() fastmath<contract> : () -> !fir.array<100x!fir.logical<4>>
! CHECK: hlfir.yield %{{.*}} : !hlfir.expr<100x!fir.logical<4>> cleanup {
! CHECK: fir.call @llvm.stackrestore.p0(%[[VAL_139]]) fastmath<contract> : (!fir.ref<i8>) -> ()
! CHECK: }
! CHECK: }
! CHECK: hlfir.yield %[[VAL_129]] : !hlfir.expr<100x!fir.logical<4>>
! CHECK: } do {
! CHECK: hlfir.region_assign {
! CHECK: hlfir.designate
! CHECK: hlfir.yield %{{.*}} : !fir.box<!fir.array<?xf32>>
! CHECK: } to {
! CHECK: %[[VAL_165:.*]] = hlfir.exactly_once : i32 {
! CHECK: %[[VAL_166:.*]] = fir.call @_QPpure_ifoo() fastmath<contract> : () -> i32
! CHECK: hlfir.yield %[[VAL_166]] : i32
! CHECK: }
! CHECK: hlfir.designate
! CHECK: hlfir.yield %{{.*}} : !fir.box<!fir.array<?xf32>>
! CHECK: }
! CHECK: }
! CHECK: }
! CHECK: }
! CHECK: return
! CHECK: }
10 changes: 10 additions & 0 deletions libc/hdr/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ add_proxy_header_library(
libc.include.math
)

add_proxy_header_library(
errno_macros
HDRS
errno_macros.h
FULL_BUILD_DEPENDS
libc.include.errno
libc.include.llvm-libc-macros.error_number_macros
libc.include.llvm-libc-macros.generic_error_number_macros
)

add_proxy_header_library(
fcntl_macros
HDRS
Expand Down
28 changes: 28 additions & 0 deletions libc/hdr/errno_macros.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//===-- Definition of macros from errno.h ---------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_HDR_ERRNO_MACROS_H
#define LLVM_LIBC_HDR_ERRNO_MACROS_H

#ifdef LIBC_FULL_BUILD

#ifdef __linux__
#include <linux/errno.h>

#include "llvm-libc-macros/error-number-macros.h"
#else // __linux__
#include "llvm-libc-macros/generic-error-number-macros.h"
#endif

#else // Overlay mode

#include <errno.h>

#endif // LLVM_LIBC_FULL_BUILD

#endif // LLVM_LIBC_HDR_ERRNO_MACROS_H
20 changes: 1 addition & 19 deletions libc/include/errno.h.def
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,11 @@

#include <linux/errno.h>

#ifndef ERFKILL
#define ERFKILL 132
#endif // ERFKILL

#ifndef EOWNERDEAD
#define EOWNERDEAD 130
#endif // EOWNERDEAD

#ifndef EHWPOISON
#define EHWPOISON 133
#endif // EHWPOISON

#ifndef ECANCELED
#define ECANCELED 125
#endif // ECANCELED

#ifndef ENOTSUP
#define ENOTSUP EOPNOTSUPP
#endif // ENOTSUP

#ifndef ENOTRECOVERABLE
#define ENOTRECOVERABLE 131
#endif // ENOTRECOVERABLE
#include "llvm-libc-macros/linux/error-number-macros.h"

#else // __linux__
#include "llvm-libc-macros/generic-error-number-macros.h"
Expand Down
6 changes: 6 additions & 0 deletions libc/include/llvm-libc-macros/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ add_macro_header(
assert-macros.h
)

add_macro_header(
error_number_macros
HDR
error-number-macros.h
)

add_macro_header(
generic_error_number_macros
HDR
Expand Down
8 changes: 8 additions & 0 deletions libc/include/llvm-libc-macros/error-number-macros.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifndef LLVM_LIBC_MACROS_ERROR_NUMBER_MACROS_H
#define LLVM_LIBC_MACROS_ERROR_NUMBER_MACROS_H

#ifdef __linux__
#include "linux/error-number-macros.h"
#endif

#endif // LLVM_LIBC_MACROS_ERROR_NUMBER_MACROS_H
2 changes: 2 additions & 0 deletions libc/include/llvm-libc-macros/generic-error-number-macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,7 @@
#define EDOM 33
#define ERANGE 34
#define EILSEQ 35
#define ENAMETOOLONG 36
#define EOVERFLOW 75

#endif // LLVM_LIBC_MACROS_GENERIC_ERROR_NUMBER_MACROS_H
12 changes: 12 additions & 0 deletions libc/include/llvm-libc-macros/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/mips)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/sparc)

add_header(
error_number_macros
HDR
error-number-macros.h
DEPENDS
.mips.error_number_macros
.sparc.error_number_macros
)

add_header(
fcntl_macros
HDR
Expand Down
32 changes: 32 additions & 0 deletions libc/include/llvm-libc-macros/linux/error-number-macros.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef LLVM_LIBC_MACROS_LINUX_ERROR_NUMBER_MACROS_H
#define LLVM_LIBC_MACROS_LINUX_ERROR_NUMBER_MACROS_H

#if defined(__mips__)
#include "mips/error-number-macros.h"

#elif defined(__sparc__)
#include "sparc/error-number-macros.h"

#else
#ifndef ECANCELED
#define ECANCELED 125
#endif // ECANCELED

#ifndef EOWNERDEAD
#define EOWNERDEAD 130
#endif // EOWNERDEAD

#ifndef ENOTRECOVERABLE
#define ENOTRECOVERABLE 131
#endif // ENOTRECOVERABLE

#ifndef ERFKILL
#define ERFKILL 132
#endif // ERFKILL

#ifndef EHWPOISON
#define EHWPOISON 133
#endif // EHWPOISON
#endif

#endif // LLVM_LIBC_MACROS_LINUX_ERROR_NUMBER_MACROS_H
5 changes: 5 additions & 0 deletions libc/include/llvm-libc-macros/linux/mips/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
add_header(
error_number_macros
HDR
error-number-macros.h
)
24 changes: 24 additions & 0 deletions libc/include/llvm-libc-macros/linux/mips/error-number-macros.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef LLVM_LIBC_MACROS_LINUX_MIPS_ERROR_NUMBER_MACROS_H
#define LLVM_LIBC_MACROS_LINUX_MIPS_ERROR_NUMBER_MACROS_H

#ifndef ECANCELED
#define ECANCELED 158
#endif // ECANCELED

#ifndef EOWNERDEAD
#define EOWNERDEAD 165
#endif // EOWNERDEAD

#ifndef ENOTRECOVERABLE
#define ENOTRECOVERABLE 166
#endif // ENOTRECOVERABLE

#ifndef ERFKILL
#define ERFKILL 167
#endif // ERFKILL

#ifndef EHWPOISON
#define EHWPOISON 168
#endif // EHWPOISON

#endif // LLVM_LIBC_MACROS_LINUX_MIPS_ERROR_NUMBER_MACROS_H
5 changes: 5 additions & 0 deletions libc/include/llvm-libc-macros/linux/sparc/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
add_header(
error_number_macros
HDR
error-number-macros.h
)
24 changes: 24 additions & 0 deletions libc/include/llvm-libc-macros/linux/sparc/error-number-macros.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef LLVM_LIBC_MACROS_LINUX_SPARC_ERROR_NUMBER_MACROS_H
#define LLVM_LIBC_MACROS_LINUX_SPARC_ERROR_NUMBER_MACROS_H

#ifndef ECANCELED
#define ECANCELED 127
#endif // ECANCELED

#ifndef EOWNERDEAD
#define EOWNERDEAD 132
#endif // EOWNERDEAD

#ifndef ENOTRECOVERABLE
#define ENOTRECOVERABLE 133
#endif // ENOTRECOVERABLE

#ifndef ERFKILL
#define ERFKILL 134
#endif // ERFKILL

#ifndef EHWPOISON
#define EHWPOISON 135
#endif // EHWPOISON

#endif // LLVM_LIBC_MACROS_LINUX_SPARC_ERROR_NUMBER_MACROS_H
2 changes: 1 addition & 1 deletion libc/src/__support/threads/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ add_header_library(
libc.src.__support.CPP.atomic
libc.src.__support.CPP.limits
libc.src.__support.CPP.optional
libc.hdr.types.struct_timespec
libc.src.__support.time.linux.abs_timeout
)

add_header_library(
Expand Down
11 changes: 4 additions & 7 deletions libc/src/__support/threads/linux/futex_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,20 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_FUTEX_UTILS_H
#define LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_FUTEX_UTILS_H

#include "hdr/types/struct_timespec.h"
#include "src/__support/CPP/atomic.h"
#include "src/__support/CPP/limits.h"
#include "src/__support/CPP/optional.h"
#include "src/__support/OSUtil/syscall.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/threads/linux/futex_word.h"
#include "src/__support/time/linux/abs_timeout.h"
#include <linux/errno.h>
#include <linux/futex.h>

namespace LIBC_NAMESPACE {
class Futex : public cpp::Atomic<FutexWordType> {
public:
struct Timeout {
timespec abs_time;
bool is_realtime;
};
using Timeout = internal::AbsTimeout;
LIBC_INLINE constexpr Futex(FutexWordType value)
: cpp::Atomic<FutexWordType>(value) {}
LIBC_INLINE Futex &operator=(FutexWordType value) {
Expand All @@ -37,7 +34,7 @@ class Futex : public cpp::Atomic<FutexWordType> {
bool is_shared = false) {
// use bitset variants to enforce abs_time
uint32_t op = is_shared ? FUTEX_WAIT_BITSET : FUTEX_WAIT_BITSET_PRIVATE;
if (timeout && timeout->is_realtime) {
if (timeout && timeout->is_realtime()) {
op |= FUTEX_CLOCK_REALTIME;
}
for (;;) {
Expand All @@ -49,7 +46,7 @@ class Futex : public cpp::Atomic<FutexWordType> {
/* futex address */ this,
/* futex operation */ op,
/* expected value */ expected,
/* timeout */ timeout ? &timeout->abs_time : nullptr,
/* timeout */ timeout ? &timeout->get_timespec() : nullptr,
/* ignored */ nullptr,
/* bitset */ FUTEX_BITSET_MATCH_ANY);

Expand Down
33 changes: 30 additions & 3 deletions libc/src/__support/time/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
add_object_library(
add_header_library(
clock_gettime
HDRS
clock_gettime.h
SRCS
clock_gettime.cpp
DEPENDS
libc.include.sys_syscall
libc.hdr.types.struct_timespec
Expand All @@ -12,3 +10,32 @@ add_object_library(
libc.src.__support.error_or
libc.src.__support.OSUtil.osutil
)

add_header_library(
clock_conversion
HDRS
clock_conversion.h
DEPENDS
.clock_gettime
libc.src.__support.time.units
)

add_header_library(
abs_timeout
HDRS
abs_timeout.h
DEPENDS
libc.hdr.types.struct_timespec
libc.src.__support.time.units
libc.src.__support.CPP.expected
)

add_header_library(
monotonicity
HDRS
monotonicity.h
DEPENDS
.clock_conversion
.abs_timeout
libc.hdr.time_macros
)
49 changes: 49 additions & 0 deletions libc/src/__support/time/linux/abs_timeout.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//===--- Linux absolute timeout ---------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_ABS_TIMEOUT_H
#define LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_ABS_TIMEOUT_H

#include "hdr/time_macros.h"
#include "hdr/types/struct_timespec.h"
#include "src/__support/CPP/expected.h"
#include "src/__support/time/units.h"

namespace LIBC_NAMESPACE {
namespace internal {
// We use AbsTimeout to remind ourselves that the timeout is an absolute time.
// This is a simple wrapper around the timespec struct that also keeps track of
// whether the time is in realtime or monotonic time.
class AbsTimeout {
timespec timeout;
bool realtime_flag;
LIBC_INLINE constexpr explicit AbsTimeout(timespec ts, bool realtime)
: timeout(ts), realtime_flag(realtime) {}

public:
enum class Error { Invalid, BeforeEpoch };
LIBC_INLINE const timespec &get_timespec() const { return timeout; }
LIBC_INLINE bool is_realtime() const { return realtime_flag; }
LIBC_INLINE static constexpr cpp::expected<AbsTimeout, Error>
from_timespec(timespec ts, bool realtime) {
using namespace time_units;
if (ts.tv_nsec < 0 || ts.tv_nsec >= 1_s_ns)
return cpp::unexpected<Error>(Error::Invalid);

// POSIX allows tv_sec to be negative. We interpret this as an expired
// timeout.
if (ts.tv_sec < 0)
return cpp::unexpected<Error>(Error::BeforeEpoch);

return AbsTimeout{ts, realtime};
}
};
} // namespace internal
} // namespace LIBC_NAMESPACE

#endif // LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_ABS_TIMEOUT_H
42 changes: 42 additions & 0 deletions libc/src/__support/time/linux/clock_conversion.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//===--- clock conversion linux implementation ------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_CLOCK_CONVERSION_H
#define LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_CLOCK_CONVERSION_H

#include "src/__support/time/linux/clock_gettime.h"
#include "src/__support/time/units.h"

namespace LIBC_NAMESPACE {
namespace internal {

LIBC_INLINE timespec convert_clock(timespec input, clockid_t from,
clockid_t to) {
using namespace time_units;
timespec from_time;
timespec to_time;
timespec output;
internal::clock_gettime(from, &from_time);
internal::clock_gettime(to, &to_time);
output.tv_sec = input.tv_sec - from_time.tv_sec + to_time.tv_sec;
output.tv_nsec = input.tv_nsec - from_time.tv_nsec + to_time.tv_nsec;

if (output.tv_nsec > 1_s_ns) {
output.tv_sec++;
output.tv_nsec -= 1_s_ns;
} else if (output.tv_nsec < 0) {
output.tv_sec--;
output.tv_nsec += 1_s_ns;
}
return output;
}

} // namespace internal
} // namespace LIBC_NAMESPACE

#endif // LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_CLOCK_CONVERSION_H
35 changes: 0 additions & 35 deletions libc/src/__support/time/linux/clock_gettime.cpp

This file was deleted.

Loading