diff --git a/.github/new-issues-labeler.yml b/.github/new-issues-labeler.yml index a5933d7fc9b37..860535bbe3083 100644 --- a/.github/new-issues-labeler.yml +++ b/.github/new-issues-labeler.yml @@ -27,3 +27,6 @@ 'bolt': - '/\bbolt(?!\-)\b/i' + +'infra:commit-access-request': + - '/Request Commit Access/' diff --git a/bolt/include/bolt/Core/BinarySection.h b/bolt/include/bolt/Core/BinarySection.h index d362961176b32..1093f6ad78a99 100644 --- a/bolt/include/bolt/Core/BinarySection.h +++ b/bolt/include/bolt/Core/BinarySection.h @@ -87,6 +87,7 @@ class BinarySection { // been renamed) uint64_t OutputAddress{0}; // Section address for the rewritten binary. uint64_t OutputSize{0}; // Section size in the rewritten binary. + // Can exceed OutputContents with padding. uint64_t OutputFileOffset{0}; // File offset in the rewritten binary file. StringRef OutputContents; // Rewritten section contents. const uint64_t SectionNumber; // Order in which the section was created. @@ -474,6 +475,11 @@ class BinarySection { /// Use name \p SectionName for the section during the emission. void emitAsData(MCStreamer &Streamer, const Twine &SectionName) const; + /// Write finalized contents of the section. If OutputSize exceeds the size of + /// the OutputContents, append zero padding to the stream and return the + /// number of byte written which should match the OutputSize. + uint64_t write(raw_ostream &OS) const; + using SymbolResolverFuncTy = llvm::function_ref; /// Flush all pending relocations to patch original contents of sections @@ -497,6 +503,9 @@ class BinarySection { IsFinalized = true; } + /// When writing section contents, add \p PaddingSize zero bytes at the end. + void addPadding(uint64_t PaddingSize) { OutputSize += PaddingSize; } + /// Reorder the contents of this section according to /p Order. If /// /p Inplace is true, the entire contents of the section is reordered, /// otherwise the new contents contain only the reordered data. diff --git a/bolt/lib/Core/BinaryEmitter.cpp b/bolt/lib/Core/BinaryEmitter.cpp index bdf784ec7b6f3..4b5d8154728cc 100644 --- a/bolt/lib/Core/BinaryEmitter.cpp +++ b/bolt/lib/Core/BinaryEmitter.cpp @@ -416,17 +416,6 @@ void BinaryEmitter::emitFunctionBody(BinaryFunction &BF, FunctionFragment &FF, BF.duplicateConstantIslands(); } - if (!FF.empty() && FF.front()->isLandingPad()) { - assert(!FF.front()->isEntryPoint() && - "Landing pad cannot be entry point of function"); - // If the first block of the fragment is a landing pad, it's offset from the - // start of the area that the corresponding LSDA describes is zero. In this - // case, the call site entries in that LSDA have 0 as offset to the landing - // pad, which the runtime interprets as "no handler". To prevent this, - // insert some padding. - Streamer.emitBytes(BC.MIB->getTrapFillValue()); - } - // Track the first emitted instruction with debug info. bool FirstInstr = true; for (BinaryBasicBlock *const BB : FF) { @@ -926,39 +915,54 @@ void BinaryEmitter::emitLSDA(BinaryFunction &BF, const FunctionFragment &FF) { // Emit the LSDA header. // If LPStart is omitted, then the start of the FDE is used as a base for - // landing pad displacements. Then if a cold fragment starts with - // a landing pad, this means that the first landing pad offset will be 0. - // As a result, the exception handling runtime will ignore this landing pad - // because zero offset denotes the absence of a landing pad. - // For this reason, when the binary has fixed starting address we emit LPStart - // as 0 and output the absolute value of the landing pad in the table. + // landing pad displacements. Then, if a cold fragment starts with a landing + // pad, this means that the first landing pad offset will be 0. However, C++ + // runtime treats 0 as if there is no landing pad present, thus we *must* emit + // non-zero offsets for all valid LPs. // - // If the base address can change, we cannot use absolute addresses for - // landing pads (at least not without runtime relocations). Hence, we fall - // back to emitting landing pads relative to the FDE start. - // As we are emitting label differences, we have to guarantee both labels are - // defined in the same section and hence cannot place the landing pad into a - // cold fragment when the corresponding call site is in the hot fragment. - // Because of this issue and the previously described issue of possible - // zero-offset landing pad we have to place landing pads in the same section - // as the corresponding invokes for shared objects. + // As a solution, for fixed-address binaries we set LPStart to 0, and for + // position-independent binaries we set LP start to FDE start minus one byte + // for FDEs that start with a landing pad. + const bool NeedsLPAdjustment = !FF.empty() && FF.front()->isLandingPad(); std::function emitLandingPad; if (BC.HasFixedLoadAddress) { Streamer.emitIntValue(dwarf::DW_EH_PE_udata4, 1); // LPStart format Streamer.emitIntValue(0, 4); // LPStart emitLandingPad = [&](const MCSymbol *LPSymbol) { - if (!LPSymbol) - Streamer.emitIntValue(0, 4); - else + if (LPSymbol) Streamer.emitSymbolValue(LPSymbol, 4); + else + Streamer.emitIntValue(0, 4); }; } else { - Streamer.emitIntValue(dwarf::DW_EH_PE_omit, 1); // LPStart format + if (NeedsLPAdjustment) { + // Use relative LPStart format and emit LPStart as [SymbolStart - 1]. + Streamer.emitIntValue(dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4, 1); + MCSymbol *DotSymbol = BC.Ctx->createTempSymbol("LPBase"); + Streamer.emitLabel(DotSymbol); + + const MCExpr *LPStartExpr = MCBinaryExpr::createSub( + MCSymbolRefExpr::create(StartSymbol, *BC.Ctx), + MCSymbolRefExpr::create(DotSymbol, *BC.Ctx), *BC.Ctx); + LPStartExpr = MCBinaryExpr::createSub( + LPStartExpr, MCConstantExpr::create(1, *BC.Ctx), *BC.Ctx); + Streamer.emitValue(LPStartExpr, 4); + } else { + // DW_EH_PE_omit means FDE start (StartSymbol) will be used as LPStart. + Streamer.emitIntValue(dwarf::DW_EH_PE_omit, 1); + } emitLandingPad = [&](const MCSymbol *LPSymbol) { - if (!LPSymbol) - Streamer.emitIntValue(0, 4); - else - Streamer.emitAbsoluteSymbolDiff(LPSymbol, StartSymbol, 4); + if (LPSymbol) { + const MCExpr *LPOffsetExpr = MCBinaryExpr::createSub( + MCSymbolRefExpr::create(LPSymbol, *BC.Ctx), + MCSymbolRefExpr::create(StartSymbol, *BC.Ctx), *BC.Ctx); + if (NeedsLPAdjustment) + LPOffsetExpr = MCBinaryExpr::createAdd( + LPOffsetExpr, MCConstantExpr::create(1, *BC.Ctx), *BC.Ctx); + Streamer.emitULEB128Value(LPOffsetExpr); + } else { + Streamer.emitULEB128IntValue(0); + } }; } @@ -972,10 +976,12 @@ void BinaryEmitter::emitLSDA(BinaryFunction &BF, const FunctionFragment &FF) { Streamer.emitLabel(TTBaseRefLabel); } - // Emit the landing pad call site table. We use signed data4 since we can emit - // a landing pad in a different part of the split function that could appear - // earlier in the address space than LPStart. - Streamer.emitIntValue(dwarf::DW_EH_PE_sdata4, 1); + // Emit encoding of entries in the call site table. The format is used for the + // call site start, length, and corresponding landing pad. + if (BC.HasFixedLoadAddress) + Streamer.emitIntValue(dwarf::DW_EH_PE_sdata4, 1); + else + Streamer.emitIntValue(dwarf::DW_EH_PE_uleb128, 1); MCSymbol *CSTStartLabel = BC.Ctx->createTempSymbol("CSTStart"); MCSymbol *CSTEndLabel = BC.Ctx->createTempSymbol("CSTEnd"); @@ -992,8 +998,13 @@ void BinaryEmitter::emitLSDA(BinaryFunction &BF, const FunctionFragment &FF) { // Start of the range is emitted relative to the start of current // function split part. - Streamer.emitAbsoluteSymbolDiff(BeginLabel, StartSymbol, 4); - Streamer.emitAbsoluteSymbolDiff(EndLabel, BeginLabel, 4); + if (BC.HasFixedLoadAddress) { + Streamer.emitAbsoluteSymbolDiff(BeginLabel, StartSymbol, 4); + Streamer.emitAbsoluteSymbolDiff(EndLabel, BeginLabel, 4); + } else { + Streamer.emitAbsoluteSymbolDiffAsULEB128(BeginLabel, StartSymbol); + Streamer.emitAbsoluteSymbolDiffAsULEB128(EndLabel, BeginLabel); + } emitLandingPad(CallSite.LP); Streamer.emitULEB128IntValue(CallSite.Action); } diff --git a/bolt/lib/Core/BinarySection.cpp b/bolt/lib/Core/BinarySection.cpp index 9ad49ca1b3a03..b16e0a4333aa2 100644 --- a/bolt/lib/Core/BinarySection.cpp +++ b/bolt/lib/Core/BinarySection.cpp @@ -142,6 +142,15 @@ void BinarySection::emitAsData(MCStreamer &Streamer, Streamer.emitLabel(BC.Ctx->getOrCreateSymbol("__hot_data_end")); } +uint64_t BinarySection::write(raw_ostream &OS) const { + const uint64_t NumValidContentBytes = + std::min(getOutputContents().size(), getOutputSize()); + OS.write(getOutputContents().data(), NumValidContentBytes); + if (getOutputSize() > NumValidContentBytes) + OS.write_zeros(getOutputSize() - NumValidContentBytes); + return getOutputSize(); +} + void BinarySection::flushPendingRelocations(raw_pwrite_stream &OS, SymbolResolverFuncTy Resolver) { if (PendingRelocations.empty() && Patches.empty()) diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp index 1fcf2bb959bbb..7059a3dd23109 100644 --- a/bolt/lib/Rewrite/RewriteInstance.cpp +++ b/bolt/lib/Rewrite/RewriteInstance.cpp @@ -3887,6 +3887,43 @@ void RewriteInstance::mapCodeSections(BOLTLinker::SectionMapper MapSection) { void RewriteInstance::mapAllocatableSections( BOLTLinker::SectionMapper MapSection) { + + if (opts::UseOldText || opts::StrictMode) { + auto tryRewriteSection = [&](BinarySection &OldSection, + BinarySection &NewSection) { + if (OldSection.getSize() < NewSection.getOutputSize()) + return; + + BC->outs() << "BOLT-INFO: rewriting " << OldSection.getName() + << " in-place\n"; + + NewSection.setOutputAddress(OldSection.getAddress()); + NewSection.setOutputFileOffset(OldSection.getInputFileOffset()); + MapSection(NewSection, OldSection.getAddress()); + + // Pad contents with zeros. + NewSection.addPadding(OldSection.getSize() - NewSection.getOutputSize()); + + // Prevent the original section name from appearing in the section header + // table. + OldSection.setAnonymous(true); + }; + + if (EHFrameSection) { + BinarySection *NewEHFrameSection = + getSection(getNewSecPrefix() + getEHFrameSectionName()); + assert(NewEHFrameSection && "New contents expected for .eh_frame"); + tryRewriteSection(*EHFrameSection, *NewEHFrameSection); + } + BinarySection *EHSection = getSection(".gcc_except_table"); + BinarySection *NewEHSection = + getSection(getNewSecPrefix() + ".gcc_except_table"); + if (EHSection) { + assert(NewEHSection && "New contents expected for .gcc_except_table"); + tryRewriteSection(*EHSection, *NewEHSection); + } + } + // Allocate read-only sections first, then writable sections. enum : uint8_t { ST_READONLY, ST_READWRITE }; for (uint8_t SType = ST_READONLY; SType <= ST_READWRITE; ++SType) { @@ -4164,7 +4201,6 @@ void RewriteInstance::rewriteNoteSections() { // New section size. uint64_t Size = 0; bool DataWritten = false; - uint8_t *SectionData = nullptr; // Copy over section contents unless it's one of the sections we overwrite. if (!willOverwriteSection(SectionName)) { Size = Section.sh_size; @@ -4196,12 +4232,7 @@ void RewriteInstance::rewriteNoteSections() { if (BSec->getAllocAddress()) { assert(!DataWritten && "Writing section twice."); (void)DataWritten; - SectionData = BSec->getOutputData(); - - LLVM_DEBUG(dbgs() << "BOLT-DEBUG: " << (Size ? "appending" : "writing") - << " contents to section " << SectionName << '\n'); - OS.write(reinterpret_cast(SectionData), BSec->getOutputSize()); - Size += BSec->getOutputSize(); + Size += BSec->write(OS); } BSec->setOutputFileOffset(NextAvailableOffset); @@ -4232,8 +4263,7 @@ void RewriteInstance::rewriteNoteSections() { << " of size " << Section.getOutputSize() << " at offset 0x" << Twine::utohexstr(Section.getOutputFileOffset()) << '\n'); - OS.write(Section.getOutputContents().data(), Section.getOutputSize()); - NextAvailableOffset += Section.getOutputSize(); + NextAvailableOffset += Section.write(OS); } } @@ -4347,6 +4377,10 @@ RewriteInstance::getOutputSections(ELFObjectFile *File, BinarySection *BinSec = BC->getSectionForSectionRef(SecRef); assert(BinSec && "Matching BinarySection should exist."); + // Exclude anonymous sections. + if (BinSec->isAnonymous()) + continue; + addSection(Section, *BinSec); } @@ -5699,8 +5733,8 @@ void RewriteInstance::rewriteFile() { << Twine::utohexstr(Section.getAllocAddress()) << "\n of size " << Section.getOutputSize() << "\n at offset " << Section.getOutputFileOffset() << '\n'; - OS.pwrite(reinterpret_cast(Section.getOutputData()), - Section.getOutputSize(), Section.getOutputFileOffset()); + OS.seek(Section.getOutputFileOffset()); + Section.write(OS); } for (BinarySection &Section : BC->allocatableSections()) @@ -5791,42 +5825,64 @@ void RewriteInstance::writeEHFrameHeader() { LLVM_DEBUG(dbgs() << "BOLT: writing a new " << getEHFrameHdrSectionName() << '\n'); - NextAvailableAddress = - appendPadding(Out->os(), NextAvailableAddress, EHFrameHdrAlign); + // Try to overwrite the original .eh_frame_hdr if the size permits. + uint64_t EHFrameHdrOutputAddress = 0; + uint64_t EHFrameHdrFileOffset = 0; + std::vector NewEHFrameHdr; + BinarySection *OldEHFrameHdrSection = getSection(getEHFrameHdrSectionName()); + if (OldEHFrameHdrSection) { + NewEHFrameHdr = CFIRdWrt->generateEHFrameHeader( + RelocatedEHFrame, NewEHFrame, OldEHFrameHdrSection->getAddress()); + if (NewEHFrameHdr.size() <= OldEHFrameHdrSection->getSize()) { + BC->outs() << "BOLT-INFO: rewriting " << getEHFrameHdrSectionName() + << " in-place\n"; + EHFrameHdrOutputAddress = OldEHFrameHdrSection->getAddress(); + EHFrameHdrFileOffset = OldEHFrameHdrSection->getInputFileOffset(); + } else { + OldEHFrameHdrSection->setOutputName(getOrgSecPrefix() + + getEHFrameHdrSectionName()); + OldEHFrameHdrSection = nullptr; + } + } - const uint64_t EHFrameHdrOutputAddress = NextAvailableAddress; - const uint64_t EHFrameHdrFileOffset = - getFileOffsetForAddress(NextAvailableAddress); + // If there was not enough space, allocate more memory for .eh_frame_hdr. + if (!OldEHFrameHdrSection) { + NextAvailableAddress = + appendPadding(Out->os(), NextAvailableAddress, EHFrameHdrAlign); - std::vector NewEHFrameHdr = CFIRdWrt->generateEHFrameHeader( - RelocatedEHFrame, NewEHFrame, EHFrameHdrOutputAddress); + EHFrameHdrOutputAddress = NextAvailableAddress; + EHFrameHdrFileOffset = getFileOffsetForAddress(NextAvailableAddress); + + NewEHFrameHdr = CFIRdWrt->generateEHFrameHeader( + RelocatedEHFrame, NewEHFrame, EHFrameHdrOutputAddress); + + NextAvailableAddress += NewEHFrameHdr.size(); + if (!BC->BOLTReserved.empty() && + (NextAvailableAddress > BC->BOLTReserved.end())) { + BC->errs() << "BOLT-ERROR: unable to fit " << getEHFrameHdrSectionName() + << " into reserved space\n"; + exit(1); + } + + // Create a new entry in the section header table. + const unsigned Flags = BinarySection::getFlags(/*IsReadOnly=*/true, + /*IsText=*/false, + /*IsAllocatable=*/true); + BinarySection &EHFrameHdrSec = BC->registerOrUpdateSection( + getNewSecPrefix() + getEHFrameHdrSectionName(), ELF::SHT_PROGBITS, + Flags, nullptr, NewEHFrameHdr.size(), /*Alignment=*/1); + EHFrameHdrSec.setOutputFileOffset(EHFrameHdrFileOffset); + EHFrameHdrSec.setOutputAddress(EHFrameHdrOutputAddress); + EHFrameHdrSec.setOutputName(getEHFrameHdrSectionName()); + } Out->os().seek(EHFrameHdrFileOffset); Out->os().write(NewEHFrameHdr.data(), NewEHFrameHdr.size()); - const unsigned Flags = BinarySection::getFlags(/*IsReadOnly=*/true, - /*IsText=*/false, - /*IsAllocatable=*/true); - BinarySection *OldEHFrameHdrSection = getSection(getEHFrameHdrSectionName()); + // Pad the contents if overwriting in-place. if (OldEHFrameHdrSection) - OldEHFrameHdrSection->setOutputName(getOrgSecPrefix() + - getEHFrameHdrSectionName()); - - BinarySection &EHFrameHdrSec = BC->registerOrUpdateSection( - getNewSecPrefix() + getEHFrameHdrSectionName(), ELF::SHT_PROGBITS, Flags, - nullptr, NewEHFrameHdr.size(), /*Alignment=*/1); - EHFrameHdrSec.setOutputFileOffset(EHFrameHdrFileOffset); - EHFrameHdrSec.setOutputAddress(EHFrameHdrOutputAddress); - EHFrameHdrSec.setOutputName(getEHFrameHdrSectionName()); - - NextAvailableAddress += EHFrameHdrSec.getOutputSize(); - - if (!BC->BOLTReserved.empty() && - (NextAvailableAddress > BC->BOLTReserved.end())) { - BC->errs() << "BOLT-ERROR: unable to fit " << getEHFrameHdrSectionName() - << " into reserved space\n"; - exit(1); - } + Out->os().write_zeros(OldEHFrameHdrSection->getSize() - + NewEHFrameHdr.size()); // Merge new .eh_frame with the relocated original so that gdb can locate all // FDEs. diff --git a/bolt/test/eh-frame-hdr.test b/bolt/test/eh-frame-hdr.test new file mode 100644 index 0000000000000..4d718c850e2f2 --- /dev/null +++ b/bolt/test/eh-frame-hdr.test @@ -0,0 +1,12 @@ +# Check that llvm-bolt overwrites .eh_frame_hdr in-place. + +REQUIRES: system-linux + +RUN: %clang %cflags %p/Inputs/hello.c -o %t -Wl,-q +RUN: llvm-bolt %t -o %t.bolt --use-old-text \ +RUN: | FileCheck %s --check-prefix=CHECK-BOLT +RUN: llvm-readelf -WS %t.bolt | FileCheck %s + +CHECK-BOLT: rewriting .eh_frame_hdr in-place + +CHECK-NOT: .bolt.org.eh_frame_hdr diff --git a/bolt/test/eh-frame-overwrite.test b/bolt/test/eh-frame-overwrite.test new file mode 100644 index 0000000000000..649d739ec6086 --- /dev/null +++ b/bolt/test/eh-frame-overwrite.test @@ -0,0 +1,8 @@ +# Check that llvm-bolt can overwrite .eh_frame section in-place. + +REQUIRES: system-linux + +RUN: %clang %cflags %p/Inputs/hello.c -o %t -Wl,-q +RUN: llvm-bolt %t -o %t.bolt --strict | FileCheck %s + +CHECK: rewriting .eh_frame in-place diff --git a/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp index 620a57194acb8..3d1f63fcf33a5 100644 --- a/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp @@ -20,7 +20,7 @@ namespace { AST_MATCHER_P(FunctionDecl, isEnabled, llvm::StringSet<>, FunctionsThatShouldNotThrow) { - return FunctionsThatShouldNotThrow.count(Node.getNameAsString()) > 0; + return FunctionsThatShouldNotThrow.contains(Node.getNameAsString()); } AST_MATCHER(FunctionDecl, isExplicitThrow) { diff --git a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp index 9bfb7e2677533..68f3ecf6bdaa8 100644 --- a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp +++ b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp @@ -418,7 +418,7 @@ ExceptionAnalyzer::ExceptionInfo::filterIgnoredExceptions( if (TD->getDeclName().isIdentifier()) { if ((IgnoreBadAlloc && (TD->getName() == "bad_alloc" && TD->isInStdNamespace())) || - (IgnoredTypes.count(TD->getName()) > 0)) + (IgnoredTypes.contains(TD->getName()))) TypesToDelete.push_back(T); } } @@ -449,7 +449,8 @@ void ExceptionAnalyzer::ExceptionInfo::reevaluateBehaviour() { ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException( const FunctionDecl *Func, const ExceptionInfo::Throwables &Caught, llvm::SmallSet &CallStack) { - if (!Func || CallStack.count(Func) || (!CallStack.empty() && !canThrow(Func))) + if (!Func || CallStack.contains(Func) || + (!CallStack.empty() && !canThrow(Func))) return ExceptionInfo::createNonThrowing(); if (const Stmt *Body = Func->getBody()) { @@ -507,7 +508,7 @@ ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException( for (unsigned I = 0; I < Try->getNumHandlers(); ++I) { const CXXCatchStmt *Catch = Try->getHandler(I); - // Everything is catched through 'catch(...)'. + // Everything is caught through 'catch(...)'. if (!Catch->getExceptionDecl()) { ExceptionInfo Rethrown = throwsException( Catch->getHandlerBlock(), Uncaught.getExceptionTypes(), CallStack); diff --git a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h index 0a8cf8668d3ca..6c2d693d64b50 100644 --- a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h +++ b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h @@ -101,8 +101,8 @@ class ExceptionAnalyzer { /// Recalculate the 'Behaviour' for example after filtering. void reevaluateBehaviour(); - /// Keep track if the entity related to this 'ExceptionInfo' can in princple - /// throw, if it's unknown or if it won't throw. + /// Keep track if the entity related to this 'ExceptionInfo' can in + /// principle throw, if it's unknown or if it won't throw. State Behaviour; /// Keep track if the entity contains any unknown elements to keep track diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp index 761f96846d453..05c8041df7de7 100644 --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -511,6 +511,35 @@ bool fromJSON(const llvm::json::Value &Params, ClientCapabilities &R, if (auto EditsNearCursor = Completion->getBoolean("editsNearCursor")) R.CompletionFixes |= *EditsNearCursor; } + if (auto *References = TextDocument->getObject("references")) { + if (auto ContainerSupport = References->getBoolean("container")) { + R.ReferenceContainer |= *ContainerSupport; + } + } + if (auto *Diagnostics = TextDocument->getObject("publishDiagnostics")) { + if (auto CodeActions = Diagnostics->getBoolean("codeActionsInline")) { + R.DiagnosticFixes |= *CodeActions; + } + } + if (auto *InactiveRegions = + TextDocument->getObject("inactiveRegionsCapabilities")) { + if (auto InactiveRegionsSupport = + InactiveRegions->getBoolean("inactiveRegions")) { + R.InactiveRegions |= *InactiveRegionsSupport; + } + } + } + if (auto *Window = Experimental->getObject("window")) { + if (auto Implicit = + Window->getBoolean("implicitWorkDoneProgressCreate")) { + R.ImplicitProgressCreation |= *Implicit; + } + } + if (auto *OffsetEncoding = Experimental->get("offsetEncoding")) { + R.offsetEncoding.emplace(); + if (!fromJSON(*OffsetEncoding, *R.offsetEncoding, + P.field("offsetEncoding"))) + return false; } } diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h index 5b28095758198..c7ef1a13e6e39 100644 --- a/clang-tools-extra/clangd/Protocol.h +++ b/clang-tools-extra/clangd/Protocol.h @@ -452,6 +452,7 @@ struct ClientCapabilities { std::optional WorkspaceSymbolKinds; /// Whether the client accepts diagnostics with codeActions attached inline. + /// This is a clangd extension. /// textDocument.publishDiagnostics.codeActionsInline. bool DiagnosticFixes = false; @@ -475,6 +476,7 @@ struct ClientCapabilities { /// Client supports displaying a container string for results of /// textDocument/reference (clangd extension) + /// textDocument.references.container bool ReferenceContainer = false; /// Client supports hierarchical document symbols. @@ -563,6 +565,7 @@ struct ClientCapabilities { /// Whether the client supports the textDocument/inactiveRegions /// notification. This is a clangd extension. + /// textDocument.inactiveRegionsCapabilities.inactiveRegions bool InactiveRegions = false; }; bool fromJSON(const llvm::json::Value &, ClientCapabilities &, diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp index 4fd11307857ff..61fa66180376c 100644 --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -63,6 +63,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include @@ -2275,7 +2276,7 @@ incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) { // Initially store the ranges in a map keyed by SymbolID of the caller. // This allows us to group different calls with the same caller // into the same CallHierarchyIncomingCall. - llvm::DenseMap> CallsIn; + llvm::DenseMap> CallsIn; // We can populate the ranges based on a refs request only. As we do so, we // also accumulate the container IDs into a lookup request. LookupRequest ContainerLookup; @@ -2285,7 +2286,7 @@ incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) { elog("incomingCalls failed to convert location: {0}", Loc.takeError()); return; } - CallsIn[R.Container].push_back(Loc->range); + CallsIn[R.Container].push_back(*Loc); ContainerLookup.IDs.insert(R.Container); }); @@ -2294,9 +2295,21 @@ incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) { Index->lookup(ContainerLookup, [&](const Symbol &Caller) { auto It = CallsIn.find(Caller.ID); assert(It != CallsIn.end()); - if (auto CHI = symbolToCallHierarchyItem(Caller, Item.uri.file())) + if (auto CHI = symbolToCallHierarchyItem(Caller, Item.uri.file())) { + std::vector FromRanges; + for (const Location &L : It->second) { + if (L.uri != CHI->uri) { + // Call location not in same file as caller. + // This can happen in some edge cases. There's not much we can do, + // since the protocol only allows returning ranges interpreted as + // being in the caller's file. + continue; + } + FromRanges.push_back(L.range); + } Results.push_back( - CallHierarchyIncomingCall{std::move(*CHI), std::move(It->second)}); + CallHierarchyIncomingCall{std::move(*CHI), std::move(FromRanges)}); + } }); // Sort results by name of container. llvm::sort(Results, [](const CallHierarchyIncomingCall &A, diff --git a/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp b/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp index 591a8b245260e..789c10bdd4822 100644 --- a/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp +++ b/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp @@ -109,14 +109,13 @@ findContextForNS(llvm::StringRef TargetNS, const DeclContext *CurContext) { // afterwards it can be shared with define-inline code action. llvm::Expected getFunctionSourceAfterReplacements(const FunctionDecl *FD, - const tooling::Replacements &Replacements) { + const tooling::Replacements &Replacements, + bool TargetFileIsHeader) { const auto &SM = FD->getASTContext().getSourceManager(); auto OrigFuncRange = toHalfOpenFileRange( SM, FD->getASTContext().getLangOpts(), FD->getSourceRange()); if (!OrigFuncRange) return error("Couldn't get range for function."); - assert(!FD->getDescribedFunctionTemplate() && - "Define out-of-line doesn't apply to function templates."); // Get new begin and end positions for the qualified function definition. unsigned FuncBegin = SM.getFileOffset(OrigFuncRange->getBegin()); @@ -129,24 +128,38 @@ getFunctionSourceAfterReplacements(const FunctionDecl *FD, if (!QualifiedFunc) return QualifiedFunc.takeError(); + auto Source = QualifiedFunc->substr(FuncBegin, FuncEnd - FuncBegin + 1); std::string TemplatePrefix; + auto AddToTemplatePrefixIfApplicable = [&](const Decl *D) { + const TemplateParameterList *Params = D->getDescribedTemplateParams(); + if (!Params) + return; + for (Decl *P : *Params) { + if (auto *TTP = dyn_cast(P)) + TTP->removeDefaultArgument(); + else if (auto *NTTP = dyn_cast(P)) + NTTP->removeDefaultArgument(); + else if (auto *TTPD = dyn_cast(P)) + TTPD->removeDefaultArgument(); + } + std::string S; + llvm::raw_string_ostream Stream(S); + Params->print(Stream, FD->getASTContext()); + if (!S.empty()) + *S.rbegin() = '\n'; // Replace space with newline + TemplatePrefix.insert(0, S); + }; + AddToTemplatePrefixIfApplicable(FD); if (auto *MD = llvm::dyn_cast(FD)) { for (const CXXRecordDecl *Parent = MD->getParent(); Parent; Parent = llvm::dyn_cast_or_null(Parent->getParent())) { - if (const TemplateParameterList *Params = - Parent->getDescribedTemplateParams()) { - std::string S; - llvm::raw_string_ostream Stream(S); - Params->print(Stream, FD->getASTContext()); - if (!S.empty()) - *S.rbegin() = '\n'; // Replace space with newline - TemplatePrefix.insert(0, S); - } + AddToTemplatePrefixIfApplicable(Parent); } } - auto Source = QualifiedFunc->substr(FuncBegin, FuncEnd - FuncBegin + 1); + if (TargetFileIsHeader) + Source.insert(0, "inline "); if (!TemplatePrefix.empty()) Source.insert(0, TemplatePrefix); return Source; @@ -202,7 +215,8 @@ deleteTokensWithKind(const syntax::TokenBuffer &TokBuf, tok::TokenKind Kind, llvm::Expected getFunctionSourceCode(const FunctionDecl *FD, const DeclContext *TargetContext, const syntax::TokenBuffer &TokBuf, - const HeuristicResolver *Resolver) { + const HeuristicResolver *Resolver, + bool TargetFileIsHeader) { auto &AST = FD->getASTContext(); auto &SM = AST.getSourceManager(); @@ -225,6 +239,8 @@ getFunctionSourceCode(const FunctionDecl *FD, const DeclContext *TargetContext, return; for (const NamedDecl *ND : Ref.Targets) { + if (ND->getKind() == Decl::TemplateTypeParm) + return; if (ND->getDeclContext() != Ref.Targets.front()->getDeclContext()) { elog("Targets from multiple contexts: {0}, {1}", printQualifiedName(*Ref.Targets.front()), @@ -337,7 +353,8 @@ getFunctionSourceCode(const FunctionDecl *FD, const DeclContext *TargetContext, if (Errors) return std::move(Errors); - return getFunctionSourceAfterReplacements(FD, DeclarationCleanups); + return getFunctionSourceAfterReplacements(FD, DeclarationCleanups, + TargetFileIsHeader); } struct InsertionPoint { @@ -419,15 +436,15 @@ class DefineOutline : public Tweak { Source->isOutOfLine()) return false; - // Bail out if this is a function template or specialization, as their + // Bail out if this is a function template specialization, as their // definitions need to be visible in all including translation units. - if (Source->getDescribedFunctionTemplate()) - return false; if (Source->getTemplateSpecializationInfo()) return false; auto *MD = llvm::dyn_cast(Source); if (!MD) { + if (Source->getDescribedFunctionTemplate()) + return false; // Can't outline free-standing functions in the same file. return !SameFile; } @@ -450,6 +467,19 @@ class DefineOutline : public Tweak { } } + // For function templates, the same limitations as for class templates + // apply. + if (const TemplateParameterList *Params = + MD->getDescribedTemplateParams()) { + // FIXME: Is this really needed? It inhibits application on + // e.g. std::enable_if. + for (NamedDecl *P : *Params) { + if (!P->getIdentifier()) + return false; + } + SameFile = true; + } + // The refactoring is meaningless for unnamed classes and namespaces, // unless we're outlining in the same file for (const DeclContext *DC = MD->getParent(); DC; DC = DC->getParent()) { @@ -485,7 +515,8 @@ class DefineOutline : public Tweak { auto FuncDef = getFunctionSourceCode( Source, InsertionPoint->EnclosingNamespace, Sel.AST->getTokens(), - Sel.AST->getHeuristicResolver()); + Sel.AST->getHeuristicResolver(), + SameFile && isHeaderFile(Sel.AST->tuPath(), Sel.AST->getLangOpts())); if (!FuncDef) return FuncDef.takeError(); diff --git a/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp b/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp index b2278ff12735d..8821d3aad9c78 100644 --- a/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp +++ b/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp @@ -491,6 +491,35 @@ TEST(CallHierarchy, HierarchyOnVar) { fromRanges(Source.range("Callee"))))); } +TEST(CallHierarchy, CallInDifferentFileThanCaller) { + Annotations Header(R"cpp( + #define WALDO void caller() { + )cpp"); + Annotations Source(R"cpp( + void call^ee(); + WALDO + callee(); + } + )cpp"); + auto TU = TestTU::withCode(Source.code()); + TU.HeaderCode = Header.code(); + auto AST = TU.build(); + auto Index = TU.index(); + + std::vector Items = + prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename)); + ASSERT_THAT(Items, ElementsAre(withName("callee"))); + + auto Incoming = incomingCalls(Items[0], Index.get()); + + // The only call site is in the source file, which is a different file from + // the declaration of the function containing the call, which is in the + // header. The protocol does not allow us to represent such calls, so we drop + // them. (The call hierarchy item itself is kept.) + EXPECT_THAT(Incoming, + ElementsAre(AllOf(from(withName("caller")), fromRanges()))); +} + } // namespace } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp b/clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp index 6a9e90c3bfa70..d2d2ae9e7bb61 100644 --- a/clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp +++ b/clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp @@ -111,11 +111,17 @@ TEST_F(DefineOutlineTest, TriggersOnFunctionDecl) { template struct Foo { void fo^o(){} }; )cpp"); - // Not available on function templates and specializations, as definition must - // be visible to all translation units. + // Not available on function template specializations and free function + // templates. EXPECT_UNAVAILABLE(R"cpp( - template void fo^o() {}; - template <> void fo^o() {}; + template void fo^o() {} + template <> void fo^o() {} + )cpp"); + + // Not available on member function templates with unnamed template + // parameters. + EXPECT_UNAVAILABLE(R"cpp( + struct Foo { template void ba^r() {} }; )cpp"); // Not available on methods of unnamed classes. @@ -237,7 +243,7 @@ TEST_F(DefineOutlineTest, ApplyTest) { Foo(T z) __attribute__((weak)) ; int bar; };template -Foo::Foo(T z) __attribute__((weak)) : bar(2){} +inline Foo::Foo(T z) __attribute__((weak)) : bar(2){} )cpp", ""}, // Virt specifiers. @@ -390,7 +396,7 @@ Foo::Foo(T z) __attribute__((weak)) : bar(2){} }; };template template -typename O1::template O2::E O1::template O2::I::foo(T, U..., V, E) { return E1; } +inline typename O1::template O2::E O1::template O2::I::foo(T, U..., V, E) { return E1; } )cpp", ""}, // Destructors @@ -399,6 +405,37 @@ typename O1::template O2::E O1::template O2::I::fo "class A { ~A(); };", "A::~A(){} ", }, + + // Member template + { + R"cpp( + struct Foo { + template + T ^bar() { return {}; } + };)cpp", + R"cpp( + struct Foo { + template + T bar() ; + };template +inline T Foo::bar() { return {}; } +)cpp", + ""}, + + // Class template with member template + { + R"cpp( + template struct Foo { + template T ^bar(const T& t, const U& u) { return {}; } + };)cpp", + R"cpp( + template struct Foo { + template T bar(const T& t, const U& u) ; + };template +template +inline T Foo::bar(const T& t, const U& u) { return {}; } +)cpp", + ""}, }; for (const auto &Case : Cases) { SCOPED_TRACE(Case.Test); diff --git a/clang-tools-extra/test/CMakeLists.txt b/clang-tools-extra/test/CMakeLists.txt index d72a117166a08..7e4d99d8cfc1d 100644 --- a/clang-tools-extra/test/CMakeLists.txt +++ b/clang-tools-extra/test/CMakeLists.txt @@ -50,8 +50,6 @@ set(CLANG_TOOLS_TEST_DEPS clang-resource-headers clang-tidy - # Clang-tidy tests need clang for building modules. - clang ) # Add lit test dependencies. diff --git a/clang/cmake/caches/CrossWinToARMLinux.cmake b/clang/cmake/caches/CrossWinToARMLinux.cmake index 87118bbd33377..853217c6db61a 100644 --- a/clang/cmake/caches/CrossWinToARMLinux.cmake +++ b/clang/cmake/caches/CrossWinToARMLinux.cmake @@ -119,7 +119,6 @@ if (NOT DEFINED CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release" CACHE STRING "") endif() -set(CMAKE_CROSSCOMPILING ON CACHE BOOL "") set(CMAKE_CL_SHOWINCLUDES_PREFIX "Note: including file: " CACHE STRING "") # Required if COMPILER_RT_DEFAULT_TARGET_ONLY is ON set(CMAKE_C_COMPILER_TARGET "${TOOLCHAIN_TARGET_TRIPLE}" CACHE STRING "") @@ -219,6 +218,11 @@ set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_CXX_ABI set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_ENABLE_NEW_DELETE_DEFINITIONS ON CACHE BOOL "") # Merge libc++ and libc++abi libraries into the single libc++ library file. set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_ENABLE_STATIC_ABI_LIBRARY ON CACHE BOOL "") +# Forcely disable the libc++ benchmarks on Windows build hosts +# (current benchmark test configuration does not support the cross builds there). +if (WIN32) + set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_INCLUDE_BENCHMARKS OFF CACHE BOOL "") +endif(WIN32) # Avoid searching for the python3 interpreter during the runtimes configuration for the cross builds. # It starts searching the python3 package using the target's sysroot path, that usually is not compatible with the build host. diff --git a/clang/docs/InternalsManual.rst b/clang/docs/InternalsManual.rst index f189cb4e6a2ac..39d389b816f12 100644 --- a/clang/docs/InternalsManual.rst +++ b/clang/docs/InternalsManual.rst @@ -160,6 +160,10 @@ wording a diagnostic. named in a diagnostic message. e.g., prefer wording like ``'this' pointer cannot be null in well-defined C++ code`` over wording like ``this pointer cannot be null in well-defined C++ code``. +* Prefer diagnostic wording without contractions whenever possible. The single + quote in a contraction can be visually distracting due to its use with + syntactic constructs and contractions can be harder to understand for non- + native English speakers. The Format String ^^^^^^^^^^^^^^^^^ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 2bd67138ecc04..999c88455b64a 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -356,6 +356,8 @@ Non-comprehensive list of changes in this release issues with the sanitizer because the counter is automatically set. - ``__builtin_reduce_add`` function can now be used in constant expressions. +- ``__builtin_reduce_mul`` function can now be used in constant expressions. +- ``__builtin_reduce_and`` function can now be used in constant expressions. New Compiler Flags ------------------ @@ -537,6 +539,28 @@ Improvements to Clang's diagnostics - Improved diagnostic message for ``__builtin_bit_cast`` size mismatch (#GH115870). +- Clang now omits shadow warnings for enum constants in separate class scopes (#GH62588). + +- When diagnosing an unused return value of a type declared ``[[nodiscard]]``, the type + itself is now included in the diagnostic. + +- Clang will now prefer the ``[[nodiscard]]`` declaration on function declarations over ``[[nodiscard]]`` + declaration on the return type of a function. Previously, when both have a ``[[nodiscard]]`` declaration attached, + the one on the return type would be preferred. This may affect the generated warning message: + + .. code-block:: c++ + + struct [[nodiscard("Reason 1")]] S {}; + [[nodiscard("Reason 2")]] S getS(); + void use() + { + getS(); // Now diagnoses "Reason 2", previously diagnoses "Reason 1" + } + +- Clang now diagnoses ``= delete("reason")`` extension warnings only in pedantic mode rather than on by default. (#GH109311). + +- Clang now diagnoses missing return value in functions containing ``if consteval`` (#GH116485). + Improvements to Clang's time-trace ---------------------------------- @@ -666,6 +690,8 @@ Bug Fixes to C++ Support - Name independent data members were not correctly initialized from default member initializers. (#GH114069) - Fixed expression transformation for ``[[assume(...)]]``, allowing using pack indexing expressions within the assumption if they also occur inside of a dependent lambda. (#GH114787) +- Clang now uses valid deduced type locations when diagnosing functions with trailing return type + missing placeholder return type. (#GH78694) Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -712,6 +738,8 @@ Target Specific Changes AMDGPU Support ^^^^^^^^^^^^^^ +- Initial support for gfx950 + - Added headers ``gpuintrin.h`` and ``amdgpuintrin.h`` that contains common definitions for GPU builtin functions. This header can be included for OpenMP, CUDA, HIP, OpenCL, and C/C++. diff --git a/clang/docs/ThreadSafetyAnalysis.rst b/clang/docs/ThreadSafetyAnalysis.rst index cc4089b97b492..f6517afc3bfc2 100644 --- a/clang/docs/ThreadSafetyAnalysis.rst +++ b/clang/docs/ThreadSafetyAnalysis.rst @@ -933,11 +933,25 @@ implementation. MutexLocker(Mutex *mu, defer_lock_t) EXCLUDES(mu) : mut(mu), locked(false) {} // Same as constructors, but without tag types. (Requires C++17 copy elision.) - static MutexLocker Lock(Mutex *mu) ACQUIRE(mu); - static MutexLocker Adopt(Mutex *mu) REQUIRES(mu); - static MutexLocker ReaderLock(Mutex *mu) ACQUIRE_SHARED(mu); - static MutexLocker AdoptReaderLock(Mutex *mu) REQUIRES_SHARED(mu); - static MutexLocker DeferLock(Mutex *mu) EXCLUDES(mu); + static MutexLocker Lock(Mutex *mu) ACQUIRE(mu) { + return MutexLocker(mu); + } + + static MutexLocker Adopt(Mutex *mu) REQUIRES(mu) { + return MutexLocker(mu, adopt_lock); + } + + static MutexLocker ReaderLock(Mutex *mu) ACQUIRE_SHARED(mu) { + return MutexLocker(mu, shared_lock); + } + + static MutexLocker AdoptReaderLock(Mutex *mu) REQUIRES_SHARED(mu) { + return MutexLocker(mu, adopt_lock, shared_lock); + } + + static MutexLocker DeferLock(Mutex *mu) EXCLUDES(mu) { + return MutexLocker(mu, defer_lock); + } // Release *this and all associated mutexes, if they are still held. // There is no warning if the scope was already unlocked before. diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 466c65a9685ad..708c8656decbe 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -3181,12 +3181,14 @@ class CallExpr : public Expr { QualType getCallReturnType(const ASTContext &Ctx) const; /// Returns the WarnUnusedResultAttr that is either declared on the called - /// function, or its return type declaration. - const Attr *getUnusedResultAttr(const ASTContext &Ctx) const; + /// function, or its return type declaration, together with a NamedDecl that + /// refers to the declaration the attribute is attached onto. + std::pair + getUnusedResultAttr(const ASTContext &Ctx) const; /// Returns true if this call expression should warn on unused results. bool hasUnusedResultAttr(const ASTContext &Ctx) const { - return getUnusedResultAttr(Ctx) != nullptr; + return getUnusedResultAttr(Ctx).second != nullptr; } SourceLocation getRParenLoc() const { return RParenLoc; } diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 6035a563d5fce..634253d003256 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -4888,3 +4888,10 @@ def ClspvLibclcBuiltin: InheritableAttr { let Documentation = [ClspvLibclcBuiltinDoc]; let SimpleHandler = 1; } + +def NoTrivialAutoVarInit: InheritableAttr { + let Spellings = [Declspec<"no_init_all">]; + let Subjects = SubjectList<[Function, Tag]>; + let Documentation = [NoTrivialAutoVarInitDocs]; + let SimpleHandler = 1; +} diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 2fdceca163ee6..6fb2eb3eb3e66 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -3921,17 +3921,42 @@ have their lifetimes extended. def LifetimeCaptureByDocs : Documentation { let Category = DocCatFunction; let Content = [{ - Similar to `lifetimebound`_, the ``lifetime_capture_by(X)`` attribute on a function -parameter or implicit object parameter indicates that that objects that are referred to -by that parameter may also be referred to by the capturing entity ``X``. +Similar to `lifetimebound`_, the ``lifetime_capture_by(X)`` attribute on a +function parameter or implicit object parameter indicates that the capturing +entity ``X`` may refer to the object referred by that parameter. + +Below is a list of types of the parameters and what they're considered to refer to: + +- A reference param (of non-view type) is considered to refer to its referenced object. +- A pointer param (of non-view type) is considered to refer to its pointee. +- View type param (type annotated with ``[[gsl::Pointer()]]``) is considered to refer + to its pointee (gsl owner). This holds true even if the view type appears as a reference + in the parameter. For example, both ``std::string_view`` and + ``const std::string_view &`` are considered to refer to a ``std::string``. +- A ``std::initializer_list`` is considered to refer to its underlying array. +- Aggregates (arrays and simple ``struct``\s) are considered to refer to all + objects that their transitive subobjects refer to. + +Clang would diagnose when a temporary object is used as an argument to such an +annotated parameter. +In this case, the capturing entity ``X`` could capture a dangling reference to this +temporary object. -By default, a reference is considered to refer to its referenced object, a -pointer is considered to refer to its pointee, a ``std::initializer_list`` -is considered to refer to its underlying array, and aggregates (arrays and -simple ``struct``\s) are considered to refer to all objects that their -transitive subobjects refer to. +.. code-block:: c++ + + void addToSet(std::string_view a [[clang::lifetime_capture_by(s)]], std::set& s) { + s.insert(a); + } + void use() { + std::set s; + addToSet(std::string(), s); // Warning: object whose reference is captured by 's' will be destroyed at the end of the full-expression. + // ^^^^^^^^^^^^^ + std::string local; + addToSet(local, s); // Ok. + } The capturing entity ``X`` can be one of the following: + - Another (named) function parameter. .. code-block:: c++ @@ -3951,7 +3976,7 @@ The capturing entity ``X`` can be one of the following: std::set s; }; -- 'global', 'unknown' (without quotes). +- `global`, `unknown`. .. code-block:: c++ @@ -3983,6 +4008,22 @@ The attribute supports specifying more than one capturing entities: s2.insert(a); } +Limitation: The capturing entity ``X`` is not used by the analysis and is +used for documentation purposes only. This is because the analysis is +statement-local and only detects use of a temporary as an argument to the +annotated parameter. + +.. code-block:: c++ + + void addToSet(std::string_view a [[clang::lifetime_capture_by(s)]], std::set& s); + void use() { + std::set s; + if (foo()) { + std::string str; + addToSet(str, s); // Not detected. + } + } + .. _`lifetimebound`: https://clang.llvm.org/docs/AttributeReference.html#lifetimebound }]; } @@ -8719,6 +8760,18 @@ Attribute used by `clspv`_ (OpenCL-C to Vulkan SPIR-V compiler) to identify func }]; } +def NoTrivialAutoVarInitDocs : Documentation { + let Category = DocCatDecl; + let Content = [{ +The ``__declspec(no_init_all)`` attribute disables the automatic initialization that the +`-ftrivial-auto-var-init`_ flag would have applied to locals in a marked function, or instances of +a marked type. Note that this attribute has no effect for locals that are automatically initialized +without the `-ftrivial-auto-var-init`_ flag. + +.. _`-ftrivial-auto-var-init`: ClangCommandLineReference.html#cmdoption-clang-ftrivial-auto-var-init +}]; +} + def DocCatNonBlockingNonAllocating : DocumentationCategory<"Performance Constraint Attributes"> { let Content = [{ The ``nonblocking``, ``blocking``, ``nonallocating`` and ``allocating`` attributes can be attached diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index f5124f4633364..aa65f94e68f9c 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -1498,7 +1498,7 @@ def ReduceOr : Builtin { def ReduceAnd : Builtin { let Spellings = ["__builtin_reduce_and"]; - let Attributes = [NoThrow, Const, CustomTypeChecking]; + let Attributes = [NoThrow, Const, CustomTypeChecking, Constexpr]; let Prototype = "void(...)"; } @@ -1510,7 +1510,7 @@ def ReduceAdd : Builtin { def ReduceMul : Builtin { let Spellings = ["__builtin_reduce_mul"]; - let Attributes = [NoThrow, Const, CustomTypeChecking]; + let Attributes = [NoThrow, Const, CustomTypeChecking, Constexpr]; let Prototype = "void(...)"; } @@ -1995,6 +1995,12 @@ def AtomicThreadFence : Builtin { let Prototype = "void(int)"; } +def ScopedAtomicThreadFence : Builtin { + let Spellings = ["__scoped_atomic_thread_fence"]; + let Attributes = [NoThrow]; + let Prototype = "void(int, int)"; +} + def AtomicSignalFence : Builtin { let Spellings = ["__atomic_signal_fence"]; let Attributes = [NoThrow]; diff --git a/clang/include/clang/Basic/BuiltinsAMDGPU.def b/clang/include/clang/Basic/BuiltinsAMDGPU.def index 8f44afa405938..7ce8f2c1669d6 100644 --- a/clang/include/clang/Basic/BuiltinsAMDGPU.def +++ b/clang/include/clang/Basic/BuiltinsAMDGPU.def @@ -431,6 +431,14 @@ TARGET_BUILTIN(__builtin_amdgcn_cvt_pk_fp8_f32, "iffiIb", "nc", "fp8-conversion- TARGET_BUILTIN(__builtin_amdgcn_cvt_sr_bf8_f32, "ifiiIi", "nc", "fp8-conversion-insts") TARGET_BUILTIN(__builtin_amdgcn_cvt_sr_fp8_f32, "ifiiIi", "nc", "fp8-conversion-insts") +//===----------------------------------------------------------------------===// +// GFX950 only builtins. +//===----------------------------------------------------------------------===// +TARGET_BUILTIN(__builtin_amdgcn_mfma_f32_16x16x32_f16, "V4fV8hV8hV4fIiIiIi", "nc", "gfx950-insts") +TARGET_BUILTIN(__builtin_amdgcn_mfma_f32_32x32x16_f16, "V16fV8hV8hV16fIiIiIi", "nc", "gfx950-insts") + +TARGET_BUILTIN(__builtin_amdgcn_mfma_f32_32x32x16_bf16, "V16fV8yV8yV16fIiIiIi", "nc", "gfx950-insts") + //===----------------------------------------------------------------------===// // GFX12+ only builtins. //===----------------------------------------------------------------------===// @@ -522,5 +530,7 @@ TARGET_BUILTIN(__builtin_amdgcn_swmmac_f32_16x16x32_fp8_bf8_w64, "V4fiV2iV4fs", TARGET_BUILTIN(__builtin_amdgcn_swmmac_f32_16x16x32_bf8_fp8_w64, "V4fiV2iV4fs", "nc", "gfx12-insts,wavefrontsize64") TARGET_BUILTIN(__builtin_amdgcn_swmmac_f32_16x16x32_bf8_bf8_w64, "V4fiV2iV4fs", "nc", "gfx12-insts,wavefrontsize64") +TARGET_BUILTIN(__builtin_amdgcn_prng_b32, "UiUi", "nc", "prng-inst") + #undef BUILTIN #undef TARGET_BUILTIN diff --git a/clang/include/clang/Basic/BuiltinsLoongArchLASX.def b/clang/include/clang/Basic/BuiltinsLoongArchLASX.def index f644b820a6189..c4ea46a3bc5b5 100644 --- a/clang/include/clang/Basic/BuiltinsLoongArchLASX.def +++ b/clang/include/clang/Basic/BuiltinsLoongArchLASX.def @@ -371,7 +371,7 @@ TARGET_BUILTIN(__builtin_lasx_xvor_v, "V32UcV32UcV32Uc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvxor_v, "V32UcV32UcV32Uc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvnor_v, "V32UcV32UcV32Uc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvandn_v, "V32UcV32UcV32Uc", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvorn_v, "V32ScV32ScV32Sc", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvorn_v, "V32UcV32UcV32Uc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvandi_b, "V32UcV32UcIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvori_b, "V32UcV32UcIUi", "nc", "lasx") diff --git a/clang/include/clang/Basic/BuiltinsLoongArchLSX.def b/clang/include/clang/Basic/BuiltinsLoongArchLSX.def index b3056971986d1..a823783af88c4 100644 --- a/clang/include/clang/Basic/BuiltinsLoongArchLSX.def +++ b/clang/include/clang/Basic/BuiltinsLoongArchLSX.def @@ -355,7 +355,7 @@ TARGET_BUILTIN(__builtin_lsx_vor_v, "V16UcV16UcV16Uc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vxor_v, "V16UcV16UcV16Uc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vnor_v, "V16UcV16UcV16Uc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vandn_v, "V16UcV16UcV16Uc", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vorn_v, "V16ScV16ScV16Sc", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vorn_v, "V16UcV16UcV16Uc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vandi_b, "V16UcV16UcIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vori_b, "V16UcV16UcIUi", "nc", "lsx") diff --git a/clang/include/clang/Basic/Cuda.h b/clang/include/clang/Basic/Cuda.h index 721e8981af6ff..c2a4addf488df 100644 --- a/clang/include/clang/Basic/Cuda.h +++ b/clang/include/clang/Basic/Cuda.h @@ -107,6 +107,7 @@ enum class OffloadArch { GFX940, GFX941, GFX942, + GFX950, GFX10_1_GENERIC, GFX1010, GFX1011, diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td index 0c131166aff28..f4a155bb00bb3 100644 --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -364,9 +364,9 @@ def err_target_unsupported_abi_with_fpu : Error< def err_ppc_impossible_musttail: Error< "'musttail' attribute for this call is impossible because %select{" - "long calls can not be tail called on PPC|" - "indirect calls can not be tail called on PPC|" - "external calls can not be tail called on PPC}0" + "long calls cannot be tail called on PPC|" + "indirect calls cannot be tail called on PPC|" + "external calls cannot be tail called on PPC}0" >; def err_aix_musttail_unsupported: Error< "'musttail' attribute is not supported on AIX">; diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index 76fdbdbfb01d9..5155b23d151c0 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -553,7 +553,7 @@ def err_test_module_file_extension_format : Error< "'blockname:major:minor:hashed:user info'">; def err_drv_module_output_with_multiple_arch : Error< - "option '-fmodule-output' can't be used with multiple arch options">; + "option '-fmodule-output' cannot be used with multiple arch options">; def warn_drv_delayed_template_parsing_after_cxx20 : Warning< "-fdelayed-template-parsing is deprecated after C++20">, diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 72eada50a56cc..df9bf94b5d039 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -453,6 +453,7 @@ def ShiftOpParentheses: DiagGroup<"shift-op-parentheses">; def OverloadedShiftOpParentheses: DiagGroup<"overloaded-shift-op-parentheses">; def DanglingAssignment: DiagGroup<"dangling-assignment">; def DanglingAssignmentGsl : DiagGroup<"dangling-assignment-gsl">; +def DanglingCapture : DiagGroup<"dangling-capture">; def DanglingElse: DiagGroup<"dangling-else">; def DanglingField : DiagGroup<"dangling-field">; def DanglingInitializerList : DiagGroup<"dangling-initializer-list">; @@ -462,6 +463,7 @@ def ReturnStackAddress : DiagGroup<"return-stack-address">; def : DiagGroup<"return-local-addr", [ReturnStackAddress]>; def Dangling : DiagGroup<"dangling", [DanglingAssignment, DanglingAssignmentGsl, + DanglingCapture, DanglingField, DanglingInitializerList, DanglingGsl, diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 0da509280068a..77bf08453dea5 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -976,7 +976,7 @@ def warn_cxx98_compat_defaulted_deleted_function : Warning< "%select{defaulted|deleted}0 function definitions are incompatible with C++98">, InGroup, DefaultIgnore; -def ext_delete_with_message : ExtWarn< +def ext_delete_with_message : Extension< "'= delete' with a message is a C++2c extension">, InGroup; def warn_cxx23_delete_with_message : Warning< "'= delete' with a message is incompatible with C++ standards before C++2c">, diff --git a/clang/include/clang/Basic/DiagnosticRefactoringKinds.td b/clang/include/clang/Basic/DiagnosticRefactoringKinds.td index 5446b32efbdd4..e060fffc7280a 100644 --- a/clang/include/clang/Basic/DiagnosticRefactoringKinds.td +++ b/clang/include/clang/Basic/DiagnosticRefactoringKinds.td @@ -14,7 +14,7 @@ let Component = "Refactoring" in { let CategoryName = "Refactoring Invocation Issue" in { -def err_refactor_no_selection : Error<"refactoring action can't be initiated " +def err_refactor_no_selection : Error<"refactoring action cannot be initiated " "without a selection">; def err_refactor_selection_no_symbol : Error<"there is no symbol at the given " "location">; @@ -26,7 +26,7 @@ def err_refactor_code_outside_of_function : Error<"the selected code is not a " def err_refactor_extract_simple_expression : Error<"the selected expression " "is too simple to extract">; def err_refactor_extract_prohibited_expression : Error<"the selected " - "expression can't be extracted">; + "expression cannot be extracted">; } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 17eb28e8fc562..157d77b38b354 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1151,7 +1151,7 @@ def err_pragma_attribute_matcher_subrule_contradicts_rule : Error< def err_pragma_attribute_matcher_negated_subrule_contradicts_subrule : Error< "negated attribute subject matcher sub-rule '%0' contradicts sub-rule '%1'">; def err_pragma_attribute_invalid_matchers : Error< - "attribute %0 can't be applied to %1">; + "attribute %0 cannot be applied to %1">; def err_pragma_attribute_stack_mismatch : Error< "'#pragma clang attribute %select{%1.|}0pop' with no matching" " '#pragma clang attribute %select{%1.|}0push'">; @@ -6150,7 +6150,7 @@ def err_mismatched_owning_module : Error< "declaration of %0 in %select{the global module|module %2}1 follows " "declaration in %select{the global module|module %4}3">; def err_multiple_decl_in_different_modules : Error< - "declaration %0 attached to named module '%1' can't be attached to " + "declaration %0 attached to named module '%1' cannot be attached to " "other modules">; def err_redefinition_different_type : Error< "redefinition of %0 with a different type%diff{: $ vs $|}1,2">; @@ -8560,7 +8560,7 @@ def err_typecheck_missing_return_type_incompatible : Error< "literal|lambda expression}2 has unspecified explicit return type">; def note_incomplete_class_and_qualified_id : Note< - "conformance of forward class %0 to protocol %1 can not be confirmed">; + "conformance of forward class %0 to protocol %1 cannot be confirmed">; def warn_incompatible_qualified_id : Warning< "%select{%diff{assigning to $ from incompatible type $|" "assigning to type from incompatible type}0,1" @@ -9300,11 +9300,11 @@ def warn_unused_container_subscript_expr : Warning< def warn_unused_call : Warning< "ignoring return value of function declared with %0 attribute">, InGroup; -def warn_unused_constructor : Warning< - "ignoring temporary created by a constructor declared with %0 attribute">, +def warn_unused_return_type : Warning< + "ignoring %select{return value|temporary}0 of type %2 declared with %1 attribute%select{|: %4}3">, InGroup; -def warn_unused_constructor_msg : Warning< - "ignoring temporary created by a constructor declared with %0 attribute: %1">, +def warn_unused_constructor : Warning< + "ignoring temporary created by a constructor declared with %0 attribute%select{|: %2}1">, InGroup; def warn_side_effects_unevaluated_context : Warning< "expression with side effects has no effect in an unevaluated context">, @@ -9313,10 +9313,7 @@ def warn_side_effects_typeid : Warning< "expression with side effects will be evaluated despite being used as an " "operand to 'typeid'">, InGroup; def warn_unused_result : Warning< - "ignoring return value of function declared with %0 attribute">, - InGroup; -def warn_unused_result_msg : Warning< - "ignoring return value of function declared with %0 attribute: %1">, + "ignoring return value of function declared with %0 attribute%select{|: %2}1">, InGroup; def warn_unused_result_typedef_unsupported_spelling : Warning< "'[[%select{nodiscard|gnu::warn_unused_result}0]]' attribute ignored when " @@ -9414,7 +9411,7 @@ let CategoryName = "Inline Assembly Issue" in { "asm constraint has an unexpected number of alternatives: %0 vs %1">; def err_asm_incomplete_type : Error<"asm operand has incomplete type %0">; def err_asm_unknown_register_name : Error<"unknown register name '%0' in asm">; - def err_asm_unwind_and_goto : Error<"unwind clobber can't be used with asm goto">; + def err_asm_unwind_and_goto : Error<"unwind clobber cannot be used with asm goto">; def err_asm_invalid_global_var_reg : Error<"register '%0' unsuitable for " "global register variables on this target">; def err_asm_register_size_mismatch : Error<"size of register '%0' does not " @@ -9433,7 +9430,7 @@ let CategoryName = "Inline Assembly Issue" in { def err_asm_input_duplicate_match : Error< "more than one input constraint matches the same output '%0'">; def err_store_value_to_reg : Error< - "impossible constraint in asm: can't store value into a register">; + "impossible constraint in asm: cannot store value into a register">; def warn_asm_label_on_auto_decl : Warning< "ignored asm label '%0' on automatic variable">; @@ -10132,10 +10129,11 @@ def err_lifetimebound_ctor_dtor : Error< "%select{constructor|destructor}0">; def err_lifetimebound_parameter_void_return_type : Error< "'lifetimebound' attribute cannot be applied to a parameter of a function " - "that returns void">; + "that returns void; did you mean 'lifetime_capture_by(X)'">; def err_lifetimebound_implicit_object_parameter_void_return_type : Error< "'lifetimebound' attribute cannot be applied to an implicit object " - "parameter of a function that returns void">; + "parameter of a function that returns void; " + "did you mean 'lifetime_capture_by(X)'">; // CHECK: returning address/reference of stack memory def warn_ret_stack_addr_ref : Warning< @@ -10230,6 +10228,12 @@ def warn_dangling_pointer_assignment : Warning< "object backing %select{|the pointer }0%1 " "will be destroyed at the end of the full-expression">, InGroup; +def warn_dangling_reference_captured : Warning< + "object whose reference is captured by '%0' will be destroyed at the end of " + "the full-expression">, InGroup, DefaultIgnore; +def warn_dangling_reference_captured_by_unknown : Warning< + "object whose reference is captured will be destroyed at the end of " + "the full-expression">, InGroup, DefaultIgnore; // For non-floating point, expressions of the form x == x or x != x // should result in a warning, since these always evaluate to a constant. @@ -10960,7 +10964,7 @@ def err_opencl_builtin_pipe_invalid_access_modifier : Error< def err_opencl_invalid_access_qualifier : Error< "access qualifier can only be used for pipe and image type">; def err_opencl_invalid_read_write : Error< - "access qualifier %0 can not be used for %1 %select{|prior to OpenCL C version 2.0 or in version 3.0 " + "access qualifier %0 cannot be used for %1 %select{|prior to OpenCL C version 2.0 or in version 3.0 " "and without __opencl_c_read_write_images feature}2">; def err_opencl_multiple_access_qualifiers : Error< "multiple access qualifiers">; @@ -11460,7 +11464,7 @@ def err_omp_wrong_linear_modifier : Error< def err_omp_wrong_linear_modifier_non_reference : Error< "variable of non-reference type %0 can be used only with 'val' modifier, but used with '%1'">; def err_omp_step_simple_modifier_exclusive : Error< - "step simple modifier is exclusive and can't be use with 'val', 'uval' or 'ref' modifier">; + "step simple modifier is exclusive and cannot be use with 'val', 'uval' or 'ref' modifier">; def err_omp_wrong_simdlen_safelen_values : Error< "the value of 'simdlen' parameter must be less than or equal to the value of the 'safelen' parameter">; def err_omp_wrong_if_directive_name_modifier : Error< @@ -11534,7 +11538,7 @@ def err_omp_schedule_nonmonotonic_static : Error< def err_omp_simple_clause_incompatible_with_ordered : Error< "'%0' clause with '%1' modifier cannot be specified if an 'ordered' clause is specified">; def err_omp_ordered_simd : Error< - "'ordered' clause with a parameter can not be specified in '#pragma omp %0' directive">; + "'ordered' clause with a parameter cannot be specified in '#pragma omp %0' directive">; def err_omp_variable_in_given_clause_and_dsa : Error< "%0 variable cannot be in a %1 clause in '#pragma omp %2' directive">; def err_omp_param_or_this_in_clause : Error< @@ -12366,7 +12370,7 @@ def err_preserve_enum_value_not_const: Error< "__builtin_preserve_enum_value argument %0 not a constant">; def err_bit_cast_non_trivially_copyable : Error< - "__builtin_bit_cast %select{source|destination}0 type must be trivially copyable">; + "'__builtin_bit_cast' %select{source|destination}0 type must be trivially copyable">; def err_bit_cast_type_size_mismatch : Error< "size of '__builtin_bit_cast' source type %0 does not match destination type %1 (%2 vs %3 bytes)">; diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index 7f5d26118bdc7..9088c867d53ce 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -116,6 +116,7 @@ FEATURE(ptrauth_function_pointer_type_discrimination, LangOpts.PointerAuthFuncti FEATURE(ptrauth_indirect_gotos, LangOpts.PointerAuthIndirectGotos) FEATURE(ptrauth_init_fini, LangOpts.PointerAuthInitFini) FEATURE(ptrauth_init_fini_address_discrimination, LangOpts.PointerAuthInitFiniAddressDiscrimination) +FEATURE(ptrauth_elf_got, LangOpts.PointerAuthELFGOT) EXTENSION(swiftcc, PP.getTargetInfo().checkCallingConvention(CC_Swift) == clang::TargetInfo::CCCR_OK) diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index 019ca25bb57ff..9cd23d123f2ba 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -1871,6 +1871,11 @@ class TargetInfo : public TransferrableTargetInfo, void CheckFixedPointBits() const; }; +namespace targets { +std::unique_ptr +AllocateTarget(const llvm::Triple &Triple, const clang::TargetOptions &Opts); +} // namespace targets + } // end namespace clang #endif diff --git a/clang/include/clang/Basic/arm_mve.td b/clang/include/clang/Basic/arm_mve.td index 1debb94a0a7b8..93abbc47c54dd 100644 --- a/clang/include/clang/Basic/arm_mve.td +++ b/clang/include/clang/Basic/arm_mve.td @@ -753,8 +753,8 @@ let params = T.Float in { defm: compare<"ne", fcmp_ne>; defm: compare<"gt", fcmp_gt>; defm: compare<"ge", fcmp_ge>; - defm: compare<"lt", fcmp_lt>; - defm: compare<"le", fcmp_le>; + defm: compare<"lt", fcmp_ult>; + defm: compare<"le", fcmp_ule>; } let params = T.Signed in { diff --git a/clang/include/clang/Basic/arm_mve_defs.td b/clang/include/clang/Basic/arm_mve_defs.td index 1a090c08cc853..634b4e9c2c9c8 100644 --- a/clang/include/clang/Basic/arm_mve_defs.td +++ b/clang/include/clang/Basic/arm_mve_defs.td @@ -116,8 +116,8 @@ def fcmp_eq: IRBuilder<"CreateFCmpOEQ">; def fcmp_ne: IRBuilder<"CreateFCmpUNE">; // not O: it must return true on NaNs def fcmp_gt: IRBuilder<"CreateFCmpOGT">; def fcmp_ge: IRBuilder<"CreateFCmpOGE">; -def fcmp_lt: IRBuilder<"CreateFCmpOLT">; -def fcmp_le: IRBuilder<"CreateFCmpOLE">; +def fcmp_ult: IRBuilder<"CreateFCmpULT">; +def fcmp_ule: IRBuilder<"CreateFCmpULE">; def splat: CGHelperFn<"ARMMVEVectorSplat">; def select: IRBuilder<"CreateSelect">; def fneg: IRBuilder<"CreateFNeg">; diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index d7230dd7272fd..5167c3c39e315 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1732,8 +1732,6 @@ defm gnu_inline_asm : BoolFOption<"gnu-inline-asm", "Disable GNU style inline asm">, PosFlag>; -def fprofile_sample_use : Flag<["-"], "fprofile-sample-use">, Group, - Visibility<[ClangOption, CLOption]>; def fno_profile_sample_use : Flag<["-"], "fno-profile-sample-use">, Group, Visibility<[ClangOption, CLOption]>; def fprofile_sample_use_EQ : Joined<["-"], "fprofile-sample-use=">, @@ -1759,8 +1757,6 @@ def fsample_profile_use_profi : Flag<["-"], "fsample-profile-use-profi">, basic block counts to branch probabilites to fix them by extended and re-engineered classic MCMF (min-cost max-flow) approach.}]>; def fno_profile_sample_accurate : Flag<["-"], "fno-profile-sample-accurate">, Group; -def fauto_profile : Flag<["-"], "fauto-profile">, Group, - Alias; def fno_auto_profile : Flag<["-"], "fno-auto-profile">, Group, Alias; def fauto_profile_EQ : Joined<["-"], "fauto-profile=">, @@ -4356,6 +4352,7 @@ defm ptrauth_indirect_gotos : OptInCC1FFlag<"ptrauth-indirect-gotos", defm ptrauth_init_fini : OptInCC1FFlag<"ptrauth-init-fini", "Enable signing of function pointers in init/fini arrays">; defm ptrauth_init_fini_address_discrimination : OptInCC1FFlag<"ptrauth-init-fini-address-discrimination", "Enable address discrimination of function pointers in init/fini arrays">; +defm ptrauth_elf_got : OptInCC1FFlag<"ptrauth-elf-got", "Enable authentication of pointers from GOT (ELF only)">; } def fenable_matrix : Flag<["-"], "fenable-matrix">, Group, diff --git a/clang/include/clang/Frontend/FrontendAction.h b/clang/include/clang/Frontend/FrontendAction.h index 039f6f247b6d8..718684a67771a 100644 --- a/clang/include/clang/Frontend/FrontendAction.h +++ b/clang/include/clang/Frontend/FrontendAction.h @@ -21,6 +21,7 @@ #include "clang/Basic/LLVM.h" #include "clang/Basic/LangOptions.h" #include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendOptions.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" @@ -185,7 +186,12 @@ class FrontendAction { virtual bool usesPreprocessorOnly() const = 0; /// For AST-based actions, the kind of translation unit we're handling. - virtual TranslationUnitKind getTranslationUnitKind() { return TU_Complete; } + virtual TranslationUnitKind getTranslationUnitKind() { + // The ASTContext, if exists, knows the exact TUKind of the frondend. + if (Instance && Instance->hasASTContext()) + return Instance->getASTContext().TUKind; + return TU_Complete; + } /// Does this action support use with PCH? virtual bool hasPCHSupport() const { return true; } diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h index 1230a3a7016fa..b1b63aedf86ab 100644 --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -177,7 +177,8 @@ class Interpreter { CodeGenerator *getCodeGen() const; std::unique_ptr GenModule(); - PartialTranslationUnit &RegisterPTU(TranslationUnitDecl *TU); + PartialTranslationUnit &RegisterPTU(TranslationUnitDecl *TU, + std::unique_ptr M = {}); // A cache for the compiled destructors used to for de-allocation of managed // clang::Values. diff --git a/clang/include/clang/Interpreter/PartialTranslationUnit.h b/clang/include/clang/Interpreter/PartialTranslationUnit.h index bf91d559452b8..c878e139fe70d 100644 --- a/clang/include/clang/Interpreter/PartialTranslationUnit.h +++ b/clang/include/clang/Interpreter/PartialTranslationUnit.h @@ -31,6 +31,9 @@ struct PartialTranslationUnit { /// The llvm IR produced for the input. std::unique_ptr TheModule; + bool operator==(const PartialTranslationUnit &other) { + return other.TUPart == TUPart && other.TheModule == TheModule; + } }; } // namespace clang diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index d6f3508a5243f..6ea6c67447b6f 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2323,6 +2323,9 @@ class Sema final : public SemaBase { bool BuiltinVectorMath(CallExpr *TheCall, QualType &Res, bool FPOnly = false); bool BuiltinVectorToScalarMath(CallExpr *TheCall); + void checkLifetimeCaptureBy(FunctionDecl *FDecl, bool IsMemberFunction, + const Expr *ThisArg, ArrayRef Args); + /// Handles the checks for format strings, non-POD arguments to vararg /// functions, NULL arguments passed to non-NULL parameters, diagnose_if /// attributes and AArch64 SME attributes. diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 8725d5455ec73..fd834c14ce790 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -44,7 +44,7 @@ namespace serialization { /// Version 4 of AST files also requires that the version control branch and /// revision match exactly, since there is no backward compatibility of /// AST files at this time. -const unsigned VERSION_MAJOR = 33; +const unsigned VERSION_MAJOR = 34; /// AST file minor version number supported by this version of /// Clang. @@ -350,9 +350,8 @@ enum ControlRecordTypes { /// and information about the compiler used to build this AST file. METADATA = 1, - /// Record code for the list of other AST files imported by - /// this AST file. - IMPORTS, + /// Record code for another AST file imported by this AST file. + IMPORT, /// Record code for the original file that was used to /// generate the AST file, including both its file ID and its diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index 9c274adc59a20..f739fe688c110 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -2389,11 +2389,8 @@ class ASTReader // Read a string static std::string ReadString(const RecordDataImpl &Record, unsigned &Idx); - - // Skip a string - static void SkipString(const RecordData &Record, unsigned &Idx) { - Idx += Record[Idx] + 1; - } + static StringRef ReadStringBlob(const RecordDataImpl &Record, unsigned &Idx, + StringRef &Blob); // Read a path std::string ReadPath(ModuleFile &F, const RecordData &Record, unsigned &Idx); @@ -2401,11 +2398,8 @@ class ASTReader // Read a path std::string ReadPath(StringRef BaseDirectory, const RecordData &Record, unsigned &Idx); - - // Skip a path - static void SkipPath(const RecordData &Record, unsigned &Idx) { - SkipString(Record, Idx); - } + std::string ReadPathBlob(StringRef BaseDirectory, const RecordData &Record, + unsigned &Idx, StringRef &Blob); /// Read a version tuple. static VersionTuple ReadVersionTuple(const RecordData &Record, unsigned &Idx); diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h index 161b2ef7c86a4..e418fdea44a0a 100644 --- a/clang/include/clang/Serialization/ASTWriter.h +++ b/clang/include/clang/Serialization/ASTWriter.h @@ -769,6 +769,8 @@ class ASTWriter : public ASTDeserializationListener, /// Add a string to the given record. void AddString(StringRef Str, RecordDataImpl &Record); + void AddStringBlob(StringRef Str, RecordDataImpl &Record, + SmallVectorImpl &Blob); /// Convert a path from this build process into one that is appropriate /// for emission in the module file. @@ -776,6 +778,8 @@ class ASTWriter : public ASTDeserializationListener, /// Add a path to the given record. void AddPath(StringRef Path, RecordDataImpl &Record); + void AddPathBlob(StringRef Str, RecordDataImpl &Record, + SmallVectorImpl &Blob); /// Emit the current record with the given path as a blob. void EmitRecordWithPath(unsigned Abbrev, RecordDataRef Record, diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 8aab8d2d2ce93..a4fb4d5a1f2ec 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -1615,22 +1615,24 @@ QualType CallExpr::getCallReturnType(const ASTContext &Ctx) const { return FnType->getReturnType(); } -const Attr *CallExpr::getUnusedResultAttr(const ASTContext &Ctx) const { +std::pair +CallExpr::getUnusedResultAttr(const ASTContext &Ctx) const { + // If the callee is marked nodiscard, return that attribute + const Decl *D = getCalleeDecl(); + if (const auto *A = D->getAttr()) + return {nullptr, A}; + // If the return type is a struct, union, or enum that is marked nodiscard, // then return the return type attribute. if (const TagDecl *TD = getCallReturnType(Ctx)->getAsTagDecl()) if (const auto *A = TD->getAttr()) - return A; + return {TD, A}; for (const auto *TD = getCallReturnType(Ctx)->getAs(); TD; TD = TD->desugar()->getAs()) if (const auto *A = TD->getDecl()->getAttr()) - return A; - - // Otherwise, see if the callee is marked nodiscard and return that attribute - // instead. - const Decl *D = getCalleeDecl(); - return D ? D->getAttr() : nullptr; + return {TD->getDecl(), A}; + return {nullptr, nullptr}; } SourceLocation CallExpr::getBeginLoc() const { diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 833b99bf1bd9f..33206f5cda202 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -13527,7 +13527,9 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, return Success(DidOverflow, E); } - case Builtin::BI__builtin_reduce_add: { + case Builtin::BI__builtin_reduce_add: + case Builtin::BI__builtin_reduce_mul: + case Builtin::BI__builtin_reduce_and: { APValue Source; if (!EvaluateAsRValue(Info, E->getArg(0), Source)) return false; @@ -13535,10 +13537,28 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, unsigned SourceLen = Source.getVectorLength(); APSInt Reduced = Source.getVectorElt(0).getInt(); for (unsigned EltNum = 1; EltNum < SourceLen; ++EltNum) { - if (!CheckedIntArithmetic( - Info, E, Reduced, Source.getVectorElt(EltNum).getInt(), - Reduced.getBitWidth() + 1, std::plus(), Reduced)) + switch (BuiltinOp) { + default: return false; + case Builtin::BI__builtin_reduce_add: { + if (!CheckedIntArithmetic( + Info, E, Reduced, Source.getVectorElt(EltNum).getInt(), + Reduced.getBitWidth() + 1, std::plus(), Reduced)) + return false; + break; + } + case Builtin::BI__builtin_reduce_mul: { + if (!CheckedIntArithmetic( + Info, E, Reduced, Source.getVectorElt(EltNum).getInt(), + Reduced.getBitWidth() * 2, std::multiplies(), Reduced)) + return false; + break; + } + case Builtin::BI__builtin_reduce_and: { + Reduced &= Source.getVectorElt(EltNum).getInt(); + break; + } + } } return Success(Reduced, E); diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp index f678ac6f2ff36..7a6bd8b6f8d07 100644 --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -3177,11 +3177,14 @@ CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) { if (!I->isConsteval()) KnownVal = tryEvaluateBool(I->getCond()); - // Add the successors. If we know that specific branches are + // Add the successors. If we know that specific branches are // unreachable, inform addSuccessor() of that knowledge. addSuccessor(Block, ThenBlock, /* IsReachable = */ !KnownVal.isFalse()); addSuccessor(Block, ElseBlock, /* IsReachable = */ !KnownVal.isTrue()); + if (I->isConsteval()) + return Block; + // Add the condition as the last statement in the new block. This may // create new blocks as the condition may contain control-flow. Any newly // created blocks will be pointed to be "Block". diff --git a/clang/lib/Basic/Cuda.cpp b/clang/lib/Basic/Cuda.cpp index 59c932468cd89..d56609a2a8f24 100644 --- a/clang/lib/Basic/Cuda.cpp +++ b/clang/lib/Basic/Cuda.cpp @@ -125,6 +125,7 @@ static const OffloadArchToStringMap arch_names[] = { GFX(940), // gfx940 GFX(941), // gfx941 GFX(942), // gfx942 + GFX(950), // gfx950 {OffloadArch::GFX10_1_GENERIC, "gfx10-1-generic", "compute_amdgcn"}, GFX(1010), // gfx1010 GFX(1011), // gfx1011 diff --git a/clang/lib/Basic/TargetDefines.h b/clang/lib/Basic/TargetDefines.h new file mode 100644 index 0000000000000..96fc4fe70fa9d --- /dev/null +++ b/clang/lib/Basic/TargetDefines.h @@ -0,0 +1,39 @@ +//===------- TargetDefines.h - Target define helpers ------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file declares a series of helper functions for defining target-specific +// macros. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_BASIC_TARGETDEFINES_H +#define LLVM_CLANG_LIB_BASIC_TARGETDEFINES_H + +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/MacroBuilder.h" +#include "llvm/ADT/StringRef.h" + +namespace clang { +namespace targets { +/// Define a macro name and standard variants. For example if MacroName is +/// "unix", then this will define "__unix", "__unix__", and "unix" when in GNU +/// mode. +LLVM_LIBRARY_VISIBILITY +void DefineStd(clang::MacroBuilder &Builder, llvm::StringRef MacroName, + const clang::LangOptions &Opts); + +LLVM_LIBRARY_VISIBILITY +void defineCPUMacros(clang::MacroBuilder &Builder, llvm::StringRef CPUName, + bool Tuning = true); + +LLVM_LIBRARY_VISIBILITY +void addCygMingDefines(const clang::LangOptions &Opts, + clang::MacroBuilder &Builder); +} // namespace targets +} // namespace clang +#endif // LLVM_CLANG_LIB_BASIC_TARGETDEFINES_H diff --git a/clang/lib/Basic/Targets.h b/clang/lib/Basic/Targets.h index b4d2486b5d2b1..e1458384fa1c8 100644 --- a/clang/lib/Basic/Targets.h +++ b/clang/lib/Basic/Targets.h @@ -15,32 +15,7 @@ #ifndef LLVM_CLANG_LIB_BASIC_TARGETS_H #define LLVM_CLANG_LIB_BASIC_TARGETS_H -#include "clang/Basic/LangOptions.h" -#include "clang/Basic/MacroBuilder.h" +#include "TargetDefines.h" #include "clang/Basic/TargetInfo.h" -#include "llvm/ADT/StringRef.h" -namespace clang { -namespace targets { - -LLVM_LIBRARY_VISIBILITY -std::unique_ptr -AllocateTarget(const llvm::Triple &Triple, const clang::TargetOptions &Opts); - -/// DefineStd - Define a macro name and standard variants. For example if -/// MacroName is "unix", then this will define "__unix", "__unix__", and "unix" -/// when in GNU mode. -LLVM_LIBRARY_VISIBILITY -void DefineStd(clang::MacroBuilder &Builder, llvm::StringRef MacroName, - const clang::LangOptions &Opts); - -LLVM_LIBRARY_VISIBILITY -void defineCPUMacros(clang::MacroBuilder &Builder, llvm::StringRef CPUName, - bool Tuning = true); - -LLVM_LIBRARY_VISIBILITY -void addCygMingDefines(const clang::LangOptions &Opts, - clang::MacroBuilder &Builder); -} // namespace targets -} // namespace clang #endif // LLVM_CLANG_LIB_BASIC_TARGETS_H diff --git a/clang/lib/Basic/Targets/NVPTX.cpp b/clang/lib/Basic/Targets/NVPTX.cpp index 0897032c4b854..dbc3fec365761 100644 --- a/clang/lib/Basic/Targets/NVPTX.cpp +++ b/clang/lib/Basic/Targets/NVPTX.cpp @@ -209,6 +209,7 @@ void NVPTXTargetInfo::getTargetDefines(const LangOptions &Opts, case OffloadArch::GFX940: case OffloadArch::GFX941: case OffloadArch::GFX942: + case OffloadArch::GFX950: case OffloadArch::GFX10_1_GENERIC: case OffloadArch::GFX1010: case OffloadArch::GFX1011: diff --git a/clang/lib/Basic/Targets/RISCV.cpp b/clang/lib/Basic/Targets/RISCV.cpp index eaaba7642bd7b..c61ee7ee20392 100644 --- a/clang/lib/Basic/Targets/RISCV.cpp +++ b/clang/lib/Basic/Targets/RISCV.cpp @@ -108,6 +108,10 @@ bool RISCVTargetInfo::validateAsmConstraint( return true; } return false; + case 'R': + // An even-odd GPR pair + Info.setAllowsRegister(); + return true; case 'v': // A vector register. if (Name[1] == 'r' || Name[1] == 'd' || Name[1] == 'm') { diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index df69d188306be..0916e14f182dd 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -5213,6 +5213,136 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, Builder.SetInsertPoint(ContBB); return RValue::get(nullptr); } + case Builtin::BI__scoped_atomic_thread_fence: { + auto ScopeModel = AtomicScopeModel::create(AtomicScopeModelKind::Generic); + + Value *Order = EmitScalarExpr(E->getArg(0)); + Value *Scope = EmitScalarExpr(E->getArg(1)); + auto Ord = dyn_cast(Order); + auto Scp = dyn_cast(Scope); + if (Ord && Scp) { + SyncScope SS = ScopeModel->isValid(Scp->getZExtValue()) + ? ScopeModel->map(Scp->getZExtValue()) + : ScopeModel->map(ScopeModel->getFallBackValue()); + switch (Ord->getZExtValue()) { + case 0: // memory_order_relaxed + default: // invalid order + break; + case 1: // memory_order_consume + case 2: // memory_order_acquire + Builder.CreateFence( + llvm::AtomicOrdering::Acquire, + getTargetHooks().getLLVMSyncScopeID(getLangOpts(), SS, + llvm::AtomicOrdering::Acquire, + getLLVMContext())); + break; + case 3: // memory_order_release + Builder.CreateFence( + llvm::AtomicOrdering::Release, + getTargetHooks().getLLVMSyncScopeID(getLangOpts(), SS, + llvm::AtomicOrdering::Release, + getLLVMContext())); + break; + case 4: // memory_order_acq_rel + Builder.CreateFence(llvm::AtomicOrdering::AcquireRelease, + getTargetHooks().getLLVMSyncScopeID( + getLangOpts(), SS, + llvm::AtomicOrdering::AcquireRelease, + getLLVMContext())); + break; + case 5: // memory_order_seq_cst + Builder.CreateFence(llvm::AtomicOrdering::SequentiallyConsistent, + getTargetHooks().getLLVMSyncScopeID( + getLangOpts(), SS, + llvm::AtomicOrdering::SequentiallyConsistent, + getLLVMContext())); + break; + } + return RValue::get(nullptr); + } + + llvm::BasicBlock *ContBB = createBasicBlock("atomic.scope.continue", CurFn); + + llvm::SmallVector> + OrderBBs; + if (Ord) { + switch (Ord->getZExtValue()) { + case 0: // memory_order_relaxed + default: // invalid order + ContBB->eraseFromParent(); + return RValue::get(nullptr); + case 1: // memory_order_consume + case 2: // memory_order_acquire + OrderBBs.emplace_back(Builder.GetInsertBlock(), + llvm::AtomicOrdering::Acquire); + break; + case 3: // memory_order_release + OrderBBs.emplace_back(Builder.GetInsertBlock(), + llvm::AtomicOrdering::Release); + break; + case 4: // memory_order_acq_rel + OrderBBs.emplace_back(Builder.GetInsertBlock(), + llvm::AtomicOrdering::AcquireRelease); + break; + case 5: // memory_order_seq_cst + OrderBBs.emplace_back(Builder.GetInsertBlock(), + llvm::AtomicOrdering::SequentiallyConsistent); + break; + } + } else { + llvm::BasicBlock *AcquireBB = createBasicBlock("acquire", CurFn); + llvm::BasicBlock *ReleaseBB = createBasicBlock("release", CurFn); + llvm::BasicBlock *AcqRelBB = createBasicBlock("acqrel", CurFn); + llvm::BasicBlock *SeqCstBB = createBasicBlock("seqcst", CurFn); + + Order = Builder.CreateIntCast(Order, Builder.getInt32Ty(), false); + llvm::SwitchInst *SI = Builder.CreateSwitch(Order, ContBB); + SI->addCase(Builder.getInt32(1), AcquireBB); + SI->addCase(Builder.getInt32(2), AcquireBB); + SI->addCase(Builder.getInt32(3), ReleaseBB); + SI->addCase(Builder.getInt32(4), AcqRelBB); + SI->addCase(Builder.getInt32(5), SeqCstBB); + + OrderBBs.emplace_back(AcquireBB, llvm::AtomicOrdering::Acquire); + OrderBBs.emplace_back(ReleaseBB, llvm::AtomicOrdering::Release); + OrderBBs.emplace_back(AcqRelBB, llvm::AtomicOrdering::AcquireRelease); + OrderBBs.emplace_back(SeqCstBB, + llvm::AtomicOrdering::SequentiallyConsistent); + } + + for (auto &[OrderBB, Ordering] : OrderBBs) { + Builder.SetInsertPoint(OrderBB); + if (Scp) { + SyncScope SS = ScopeModel->isValid(Scp->getZExtValue()) + ? ScopeModel->map(Scp->getZExtValue()) + : ScopeModel->map(ScopeModel->getFallBackValue()); + Builder.CreateFence(Ordering, + getTargetHooks().getLLVMSyncScopeID( + getLangOpts(), SS, Ordering, getLLVMContext())); + Builder.CreateBr(ContBB); + } else { + llvm::DenseMap BBs; + for (unsigned Scp : ScopeModel->getRuntimeValues()) + BBs[Scp] = createBasicBlock(getAsString(ScopeModel->map(Scp)), CurFn); + + auto *SC = Builder.CreateIntCast(Scope, Builder.getInt32Ty(), false); + llvm::SwitchInst *SI = Builder.CreateSwitch(SC, ContBB); + for (unsigned Scp : ScopeModel->getRuntimeValues()) { + auto *B = BBs[Scp]; + SI->addCase(Builder.getInt32(Scp), B); + + Builder.SetInsertPoint(B); + Builder.CreateFence(Ordering, getTargetHooks().getLLVMSyncScopeID( + getLangOpts(), ScopeModel->map(Scp), + Ordering, getLLVMContext())); + Builder.CreateBr(ContBB); + } + } + } + + Builder.SetInsertPoint(ContBB); + return RValue::get(nullptr); + } case Builtin::BI__builtin_signbit: case Builtin::BI__builtin_signbitf: diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 6e9d28cea28e7..47b21bc9f63f0 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -1899,13 +1899,16 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) { const Address Loc = locIsByrefHeader ? emission.getObjectAddress(*this) : emission.Addr; + auto hasNoTrivialAutoVarInitAttr = [&](const Decl *D) { + return D && D->hasAttr(); + }; // Note: constexpr already initializes everything correctly. LangOptions::TrivialAutoVarInitKind trivialAutoVarInit = - (D.isConstexpr() + ((D.isConstexpr() || D.getAttr() || + hasNoTrivialAutoVarInitAttr(type->getAsTagDecl()) || + hasNoTrivialAutoVarInitAttr(CurFuncDecl)) ? LangOptions::TrivialAutoVarInitKind::Uninitialized - : (D.getAttr() - ? LangOptions::TrivialAutoVarInitKind::Uninitialized - : getContext().getLangOpts().getTrivialAutoVarInit())); + : getContext().getLangOpts().getTrivialAutoVarInit()); auto initializeWhatIsTechnicallyUninitialized = [&](Address Loc) { if (trivialAutoVarInit == @@ -1944,13 +1947,13 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) { replaceUndef(CGM, isPattern, constant)); } - if (constant && D.getType()->isBitIntType() && - CGM.getTypes().typeRequiresSplitIntoByteArray(D.getType())) { + if (constant && type->isBitIntType() && + CGM.getTypes().typeRequiresSplitIntoByteArray(type)) { // Constants for long _BitInt types are split into individual bytes. // Try to fold these back into an integer constant so it can be stored // properly. - llvm::Type *LoadType = CGM.getTypes().convertTypeForLoadStore( - D.getType(), constant->getType()); + llvm::Type *LoadType = + CGM.getTypes().convertTypeForLoadStore(type, constant->getType()); constant = llvm::ConstantFoldLoadFromConst( constant, LoadType, llvm::APInt::getZero(32), CGM.getDataLayout()); } @@ -1967,8 +1970,7 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) { // It may be that the Init expression uses other uninitialized memory, // but auto-var-init here would not help, as auto-init would get // overwritten by Init. - if (!D.getType()->isScalarType() || capturedByInit || - isAccessedBy(D, Init)) { + if (!type->isScalarType() || capturedByInit || isAccessedBy(D, Init)) { initializeWhatIsTechnicallyUninitialized(Loc); } } diff --git a/clang/lib/CodeGen/CGExprComplex.cpp b/clang/lib/CodeGen/CGExprComplex.cpp index 16fdcf6b82853..ac31dff11b585 100644 --- a/clang/lib/CodeGen/CGExprComplex.cpp +++ b/clang/lib/CodeGen/CGExprComplex.cpp @@ -471,7 +471,7 @@ ComplexPairTy ComplexExprEmitter::VisitExpr(Expr *E) { CGF.ErrorUnsupported(E, "complex expression"); llvm::Type *EltTy = CGF.ConvertType(getComplexType(E->getType())->getElementType()); - llvm::Value *U = llvm::UndefValue::get(EltTy); + llvm::Value *U = llvm::PoisonValue::get(EltTy); return ComplexPairTy(U, U); } @@ -1449,7 +1449,7 @@ ComplexPairTy ComplexExprEmitter::VisitVAArgExpr(VAArgExpr *E) { CGF.ErrorUnsupported(E, "complex va_arg expression"); llvm::Type *EltTy = CGF.ConvertType(E->getType()->castAs()->getElementType()); - llvm::Value *U = llvm::UndefValue::get(EltTy); + llvm::Value *U = llvm::PoisonValue::get(EltTy); return ComplexPairTy(U, U); } diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 287d911e10ba5..4ae8a2b22b1bb 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -1828,7 +1828,7 @@ Value *ScalarExprEmitter::VisitExpr(Expr *E) { CGF.ErrorUnsupported(E, "scalar expression"); if (E->getType()->isVoidType()) return nullptr; - return llvm::UndefValue::get(CGF.ConvertType(E->getType())); + return llvm::PoisonValue::get(CGF.ConvertType(E->getType())); } Value * diff --git a/clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp b/clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp index 73e3f9e256f0d..756f0482b8ea7 100644 --- a/clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp +++ b/clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp @@ -2304,6 +2304,7 @@ void CGOpenMPRuntimeGPU::processRequiresDirective(const OMPRequiresDecl *D) { case OffloadArch::GFX940: case OffloadArch::GFX941: case OffloadArch::GFX942: + case OffloadArch::GFX950: case OffloadArch::GFX10_1_GENERIC: case OffloadArch::GFX1010: case OffloadArch::GFX1011: diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 6a2f82f9e1390..ef6bb4f049d6e 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -486,7 +486,7 @@ void CodeGenFunction::FinishFunction(SourceLocation EndLoc) { if (IndirectBranch) { llvm::PHINode *PN = cast(IndirectBranch->getAddress()); if (PN->getNumIncomingValues() == 0) { - PN->replaceAllUsesWith(llvm::UndefValue::get(PN->getType())); + PN->replaceAllUsesWith(llvm::PoisonValue::get(PN->getType())); PN->eraseFromParent(); } } @@ -635,7 +635,9 @@ void CodeGenFunction::EmitKernelMetadata(const FunctionDecl *FD, CGM.GenKernelArgMetadata(Fn, FD, this); - if (!getLangOpts().OpenCL) + if (!(getLangOpts().OpenCL || + (getLangOpts().CUDA && + getContext().getTargetInfo().getTriple().isSPIRV()))) return; if (const VecTypeHintAttr *A = FD->getAttr()) { @@ -1022,6 +1024,8 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, } if (FD && (getLangOpts().OpenCL || + (getLangOpts().CUDA && + getContext().getTargetInfo().getTriple().isSPIRV()) || ((getLangOpts().HIP || getLangOpts().OffloadViaLLVM) && getLangOpts().CUDAIsDevice))) { // Add metadata for a kernel function. @@ -1106,8 +1110,8 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, // Create a marker to make it easy to insert allocas into the entryblock // later. Don't create this with the builder, because we don't want it // folded. - llvm::Value *Undef = llvm::UndefValue::get(Int32Ty); - AllocaInsertPt = new llvm::BitCastInst(Undef, Int32Ty, "allocapt", EntryBB); + llvm::Value *Poison = llvm::PoisonValue::get(Int32Ty); + AllocaInsertPt = new llvm::BitCastInst(Poison, Int32Ty, "allocapt", EntryBB); ReturnBlock = getJumpDestInCurrentScope("return"); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 508f53482d4e1..b854eeb62a80c 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -733,7 +733,7 @@ void CodeGenModule::checkAliases() { for (const GlobalDecl &GD : Aliases) { StringRef MangledName = getMangledName(GD); llvm::GlobalValue *Alias = GetGlobalValue(MangledName); - Alias->replaceAllUsesWith(llvm::UndefValue::get(Alias->getType())); + Alias->replaceAllUsesWith(llvm::PoisonValue::get(Alias->getType())); Alias->eraseFromParent(); } } @@ -1213,6 +1213,9 @@ void CodeGenModule::Release() { getModule().addModuleFlag(llvm::Module::Min, "sign-return-address-with-bkey", 1); + if (LangOpts.PointerAuthELFGOT) + getModule().addModuleFlag(llvm::Module::Min, "ptrauth-elf-got", 1); + if (getTriple().isOSLinux()) { assert(getTriple().isOSBinFormatELF()); using namespace llvm::ELF; @@ -5569,7 +5572,7 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D, } } else { ErrorUnsupported(D, "static initializer"); - Init = llvm::UndefValue::get(getTypes().ConvertType(T)); + Init = llvm::PoisonValue::get(getTypes().ConvertType(T)); } } else { Init = Initializer; diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp index b91233ee2c50a..1abfe8fd92807 100644 --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -65,9 +65,9 @@ static const SanitizerMask AlwaysRecoverable = SanitizerKind::KernelAddress | static const SanitizerMask NeedsLTO = SanitizerKind::CFI; static const SanitizerMask TrappingSupported = (SanitizerKind::Undefined & ~SanitizerKind::Vptr) | SanitizerKind::Integer | - SanitizerKind::Nullability | SanitizerKind::LocalBounds | - SanitizerKind::CFI | SanitizerKind::FloatDivideByZero | - SanitizerKind::ObjCCast; + SanitizerKind::ImplicitConversion | SanitizerKind::Nullability | + SanitizerKind::LocalBounds | SanitizerKind::CFI | + SanitizerKind::FloatDivideByZero | SanitizerKind::ObjCCast; static const SanitizerMask TrappingDefault = SanitizerKind::CFI; static const SanitizerMask CFIClasses = SanitizerKind::CFIVCall | SanitizerKind::CFINVCall | diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp index cbba4289eb945..8d977149e6248 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -1763,18 +1763,13 @@ Arg *tools::getLastProfileUseArg(const ArgList &Args) { Arg *tools::getLastProfileSampleUseArg(const ArgList &Args) { auto *ProfileSampleUseArg = Args.getLastArg( - options::OPT_fprofile_sample_use, options::OPT_fprofile_sample_use_EQ, - options::OPT_fauto_profile, options::OPT_fauto_profile_EQ, - options::OPT_fno_profile_sample_use, options::OPT_fno_auto_profile); - - if (ProfileSampleUseArg && - (ProfileSampleUseArg->getOption().matches( - options::OPT_fno_profile_sample_use) || - ProfileSampleUseArg->getOption().matches(options::OPT_fno_auto_profile))) + options::OPT_fprofile_sample_use_EQ, options::OPT_fno_profile_sample_use); + + if (ProfileSampleUseArg && (ProfileSampleUseArg->getOption().matches( + options::OPT_fno_profile_sample_use))) return nullptr; - return Args.getLastArg(options::OPT_fprofile_sample_use_EQ, - options::OPT_fauto_profile_EQ); + return Args.getLastArg(options::OPT_fprofile_sample_use_EQ); } const char *tools::RelocationModelName(llvm::Reloc::Model Model) { diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 269cbef272079..bc5239209f3aa 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -1554,7 +1554,7 @@ class AnnotatingParser { }; if (IsInstancePort()) - Tok->setFinalizedType(TT_VerilogInstancePortLParen); + Tok->setType(TT_VerilogInstancePortLParen); } if (!parseParens()) @@ -1730,7 +1730,7 @@ class AnnotatingParser { Tok->setType(TT_InheritanceComma); break; case Context::VerilogInstancePortList: - Tok->setFinalizedType(TT_VerilogInstancePortComma); + Tok->setType(TT_VerilogInstancePortComma); break; default: if (Style.isVerilog() && Contexts.size() == 1 && diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index 5f1dd38ef1eb3..c182aaf0876d1 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -4441,7 +4441,8 @@ unsigned UnwrappedLineParser::parseVerilogHierarchyHeader() { Prev->setFinalizedType(TT_VerilogDimensionedTypeName); parseSquare(); } else if (Keywords.isVerilogIdentifier(*FormatTok) || - FormatTok->isOneOf(Keywords.kw_automatic, tok::kw_static)) { + FormatTok->isOneOf(tok::hash, tok::hashhash, tok::coloncolon, + Keywords.kw_automatic, tok::kw_static)) { nextToken(); } else { break; diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index e3145dcacf58d..3dd94c31b2bc7 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -3452,6 +3452,8 @@ static void GeneratePointerAuthArgs(const LangOptions &Opts, GenerateArg(Consumer, OPT_fptrauth_init_fini); if (Opts.PointerAuthInitFiniAddressDiscrimination) GenerateArg(Consumer, OPT_fptrauth_init_fini_address_discrimination); + if (Opts.PointerAuthELFGOT) + GenerateArg(Consumer, OPT_fptrauth_elf_got); } static void ParsePointerAuthArgs(LangOptions &Opts, ArgList &Args, @@ -3472,6 +3474,7 @@ static void ParsePointerAuthArgs(LangOptions &Opts, ArgList &Args, Opts.PointerAuthInitFini = Args.hasArg(OPT_fptrauth_init_fini); Opts.PointerAuthInitFiniAddressDiscrimination = Args.hasArg(OPT_fptrauth_init_fini_address_discrimination); + Opts.PointerAuthELFGOT = Args.hasArg(OPT_fptrauth_elf_got); } /// Check if input file kind and language standard are compatible. diff --git a/clang/lib/Headers/lasxintrin.h b/clang/lib/Headers/lasxintrin.h index dafc2a2f3e6a7..c065ea92a2dd5 100644 --- a/clang/lib/Headers/lasxintrin.h +++ b/clang/lib/Headers/lasxintrin.h @@ -2585,7 +2585,7 @@ extern __inline extern __inline __attribute__((__gnu_inline__, __always_inline__, __artificial__)) __m256i __lasx_xvorn_v(__m256i _1, __m256i _2) { - return (__m256i)__builtin_lasx_xvorn_v((v32i8)_1, (v32i8)_2); + return (__m256i)__builtin_lasx_xvorn_v((v32u8)_1, (v32u8)_2); } #define __lasx_xvldi(/*i13*/ _1) ((__m256i)__builtin_lasx_xvldi((_1))) diff --git a/clang/lib/Headers/lsxintrin.h b/clang/lib/Headers/lsxintrin.h index f347955ce6fb5..f020b0c18f0d2 100644 --- a/clang/lib/Headers/lsxintrin.h +++ b/clang/lib/Headers/lsxintrin.h @@ -3425,7 +3425,7 @@ extern __inline extern __inline __attribute__((__gnu_inline__, __always_inline__, __artificial__)) __m128i __lsx_vorn_v(__m128i _1, __m128i _2) { - return (__m128i)__builtin_lsx_vorn_v((v16i8)_1, (v16i8)_2); + return (__m128i)__builtin_lsx_vorn_v((v16u8)_1, (v16u8)_2); } #define __lsx_vldi(/*i13*/ _1) ((__m128i)__builtin_lsx_vldi((_1))) diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt index d5ffe78251d25..df7ea82e0dada 100644 --- a/clang/lib/Interpreter/CMakeLists.txt +++ b/clang/lib/Interpreter/CMakeLists.txt @@ -10,7 +10,8 @@ set(LLVM_LINK_COMPONENTS Support Target TargetParser - ) + TransformUtils + ) if (EMSCRIPTEN AND "lld" IN_LIST LLVM_ENABLE_PROJECTS) set(WASM_SRC Wasm.cpp) diff --git a/clang/lib/Interpreter/IncrementalExecutor.h b/clang/lib/Interpreter/IncrementalExecutor.h index 7954cde36588b..dbd61f0b8b1eb 100644 --- a/clang/lib/Interpreter/IncrementalExecutor.h +++ b/clang/lib/Interpreter/IncrementalExecutor.h @@ -56,7 +56,7 @@ class IncrementalExecutor { virtual llvm::Error addModule(PartialTranslationUnit &PTU); virtual llvm::Error removeModule(PartialTranslationUnit &PTU); virtual llvm::Error runCtors() const; - llvm::Error cleanUp(); + virtual llvm::Error cleanUp(); llvm::Expected getSymbolAddress(llvm::StringRef Name, SymbolNameKind NameKind) const; diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index bc96da811d44c..94f0156ec151f 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -50,6 +50,9 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include "llvm/TargetParser/Host.h" +#include "llvm/Transforms/Utils/Cloning.h" // for CloneModule + +#define DEBUG_TYPE "clang-repl" using namespace clang; // FIXME: Figure out how to unify with namespace init_convenience from @@ -196,7 +199,6 @@ IncrementalCompilerBuilder::CreateCpp() { #ifdef __EMSCRIPTEN__ Argv.push_back("-target"); Argv.push_back("wasm32-unknown-emscripten"); - Argv.push_back("-pie"); Argv.push_back("-shared"); #endif Argv.insert(Argv.end(), UserArgs.begin(), UserArgs.end()); @@ -339,19 +341,8 @@ class IncrementalAction : public WrapperFrontendAction { } void ExecuteAction() override { - CompilerInstance &CI = getCompilerInstance(); - assert(CI.hasPreprocessor() && "No PP!"); - - // Use a code completion consumer? - CodeCompleteConsumer *CompletionConsumer = nullptr; - if (CI.hasCodeCompletionConsumer()) - CompletionConsumer = &CI.getCodeCompletionConsumer(); - - Preprocessor &PP = CI.getPreprocessor(); - PP.EnterMainSourceFile(); - - if (!CI.hasSema()) - CI.createSema(getTranslationUnitKind(), CompletionConsumer); + WrapperFrontendAction::ExecuteAction(); + getCompilerInstance().getSema().CurContext = nullptr; } // Do not terminate after processing the input. This allows us to keep various @@ -385,8 +376,6 @@ Interpreter::Interpreter(std::unique_ptr Instance, return; CI->ExecuteAction(*Act); - ASTContext &C = CI->getASTContext(); - IncrParser = std::make_unique(*CI, ErrOut); if (ErrOut) @@ -394,18 +383,22 @@ Interpreter::Interpreter(std::unique_ptr Instance, if (getCodeGen()) { CachedInCodeGenModule = GenModule(); + // The initial PTU is filled by `-include` or by CUDA includes + // automatically. + if (!CI->getPreprocessorOpts().Includes.empty()) { + // We can't really directly pass the CachedInCodeGenModule to the Jit + // because it will steal it, causing dangling references as explained in + // Interpreter::Execute + auto M = llvm::CloneModule(*CachedInCodeGenModule); + ASTContext &C = CI->getASTContext(); + RegisterPTU(C.getTranslationUnitDecl(), std::move(M)); + } if (llvm::Error Err = CreateExecutor()) { ErrOut = joinErrors(std::move(ErrOut), std::move(Err)); return; } } - // The initial PTU is filled by `-include` or by CUDA includes automatically. - RegisterPTU(C.getTranslationUnitDecl()); - - // Prepare the IncrParser for input. - llvm::cantFail(Parse("")); - // Not all frontends support code-generation, e.g. ast-dump actions don't if (getCodeGen()) { // Process the PTUs that came from initialization. For example -include will @@ -535,14 +528,25 @@ size_t Interpreter::getEffectivePTUSize() const { return PTUs.size() - InitPTUSize; } -PartialTranslationUnit &Interpreter::RegisterPTU(TranslationUnitDecl *TU) { +PartialTranslationUnit & +Interpreter::RegisterPTU(TranslationUnitDecl *TU, + std::unique_ptr M /*={}*/) { PTUs.emplace_back(PartialTranslationUnit()); PartialTranslationUnit &LastPTU = PTUs.back(); LastPTU.TUPart = TU; - if (std::unique_ptr M = GenModule()) - LastPTU.TheModule = std::move(M); + if (!M) + M = GenModule(); + + assert((!getCodeGen() || M) && "Must have a llvm::Module at this point"); + LastPTU.TheModule = std::move(M); + LLVM_DEBUG(llvm::dbgs() << "compile-ptu " << PTUs.size() - 1 + << ": [TU=" << LastPTU.TUPart); + if (LastPTU.TheModule) + LLVM_DEBUG(llvm::dbgs() << ", M=" << LastPTU.TheModule.get() << " (" + << LastPTU.TheModule->getName() << ")"); + LLVM_DEBUG(llvm::dbgs() << "]\n"); return LastPTU; } @@ -615,6 +619,14 @@ void Interpreter::ResetExecutor() { IncrExecutor.reset(); } llvm::Error Interpreter::Execute(PartialTranslationUnit &T) { assert(T.TheModule); + LLVM_DEBUG(llvm::dbgs() + << "execute-ptu " + << ((std::find(PTUs.begin(), PTUs.end(), T) != PTUs.end()) + ? std::distance(PTUs.begin(), + std::find(PTUs.begin(), PTUs.end(), T)) + : -1) + << ": [TU=" << T.TUPart << ", M=" << T.TheModule.get() << " (" + << T.TheModule->getName() << ")]\n"); if (!IncrExecutor) { auto Err = CreateExecutor(); if (Err) @@ -723,10 +735,12 @@ std::unique_ptr Interpreter::GenModule() { // of the module which does not map well to CodeGen's design. To work this // around we created an empty module to make CodeGen happy. We should make // sure it always stays empty. - assert((!CachedInCodeGenModule || (CachedInCodeGenModule->empty() && - CachedInCodeGenModule->global_empty() && - CachedInCodeGenModule->alias_empty() && - CachedInCodeGenModule->ifunc_empty())) && + assert(((!CachedInCodeGenModule || + !getCompilerInstance()->getPreprocessorOpts().Includes.empty()) || + (CachedInCodeGenModule->empty() && + CachedInCodeGenModule->global_empty() && + CachedInCodeGenModule->alias_empty() && + CachedInCodeGenModule->ifunc_empty())) && "CodeGen wrote to a readonly module"); std::unique_ptr M(CG->ReleaseModule()); CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext()); diff --git a/clang/lib/Interpreter/Wasm.cpp b/clang/lib/Interpreter/Wasm.cpp index 1001410aa0f27..79efbaa03982d 100644 --- a/clang/lib/Interpreter/Wasm.cpp +++ b/clang/lib/Interpreter/Wasm.cpp @@ -72,13 +72,13 @@ llvm::Error WasmIncrementalExecutor::addModule(PartialTranslationUnit &PTU) { OutputFile.close(); std::vector LinkerArgs = {"wasm-ld", - "-pie", + "-shared", "--import-memory", "--no-entry", "--export-all", "--experimental-pic", - "--no-export-dynamic", "--stack-first", + "--allow-undefined", OutputFileName.c_str(), "-o", OutputFileName.c_str()}; @@ -109,6 +109,12 @@ llvm::Error WasmIncrementalExecutor::runCtors() const { return llvm::Error::success(); } +llvm::Error WasmIncrementalExecutor::cleanUp() const { + // Can't call cleanUp through IncrementalExecutor as it + // tries to deinitialize JIT which hasn't been initialized + return llvm::Error::success(); +} + WasmIncrementalExecutor::~WasmIncrementalExecutor() = default; } // namespace clang diff --git a/clang/lib/Interpreter/Wasm.h b/clang/lib/Interpreter/Wasm.h index b1fd88024f14d..4632613326d39 100644 --- a/clang/lib/Interpreter/Wasm.h +++ b/clang/lib/Interpreter/Wasm.h @@ -28,6 +28,7 @@ class WasmIncrementalExecutor : public IncrementalExecutor { llvm::Error addModule(PartialTranslationUnit &PTU) override; llvm::Error removeModule(PartialTranslationUnit &PTU) override; llvm::Error runCtors() const override; + llvm::Error cleanUp() override; ~WasmIncrementalExecutor() override; }; diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp index 239b70698e487..bcbf4dfbabafa 100644 --- a/clang/lib/Parse/ParseObjc.cpp +++ b/clang/lib/Parse/ParseObjc.cpp @@ -1454,7 +1454,7 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, SmallVector KeyIdents; SmallVector KeyLocs; - SmallVector ObjCParamInfo; + SmallVector ArgInfos; ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope | Scope::FunctionDeclarationScope | Scope::DeclScope); @@ -1495,9 +1495,7 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, ArgInfo.NameLoc = Tok.getLocation(); ConsumeToken(); // Eat the identifier. - ParmVarDecl *Param = Actions.ObjC().ActOnMethodParmDeclaration( - getCurScope(), ArgInfo, ObjCParamInfo.size(), MethodDefinition); - ObjCParamInfo.push_back(Param); + ArgInfos.push_back(ArgInfo); KeyIdents.push_back(SelIdent); KeyLocs.push_back(selLoc); @@ -1557,6 +1555,17 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, nullptr)); } + // Turn ArgInfos into parameters. This must happen after parsing all + // parameters for bug compatibility with previous versions of Clang. (For + // instance, if a method declares a parameter called "id", that parameter must + // not shadow the "id" type.) + SmallVector ObjCParamInfo; + for (auto &ArgInfo : ArgInfos) { + ParmVarDecl *Param = Actions.ObjC().ActOnMethodParmDeclaration( + getCurScope(), ArgInfo, ObjCParamInfo.size(), MethodDefinition); + ObjCParamInfo.push_back(Param); + } + // FIXME: Add support for optional parameter list... // If attributes exist after the method, parse them. MaybeParseAttributes(PAKM_CXX11 | (getLangOpts().ObjC ? PAKM_GNU : 0), diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index f11fd3a7e4038..075c0df3f5496 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -2262,19 +2262,28 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler { MsgParam = 5; } else if (const auto *ECE = dyn_cast(Operation)) { QualType destType = ECE->getType(); + bool destTypeComplete = true; + if (!isa(destType)) return; + destType = destType.getTypePtr()->getPointeeType(); + if (const auto *D = destType->getAsTagDecl()) + destTypeComplete = D->isCompleteDefinition(); - const uint64_t dSize = - Ctx.getTypeSize(destType.getTypePtr()->getPointeeType()); + // If destination type is incomplete, it is unsafe to cast to anyway, no + // need to check its type: + if (destTypeComplete) { + const uint64_t dSize = Ctx.getTypeSize(destType); + QualType srcType = ECE->getSubExpr()->getType(); - QualType srcType = ECE->getSubExpr()->getType(); - const uint64_t sSize = - Ctx.getTypeSize(srcType.getTypePtr()->getPointeeType()); + assert(srcType->isPointerType()); - if (sSize >= dSize) - return; + const uint64_t sSize = + Ctx.getTypeSize(srcType.getTypePtr()->getPointeeType()); + if (sSize >= dSize) + return; + } if (const auto *CE = dyn_cast( ECE->getSubExpr()->IgnoreParens())) { D = CE->getMethodDecl(); diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp index a1a402b4a2b53..8886e5e307ddf 100644 --- a/clang/lib/Sema/CheckExprLifetime.cpp +++ b/clang/lib/Sema/CheckExprLifetime.cpp @@ -45,10 +45,14 @@ enum LifetimeKind { /// a default member initializer), the program is ill-formed. LK_MemInitializer, - /// The lifetime of a temporary bound to this entity probably ends too soon, + /// The lifetime of a temporary bound to this entity may end too soon, /// because the entity is a pointer and we assign the address of a temporary /// object to it. LK_Assignment, + + /// The lifetime of a temporary bound to this entity may end too soon, + /// because the entity may capture the reference to a temporary object. + LK_LifetimeCapture, }; using LifetimeResult = llvm::PointerIntPair; @@ -1095,9 +1099,7 @@ static bool pathOnlyHandlesGslPointer(const IndirectLocalPath &Path) { } static bool isAssignmentOperatorLifetimeBound(CXXMethodDecl *CMD) { - if (!CMD) - return false; - return isNormalAssignmentOperator(CMD) && CMD->param_size() == 1 && + return CMD && isNormalAssignmentOperator(CMD) && CMD->param_size() == 1 && CMD->getParamDecl(0)->hasAttr(); } @@ -1110,13 +1112,14 @@ static bool shouldRunGSLAssignmentAnalysis(const Sema &SemaRef, isAssignmentOperatorLifetimeBound(Entity.AssignmentOperator))); } -static void checkExprLifetimeImpl(Sema &SemaRef, - const InitializedEntity *InitEntity, - const InitializedEntity *ExtendingEntity, - LifetimeKind LK, - const AssignedEntity *AEntity, Expr *Init) { - assert((AEntity && LK == LK_Assignment) || - (InitEntity && LK != LK_Assignment)); +static void +checkExprLifetimeImpl(Sema &SemaRef, const InitializedEntity *InitEntity, + const InitializedEntity *ExtendingEntity, LifetimeKind LK, + const AssignedEntity *AEntity, + const CapturingEntity *CapEntity, Expr *Init) { + assert(!AEntity || LK == LK_Assignment); + assert(!CapEntity || LK == LK_LifetimeCapture); + assert(!InitEntity || (LK != LK_Assignment && LK != LK_LifetimeCapture)); // If this entity doesn't have an interesting lifetime, don't bother looking // for temporaries within its initializer. if (LK == LK_FullExpression) @@ -1199,12 +1202,23 @@ static void checkExprLifetimeImpl(Sema &SemaRef, break; } + case LK_LifetimeCapture: { + // The captured entity has lifetime beyond the full-expression, + // and the capturing entity does too, so don't warn. + if (!MTE) + return false; + if (CapEntity->Entity) + SemaRef.Diag(DiagLoc, diag::warn_dangling_reference_captured) + << CapEntity->Entity << DiagRange; + else + SemaRef.Diag(DiagLoc, diag::warn_dangling_reference_captured_by_unknown) + << DiagRange; + return false; + } + case LK_Assignment: { if (!MTE || pathContainsInit(Path)) return false; - assert(shouldLifetimeExtendThroughPath(Path) == - PathLifetimeKind::NoExtend && - "No lifetime extension for assignments"); if (IsGslPtrValueFromGslTempOwner) SemaRef.Diag(DiagLoc, diag::warn_dangling_lifetime_pointer_assignment) << AEntity->LHS << DiagRange; @@ -1413,13 +1427,23 @@ static void checkExprLifetimeImpl(Sema &SemaRef, }; llvm::SmallVector Path; - if (LK == LK_Assignment && - shouldRunGSLAssignmentAnalysis(SemaRef, *AEntity)) { - Path.push_back( - {isAssignmentOperatorLifetimeBound(AEntity->AssignmentOperator) - ? IndirectLocalPathEntry::LifetimeBoundCall - : IndirectLocalPathEntry::GslPointerAssignment, - Init}); + switch (LK) { + case LK_Assignment: { + if (shouldRunGSLAssignmentAnalysis(SemaRef, *AEntity)) + Path.push_back( + {isAssignmentOperatorLifetimeBound(AEntity->AssignmentOperator) + ? IndirectLocalPathEntry::LifetimeBoundCall + : IndirectLocalPathEntry::GslPointerAssignment, + Init}); + break; + } + case LK_LifetimeCapture: { + if (isPointerLikeType(Init->getType())) + Path.push_back({IndirectLocalPathEntry::GslPointerInit, Init}); + break; + } + default: + break; } if (Init->isGLValue()) @@ -1432,23 +1456,23 @@ static void checkExprLifetimeImpl(Sema &SemaRef, /*RevisitSubinits=*/!InitEntity); } -void checkExprLifetime(Sema &SemaRef, const InitializedEntity &Entity, +void checkInitLifetime(Sema &SemaRef, const InitializedEntity &Entity, Expr *Init) { auto LTResult = getEntityLifetime(&Entity); LifetimeKind LK = LTResult.getInt(); const InitializedEntity *ExtendingEntity = LTResult.getPointer(); checkExprLifetimeImpl(SemaRef, &Entity, ExtendingEntity, LK, - /*AEntity*/ nullptr, Init); + /*AEntity=*/nullptr, /*CapEntity=*/nullptr, Init); } void checkExprLifetimeMustTailArg(Sema &SemaRef, const InitializedEntity &Entity, Expr *Init) { checkExprLifetimeImpl(SemaRef, &Entity, nullptr, LK_MustTail, - /*AEntity*/ nullptr, Init); + /*AEntity=*/nullptr, /*CapEntity=*/nullptr, Init); } -void checkExprLifetime(Sema &SemaRef, const AssignedEntity &Entity, - Expr *Init) { +void checkAssignmentLifetime(Sema &SemaRef, const AssignedEntity &Entity, + Expr *Init) { bool EnableDanglingPointerAssignment = !SemaRef.getDiagnostics().isIgnored( diag::warn_dangling_pointer_assignment, SourceLocation()); bool RunAnalysis = (EnableDanglingPointerAssignment && @@ -1460,7 +1484,20 @@ void checkExprLifetime(Sema &SemaRef, const AssignedEntity &Entity, checkExprLifetimeImpl(SemaRef, /*InitEntity=*/nullptr, /*ExtendingEntity=*/nullptr, LK_Assignment, &Entity, - Init); + /*CapEntity=*/nullptr, Init); +} + +void checkCaptureByLifetime(Sema &SemaRef, const CapturingEntity &Entity, + Expr *Init) { + if (SemaRef.getDiagnostics().isIgnored(diag::warn_dangling_reference_captured, + SourceLocation()) && + SemaRef.getDiagnostics().isIgnored( + diag::warn_dangling_reference_captured_by_unknown, SourceLocation())) + return; + return checkExprLifetimeImpl(SemaRef, /*InitEntity=*/nullptr, + /*ExtendingEntity=*/nullptr, LK_LifetimeCapture, + /*AEntity=*/nullptr, + /*CapEntity=*/&Entity, Init); } } // namespace clang::sema diff --git a/clang/lib/Sema/CheckExprLifetime.h b/clang/lib/Sema/CheckExprLifetime.h index 903f312f3533e..38b7061988dc7 100644 --- a/clang/lib/Sema/CheckExprLifetime.h +++ b/clang/lib/Sema/CheckExprLifetime.h @@ -25,15 +25,31 @@ struct AssignedEntity { CXXMethodDecl *AssignmentOperator = nullptr; }; +struct CapturingEntity { + // In an function call involving a lifetime capture, this would be the + // argument capturing the lifetime of another argument. + // void addToSet(std::string_view sv [[clang::lifetime_capture_by(setsv)]], + // set& setsv); + // set setsv; + // addToSet(std::string(), setsv); // Here 'setsv' is the 'Entity'. + // + // This is 'nullptr' when the capturing entity is 'global' or 'unknown'. + Expr *Entity = nullptr; +}; + /// Check that the lifetime of the given expr (and its subobjects) is /// sufficient for initializing the entity, and perform lifetime extension /// (when permitted) if not. -void checkExprLifetime(Sema &SemaRef, const InitializedEntity &Entity, +void checkInitLifetime(Sema &SemaRef, const InitializedEntity &Entity, Expr *Init); /// Check that the lifetime of the given expr (and its subobjects) is /// sufficient for assigning to the entity. -void checkExprLifetime(Sema &SemaRef, const AssignedEntity &Entity, Expr *Init); +void checkAssignmentLifetime(Sema &SemaRef, const AssignedEntity &Entity, + Expr *Init); + +void checkCaptureByLifetime(Sema &SemaRef, const CapturingEntity &Entity, + Expr *Init); /// Check that the lifetime of the given expr (and its subobjects) is /// sufficient, assuming that it is passed as an argument to a musttail diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index cac15b974a276..a14e7d50a6043 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -139,7 +139,7 @@ struct BuiltinTypeDeclBuilder { Attr *ResourceAttr = HLSLResourceAttr::CreateImplicit(Ctx, RK); if (CreateHLSLAttributedResourceType(S, Ctx.HLSLResourceTy, Attrs, AttributedResTy)) - addMemberVariable("h", AttributedResTy, {ResourceAttr}, Access); + addMemberVariable("__handle", AttributedResTy, {ResourceAttr}, Access); return *this; } @@ -212,11 +212,11 @@ struct BuiltinTypeDeclBuilder { // Subscript operators return references to elements, const makes the // reference and method const so that the underlying data is not mutable. - ReturnTy = AST.getLValueReferenceType(ReturnTy); if (IsConst) { ExtInfo.TypeQuals.addConst(); ReturnTy.addConst(); } + ReturnTy = AST.getLValueReferenceType(ReturnTy); QualType MethodTy = AST.getFunctionType(ReturnTy, {AST.UnsignedIntTy}, ExtInfo); @@ -480,8 +480,8 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { onCompletion(Decl, [this](CXXRecordDecl *Decl) { setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, - ResourceKind::TypedBuffer, - /*IsROV=*/false, /*RawBuffer=*/false) + ResourceKind::TypedBuffer, /*IsROV=*/false, + /*RawBuffer=*/false) .addArraySubscriptOperators() .completeDefinition(); }); @@ -503,8 +503,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { .Record; onCompletion(Decl, [this](CXXRecordDecl *Decl) { setupBufferType(Decl, *SemaPtr, ResourceClass::SRV, ResourceKind::RawBuffer, - /*IsROV=*/false, - /*RawBuffer=*/true) + /*IsROV=*/false, /*RawBuffer=*/true) .addArraySubscriptOperators() .completeDefinition(); }); @@ -514,8 +513,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { .Record; onCompletion(Decl, [this](CXXRecordDecl *Decl) { setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer, - /*IsROV=*/false, - /*RawBuffer=*/true) + /*IsROV=*/false, /*RawBuffer=*/true) .addArraySubscriptOperators() .completeDefinition(); }); @@ -526,8 +524,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { .Record; onCompletion(Decl, [this](CXXRecordDecl *Decl) { setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer, - /*IsROV=*/false, - /*RawBuffer=*/true) + /*IsROV=*/false, /*RawBuffer=*/true) .completeDefinition(); }); @@ -537,8 +534,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { .Record; onCompletion(Decl, [this](CXXRecordDecl *Decl) { setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer, - /*IsROV=*/false, - /*RawBuffer=*/true) + /*IsROV=*/false, /*RawBuffer=*/true) .completeDefinition(); }); @@ -547,9 +543,8 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { .addSimpleTemplateParams(*SemaPtr, {"element_type"}) .Record; onCompletion(Decl, [this](CXXRecordDecl *Decl) { - setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, - ResourceKind::TypedBuffer, /*IsROV=*/true, - /*RawBuffer=*/true) + setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer, + /*IsROV=*/true, /*RawBuffer=*/true) .addArraySubscriptOperators() .completeDefinition(); }); diff --git a/clang/lib/Sema/JumpDiagnostics.cpp b/clang/lib/Sema/JumpDiagnostics.cpp index 8c830769a969c..d465599450e7f 100644 --- a/clang/lib/Sema/JumpDiagnostics.cpp +++ b/clang/lib/Sema/JumpDiagnostics.cpp @@ -179,9 +179,9 @@ static ScopePair GetDiagForGotoScopeDecl(Sema &S, const Decl *D) { } } - const Expr *Init = VD->getInit(); - if (S.Context.getLangOpts().CPlusPlus && VD->hasLocalStorage() && Init && - !Init->containsErrors()) { + if (const Expr *Init = VD->getInit(); S.Context.getLangOpts().CPlusPlus && + VD->hasLocalStorage() && Init && + !Init->containsErrors()) { // C++11 [stmt.dcl]p3: // A program that jumps from a point where a variable with automatic // storage duration is not in scope to a point where it is in scope diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 2d4a7cd287b70..2fd990750ed21 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "CheckExprLifetime.h" #include "clang/AST/APValue.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" @@ -3222,6 +3223,47 @@ void Sema::CheckArgAlignment(SourceLocation Loc, NamedDecl *FDecl, << ParamName << (FDecl != nullptr) << FDecl; } +void Sema::checkLifetimeCaptureBy(FunctionDecl *FD, bool IsMemberFunction, + const Expr *ThisArg, + ArrayRef Args) { + if (!FD || Args.empty()) + return; + auto GetArgAt = [&](int Idx) -> const Expr * { + if (Idx == LifetimeCaptureByAttr::GLOBAL || + Idx == LifetimeCaptureByAttr::UNKNOWN) + return nullptr; + if (IsMemberFunction && Idx == 0) + return ThisArg; + return Args[Idx - IsMemberFunction]; + }; + auto HandleCaptureByAttr = [&](const LifetimeCaptureByAttr *Attr, + unsigned ArgIdx) { + if (!Attr) + return; + Expr *Captured = const_cast(GetArgAt(ArgIdx)); + for (int CapturingParamIdx : Attr->params()) { + Expr *Capturing = const_cast(GetArgAt(CapturingParamIdx)); + CapturingEntity CE{Capturing}; + // Ensure that 'Captured' outlives the 'Capturing' entity. + checkCaptureByLifetime(*this, CE, Captured); + } + }; + for (unsigned I = 0; I < FD->getNumParams(); ++I) + HandleCaptureByAttr(FD->getParamDecl(I)->getAttr(), + I + IsMemberFunction); + // Check when the implicit object param is captured. + if (IsMemberFunction) { + TypeSourceInfo *TSI = FD->getTypeSourceInfo(); + if (!TSI) + return; + AttributedTypeLoc ATL; + for (TypeLoc TL = TSI->getTypeLoc(); + (ATL = TL.getAsAdjusted()); + TL = ATL.getModifiedLoc()) + HandleCaptureByAttr(ATL.getAttrAs(), 0); + } +} + void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto, const Expr *ThisArg, ArrayRef Args, bool IsMemberFunction, SourceLocation Loc, @@ -3262,7 +3304,8 @@ void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto, } } } - + if (FD) + checkLifetimeCaptureBy(FD, IsMemberFunction, ThisArg, Args); if (FDecl || Proto) { CheckNonNullArguments(*this, FDecl, Proto, Args, Loc); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index a36ca61a1bef3..be570f3a1829d 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -8350,9 +8350,15 @@ void Sema::CheckShadow(NamedDecl *D, NamedDecl *ShadowedDecl, return; // Only warn about certain kinds of shadowing for class members. - if (NewDC && NewDC->isRecord()) { + if (NewDC) { // In particular, don't warn about shadowing non-class members. - if (!OldDC->isRecord()) + if (NewDC->isRecord() && !OldDC->isRecord()) + return; + + // Skip shadowing check if we're in a class scope, dealing with an enum + // constant in a different context. + DeclContext *ReDC = NewDC->getRedeclContext(); + if (ReDC->isRecord() && isa(D) && !OldDC->Equals(ReDC)) return; // TODO: should we warn about static data members shadowing @@ -8363,7 +8369,6 @@ void Sema::CheckShadow(NamedDecl *D, NamedDecl *ShadowedDecl, // shadowing context, but that's just a false negative. } - DeclarationName Name = R.getLookupName(); // Emit warning and note. diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 0f5baa1e1eb36..146d9c86e0715 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -7368,7 +7368,9 @@ void Sema::ProcessDeclAttributeList( // good to have a way to specify "these attributes must appear as a group", // for these. Additionally, it would be good to have a way to specify "these // attribute must never appear as a group" for attributes like cold and hot. - if (!D->hasAttr()) { + if (!(D->hasAttr() || + (D->hasAttr() && + Context.getTargetInfo().getTriple().isSPIRV()))) { // These attributes cannot be applied to a non-kernel function. if (const auto *A = D->getAttr()) { // FIXME: This emits a different error message than diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index dcf495b700540..6c7472ce92703 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -13821,7 +13821,7 @@ QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS, CheckForNullPointerDereference(*this, LHSExpr); AssignedEntity AE{LHSExpr}; - checkExprLifetime(*this, AE, RHS.get()); + checkAssignmentLifetime(*this, AE, RHS.get()); if (getLangOpts().CPlusPlus20 && LHSType.isVolatileQualified()) { if (CompoundType.isNull()) { diff --git a/clang/lib/Sema/SemaFunctionEffects.cpp b/clang/lib/Sema/SemaFunctionEffects.cpp index 4b5ddb74b1262..6fe4d2353a228 100644 --- a/clang/lib/Sema/SemaFunctionEffects.cpp +++ b/clang/lib/Sema/SemaFunctionEffects.cpp @@ -807,7 +807,8 @@ class Analyzer { auto MaybeAddTemplateNote = [&](const Decl *D) { if (const FunctionDecl *FD = dyn_cast(D)) { - while (FD != nullptr && FD->isTemplateInstantiation()) { + while (FD != nullptr && FD->isTemplateInstantiation() && + FD->getPointOfInstantiation().isValid()) { S.Diag(FD->getPointOfInstantiation(), diag::note_func_effect_from_template); FD = FD->getTemplateInstantiationPattern(); diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 1e98a074894ad..7c03a12e81280 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -7401,7 +7401,7 @@ PerformConstructorInitialization(Sema &S, void Sema::checkInitializerLifetime(const InitializedEntity &Entity, Expr *Init) { - return sema::checkExprLifetime(*this, Entity, Init); + return sema::checkInitLifetime(*this, Entity, Init); } static void DiagnoseNarrowingInInitList(Sema &S, diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index e7afa0f4c81fc..a67c0b2b367d1 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -1950,8 +1950,6 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body) { LambdaScopeInfo LSI = *cast(FunctionScopes.back()); ActOnFinishFunctionBody(LSI.CallOperator, Body); - maybeAddDeclWithEffects(LSI.CallOperator); - return BuildLambdaExpr(StartLoc, Body->getEndLoc(), &LSI); } @@ -2284,6 +2282,7 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc, case ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed: break; } + maybeAddDeclWithEffects(LSI->CallOperator); } return MaybeBindToTemporary(Lambda); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index a239f2c6e88e4..e4bf9aa521224 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -14809,7 +14809,7 @@ ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc, // Check for a self move. DiagnoseSelfMove(Args[0], Args[1], OpLoc); // lifetime check. - checkExprLifetime( + checkAssignmentLifetime( *this, AssignedEntity{Args[0], dyn_cast(FnDecl)}, Args[1]); } diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index f3ee5211acdd1..d6bc66246c758 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -200,23 +200,30 @@ static bool DiagnoseUnusedComparison(Sema &S, const Expr *E) { return true; } -static bool DiagnoseNoDiscard(Sema &S, const WarnUnusedResultAttr *A, - SourceLocation Loc, SourceRange R1, - SourceRange R2, bool IsCtor) { +static bool DiagnoseNoDiscard(Sema &S, const NamedDecl *OffendingDecl, + const WarnUnusedResultAttr *A, SourceLocation Loc, + SourceRange R1, SourceRange R2, bool IsCtor) { if (!A) return false; StringRef Msg = A->getMessage(); if (Msg.empty()) { + if (OffendingDecl) + return S.Diag(Loc, diag::warn_unused_return_type) + << IsCtor << A << OffendingDecl << false << R1 << R2; if (IsCtor) - return S.Diag(Loc, diag::warn_unused_constructor) << A << R1 << R2; - return S.Diag(Loc, diag::warn_unused_result) << A << R1 << R2; + return S.Diag(Loc, diag::warn_unused_constructor) + << A << false << R1 << R2; + return S.Diag(Loc, diag::warn_unused_result) << A << false << R1 << R2; } + if (OffendingDecl) + return S.Diag(Loc, diag::warn_unused_return_type) + << IsCtor << A << OffendingDecl << true << Msg << R1 << R2; if (IsCtor) - return S.Diag(Loc, diag::warn_unused_constructor_msg) << A << Msg << R1 - << R2; - return S.Diag(Loc, diag::warn_unused_result_msg) << A << Msg << R1 << R2; + return S.Diag(Loc, diag::warn_unused_constructor) + << A << true << Msg << R1 << R2; + return S.Diag(Loc, diag::warn_unused_result) << A << true << Msg << R1 << R2; } void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) { @@ -286,9 +293,10 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) { if (E->getType()->isVoidType()) return; - if (DiagnoseNoDiscard(*this, cast_or_null( - CE->getUnusedResultAttr(Context)), - Loc, R1, R2, /*isCtor=*/false)) + auto [OffendingDecl, A] = CE->getUnusedResultAttr(Context); + if (DiagnoseNoDiscard(*this, OffendingDecl, + cast_or_null(A), Loc, R1, R2, + /*isCtor=*/false)) return; // If the callee has attribute pure, const, or warn_unused_result, warn with @@ -309,16 +317,21 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) { } } else if (const auto *CE = dyn_cast(E)) { if (const CXXConstructorDecl *Ctor = CE->getConstructor()) { + const NamedDecl *OffendingDecl = nullptr; const auto *A = Ctor->getAttr(); - A = A ? A : Ctor->getParent()->getAttr(); - if (DiagnoseNoDiscard(*this, A, Loc, R1, R2, /*isCtor=*/true)) + if (!A) { + OffendingDecl = Ctor->getParent(); + A = OffendingDecl->getAttr(); + } + if (DiagnoseNoDiscard(*this, OffendingDecl, A, Loc, R1, R2, + /*isCtor=*/true)) return; } } else if (const auto *ILE = dyn_cast(E)) { if (const TagDecl *TD = ILE->getType()->getAsTagDecl()) { - if (DiagnoseNoDiscard(*this, TD->getAttr(), Loc, R1, - R2, /*isCtor=*/false)) + if (DiagnoseNoDiscard(*this, TD, TD->getAttr(), Loc, + R1, R2, /*isCtor=*/false)) return; } } else if (ShouldSuppress) @@ -332,8 +345,8 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) { } const ObjCMethodDecl *MD = ME->getMethodDecl(); if (MD) { - if (DiagnoseNoDiscard(*this, MD->getAttr(), Loc, R1, - R2, /*isCtor=*/false)) + if (DiagnoseNoDiscard(*this, nullptr, MD->getAttr(), + Loc, R1, R2, /*isCtor=*/false)) return; } } else if (const PseudoObjectExpr *POE = dyn_cast(E)) { diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 76e3fcc124178..f32edc5ac0644 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -4882,9 +4882,17 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, cast(T)->getKeyword() != AutoTypeKeyword::Auto || cast(T)->isConstrained())) { - S.Diag(D.getDeclSpec().getTypeSpecTypeLoc(), - diag::err_trailing_return_without_auto) - << T << D.getDeclSpec().getSourceRange(); + // Attach a valid source location for diagnostics on functions with + // trailing return types missing 'auto'. Attempt to get the location + // from the declared type; if invalid, fall back to the trailing + // return type's location. + SourceLocation Loc = D.getDeclSpec().getTypeSpecTypeLoc(); + SourceRange SR = D.getDeclSpec().getSourceRange(); + if (Loc.isInvalid()) { + Loc = FTI.getTrailingReturnTypeLoc(); + SR = D.getSourceRange(); + } + S.Diag(Loc, diag::err_trailing_return_without_auto) << T << SR; D.setInvalidType(true); // FIXME: recover and fill decls in `TypeLoc`s. AreDeclaratorChunksValid = false; diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 8b928ede395ae..ec85fad3389a1 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -3092,98 +3092,97 @@ ASTReader::ReadControlBlock(ModuleFile &F, break; } - case IMPORTS: { + case IMPORT: { // Validate the AST before processing any imports (otherwise, untangling // them can be error-prone and expensive). A module will have a name and // will already have been validated, but this catches the PCH case. if (ASTReadResult Result = readUnhashedControlBlockOnce()) return Result; - // Load each of the imported PCH files. - unsigned Idx = 0, N = Record.size(); - while (Idx < N) { - // Read information about the AST file. - ModuleKind ImportedKind = (ModuleKind)Record[Idx++]; - // Whether we're importing a standard c++ module. - bool IsImportingStdCXXModule = Record[Idx++]; - // The import location will be the local one for now; we will adjust - // all import locations of module imports after the global source - // location info are setup, in ReadAST. - auto [ImportLoc, ImportModuleFileIndex] = - ReadUntranslatedSourceLocation(Record[Idx++]); - // The import location must belong to the current module file itself. - assert(ImportModuleFileIndex == 0); - off_t StoredSize = !IsImportingStdCXXModule ? (off_t)Record[Idx++] : 0; - time_t StoredModTime = - !IsImportingStdCXXModule ? (time_t)Record[Idx++] : 0; - - ASTFileSignature StoredSignature; - if (!IsImportingStdCXXModule) { - auto FirstSignatureByte = Record.begin() + Idx; - StoredSignature = ASTFileSignature::create( - FirstSignatureByte, FirstSignatureByte + ASTFileSignature::size); - Idx += ASTFileSignature::size; - } + unsigned Idx = 0; + // Read information about the AST file. + ModuleKind ImportedKind = (ModuleKind)Record[Idx++]; + + // The import location will be the local one for now; we will adjust + // all import locations of module imports after the global source + // location info are setup, in ReadAST. + auto [ImportLoc, ImportModuleFileIndex] = + ReadUntranslatedSourceLocation(Record[Idx++]); + // The import location must belong to the current module file itself. + assert(ImportModuleFileIndex == 0); + + StringRef ImportedName = ReadStringBlob(Record, Idx, Blob); + + bool IsImportingStdCXXModule = Record[Idx++]; + + off_t StoredSize = 0; + time_t StoredModTime = 0; + ASTFileSignature StoredSignature; + std::string ImportedFile; + + // For prebuilt and explicit modules first consult the file map for + // an override. Note that here we don't search prebuilt module + // directories if we're not importing standard c++ module, only the + // explicit name to file mappings. Also, we will still verify the + // size/signature making sure it is essentially the same file but + // perhaps in a different location. + if (ImportedKind == MK_PrebuiltModule || ImportedKind == MK_ExplicitModule) + ImportedFile = PP.getHeaderSearchInfo().getPrebuiltModuleFileName( + ImportedName, /*FileMapOnly*/ !IsImportingStdCXXModule); + + if (IsImportingStdCXXModule && ImportedFile.empty()) { + Diag(diag::err_failed_to_find_module_file) << ImportedName; + return Missing; + } - std::string ImportedName = ReadString(Record, Idx); - std::string ImportedFile; - - // For prebuilt and explicit modules first consult the file map for - // an override. Note that here we don't search prebuilt module - // directories if we're not importing standard c++ module, only the - // explicit name to file mappings. Also, we will still verify the - // size/signature making sure it is essentially the same file but - // perhaps in a different location. - if (ImportedKind == MK_PrebuiltModule || ImportedKind == MK_ExplicitModule) - ImportedFile = PP.getHeaderSearchInfo().getPrebuiltModuleFileName( - ImportedName, /*FileMapOnly*/ !IsImportingStdCXXModule); - - // For C++20 Modules, we won't record the path to the imported modules - // in the BMI - if (!IsImportingStdCXXModule) { - if (ImportedFile.empty()) { - // Use BaseDirectoryAsWritten to ensure we use the same path in the - // ModuleCache as when writing. - ImportedFile = ReadPath(BaseDirectoryAsWritten, Record, Idx); - } else - SkipPath(Record, Idx); - } else if (ImportedFile.empty()) { - Diag(clang::diag::err_failed_to_find_module_file) << ImportedName; - return Missing; - } + if (!IsImportingStdCXXModule) { + StoredSize = (off_t)Record[Idx++]; + StoredModTime = (time_t)Record[Idx++]; - // If our client can't cope with us being out of date, we can't cope with - // our dependency being missing. - unsigned Capabilities = ClientLoadCapabilities; - if ((ClientLoadCapabilities & ARR_OutOfDate) == 0) - Capabilities &= ~ARR_Missing; - - // Load the AST file. - auto Result = ReadASTCore(ImportedFile, ImportedKind, ImportLoc, &F, - Loaded, StoredSize, StoredModTime, - StoredSignature, Capabilities); - - // If we diagnosed a problem, produce a backtrace. - bool recompilingFinalized = - Result == OutOfDate && (Capabilities & ARR_OutOfDate) && - getModuleManager().getModuleCache().isPCMFinal(F.FileName); - if (isDiagnosedResult(Result, Capabilities) || recompilingFinalized) - Diag(diag::note_module_file_imported_by) - << F.FileName << !F.ModuleName.empty() << F.ModuleName; - if (recompilingFinalized) - Diag(diag::note_module_file_conflict); - - switch (Result) { - case Failure: return Failure; - // If we have to ignore the dependency, we'll have to ignore this too. - case Missing: - case OutOfDate: return OutOfDate; - case VersionMismatch: return VersionMismatch; - case ConfigurationMismatch: return ConfigurationMismatch; - case HadErrors: return HadErrors; - case Success: break; + StringRef SignatureBytes = Blob.substr(0, ASTFileSignature::size); + StoredSignature = ASTFileSignature::create(SignatureBytes.begin(), + SignatureBytes.end()); + Blob = Blob.substr(ASTFileSignature::size); + + if (ImportedFile.empty()) { + // Use BaseDirectoryAsWritten to ensure we use the same path in the + // ModuleCache as when writing. + ImportedFile = + ReadPathBlob(BaseDirectoryAsWritten, Record, Idx, Blob); } } + + // If our client can't cope with us being out of date, we can't cope with + // our dependency being missing. + unsigned Capabilities = ClientLoadCapabilities; + if ((ClientLoadCapabilities & ARR_OutOfDate) == 0) + Capabilities &= ~ARR_Missing; + + // Load the AST file. + auto Result = ReadASTCore(ImportedFile, ImportedKind, ImportLoc, &F, + Loaded, StoredSize, StoredModTime, + StoredSignature, Capabilities); + + // If we diagnosed a problem, produce a backtrace. + bool recompilingFinalized = + Result == OutOfDate && (Capabilities & ARR_OutOfDate) && + getModuleManager().getModuleCache().isPCMFinal(F.FileName); + if (isDiagnosedResult(Result, Capabilities) || recompilingFinalized) + Diag(diag::note_module_file_imported_by) + << F.FileName << !F.ModuleName.empty() << F.ModuleName; + if (recompilingFinalized) + Diag(diag::note_module_file_conflict); + + switch (Result) { + case Failure: return Failure; + // If we have to ignore the dependency, we'll have to ignore this too. + case Missing: + case OutOfDate: return OutOfDate; + case VersionMismatch: return VersionMismatch; + case ConfigurationMismatch: return ConfigurationMismatch; + case HadErrors: return HadErrors; + case Success: break; + } break; } @@ -5624,36 +5623,38 @@ bool ASTReader::readASTFileControlBlock( break; } - case IMPORTS: { + case IMPORT: { if (!NeedsImports) break; - unsigned Idx = 0, N = Record.size(); - while (Idx < N) { - // Read information about the AST file. + unsigned Idx = 0; + // Read information about the AST file. + + // Skip Kind + Idx++; - // Skip Kind - Idx++; - bool IsStandardCXXModule = Record[Idx++]; + // Skip ImportLoc + Idx++; - // Skip ImportLoc - Idx++; + StringRef ModuleName = ReadStringBlob(Record, Idx, Blob); - // In C++20 Modules, we don't record the path to imported - // modules in the BMI files. - if (IsStandardCXXModule) { - std::string ModuleName = ReadString(Record, Idx); - Listener.visitImport(ModuleName, /*Filename=*/""); - continue; - } + bool IsStandardCXXModule = Record[Idx++]; - // Skip Size, ModTime and Signature - Idx += 1 + 1 + ASTFileSignature::size; - std::string ModuleName = ReadString(Record, Idx); - std::string FilenameStr = ReadString(Record, Idx); - auto Filename = ResolveImportedPath(PathBuf, FilenameStr, ModuleDir); - Listener.visitImport(ModuleName, *Filename); + // In C++20 Modules, we don't record the path to imported + // modules in the BMI files. + if (IsStandardCXXModule) { + Listener.visitImport(ModuleName, /*Filename=*/""); + continue; } + + // Skip Size and ModTime. + Idx += 1 + 1; + // Skip signature. + Blob = Blob.substr(ASTFileSignature::size); + + StringRef FilenameStr = ReadStringBlob(Record, Idx, Blob); + auto Filename = ResolveImportedPath(PathBuf, FilenameStr, ModuleDir); + Listener.visitImport(ModuleName, *Filename); break; } @@ -9602,6 +9603,14 @@ std::string ASTReader::ReadString(const RecordDataImpl &Record, unsigned &Idx) { return Result; } +StringRef ASTReader::ReadStringBlob(const RecordDataImpl &Record, unsigned &Idx, + StringRef &Blob) { + unsigned Len = Record[Idx++]; + StringRef Result = Blob.substr(0, Len); + Blob = Blob.substr(Len); + return Result; +} + std::string ASTReader::ReadPath(ModuleFile &F, const RecordData &Record, unsigned &Idx) { return ReadPath(F.BaseDirectory, Record, Idx); @@ -9613,6 +9622,13 @@ std::string ASTReader::ReadPath(StringRef BaseDirectory, return ResolveImportedPathAndAllocate(PathBuf, Filename, BaseDirectory); } +std::string ASTReader::ReadPathBlob(StringRef BaseDirectory, + const RecordData &Record, unsigned &Idx, + StringRef &Blob) { + StringRef Filename = ReadStringBlob(Record, Idx, Blob); + return ResolveImportedPathAndAllocate(PathBuf, Filename, BaseDirectory); +} + VersionTuple ASTReader::ReadVersionTuple(const RecordData &Record, unsigned &Idx) { unsigned Major = Record[Idx++]; diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 88b3e649a5d46..a52d59c61c4ce 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -878,7 +878,7 @@ void ASTWriter::WriteBlockInfoBlock() { RECORD(MODULE_NAME); RECORD(MODULE_DIRECTORY); RECORD(MODULE_MAP_FILE); - RECORD(IMPORTS); + RECORD(IMPORT); RECORD(ORIGINAL_FILE); RECORD(ORIGINAL_FILE_ID); RECORD(INPUT_FILE_OFFSETS); @@ -1536,34 +1536,53 @@ void ASTWriter::WriteControlBlock(Preprocessor &PP, StringRef isysroot) { // Imports if (Chain) { - serialization::ModuleManager &Mgr = Chain->getModuleManager(); - Record.clear(); + auto Abbrev = std::make_shared(); + Abbrev->Add(BitCodeAbbrevOp(IMPORT)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Kind + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // ImportLoc + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Module name len + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Standard C++ mod + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // File size + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // File timestamp + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // File name len + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Strings + unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); - for (ModuleFile &M : Mgr) { + SmallString<128> Blob; + + for (ModuleFile &M : Chain->getModuleManager()) { // Skip modules that weren't directly imported. if (!M.isDirectlyImported()) continue; + Record.clear(); + Blob.clear(); + + Record.push_back(IMPORT); Record.push_back((unsigned)M.Kind); // FIXME: Stable encoding - Record.push_back(M.StandardCXXModule); AddSourceLocation(M.ImportLoc, Record); + AddStringBlob(M.ModuleName, Record, Blob); + Record.push_back(M.StandardCXXModule); // We don't want to hard code the information about imported modules // in the C++20 named modules. - if (!M.StandardCXXModule) { + if (M.StandardCXXModule) { + Record.push_back(0); + Record.push_back(0); + Record.push_back(0); + } else { // If we have calculated signature, there is no need to store // the size or timestamp. Record.push_back(M.Signature ? 0 : M.File.getSize()); Record.push_back(M.Signature ? 0 : getTimestampForOutput(M.File)); - llvm::append_range(Record, M.Signature); - } - AddString(M.ModuleName, Record); + llvm::append_range(Blob, M.Signature); - if (!M.StandardCXXModule) - AddPath(M.FileName, Record); + AddPathBlob(M.FileName, Record, Blob); + } + + Stream.EmitRecordWithBlob(AbbrevCode, Record, Blob); } - Stream.EmitRecord(IMPORTS, Record); } // Write the options block. @@ -4777,6 +4796,12 @@ void ASTWriter::AddString(StringRef Str, RecordDataImpl &Record) { Record.insert(Record.end(), Str.begin(), Str.end()); } +void ASTWriter::AddStringBlob(StringRef Str, RecordDataImpl &Record, + SmallVectorImpl &Blob) { + Record.push_back(Str.size()); + Blob.insert(Blob.end(), Str.begin(), Str.end()); +} + bool ASTWriter::PreparePathForOutput(SmallVectorImpl &Path) { assert(WritingAST && "can't prepare path for output when not writing AST"); @@ -4805,6 +4830,13 @@ void ASTWriter::AddPath(StringRef Path, RecordDataImpl &Record) { AddString(FilePath, Record); } +void ASTWriter::AddPathBlob(StringRef Path, RecordDataImpl &Record, + SmallVectorImpl &Blob) { + SmallString<128> FilePath(Path); + PreparePathForOutput(FilePath); + AddStringBlob(FilePath, Record, Blob); +} + void ASTWriter::EmitRecordWithPath(unsigned Abbrev, RecordDataRef Record, StringRef Path) { SmallString<128> FilePath(Path); diff --git a/clang/lib/Serialization/GlobalModuleIndex.cpp b/clang/lib/Serialization/GlobalModuleIndex.cpp index 9c48712a0b3fb..4b920fccecac3 100644 --- a/clang/lib/Serialization/GlobalModuleIndex.cpp +++ b/clang/lib/Serialization/GlobalModuleIndex.cpp @@ -614,62 +614,58 @@ llvm::Error GlobalModuleIndexBuilder::loadModuleFile(FileEntryRef File) { unsigned Code = MaybeCode.get(); // Handle module dependencies. - if (State == ControlBlock && Code == IMPORTS) { - // Load each of the imported PCH files. - unsigned Idx = 0, N = Record.size(); - while (Idx < N) { - // Read information about the AST file. - - // Skip the imported kind - ++Idx; - - // Skip if it is standard C++ module - ++Idx; - - // Skip the import location - ++Idx; - - // Load stored size/modification time. - off_t StoredSize = (off_t)Record[Idx++]; - time_t StoredModTime = (time_t)Record[Idx++]; - - // Skip the stored signature. - // FIXME: we could read the signature out of the import and validate it. - auto FirstSignatureByte = Record.begin() + Idx; - ASTFileSignature StoredSignature = ASTFileSignature::create( - FirstSignatureByte, FirstSignatureByte + ASTFileSignature::size); - Idx += ASTFileSignature::size; - - // Skip the module name (currently this is only used for prebuilt - // modules while here we are only dealing with cached). - Idx += Record[Idx] + 1; - - // Retrieve the imported file name. - unsigned Length = Record[Idx++]; - SmallString<128> ImportedFile(Record.begin() + Idx, - Record.begin() + Idx + Length); - Idx += Length; - - // Find the imported module file. - auto DependsOnFile = - FileMgr.getOptionalFileRef(ImportedFile, /*OpenFile=*/false, - /*CacheFailure=*/false); - - if (!DependsOnFile) - return llvm::createStringError(std::errc::bad_file_descriptor, - "imported file \"%s\" not found", - ImportedFile.c_str()); - - // Save the information in ImportedModuleFileInfo so we can verify after - // loading all pcms. - ImportedModuleFiles.insert(std::make_pair( - *DependsOnFile, ImportedModuleFileInfo(StoredSize, StoredModTime, - StoredSignature))); - - // Record the dependency. - unsigned DependsOnID = getModuleFileInfo(*DependsOnFile).ID; - getModuleFileInfo(File).Dependencies.push_back(DependsOnID); - } + if (State == ControlBlock && Code == IMPORT) { + unsigned Idx = 0; + // Read information about the AST file. + + // Skip the imported kind + ++Idx; + + // Skip the import location + ++Idx; + + // Skip the module name (currently this is only used for prebuilt + // modules while here we are only dealing with cached). + Blob = Blob.substr(Record[Idx++]); + + // Skip if it is standard C++ module + ++Idx; + + // Load stored size/modification time. + off_t StoredSize = (off_t)Record[Idx++]; + time_t StoredModTime = (time_t)Record[Idx++]; + + // Skip the stored signature. + // FIXME: we could read the signature out of the import and validate it. + StringRef SignatureBytes = Blob.substr(0, ASTFileSignature::size); + auto StoredSignature = ASTFileSignature::create(SignatureBytes.begin(), + SignatureBytes.end()); + Blob = Blob.substr(ASTFileSignature::size); + + // Retrieve the imported file name. + unsigned Length = Record[Idx++]; + StringRef ImportedFile = Blob.substr(0, Length); + Blob = Blob.substr(Length); + + // Find the imported module file. + auto DependsOnFile = + FileMgr.getOptionalFileRef(ImportedFile, /*OpenFile=*/false, + /*CacheFailure=*/false); + + if (!DependsOnFile) + return llvm::createStringError(std::errc::bad_file_descriptor, + "imported file \"%s\" not found", + std::string(ImportedFile).c_str()); + + // Save the information in ImportedModuleFileInfo so we can verify after + // loading all pcms. + ImportedModuleFiles.insert(std::make_pair( + *DependsOnFile, ImportedModuleFileInfo(StoredSize, StoredModTime, + StoredSignature))); + + // Record the dependency. + unsigned DependsOnID = getModuleFileInfo(*DependsOnFile).ID; + getModuleFileInfo(File).Dependencies.push_back(DependsOnID); continue; } diff --git a/clang/test/AST/HLSL/AppendStructuredBuffer-AST.hlsl b/clang/test/AST/HLSL/AppendStructuredBuffer-AST.hlsl index 5a13ca7735f99..8c951e9829211 100644 --- a/clang/test/AST/HLSL/AppendStructuredBuffer-AST.hlsl +++ b/clang/test/AST/HLSL/AppendStructuredBuffer-AST.hlsl @@ -30,20 +30,20 @@ AppendStructuredBuffer Buffer; // CHECK-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <> implicit class AppendStructuredBuffer definition // CHECK: FinalAttr 0x{{[0-9A-Fa-f]+}} <> Implicit final -// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <> implicit h '__hlsl_resource_t +// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <> implicit __handle '__hlsl_resource_t // CHECK-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] // CHECK-SAME{LITERAL}: [[hlsl::raw_buffer]] // CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]] // CHECK-NEXT: HLSLResourceAttr 0x{{[0-9A-Fa-f]+}} <> Implicit RawBuffer -// CHECK-NOT: CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <> operator[] 'element_type &const (unsigned int) const' +// CHECK-NOT: CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <> operator[] 'const element_type &(unsigned int) const' // CHECK-NOT: CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <> operator[] 'element_type &(unsigned int)' // CHECK: ClassTemplateSpecializationDecl 0x{{[0-9A-Fa-f]+}} <> class AppendStructuredBuffer definition // CHECK: TemplateArgument type 'int' // CHECK-NEXT: BuiltinType 0x{{[0-9A-Fa-f]+}} 'int' // CHECK-NEXT: FinalAttr 0x{{[0-9A-Fa-f]+}} <> Implicit final -// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <> implicit h '__hlsl_resource_t +// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <> implicit __handle '__hlsl_resource_t // CHECK-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] // CHECK-SAME{LITERAL}: [[hlsl::raw_buffer]] // CHECK-SAME{LITERAL}: [[hlsl::contained_type(int)]] diff --git a/clang/test/AST/HLSL/ConsumeStructuredBuffer-AST.hlsl b/clang/test/AST/HLSL/ConsumeStructuredBuffer-AST.hlsl index b75f3fcb959cf..86e3d387883dc 100644 --- a/clang/test/AST/HLSL/ConsumeStructuredBuffer-AST.hlsl +++ b/clang/test/AST/HLSL/ConsumeStructuredBuffer-AST.hlsl @@ -30,13 +30,13 @@ ConsumeStructuredBuffer Buffer; // CHECK-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <> implicit class ConsumeStructuredBuffer definition // CHECK: FinalAttr 0x{{[0-9A-Fa-f]+}} <> Implicit final -// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <> implicit h '__hlsl_resource_t +// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <> implicit __handle '__hlsl_resource_t // CHECK-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] // CHECK-SAME{LITERAL}: [[hlsl::raw_buffer]] // CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]] // CHECK-NEXT: HLSLResourceAttr 0x{{[0-9A-Fa-f]+}} <> Implicit RawBuffer -// CHECK-NOT: CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <> operator[] 'element_type &const (unsigned int) const' +// CHECK-NOT: CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <> operator[] 'const element_type &(unsigned int) const' // CHECK-NOT: CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <> operator[] 'element_type &(unsigned int)' // CHECK: ClassTemplateSpecializationDecl 0x{{[0-9A-Fa-f]+}} <> class ConsumeStructuredBuffer definition @@ -44,7 +44,7 @@ ConsumeStructuredBuffer Buffer; // CHECK: TemplateArgument type 'int' // CHECK-NEXT: BuiltinType 0x{{[0-9A-Fa-f]+}} 'int' // CHECK-NEXT: FinalAttr 0x{{[0-9A-Fa-f]+}} <> Implicit final -// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <> implicit h '__hlsl_resource_t +// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <> implicit __handle '__hlsl_resource_t // CHECK-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] // CHECK-SAME{LITERAL}: [[hlsl::raw_buffer]] // CHECK-SAME{LITERAL}: [[hlsl::contained_type(int)]] diff --git a/clang/test/AST/HLSL/RWBuffer-AST.hlsl b/clang/test/AST/HLSL/RWBuffer-AST.hlsl index ebddd72ddb1e0..f2eba75481fd5 100644 --- a/clang/test/AST/HLSL/RWBuffer-AST.hlsl +++ b/clang/test/AST/HLSL/RWBuffer-AST.hlsl @@ -29,12 +29,12 @@ RWBuffer Buffer; // CHECK-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <> implicit class RWBuffer definition // CHECK: FinalAttr 0x{{[0-9A-Fa-f]+}} <> Implicit final -// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <> implicit h '__hlsl_resource_t +// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <> implicit __handle '__hlsl_resource_t // CHECK-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] // CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]] // CHECK-NEXT: HLSLResourceAttr 0x{{[0-9A-Fa-f]+}} <> Implicit TypedBuffer -// CHECK: CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <> operator[] 'element_type &const (unsigned int) const' +// CHECK: CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <> operator[] 'const element_type &(unsigned int) const' // CHECK-NEXT: ParmVarDecl 0x{{[0-9A-Fa-f]+}} <> Idx 'unsigned int' // CHECK-NEXT: CompoundStmt 0x{{[0-9A-Fa-f]+}} <> // CHECK-NEXT: ReturnStmt 0x{{[0-9A-Fa-f]+}} <> @@ -55,7 +55,7 @@ RWBuffer Buffer; // CHECK: TemplateArgument type 'float' // CHECK-NEXT: BuiltinType 0x{{[0-9A-Fa-f]+}} 'float' // CHECK-NEXT: FinalAttr 0x{{[0-9A-Fa-f]+}} <> Implicit final -// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <> implicit h '__hlsl_resource_t -// CHECK-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] +// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <> implicit __handle '__hlsl_resource_t +// CHECK-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] // CHECK-SAME{LITERAL}: [[hlsl::contained_type(float)]] // CHECK-NEXT: HLSLResourceAttr 0x{{[0-9A-Fa-f]+}} <> Implicit TypedBuffer diff --git a/clang/test/AST/HLSL/RWStructuredBuffer-AST.hlsl b/clang/test/AST/HLSL/RWStructuredBuffer-AST.hlsl index 4a1e1d7570e5e..cc10b41b7c2b0 100644 --- a/clang/test/AST/HLSL/RWStructuredBuffer-AST.hlsl +++ b/clang/test/AST/HLSL/RWStructuredBuffer-AST.hlsl @@ -30,13 +30,13 @@ RWStructuredBuffer Buffer; // CHECK-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <> implicit class RWStructuredBuffer definition // CHECK: FinalAttr 0x{{[0-9A-Fa-f]+}} <> Implicit final -// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <> implicit h '__hlsl_resource_t +// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <> implicit __handle '__hlsl_resource_t // CHECK-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] // CHECK-SAME{LITERAL}: [[hlsl::raw_buffer]] // CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]] // CHECK-NEXT: HLSLResourceAttr 0x{{[0-9A-Fa-f]+}} <> Implicit RawBuffer -// CHECK: CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <> operator[] 'element_type &const (unsigned int) const' +// CHECK: CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <> operator[] 'const element_type &(unsigned int) const' // CHECK-NEXT: ParmVarDecl 0x{{[0-9A-Fa-f]+}} <> Idx 'unsigned int' // CHECK-NEXT: CompoundStmt 0x{{[0-9A-Fa-f]+}} <> // CHECK-NEXT: ReturnStmt 0x{{[0-9A-Fa-f]+}} <> @@ -57,7 +57,7 @@ RWStructuredBuffer Buffer; // CHECK: TemplateArgument type 'int' // CHECK-NEXT: BuiltinType 0x{{[0-9A-Fa-f]+}} 'int' // CHECK-NEXT: FinalAttr 0x{{[0-9A-Fa-f]+}} <> Implicit final -// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <> implicit h '__hlsl_resource_t +// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <> implicit __handle '__hlsl_resource_t // CHECK-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] // CHECK-SAME{LITERAL}: [[hlsl::raw_buffer]] // CHECK-SAME{LITERAL}: [[hlsl::contained_type(int)]] diff --git a/clang/test/AST/HLSL/RasterizerOrderedStructuredBuffer-AST.hlsl b/clang/test/AST/HLSL/RasterizerOrderedStructuredBuffer-AST.hlsl index f334e1bb6db3f..1aac67b5ced5b 100644 --- a/clang/test/AST/HLSL/RasterizerOrderedStructuredBuffer-AST.hlsl +++ b/clang/test/AST/HLSL/RasterizerOrderedStructuredBuffer-AST.hlsl @@ -30,14 +30,14 @@ RasterizerOrderedStructuredBuffer Buffer; // CHECK-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <> implicit class RasterizerOrderedStructuredBuffer definition // CHECK: FinalAttr 0x{{[0-9A-Fa-f]+}} <> Implicit final -// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <> implicit h '__hlsl_resource_t +// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <> implicit __handle '__hlsl_resource_t // CHECK-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] // CHECK-SAME{LITERAL}: [[hlsl::is_rov]] // CHECK-SAME{LITERAL}: [[hlsl::raw_buffer]] // CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]] -// CHECK-NEXT: HLSLResourceAttr 0x{{[0-9A-Fa-f]+}} <> Implicit TypedBuffer +// CHECK-NEXT: HLSLResourceAttr 0x{{[0-9A-Fa-f]+}} <> Implicit RawBuffer -// CHECK: CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <> operator[] 'element_type &const (unsigned int) const' +// CHECK: CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <> operator[] 'const element_type &(unsigned int) const' // CHECK-NEXT: ParmVarDecl 0x{{[0-9A-Fa-f]+}} <> Idx 'unsigned int' // CHECK-NEXT: CompoundStmt 0x{{[0-9A-Fa-f]+}} <> // CHECK-NEXT: ReturnStmt 0x{{[0-9A-Fa-f]+}} <> @@ -58,9 +58,9 @@ RasterizerOrderedStructuredBuffer Buffer; // CHECK: TemplateArgument type 'int' // CHECK-NEXT: BuiltinType 0x{{[0-9A-Fa-f]+}} 'int' // CHECK-NEXT: FinalAttr 0x{{[0-9A-Fa-f]+}} <> Implicit final -// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <> implicit h '__hlsl_resource_t +// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <> implicit __handle '__hlsl_resource_t // CHECK-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] // CHECK-SAME{LITERAL}: [[hlsl::is_rov]] // CHECK-SAME{LITERAL}: [[hlsl::raw_buffer]] // CHECK-SAME{LITERAL}: [[hlsl::contained_type(int)]] -// CHECK-NEXT: HLSLResourceAttr 0x{{[0-9A-Fa-f]+}} <> Implicit TypedBuffer +// CHECK-NEXT: HLSLResourceAttr 0x{{[0-9A-Fa-f]+}} <> Implicit RawBuffer diff --git a/clang/test/AST/HLSL/StructuredBuffer-AST.hlsl b/clang/test/AST/HLSL/StructuredBuffer-AST.hlsl index 521c3d45b2022..95ae20ead32bf 100644 --- a/clang/test/AST/HLSL/StructuredBuffer-AST.hlsl +++ b/clang/test/AST/HLSL/StructuredBuffer-AST.hlsl @@ -30,13 +30,13 @@ StructuredBuffer Buffer; // CHECK-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <> implicit class StructuredBuffer definition // CHECK: FinalAttr 0x{{[0-9A-Fa-f]+}} <> Implicit final -// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <> implicit h '__hlsl_resource_t +// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <> implicit __handle '__hlsl_resource_t // CHECK-SAME{LITERAL}: [[hlsl::resource_class(SRV)]] // CHECK-SAME{LITERAL}: [[hlsl::raw_buffer]] // CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]] // CHECK-NEXT: HLSLResourceAttr 0x{{[0-9A-Fa-f]+}} <> Implicit RawBuffer -// CHECK: CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <> operator[] 'element_type &const (unsigned int) const' +// CHECK: CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <> operator[] 'const element_type &(unsigned int) const' // CHECK-NEXT: ParmVarDecl 0x{{[0-9A-Fa-f]+}} <> Idx 'unsigned int' // CHECK-NEXT: CompoundStmt 0x{{[0-9A-Fa-f]+}} <> // CHECK-NEXT: ReturnStmt 0x{{[0-9A-Fa-f]+}} <> @@ -57,7 +57,7 @@ StructuredBuffer Buffer; // CHECK: TemplateArgument type 'float' // CHECK-NEXT: BuiltinType 0x{{[0-9A-Fa-f]+}} 'float' // CHECK-NEXT: FinalAttr 0x{{[0-9A-Fa-f]+}} <> Implicit final -// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <> implicit h '__hlsl_resource_t +// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <> implicit __handle '__hlsl_resource_t // CHECK-SAME{LITERAL}: [[hlsl::resource_class(SRV)]] // CHECK-SAME{LITERAL}: [[hlsl::raw_buffer]] // CHECK-SAME{LITERAL}: [[hlsl::contained_type(float)]] diff --git a/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp index 693ca29370cf3..da1f8201f55dc 100644 --- a/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp @@ -17,10 +17,10 @@ E get_e(); // cxx11-warning@-1 {{use of the 'nodiscard' attribute is a C++17 extension}} void f() { - get_s(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + get_s(); // expected-warning {{ignoring return value of type 'S' declared with 'nodiscard' attribute}} get_i(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} get_vi(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} - get_e(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + get_e(); // expected-warning {{ignoring return value of type 'E' declared with 'nodiscard' attribute}} // Okay, warnings are not encouraged get_s_ref(); @@ -54,10 +54,10 @@ void f() { fp3 three; fp2_alias four; - one(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} - two(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} - three(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} - four(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + one(); // expected-warning {{ignoring return value of type 'E' declared with 'nodiscard' attribute}} + two(); // expected-warning {{ignoring return value of type 'S' declared with 'nodiscard' attribute}} + three(); // expected-warning {{ignoring return value of type 'S' declared with 'nodiscard' attribute}} + four(); // expected-warning {{ignoring return value of type 'S' declared with 'nodiscard' attribute}} // These are all okay because of the explicit cast to void. (void)one(); @@ -84,8 +84,8 @@ LaterReason get_later_reason(); // cxx11-17-warning@-1 {{use of the 'nodiscard' attribute is a C++20 extension}} void cxx20_use() { - get_reason(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute: reason}} - get_later_reason(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute: later reason}} + get_reason(); // expected-warning {{ignoring return value of type 'ReasonStruct' declared with 'nodiscard' attribute: reason}} + get_later_reason(); // expected-warning {{ignoring return value of type 'LaterReason' declared with 'nodiscard' attribute: later reason}} another_reason(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute: another reason}} conflicting_reason(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute: special reason}} } @@ -115,20 +115,20 @@ void usage() { S('A'); // expected-warning {{ignoring temporary created by a constructor declared with 'nodiscard' attribute: Don't let that S-Char go!}} S(1); S(2.2); - Y(); // expected-warning {{ignoring temporary created by a constructor declared with 'nodiscard' attribute: Don't throw me away either!}} + Y(); // expected-warning {{ignoring temporary of type 'Y' declared with 'nodiscard' attribute: Don't throw me away either!}} S s; - ConvertTo{}; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute: Don't throw me away!}} + ConvertTo{}; // expected-warning {{ignoring return value of type 'ConvertTo' declared with 'nodiscard' attribute: Don't throw me away!}} // AST is different in C++17 mode. Before, a move ctor for ConvertTo is there // as well, hence the constructor warning. - // since-cxx17-warning@+2 {{ignoring return value of function declared with 'nodiscard' attribute: Don't throw me away!}} - // cxx11-warning@+1 {{ignoring temporary created by a constructor declared with 'nodiscard' attribute: Don't throw me away!}} + // since-cxx17-warning@+2 {{ignoring return value of type 'ConvertTo' declared with 'nodiscard' attribute: Don't throw me away!}} + // cxx11-warning@+1 {{ignoring temporary of type 'ConvertTo' declared with 'nodiscard' attribute: Don't throw me away!}} (ConvertTo) s; (int)s; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} (S)'c'; // expected-warning {{ignoring temporary created by a constructor declared with 'nodiscard' attribute: Don't let that S-Char go!}} - // since-cxx17-warning@+2 {{ignoring return value of function declared with 'nodiscard' attribute: Don't throw me away!}} - // cxx11-warning@+1 {{ignoring temporary created by a constructor declared with 'nodiscard' attribute: Don't throw me away!}} + // since-cxx17-warning@+2 {{ignoring return value of type 'ConvertTo' declared with 'nodiscard' attribute: Don't throw me away!}} + // cxx11-warning@+1 {{ignoring temporary of type 'ConvertTo' declared with 'nodiscard' attribute: Don't throw me away!}} static_cast(s); static_cast(s); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} static_cast(s); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute: Don't throw away as a double}} diff --git a/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p3.cpp b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p3.cpp index a3543cff7d2c9..b37517921b1ca 100644 --- a/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p3.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p3.cpp @@ -8,7 +8,7 @@ namespace std_example { error_info enable_missile_safety_mode(); void launch_missiles(); void test_missiles() { - enable_missile_safety_mode(); // expected-warning {{ignoring return value of function declared with 'nodiscard'}} + enable_missile_safety_mode(); // expected-warning {{ignoring return value of type 'error_info' declared with 'nodiscard'}} launch_missiles(); } diff --git a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p2-cxx0x.cpp b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p2-cxx0x.cpp index ce90728861605..881742df7e8b2 100644 --- a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p2-cxx0x.cpp +++ b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p2-cxx0x.cpp @@ -1,7 +1,18 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s +// RUN: not %clang_cc1 -fsyntax-only -std=c++11 -fno-diagnostics-show-line-numbers -fcaret-diagnostics-max-lines=1 %s 2>&1 | FileCheck %s -strict-whitespace auto a() -> int; // ok const auto b() -> int; // expected-error {{function with trailing return type must specify return type 'auto', not 'const auto'}} auto *c() -> int; // expected-error {{function with trailing return type must specify return type 'auto', not 'auto *'}} auto (d() -> int); // expected-error {{trailing return type may not be nested within parentheses}} auto e() -> auto (*)() -> auto (*)() -> void; // ok: same as void (*(*e())())(); + +namespace GH78694 { + +template struct B { + // CHECK: error: function with trailing return type must specify return type 'auto', not 'void' + // CHECK-NEXT: {{^}} template B(U) -> B; + // CHECK-NEXT: {{^}} ~~~~~~~~^~~~~~{{$}} + template B(U) -> B; // expected-error {{function with trailing return type must specify return type 'auto', not 'void'}} +}; +} diff --git a/clang/test/CodeGen/AArch64/elf-pauthabi.c b/clang/test/CodeGen/AArch64/elf-pauthabi.c index 023fa8c18e130..b176f708db85b 100644 --- a/clang/test/CodeGen/AArch64/elf-pauthabi.c +++ b/clang/test/CodeGen/AArch64/elf-pauthabi.c @@ -1,5 +1,3 @@ -//// TODO: also test with -fptrauth-elf-got when the driver flag is supported - // RUN: %clang_cc1 -triple aarch64-linux -emit-llvm -o - \ // RUN: -fptrauth-intrinsics \ // RUN: -fptrauth-calls \ @@ -9,6 +7,7 @@ // RUN: -fptrauth-vtable-pointer-type-discrimination \ // RUN: -fptrauth-init-fini \ // RUN: -fptrauth-init-fini-address-discrimination \ +// RUN: -fptrauth-elf-got \ // RUN: -fptrauth-indirect-gotos \ // RUN: -fptrauth-type-info-vtable-pointer-discrimination \ // RUN: -fptrauth-function-pointer-type-discrimination %s | \ @@ -42,6 +41,9 @@ // RUN: -fptrauth-calls -fptrauth-init-fini -fptrauth-init-fini-address-discrimination %s | \ // RUN: FileCheck %s --check-prefix=INITFINIADDR +// RUN: %clang_cc1 -triple aarch64-linux -emit-llvm -o - \ +// RUN: -fptrauth-elf-got %s | FileCheck %s --check-prefix=ELFGOT + // RUN: %clang_cc1 -triple aarch64-linux -emit-llvm -o - \ // RUN: -fptrauth-indirect-gotos %s | FileCheck %s --check-prefix=GOTOS @@ -54,7 +56,7 @@ // RUN: FileCheck %s --check-prefix=FPTRTYPE // ALL: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 268435458} -// ALL: !{i32 1, !"aarch64-elf-pauthabi-version", i32 3839} +// ALL: !{i32 1, !"aarch64-elf-pauthabi-version", i32 4095} // INTRIN: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 268435458} // INTRIN: !{i32 1, !"aarch64-elf-pauthabi-version", i32 1} @@ -80,6 +82,9 @@ // INITFINIADDR: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 268435458} // INITFINIADDR: !{i32 1, !"aarch64-elf-pauthabi-version", i32 194} +// ELFGOT: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 268435458} +// ELFGOT: !{i32 1, !"aarch64-elf-pauthabi-version", i32 256} + // GOTOS: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 268435458} // GOTOS: !{i32 1, !"aarch64-elf-pauthabi-version", i32 512} diff --git a/clang/test/CodeGen/PowerPC/musttail-forward-declaration-inline.c b/clang/test/CodeGen/PowerPC/musttail-forward-declaration-inline.c index 3d8ff3985cb0f..d0ec21209582e 100644 --- a/clang/test/CodeGen/PowerPC/musttail-forward-declaration-inline.c +++ b/clang/test/CodeGen/PowerPC/musttail-forward-declaration-inline.c @@ -3,7 +3,7 @@ inline int func2(int i); int external_call2(int i) { - // expected-error@+1 {{'musttail' attribute for this call is impossible because external calls can not be tail called on PPC}} + // expected-error@+1 {{'musttail' attribute for this call is impossible because external calls cannot be tail called on PPC}} [[clang::musttail]] return func2(i); } diff --git a/clang/test/CodeGen/PowerPC/musttail-forward-declaration-weak.c b/clang/test/CodeGen/PowerPC/musttail-forward-declaration-weak.c index 4314bbdd30619..57226d2109f32 100644 --- a/clang/test/CodeGen/PowerPC/musttail-forward-declaration-weak.c +++ b/clang/test/CodeGen/PowerPC/musttail-forward-declaration-weak.c @@ -3,7 +3,7 @@ int func2(int i); int external_call2(int i) { - // expected-error@+1 {{'musttail' attribute for this call is impossible because external calls can not be tail called on PPC}} + // expected-error@+1 {{'musttail' attribute for this call is impossible because external calls cannot be tail called on PPC}} [[clang::musttail]] return func2(i); } diff --git a/clang/test/CodeGen/PowerPC/musttail-indirect.cpp b/clang/test/CodeGen/PowerPC/musttail-indirect.cpp index 3f495002606d4..cc506d4f7bc1f 100644 --- a/clang/test/CodeGen/PowerPC/musttail-indirect.cpp +++ b/clang/test/CodeGen/PowerPC/musttail-indirect.cpp @@ -3,6 +3,6 @@ void name(int *params) { auto fn = (void (*)(int *))1; - // expected-error@+1 {{'musttail' attribute for this call is impossible because indirect calls can not be tail called on PPC}} + // expected-error@+1 {{'musttail' attribute for this call is impossible because indirect calls cannot be tail called on PPC}} [[clang::musttail]] return fn(params); } diff --git a/clang/test/CodeGen/PowerPC/musttail-inline.c b/clang/test/CodeGen/PowerPC/musttail-inline.c index 05aac88697127..1ac841f088cf5 100644 --- a/clang/test/CodeGen/PowerPC/musttail-inline.c +++ b/clang/test/CodeGen/PowerPC/musttail-inline.c @@ -7,6 +7,6 @@ inline int foo(int x) { int bar(int x) { - // expected-error@+1 {{'musttail' attribute for this call is impossible because external calls can not be tail called on PPC}} + // expected-error@+1 {{'musttail' attribute for this call is impossible because external calls cannot be tail called on PPC}} [[clang::musttail]] return foo(1); } diff --git a/clang/test/CodeGen/PowerPC/musttail-undefined.c b/clang/test/CodeGen/PowerPC/musttail-undefined.c index f2259adb01848..fb3845218a622 100644 --- a/clang/test/CodeGen/PowerPC/musttail-undefined.c +++ b/clang/test/CodeGen/PowerPC/musttail-undefined.c @@ -5,6 +5,6 @@ int foo(int x); int bar(int x) { - // expected-error@+1 {{'musttail' attribute for this call is impossible because external calls can not be tail called on PPC}} + // expected-error@+1 {{'musttail' attribute for this call is impossible because external calls cannot be tail called on PPC}} [[clang::musttail]] return foo(x); } diff --git a/clang/test/CodeGen/PowerPC/musttail-weak.c b/clang/test/CodeGen/PowerPC/musttail-weak.c index dccc7a4d8cdd2..1070b91bc5f35 100644 --- a/clang/test/CodeGen/PowerPC/musttail-weak.c +++ b/clang/test/CodeGen/PowerPC/musttail-weak.c @@ -7,7 +7,7 @@ __attribute__((weak)) int func2(int i) { return 0; } int external_call2(int i) { - // linux-error@+2 {{'musttail' attribute for this call is impossible because external calls can not be tail called on PPC}} + // linux-error@+2 {{'musttail' attribute for this call is impossible because external calls cannot be tail called on PPC}} // aix-error@+1 {{'musttail' attribute is not supported on AIX}} [[clang::musttail]] return func2(i); } diff --git a/clang/test/CodeGen/PowerPC/musttail.c b/clang/test/CodeGen/PowerPC/musttail.c index e3129263d2460..7a74d084c67be 100644 --- a/clang/test/CodeGen/PowerPC/musttail.c +++ b/clang/test/CodeGen/PowerPC/musttail.c @@ -14,7 +14,7 @@ int foo(int x) { int bar(int x) { // good-no-diagnostics - // longcall-error@+2 {{'musttail' attribute for this call is impossible because long calls can not be tail called on PPC}} + // longcall-error@+2 {{'musttail' attribute for this call is impossible because long calls cannot be tail called on PPC}} // aix-error@+1 {{'musttail' attribute is not supported on AIX}} [[clang::musttail]] return foo(1); } diff --git a/clang/test/CodeGen/RISCV/riscv-inline-asm.c b/clang/test/CodeGen/RISCV/riscv-inline-asm.c index 75b91d3c497c5..de90e513ea1ff 100644 --- a/clang/test/CodeGen/RISCV/riscv-inline-asm.c +++ b/clang/test/CodeGen/RISCV/riscv-inline-asm.c @@ -33,6 +33,19 @@ void test_cf(float f, double d) { asm volatile("" : "=cf"(cd) : "cf"(d)); } +#if __riscv_xlen == 32 +typedef long long double_xlen_t; +#elif __riscv_xlen == 64 +typedef __int128_t double_xlen_t; +#endif +double_xlen_t test_R_wide_scalar(double_xlen_t p) { +// CHECK-LABEL: define{{.*}} {{i128|i64}} @test_R_wide_scalar( +// CHECK: call {{i128|i64}} asm sideeffect "", "=R,R"({{i128|i64}} %{{.*}}) + double_xlen_t ret; + asm volatile("" : "=R"(ret) : "R"(p)); + return ret; +} + void test_I(void) { // CHECK-LABEL: define{{.*}} void @test_I() // CHECK: call void asm sideeffect "", "I"(i32 2047) diff --git a/clang/test/CodeGen/X86/x86_64-PR42672.c b/clang/test/CodeGen/X86/x86_64-PR42672.c index 6fe612d0aabdb..42894c0c4cb57 100644 --- a/clang/test/CodeGen/X86/x86_64-PR42672.c +++ b/clang/test/CodeGen/X86/x86_64-PR42672.c @@ -58,7 +58,7 @@ void odd_struct(void) { : "=r"(str)); #endif } -// CHECK-IMPOSSIBLE_ODD: impossible constraint in asm: can't store value into a register +// CHECK-IMPOSSIBLE_ODD: impossible constraint in asm: cannot store value into a register // Check Clang reports an error if attempting to return a big structure via a register. void big_struct(void) { @@ -70,7 +70,7 @@ void big_struct(void) { : "=r"(str)); #endif } -// CHECK-IMPOSSIBLE_BIG: impossible constraint in asm: can't store value into a register +// CHECK-IMPOSSIBLE_BIG: impossible constraint in asm: cannot store value into a register // Clang is able to emit LLVM IR for an 16-byte structure. void x_constraint_fit(void) { @@ -103,7 +103,7 @@ void x_constraint_nofit(void) { // http://crbug.com/999160 // Clang used to report the following message: -// "impossible constraint in asm: can't store struct into a register" +// "impossible constraint in asm: cannot store struct into a register" // for the assembly directive below, although there's no struct. void crbug_999160_regtest(void) { #ifdef IMPOSSIBLE_9BYTES @@ -113,7 +113,7 @@ void crbug_999160_regtest(void) { #endif } -// CHECK-IMPOSSIBLE_9BYTES: impossible constraint in asm: can't store value into a register +// CHECK-IMPOSSIBLE_9BYTES: impossible constraint in asm: cannot store value into a register void crbug_999160_regtest_v2(void) { #ifdef IMPOSSIBLE_9BYTES_V2 @@ -121,4 +121,4 @@ void crbug_999160_regtest_v2(void) { asm("" : "=r"(buf) : "0"(buf)); #endif } -// CHECK-IMPOSSIBLE_9BYTES_V2: impossible constraint in asm: can't store value into a register +// CHECK-IMPOSSIBLE_9BYTES_V2: impossible constraint in asm: cannot store value into a register diff --git a/clang/test/CodeGen/arm-mve-intrinsics/compare.c b/clang/test/CodeGen/arm-mve-intrinsics/compare.c index 8f190990a6586..8886cf5c10058 100644 --- a/clang/test/CodeGen/arm-mve-intrinsics/compare.c +++ b/clang/test/CodeGen/arm-mve-intrinsics/compare.c @@ -2376,7 +2376,7 @@ mve_pred16_t test_vcmphiq_m_n_u32(uint32x4_t a, uint32_t b, mve_pred16_t p) // CHECK-LABEL: @test_vcmpleq_f16( // CHECK-NEXT: entry: -// CHECK-NEXT: [[TMP0:%.*]] = fcmp ole <8 x half> [[A:%.*]], [[B:%.*]] +// CHECK-NEXT: [[TMP0:%.*]] = fcmp ule <8 x half> [[A:%.*]], [[B:%.*]] // CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.arm.mve.pred.v2i.v8i1(<8 x i1> [[TMP0]]) // CHECK-NEXT: [[TMP2:%.*]] = trunc i32 [[TMP1]] to i16 // CHECK-NEXT: ret i16 [[TMP2]] @@ -2392,7 +2392,7 @@ mve_pred16_t test_vcmpleq_f16(float16x8_t a, float16x8_t b) // CHECK-LABEL: @test_vcmpleq_f32( // CHECK-NEXT: entry: -// CHECK-NEXT: [[TMP0:%.*]] = fcmp ole <4 x float> [[A:%.*]], [[B:%.*]] +// CHECK-NEXT: [[TMP0:%.*]] = fcmp ule <4 x float> [[A:%.*]], [[B:%.*]] // CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.arm.mve.pred.v2i.v4i1(<4 x i1> [[TMP0]]) // CHECK-NEXT: [[TMP2:%.*]] = trunc i32 [[TMP1]] to i16 // CHECK-NEXT: ret i16 [[TMP2]] @@ -2458,7 +2458,7 @@ mve_pred16_t test_vcmpleq_s32(int32x4_t a, int32x4_t b) // CHECK-NEXT: entry: // CHECK-NEXT: [[DOTSPLATINSERT:%.*]] = insertelement <8 x half> poison, half [[B:%.*]], i64 0 // CHECK-NEXT: [[DOTSPLAT:%.*]] = shufflevector <8 x half> [[DOTSPLATINSERT]], <8 x half> poison, <8 x i32> zeroinitializer -// CHECK-NEXT: [[TMP0:%.*]] = fcmp ole <8 x half> [[A:%.*]], [[DOTSPLAT]] +// CHECK-NEXT: [[TMP0:%.*]] = fcmp ule <8 x half> [[A:%.*]], [[DOTSPLAT]] // CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.arm.mve.pred.v2i.v8i1(<8 x i1> [[TMP0]]) // CHECK-NEXT: [[TMP2:%.*]] = trunc i32 [[TMP1]] to i16 // CHECK-NEXT: ret i16 [[TMP2]] @@ -2476,7 +2476,7 @@ mve_pred16_t test_vcmpleq_n_f16(float16x8_t a, float16_t b) // CHECK-NEXT: entry: // CHECK-NEXT: [[DOTSPLATINSERT:%.*]] = insertelement <4 x float> poison, float [[B:%.*]], i64 0 // CHECK-NEXT: [[DOTSPLAT:%.*]] = shufflevector <4 x float> [[DOTSPLATINSERT]], <4 x float> poison, <4 x i32> zeroinitializer -// CHECK-NEXT: [[TMP0:%.*]] = fcmp ole <4 x float> [[A:%.*]], [[DOTSPLAT]] +// CHECK-NEXT: [[TMP0:%.*]] = fcmp ule <4 x float> [[A:%.*]], [[DOTSPLAT]] // CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.arm.mve.pred.v2i.v4i1(<4 x i1> [[TMP0]]) // CHECK-NEXT: [[TMP2:%.*]] = trunc i32 [[TMP1]] to i16 // CHECK-NEXT: ret i16 [[TMP2]] @@ -2548,7 +2548,7 @@ mve_pred16_t test_vcmpleq_n_s32(int32x4_t a, int32_t b) // CHECK-NEXT: entry: // CHECK-NEXT: [[TMP0:%.*]] = zext i16 [[P:%.*]] to i32 // CHECK-NEXT: [[TMP1:%.*]] = call <8 x i1> @llvm.arm.mve.pred.i2v.v8i1(i32 [[TMP0]]) -// CHECK-NEXT: [[TMP2:%.*]] = fcmp ole <8 x half> [[A:%.*]], [[B:%.*]] +// CHECK-NEXT: [[TMP2:%.*]] = fcmp ule <8 x half> [[A:%.*]], [[B:%.*]] // CHECK-NEXT: [[TMP3:%.*]] = and <8 x i1> [[TMP1]], [[TMP2]] // CHECK-NEXT: [[TMP4:%.*]] = call i32 @llvm.arm.mve.pred.v2i.v8i1(<8 x i1> [[TMP3]]) // CHECK-NEXT: [[TMP5:%.*]] = trunc i32 [[TMP4]] to i16 @@ -2567,7 +2567,7 @@ mve_pred16_t test_vcmpleq_m_f16(float16x8_t a, float16x8_t b, mve_pred16_t p) // CHECK-NEXT: entry: // CHECK-NEXT: [[TMP0:%.*]] = zext i16 [[P:%.*]] to i32 // CHECK-NEXT: [[TMP1:%.*]] = call <4 x i1> @llvm.arm.mve.pred.i2v.v4i1(i32 [[TMP0]]) -// CHECK-NEXT: [[TMP2:%.*]] = fcmp ole <4 x float> [[A:%.*]], [[B:%.*]] +// CHECK-NEXT: [[TMP2:%.*]] = fcmp ule <4 x float> [[A:%.*]], [[B:%.*]] // CHECK-NEXT: [[TMP3:%.*]] = and <4 x i1> [[TMP1]], [[TMP2]] // CHECK-NEXT: [[TMP4:%.*]] = call i32 @llvm.arm.mve.pred.v2i.v4i1(<4 x i1> [[TMP3]]) // CHECK-NEXT: [[TMP5:%.*]] = trunc i32 [[TMP4]] to i16 @@ -2645,7 +2645,7 @@ mve_pred16_t test_vcmpleq_m_s32(int32x4_t a, int32x4_t b, mve_pred16_t p) // CHECK-NEXT: [[TMP1:%.*]] = call <8 x i1> @llvm.arm.mve.pred.i2v.v8i1(i32 [[TMP0]]) // CHECK-NEXT: [[DOTSPLATINSERT:%.*]] = insertelement <8 x half> poison, half [[B:%.*]], i64 0 // CHECK-NEXT: [[DOTSPLAT:%.*]] = shufflevector <8 x half> [[DOTSPLATINSERT]], <8 x half> poison, <8 x i32> zeroinitializer -// CHECK-NEXT: [[TMP2:%.*]] = fcmp ole <8 x half> [[A:%.*]], [[DOTSPLAT]] +// CHECK-NEXT: [[TMP2:%.*]] = fcmp ule <8 x half> [[A:%.*]], [[DOTSPLAT]] // CHECK-NEXT: [[TMP3:%.*]] = and <8 x i1> [[TMP1]], [[TMP2]] // CHECK-NEXT: [[TMP4:%.*]] = call i32 @llvm.arm.mve.pred.v2i.v8i1(<8 x i1> [[TMP3]]) // CHECK-NEXT: [[TMP5:%.*]] = trunc i32 [[TMP4]] to i16 @@ -2666,7 +2666,7 @@ mve_pred16_t test_vcmpleq_m_n_f16(float16x8_t a, float16_t b, mve_pred16_t p) // CHECK-NEXT: [[TMP1:%.*]] = call <4 x i1> @llvm.arm.mve.pred.i2v.v4i1(i32 [[TMP0]]) // CHECK-NEXT: [[DOTSPLATINSERT:%.*]] = insertelement <4 x float> poison, float [[B:%.*]], i64 0 // CHECK-NEXT: [[DOTSPLAT:%.*]] = shufflevector <4 x float> [[DOTSPLATINSERT]], <4 x float> poison, <4 x i32> zeroinitializer -// CHECK-NEXT: [[TMP2:%.*]] = fcmp ole <4 x float> [[A:%.*]], [[DOTSPLAT]] +// CHECK-NEXT: [[TMP2:%.*]] = fcmp ule <4 x float> [[A:%.*]], [[DOTSPLAT]] // CHECK-NEXT: [[TMP3:%.*]] = and <4 x i1> [[TMP1]], [[TMP2]] // CHECK-NEXT: [[TMP4:%.*]] = call i32 @llvm.arm.mve.pred.v2i.v4i1(<4 x i1> [[TMP3]]) // CHECK-NEXT: [[TMP5:%.*]] = trunc i32 [[TMP4]] to i16 @@ -2746,7 +2746,7 @@ mve_pred16_t test_vcmpleq_m_n_s32(int32x4_t a, int32_t b, mve_pred16_t p) // CHECK-LABEL: @test_vcmpltq_f16( // CHECK-NEXT: entry: -// CHECK-NEXT: [[TMP0:%.*]] = fcmp olt <8 x half> [[A:%.*]], [[B:%.*]] +// CHECK-NEXT: [[TMP0:%.*]] = fcmp ult <8 x half> [[A:%.*]], [[B:%.*]] // CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.arm.mve.pred.v2i.v8i1(<8 x i1> [[TMP0]]) // CHECK-NEXT: [[TMP2:%.*]] = trunc i32 [[TMP1]] to i16 // CHECK-NEXT: ret i16 [[TMP2]] @@ -2762,7 +2762,7 @@ mve_pred16_t test_vcmpltq_f16(float16x8_t a, float16x8_t b) // CHECK-LABEL: @test_vcmpltq_f32( // CHECK-NEXT: entry: -// CHECK-NEXT: [[TMP0:%.*]] = fcmp olt <4 x float> [[A:%.*]], [[B:%.*]] +// CHECK-NEXT: [[TMP0:%.*]] = fcmp ult <4 x float> [[A:%.*]], [[B:%.*]] // CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.arm.mve.pred.v2i.v4i1(<4 x i1> [[TMP0]]) // CHECK-NEXT: [[TMP2:%.*]] = trunc i32 [[TMP1]] to i16 // CHECK-NEXT: ret i16 [[TMP2]] @@ -2828,7 +2828,7 @@ mve_pred16_t test_vcmpltq_s32(int32x4_t a, int32x4_t b) // CHECK-NEXT: entry: // CHECK-NEXT: [[DOTSPLATINSERT:%.*]] = insertelement <8 x half> poison, half [[B:%.*]], i64 0 // CHECK-NEXT: [[DOTSPLAT:%.*]] = shufflevector <8 x half> [[DOTSPLATINSERT]], <8 x half> poison, <8 x i32> zeroinitializer -// CHECK-NEXT: [[TMP0:%.*]] = fcmp olt <8 x half> [[A:%.*]], [[DOTSPLAT]] +// CHECK-NEXT: [[TMP0:%.*]] = fcmp ult <8 x half> [[A:%.*]], [[DOTSPLAT]] // CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.arm.mve.pred.v2i.v8i1(<8 x i1> [[TMP0]]) // CHECK-NEXT: [[TMP2:%.*]] = trunc i32 [[TMP1]] to i16 // CHECK-NEXT: ret i16 [[TMP2]] @@ -2846,7 +2846,7 @@ mve_pred16_t test_vcmpltq_n_f16(float16x8_t a, float16_t b) // CHECK-NEXT: entry: // CHECK-NEXT: [[DOTSPLATINSERT:%.*]] = insertelement <4 x float> poison, float [[B:%.*]], i64 0 // CHECK-NEXT: [[DOTSPLAT:%.*]] = shufflevector <4 x float> [[DOTSPLATINSERT]], <4 x float> poison, <4 x i32> zeroinitializer -// CHECK-NEXT: [[TMP0:%.*]] = fcmp olt <4 x float> [[A:%.*]], [[DOTSPLAT]] +// CHECK-NEXT: [[TMP0:%.*]] = fcmp ult <4 x float> [[A:%.*]], [[DOTSPLAT]] // CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.arm.mve.pred.v2i.v4i1(<4 x i1> [[TMP0]]) // CHECK-NEXT: [[TMP2:%.*]] = trunc i32 [[TMP1]] to i16 // CHECK-NEXT: ret i16 [[TMP2]] @@ -2918,7 +2918,7 @@ mve_pred16_t test_vcmpltq_n_s32(int32x4_t a, int32_t b) // CHECK-NEXT: entry: // CHECK-NEXT: [[TMP0:%.*]] = zext i16 [[P:%.*]] to i32 // CHECK-NEXT: [[TMP1:%.*]] = call <8 x i1> @llvm.arm.mve.pred.i2v.v8i1(i32 [[TMP0]]) -// CHECK-NEXT: [[TMP2:%.*]] = fcmp olt <8 x half> [[A:%.*]], [[B:%.*]] +// CHECK-NEXT: [[TMP2:%.*]] = fcmp ult <8 x half> [[A:%.*]], [[B:%.*]] // CHECK-NEXT: [[TMP3:%.*]] = and <8 x i1> [[TMP1]], [[TMP2]] // CHECK-NEXT: [[TMP4:%.*]] = call i32 @llvm.arm.mve.pred.v2i.v8i1(<8 x i1> [[TMP3]]) // CHECK-NEXT: [[TMP5:%.*]] = trunc i32 [[TMP4]] to i16 @@ -2937,7 +2937,7 @@ mve_pred16_t test_vcmpltq_m_f16(float16x8_t a, float16x8_t b, mve_pred16_t p) // CHECK-NEXT: entry: // CHECK-NEXT: [[TMP0:%.*]] = zext i16 [[P:%.*]] to i32 // CHECK-NEXT: [[TMP1:%.*]] = call <4 x i1> @llvm.arm.mve.pred.i2v.v4i1(i32 [[TMP0]]) -// CHECK-NEXT: [[TMP2:%.*]] = fcmp olt <4 x float> [[A:%.*]], [[B:%.*]] +// CHECK-NEXT: [[TMP2:%.*]] = fcmp ult <4 x float> [[A:%.*]], [[B:%.*]] // CHECK-NEXT: [[TMP3:%.*]] = and <4 x i1> [[TMP1]], [[TMP2]] // CHECK-NEXT: [[TMP4:%.*]] = call i32 @llvm.arm.mve.pred.v2i.v4i1(<4 x i1> [[TMP3]]) // CHECK-NEXT: [[TMP5:%.*]] = trunc i32 [[TMP4]] to i16 @@ -3015,7 +3015,7 @@ mve_pred16_t test_vcmpltq_m_s32(int32x4_t a, int32x4_t b, mve_pred16_t p) // CHECK-NEXT: [[TMP1:%.*]] = call <8 x i1> @llvm.arm.mve.pred.i2v.v8i1(i32 [[TMP0]]) // CHECK-NEXT: [[DOTSPLATINSERT:%.*]] = insertelement <8 x half> poison, half [[B:%.*]], i64 0 // CHECK-NEXT: [[DOTSPLAT:%.*]] = shufflevector <8 x half> [[DOTSPLATINSERT]], <8 x half> poison, <8 x i32> zeroinitializer -// CHECK-NEXT: [[TMP2:%.*]] = fcmp olt <8 x half> [[A:%.*]], [[DOTSPLAT]] +// CHECK-NEXT: [[TMP2:%.*]] = fcmp ult <8 x half> [[A:%.*]], [[DOTSPLAT]] // CHECK-NEXT: [[TMP3:%.*]] = and <8 x i1> [[TMP1]], [[TMP2]] // CHECK-NEXT: [[TMP4:%.*]] = call i32 @llvm.arm.mve.pred.v2i.v8i1(<8 x i1> [[TMP3]]) // CHECK-NEXT: [[TMP5:%.*]] = trunc i32 [[TMP4]] to i16 @@ -3036,7 +3036,7 @@ mve_pred16_t test_vcmpltq_m_n_f16(float16x8_t a, float16_t b, mve_pred16_t p) // CHECK-NEXT: [[TMP1:%.*]] = call <4 x i1> @llvm.arm.mve.pred.i2v.v4i1(i32 [[TMP0]]) // CHECK-NEXT: [[DOTSPLATINSERT:%.*]] = insertelement <4 x float> poison, float [[B:%.*]], i64 0 // CHECK-NEXT: [[DOTSPLAT:%.*]] = shufflevector <4 x float> [[DOTSPLATINSERT]], <4 x float> poison, <4 x i32> zeroinitializer -// CHECK-NEXT: [[TMP2:%.*]] = fcmp olt <4 x float> [[A:%.*]], [[DOTSPLAT]] +// CHECK-NEXT: [[TMP2:%.*]] = fcmp ult <4 x float> [[A:%.*]], [[DOTSPLAT]] // CHECK-NEXT: [[TMP3:%.*]] = and <4 x i1> [[TMP1]], [[TMP2]] // CHECK-NEXT: [[TMP4:%.*]] = call i32 @llvm.arm.mve.pred.v2i.v4i1(<4 x i1> [[TMP3]]) // CHECK-NEXT: [[TMP5:%.*]] = trunc i32 [[TMP4]] to i16 diff --git a/clang/test/CodeGen/embed-bitcode-marker-with-nonzero-as.c b/clang/test/CodeGen/embed-bitcode-marker-with-nonzero-as.c new file mode 100644 index 0000000000000..df7118859c764 --- /dev/null +++ b/clang/test/CodeGen/embed-bitcode-marker-with-nonzero-as.c @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -triple spirv64-amd-amdhsa -emit-llvm -fcuda-is-device -fembed-bitcode=marker -x hip %s -o - \ +// RUN: | FileCheck %s --check-prefix=CHECK + +// CHECK: @llvm.embedded.module = private addrspace(1) constant [0 x i8] zeroinitializer, section ".llvmbc", align 1 +// CHECK-NEXT: @llvm.cmdline = private addrspace(1) constant [{{[0-9]+}} x i8] c"{{.*}}", section ".llvmcmd", align 1 +// CHECK-NEXT: @llvm.compiler.used = appending addrspace(1) global [5 x ptr addrspace(4)] [ptr addrspace(4) addrspacecast (ptr addrspace(1) @foo.managed to ptr addrspace(4)), ptr addrspace(4) addrspacecast (ptr addrspace(1) @foo to ptr addrspace(4)), ptr addrspace(4) addrspacecast (ptr addrspace(1) @__hip_cuid_ to ptr addrspace(4)), ptr addrspace(4) addrspacecast (ptr addrspace(1) @llvm.embedded.module to ptr addrspace(4)), ptr addrspace(4) addrspacecast (ptr addrspace(1) @llvm.cmdline to ptr addrspace(4))], section "llvm.metadata" + +__attribute__((managed)) int foo = 42; diff --git a/clang/test/CodeGen/ptrauth-module-flags.c b/clang/test/CodeGen/ptrauth-module-flags.c new file mode 100644 index 0000000000000..5a7e9a7c2a36f --- /dev/null +++ b/clang/test/CodeGen/ptrauth-module-flags.c @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -triple aarch64-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefix=OFF +// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-elf-got -emit-llvm %s -o - | FileCheck %s --check-prefix=ELFGOT + +// ELFGOT: !llvm.module.flags = !{ +// ELFGOT-SAME: !1 +// ELFGOT: !1 = !{i32 8, !"ptrauth-elf-got", i32 1} + +// OFF-NOT: "ptrauth- diff --git a/clang/test/CodeGen/scoped-fence-ops.c b/clang/test/CodeGen/scoped-fence-ops.c new file mode 100644 index 0000000000000..376cb11e84d3d --- /dev/null +++ b/clang/test/CodeGen/scoped-fence-ops.c @@ -0,0 +1,257 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// RUN: %clang_cc1 %s -emit-llvm -o - -triple=amdgcn-amd-amdhsa -ffreestanding \ +// RUN: -fvisibility=hidden | FileCheck --check-prefix=AMDGCN %s +// RUN: %clang_cc1 %s -emit-llvm -o - -triple=spirv64-unknown-unknown -ffreestanding \ +// RUN: -fvisibility=hidden | FileCheck --check-prefix=SPIRV %s +// RUN: %clang_cc1 %s -emit-llvm -o - -triple=x86_64-unknown-linux-gnu -ffreestanding \ +// RUN: -fvisibility=hidden | FileCheck --check-prefix=X86_64 %s + +// AMDGCN-LABEL: define hidden void @fe1a( +// AMDGCN-SAME: ) #[[ATTR0:[0-9]+]] { +// AMDGCN-NEXT: [[ENTRY:.*:]] +// AMDGCN-NEXT: fence syncscope("workgroup-one-as") release +// AMDGCN-NEXT: ret void +// +// SPIRV-LABEL: define hidden spir_func void @fe1a( +// SPIRV-SAME: ) #[[ATTR0:[0-9]+]] { +// SPIRV-NEXT: [[ENTRY:.*:]] +// SPIRV-NEXT: fence syncscope("workgroup") release +// SPIRV-NEXT: ret void +// +// X86_64-LABEL: define hidden void @fe1a( +// X86_64-SAME: ) #[[ATTR0:[0-9]+]] { +// X86_64-NEXT: [[ENTRY:.*:]] +// X86_64-NEXT: fence release +// X86_64-NEXT: ret void +// +void fe1a() { + __scoped_atomic_thread_fence(__ATOMIC_RELEASE, __MEMORY_SCOPE_WRKGRP); +} + +// AMDGCN-LABEL: define hidden void @fe1b( +// AMDGCN-SAME: i32 noundef [[ORD:%.*]]) #[[ATTR0]] { +// AMDGCN-NEXT: [[ENTRY:.*:]] +// AMDGCN-NEXT: [[ORD_ADDR:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGCN-NEXT: [[ORD_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[ORD_ADDR]] to ptr +// AMDGCN-NEXT: store i32 [[ORD]], ptr [[ORD_ADDR_ASCAST]], align 4 +// AMDGCN-NEXT: [[TMP0:%.*]] = load i32, ptr [[ORD_ADDR_ASCAST]], align 4 +// AMDGCN-NEXT: switch i32 [[TMP0]], label %[[ATOMIC_SCOPE_CONTINUE:.*]] [ +// AMDGCN-NEXT: i32 1, label %[[ACQUIRE:.*]] +// AMDGCN-NEXT: i32 2, label %[[ACQUIRE]] +// AMDGCN-NEXT: i32 3, label %[[RELEASE:.*]] +// AMDGCN-NEXT: i32 4, label %[[ACQREL:.*]] +// AMDGCN-NEXT: i32 5, label %[[SEQCST:.*]] +// AMDGCN-NEXT: ] +// AMDGCN: [[ATOMIC_SCOPE_CONTINUE]]: +// AMDGCN-NEXT: ret void +// AMDGCN: [[ACQUIRE]]: +// AMDGCN-NEXT: fence syncscope("workgroup-one-as") acquire +// AMDGCN-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// AMDGCN: [[RELEASE]]: +// AMDGCN-NEXT: fence syncscope("workgroup-one-as") release +// AMDGCN-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// AMDGCN: [[ACQREL]]: +// AMDGCN-NEXT: fence syncscope("workgroup-one-as") acq_rel +// AMDGCN-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// AMDGCN: [[SEQCST]]: +// AMDGCN-NEXT: fence syncscope("workgroup") seq_cst +// AMDGCN-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// +// SPIRV-LABEL: define hidden spir_func void @fe1b( +// SPIRV-SAME: i32 noundef [[ORD:%.*]]) #[[ATTR0]] { +// SPIRV-NEXT: [[ENTRY:.*:]] +// SPIRV-NEXT: [[ORD_ADDR:%.*]] = alloca i32, align 4 +// SPIRV-NEXT: store i32 [[ORD]], ptr [[ORD_ADDR]], align 4 +// SPIRV-NEXT: [[TMP0:%.*]] = load i32, ptr [[ORD_ADDR]], align 4 +// SPIRV-NEXT: switch i32 [[TMP0]], label %[[ATOMIC_SCOPE_CONTINUE:.*]] [ +// SPIRV-NEXT: i32 1, label %[[ACQUIRE:.*]] +// SPIRV-NEXT: i32 2, label %[[ACQUIRE]] +// SPIRV-NEXT: i32 3, label %[[RELEASE:.*]] +// SPIRV-NEXT: i32 4, label %[[ACQREL:.*]] +// SPIRV-NEXT: i32 5, label %[[SEQCST:.*]] +// SPIRV-NEXT: ] +// SPIRV: [[ATOMIC_SCOPE_CONTINUE]]: +// SPIRV-NEXT: ret void +// SPIRV: [[ACQUIRE]]: +// SPIRV-NEXT: fence syncscope("workgroup") acquire +// SPIRV-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// SPIRV: [[RELEASE]]: +// SPIRV-NEXT: fence syncscope("workgroup") release +// SPIRV-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// SPIRV: [[ACQREL]]: +// SPIRV-NEXT: fence syncscope("workgroup") acq_rel +// SPIRV-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// SPIRV: [[SEQCST]]: +// SPIRV-NEXT: fence syncscope("workgroup") seq_cst +// SPIRV-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// +// X86_64-LABEL: define hidden void @fe1b( +// X86_64-SAME: i32 noundef [[ORD:%.*]]) #[[ATTR0]] { +// X86_64-NEXT: [[ENTRY:.*:]] +// X86_64-NEXT: [[ORD_ADDR:%.*]] = alloca i32, align 4 +// X86_64-NEXT: store i32 [[ORD]], ptr [[ORD_ADDR]], align 4 +// X86_64-NEXT: [[TMP0:%.*]] = load i32, ptr [[ORD_ADDR]], align 4 +// X86_64-NEXT: switch i32 [[TMP0]], label %[[ATOMIC_SCOPE_CONTINUE:.*]] [ +// X86_64-NEXT: i32 1, label %[[ACQUIRE:.*]] +// X86_64-NEXT: i32 2, label %[[ACQUIRE]] +// X86_64-NEXT: i32 3, label %[[RELEASE:.*]] +// X86_64-NEXT: i32 4, label %[[ACQREL:.*]] +// X86_64-NEXT: i32 5, label %[[SEQCST:.*]] +// X86_64-NEXT: ] +// X86_64: [[ATOMIC_SCOPE_CONTINUE]]: +// X86_64-NEXT: ret void +// X86_64: [[ACQUIRE]]: +// X86_64-NEXT: fence acquire +// X86_64-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// X86_64: [[RELEASE]]: +// X86_64-NEXT: fence release +// X86_64-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// X86_64: [[ACQREL]]: +// X86_64-NEXT: fence acq_rel +// X86_64-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// X86_64: [[SEQCST]]: +// X86_64-NEXT: fence seq_cst +// X86_64-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// +void fe1b(int ord) { + __scoped_atomic_thread_fence(ord, __MEMORY_SCOPE_WRKGRP); +} + +// AMDGCN-LABEL: define hidden void @fe1c( +// AMDGCN-SAME: i32 noundef [[SCOPE:%.*]]) #[[ATTR0]] { +// AMDGCN-NEXT: [[ENTRY:.*:]] +// AMDGCN-NEXT: [[SCOPE_ADDR:%.*]] = alloca i32, align 4, addrspace(5) +// AMDGCN-NEXT: [[SCOPE_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[SCOPE_ADDR]] to ptr +// AMDGCN-NEXT: store i32 [[SCOPE]], ptr [[SCOPE_ADDR_ASCAST]], align 4 +// AMDGCN-NEXT: [[TMP0:%.*]] = load i32, ptr [[SCOPE_ADDR_ASCAST]], align 4 +// AMDGCN-NEXT: switch i32 [[TMP0]], label %[[ATOMIC_SCOPE_CONTINUE:.*]] [ +// AMDGCN-NEXT: i32 1, label %[[DEVICE_SCOPE:.*]] +// AMDGCN-NEXT: i32 0, label %[[SYSTEM_SCOPE:.*]] +// AMDGCN-NEXT: i32 2, label %[[WORKGROUP_SCOPE:.*]] +// AMDGCN-NEXT: i32 3, label %[[WAVEFRONT_SCOPE:.*]] +// AMDGCN-NEXT: i32 4, label %[[SINGLE_SCOPE:.*]] +// AMDGCN-NEXT: ] +// AMDGCN: [[ATOMIC_SCOPE_CONTINUE]]: +// AMDGCN-NEXT: ret void +// AMDGCN: [[DEVICE_SCOPE]]: +// AMDGCN-NEXT: fence syncscope("agent-one-as") release +// AMDGCN-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// AMDGCN: [[SYSTEM_SCOPE]]: +// AMDGCN-NEXT: fence syncscope("one-as") release +// AMDGCN-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// AMDGCN: [[WORKGROUP_SCOPE]]: +// AMDGCN-NEXT: fence syncscope("workgroup-one-as") release +// AMDGCN-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// AMDGCN: [[WAVEFRONT_SCOPE]]: +// AMDGCN-NEXT: fence syncscope("wavefront-one-as") release +// AMDGCN-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// AMDGCN: [[SINGLE_SCOPE]]: +// AMDGCN-NEXT: fence syncscope("singlethread-one-as") release +// AMDGCN-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// +// SPIRV-LABEL: define hidden spir_func void @fe1c( +// SPIRV-SAME: i32 noundef [[SCOPE:%.*]]) #[[ATTR0]] { +// SPIRV-NEXT: [[ENTRY:.*:]] +// SPIRV-NEXT: [[SCOPE_ADDR:%.*]] = alloca i32, align 4 +// SPIRV-NEXT: store i32 [[SCOPE]], ptr [[SCOPE_ADDR]], align 4 +// SPIRV-NEXT: [[TMP0:%.*]] = load i32, ptr [[SCOPE_ADDR]], align 4 +// SPIRV-NEXT: switch i32 [[TMP0]], label %[[ATOMIC_SCOPE_CONTINUE:.*]] [ +// SPIRV-NEXT: i32 1, label %[[DEVICE_SCOPE:.*]] +// SPIRV-NEXT: i32 0, label %[[SYSTEM_SCOPE:.*]] +// SPIRV-NEXT: i32 2, label %[[WORKGROUP_SCOPE:.*]] +// SPIRV-NEXT: i32 3, label %[[WAVEFRONT_SCOPE:.*]] +// SPIRV-NEXT: i32 4, label %[[SINGLE_SCOPE:.*]] +// SPIRV-NEXT: ] +// SPIRV: [[ATOMIC_SCOPE_CONTINUE]]: +// SPIRV-NEXT: ret void +// SPIRV: [[DEVICE_SCOPE]]: +// SPIRV-NEXT: fence syncscope("device") release +// SPIRV-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// SPIRV: [[SYSTEM_SCOPE]]: +// SPIRV-NEXT: fence release +// SPIRV-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// SPIRV: [[WORKGROUP_SCOPE]]: +// SPIRV-NEXT: fence syncscope("workgroup") release +// SPIRV-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// SPIRV: [[WAVEFRONT_SCOPE]]: +// SPIRV-NEXT: fence syncscope("subgroup") release +// SPIRV-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// SPIRV: [[SINGLE_SCOPE]]: +// SPIRV-NEXT: fence syncscope("singlethread") release +// SPIRV-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// +// X86_64-LABEL: define hidden void @fe1c( +// X86_64-SAME: i32 noundef [[SCOPE:%.*]]) #[[ATTR0]] { +// X86_64-NEXT: [[ENTRY:.*:]] +// X86_64-NEXT: [[SCOPE_ADDR:%.*]] = alloca i32, align 4 +// X86_64-NEXT: store i32 [[SCOPE]], ptr [[SCOPE_ADDR]], align 4 +// X86_64-NEXT: [[TMP0:%.*]] = load i32, ptr [[SCOPE_ADDR]], align 4 +// X86_64-NEXT: switch i32 [[TMP0]], label %[[ATOMIC_SCOPE_CONTINUE:.*]] [ +// X86_64-NEXT: i32 1, label %[[DEVICE_SCOPE:.*]] +// X86_64-NEXT: i32 0, label %[[SYSTEM_SCOPE:.*]] +// X86_64-NEXT: i32 2, label %[[WORKGROUP_SCOPE:.*]] +// X86_64-NEXT: i32 3, label %[[WAVEFRONT_SCOPE:.*]] +// X86_64-NEXT: i32 4, label %[[SINGLE_SCOPE:.*]] +// X86_64-NEXT: ] +// X86_64: [[ATOMIC_SCOPE_CONTINUE]]: +// X86_64-NEXT: ret void +// X86_64: [[DEVICE_SCOPE]]: +// X86_64-NEXT: fence release +// X86_64-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// X86_64: [[SYSTEM_SCOPE]]: +// X86_64-NEXT: fence release +// X86_64-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// X86_64: [[WORKGROUP_SCOPE]]: +// X86_64-NEXT: fence release +// X86_64-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// X86_64: [[WAVEFRONT_SCOPE]]: +// X86_64-NEXT: fence release +// X86_64-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// X86_64: [[SINGLE_SCOPE]]: +// X86_64-NEXT: fence release +// X86_64-NEXT: br label %[[ATOMIC_SCOPE_CONTINUE]] +// +void fe1c(int scope) { + __scoped_atomic_thread_fence(__ATOMIC_RELEASE, scope); +} + +// AMDGCN-LABEL: define hidden void @fe2a( +// AMDGCN-SAME: ) #[[ATTR0]] { +// AMDGCN-NEXT: [[ENTRY:.*:]] +// AMDGCN-NEXT: ret void +// +// SPIRV-LABEL: define hidden spir_func void @fe2a( +// SPIRV-SAME: ) #[[ATTR0]] { +// SPIRV-NEXT: [[ENTRY:.*:]] +// SPIRV-NEXT: ret void +// +// X86_64-LABEL: define hidden void @fe2a( +// X86_64-SAME: ) #[[ATTR0]] { +// X86_64-NEXT: [[ENTRY:.*:]] +// X86_64-NEXT: ret void +// +void fe2a() { + __scoped_atomic_thread_fence(999, __MEMORY_SCOPE_SYSTEM); +} + +// AMDGCN-LABEL: define hidden void @fe2b( +// AMDGCN-SAME: ) #[[ATTR0]] { +// AMDGCN-NEXT: [[ENTRY:.*:]] +// AMDGCN-NEXT: fence syncscope("one-as") release +// AMDGCN-NEXT: ret void +// +// SPIRV-LABEL: define hidden spir_func void @fe2b( +// SPIRV-SAME: ) #[[ATTR0]] { +// SPIRV-NEXT: [[ENTRY:.*:]] +// SPIRV-NEXT: fence release +// SPIRV-NEXT: ret void +// +// X86_64-LABEL: define hidden void @fe2b( +// X86_64-SAME: ) #[[ATTR0]] { +// X86_64-NEXT: [[ENTRY:.*:]] +// X86_64-NEXT: fence release +// X86_64-NEXT: ret void +// +void fe2b() { + __scoped_atomic_thread_fence(__ATOMIC_RELEASE, 999); +} diff --git a/clang/test/CodeGenCUDASPIRV/spirv-attrs.cu b/clang/test/CodeGenCUDASPIRV/spirv-attrs.cu new file mode 100644 index 0000000000000..466aee00717a0 --- /dev/null +++ b/clang/test/CodeGenCUDASPIRV/spirv-attrs.cu @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -fcuda-is-device -triple spirv64 -o - -emit-llvm -x cuda %s | FileCheck %s +// RUN: %clang_cc1 -fcuda-is-device -triple spirv32 -o - -emit-llvm -x cuda %s | FileCheck %s + +#define __global__ __attribute__((global)) + +__attribute__((reqd_work_group_size(128, 1, 1))) +__global__ void reqd_work_group_size_128_1_1() {} + +__attribute__((work_group_size_hint(2, 2, 2))) +__global__ void work_group_size_hint_2_2_2() {} + +__attribute__((vec_type_hint(int))) +__global__ void vec_type_hint_int() {} + +__attribute__((intel_reqd_sub_group_size(64))) +__global__ void intel_reqd_sub_group_size_64() {} + +// CHECK: define spir_kernel void @_Z28reqd_work_group_size_128_1_1v() #[[ATTR:[0-9]+]] !reqd_work_group_size ![[WG_SIZE:[0-9]+]] +// CHECK: define spir_kernel void @_Z26work_group_size_hint_2_2_2v() #[[ATTR]] !work_group_size_hint ![[WG_HINT:[0-9]+]] +// CHECK: define spir_kernel void @_Z17vec_type_hint_intv() #[[ATTR]] !vec_type_hint ![[VEC_HINT:[0-9]+]] +// CHECK: define spir_kernel void @_Z28intel_reqd_sub_group_size_64v() #[[ATTR]] !intel_reqd_sub_group_size ![[SUB_GRP:[0-9]+]] + +// CHECK: attributes #[[ATTR]] = { {{.*}} } + +// CHECK: ![[WG_SIZE]] = !{i32 128, i32 1, i32 1} +// CHECK: ![[WG_HINT]] = !{i32 2, i32 2, i32 2} +// CHECK: ![[VEC_HINT]] = !{i32 undef, i32 1} +// CHECK: ![[SUB_GRP]] = !{i32 64} diff --git a/clang/test/CodeGenCXX/auto-var-init-attr.cpp b/clang/test/CodeGenCXX/auto-var-init-attr.cpp new file mode 100644 index 0000000000000..5481c6e8613c5 --- /dev/null +++ b/clang/test/CodeGenCXX/auto-var-init-attr.cpp @@ -0,0 +1,59 @@ +// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown -fblocks -fdeclspec -ftrivial-auto-var-init=zero %s -emit-llvm -o - | FileCheck %s + +struct S { char c; }; +class C { char c; }; +enum class E { ZERO }; +union U { char c; int i; }; + +struct __declspec(no_init_all) NoInitS { char c; }; +class __declspec(no_init_all) NoInitC { char c; }; +enum class __declspec(no_init_all) NoInitE { ZERO }; +union __declspec(no_init_all) NoInitU { char c; int i; }; + +extern "C" { + void test_no_attr() { + // CHECK-LABEL: @test_no_attr() + // CHECK-NEXT: entry: + // CHECK-NEXT: %s = alloca %struct.S, align 1 + // CHECK-NEXT: %c = alloca %class.C, align 1 + // CHECK-NEXT: %e = alloca i32, align 4 + // CHECK-NEXT: %u = alloca %union.U, align 4 + // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 %s, i8 0, i64 1, i1 false) + // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 %c, i8 0, i64 1, i1 false) + // CHECK-NEXT: store i32 0, ptr %e, align 4 + // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 %u, i8 0, i64 4, i1 false) + // CHECK-NEXT ret void + S s; + C c; + E e; + U u; + } + + void __declspec(no_init_all) test_attr_on_function() { + // CHECK-LABEL: @test_attr_on_function() + // CHECK-NEXT: entry: + // CHECK-NEXT: %s = alloca %struct.S, align 1 + // CHECK-NEXT: %c = alloca %class.C, align 1 + // CHECK-NEXT: %e = alloca i32, align 4 + // CHECK-NEXT: %u = alloca %union.U, align 4 + // CHECK-NEXT: ret void + S s; + C c; + E e; + U u; + } + + void test_attr_on_decl() { + // CHECK-LABEL: @test_attr_on_decl() + // CHECK-NEXT: entry: + // CHECK-NEXT: %s = alloca %struct.NoInitS, align 1 + // CHECK-NEXT: %c = alloca %class.NoInitC, align 1 + // CHECK-NEXT: %e = alloca i32, align 4 + // CHECK-NEXT: %u = alloca %union.NoInitU, align 4 + // CHECK-NEXT: ret void + NoInitS s; + NoInitC c; + NoInitE e; + NoInitU u; + } +} \ No newline at end of file diff --git a/clang/test/CodeGenOpenCL/amdgpu-features.cl b/clang/test/CodeGenOpenCL/amdgpu-features.cl index 8b56ec94f2c4e..61cbf5e65d0d2 100644 --- a/clang/test/CodeGenOpenCL/amdgpu-features.cl +++ b/clang/test/CodeGenOpenCL/amdgpu-features.cl @@ -32,6 +32,7 @@ // RUN: %clang_cc1 -triple amdgcn -target-cpu gfx940 -emit-llvm -o - %s | FileCheck --check-prefix=GFX940 %s // RUN: %clang_cc1 -triple amdgcn -target-cpu gfx941 -emit-llvm -o - %s | FileCheck --check-prefix=GFX941 %s // RUN: %clang_cc1 -triple amdgcn -target-cpu gfx942 -emit-llvm -o - %s | FileCheck --check-prefix=GFX942 %s +// RUN: %clang_cc1 -triple amdgcn -target-cpu gfx950 -emit-llvm -o - %s | FileCheck --check-prefix=GFX950 %s // RUN: %clang_cc1 -triple amdgcn -target-cpu gfx1010 -emit-llvm -o - %s | FileCheck --check-prefix=GFX1010 %s // RUN: %clang_cc1 -triple amdgcn -target-cpu gfx1011 -emit-llvm -o - %s | FileCheck --check-prefix=GFX1011 %s // RUN: %clang_cc1 -triple amdgcn -target-cpu gfx1012 -emit-llvm -o - %s | FileCheck --check-prefix=GFX1012 %s @@ -88,6 +89,7 @@ // GFX941: "target-features"="+16-bit-insts,+atomic-buffer-global-pk-add-f16-insts,+atomic-ds-pk-add-16-insts,+atomic-fadd-rtn-insts,+atomic-flat-pk-add-16-insts,+atomic-global-pk-add-bf16-inst,+ci-insts,+dl-insts,+dot1-insts,+dot10-insts,+dot2-insts,+dot3-insts,+dot4-insts,+dot5-insts,+dot6-insts,+dot7-insts,+dpp,+fp8-conversion-insts,+fp8-insts,+gfx8-insts,+gfx9-insts,+gfx90a-insts,+gfx940-insts,+mai-insts,+s-memrealtime,+s-memtime-inst,+wavefrontsize64,+xf32-insts" // GFX942: "target-features"="+16-bit-insts,+atomic-buffer-global-pk-add-f16-insts,+atomic-ds-pk-add-16-insts,+atomic-fadd-rtn-insts,+atomic-flat-pk-add-16-insts,+atomic-global-pk-add-bf16-inst,+ci-insts,+dl-insts,+dot1-insts,+dot10-insts,+dot2-insts,+dot3-insts,+dot4-insts,+dot5-insts,+dot6-insts,+dot7-insts,+dpp,+fp8-conversion-insts,+fp8-insts,+gfx8-insts,+gfx9-insts,+gfx90a-insts,+gfx940-insts,+mai-insts,+s-memrealtime,+s-memtime-inst,+wavefrontsize64,+xf32-insts" // GFX9_4_Generic: "target-features"="+16-bit-insts,+atomic-buffer-global-pk-add-f16-insts,+atomic-ds-pk-add-16-insts,+atomic-fadd-rtn-insts,+atomic-flat-pk-add-16-insts,+atomic-global-pk-add-bf16-inst,+ci-insts,+dl-insts,+dot1-insts,+dot10-insts,+dot2-insts,+dot3-insts,+dot4-insts,+dot5-insts,+dot6-insts,+dot7-insts,+dpp,+gfx8-insts,+gfx9-insts,+gfx90a-insts,+gfx940-insts,+mai-insts,+s-memrealtime,+s-memtime-inst,+wavefrontsize64" +// GFX950: "target-features"="+16-bit-insts,+atomic-buffer-global-pk-add-f16-insts,+atomic-ds-pk-add-16-insts,+atomic-fadd-rtn-insts,+atomic-flat-pk-add-16-insts,+atomic-global-pk-add-bf16-inst,+ci-insts,+dl-insts,+dot1-insts,+dot10-insts,+dot2-insts,+dot3-insts,+dot4-insts,+dot5-insts,+dot6-insts,+dot7-insts,+dpp,+fp8-conversion-insts,+fp8-insts,+gfx8-insts,+gfx9-insts,+gfx90a-insts,+gfx940-insts,+gfx950-insts,+mai-insts,+prng-inst,+s-memrealtime,+s-memtime-inst,+wavefrontsize64" // GFX1010: "target-features"="+16-bit-insts,+ci-insts,+dl-insts,+dpp,+gfx10-insts,+gfx8-insts,+gfx9-insts,+s-memrealtime,+s-memtime-inst,+wavefrontsize32" // GFX1011: "target-features"="+16-bit-insts,+ci-insts,+dl-insts,+dot1-insts,+dot10-insts,+dot2-insts,+dot5-insts,+dot6-insts,+dot7-insts,+dpp,+gfx10-insts,+gfx8-insts,+gfx9-insts,+s-memrealtime,+s-memtime-inst,+wavefrontsize32" // GFX1012: "target-features"="+16-bit-insts,+ci-insts,+dl-insts,+dot1-insts,+dot10-insts,+dot2-insts,+dot5-insts,+dot6-insts,+dot7-insts,+dpp,+gfx10-insts,+gfx8-insts,+gfx9-insts,+s-memrealtime,+s-memtime-inst,+wavefrontsize32" diff --git a/clang/test/CodeGenOpenCL/builtins-amdgcn-gfx950-err.cl b/clang/test/CodeGenOpenCL/builtins-amdgcn-gfx950-err.cl new file mode 100644 index 0000000000000..86f4f73c81c0f --- /dev/null +++ b/clang/test/CodeGenOpenCL/builtins-amdgcn-gfx950-err.cl @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -O0 -cl-std=CL2.0 -triple amdgcn-amd-amdhsa -target-cpu gfx906 -emit-llvm \ +// RUN: -verify -o - %s +// RUN: %clang_cc1 -O0 -cl-std=CL2.0 -triple amdgcn-amd-amdhsa -target-cpu gfx90a -emit-llvm \ +// RUN: -verify -o - %s +// RUN: %clang_cc1 -O0 -cl-std=CL2.0 -triple amdgcn-amd-amdhsa -target-cpu gfx940 -emit-llvm \ +// RUN: -verify -o - %s +// RUN: %clang_cc1 -O0 -cl-std=CL2.0 -triple amdgcn-amd-amdhsa -target-cpu gfx1200 -emit-llvm \ +// RUN: -verify -o - %s + + +// REQUIRES: amdgpu-registered-target + +typedef unsigned int uint; +void test_prng_b32(global uint* out, uint a) { + *out = __builtin_amdgcn_prng_b32(a); // expected-error{{'__builtin_amdgcn_prng_b32' needs target feature prng-inst}} +} diff --git a/clang/test/CodeGenOpenCL/builtins-amdgcn-gfx950.cl b/clang/test/CodeGenOpenCL/builtins-amdgcn-gfx950.cl new file mode 100644 index 0000000000000..f31ba85a52a7a --- /dev/null +++ b/clang/test/CodeGenOpenCL/builtins-amdgcn-gfx950.cl @@ -0,0 +1,21 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py +// RUN: %clang_cc1 -cl-std=CL1.2 -O0 -triple amdgcn-unknown-unknown -target-cpu gfx950 -emit-llvm -o - %s | FileCheck %s +// REQUIRES: amdgpu-registered-target + +typedef unsigned int uint; + +// CHECK-LABEL: @test_prng_b32( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[OUT_ADDR:%.*]] = alloca ptr addrspace(1), align 8, addrspace(5) +// CHECK-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4, addrspace(5) +// CHECK-NEXT: store ptr addrspace(1) [[OUT:%.*]], ptr addrspace(5) [[OUT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[A:%.*]], ptr addrspace(5) [[A_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr addrspace(5) [[A_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.amdgcn.prng.b32(i32 [[TMP0]]) +// CHECK-NEXT: [[TMP2:%.*]] = load ptr addrspace(1), ptr addrspace(5) [[OUT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[TMP1]], ptr addrspace(1) [[TMP2]], align 4 +// CHECK-NEXT: ret void +// +void test_prng_b32(global uint* out, uint a) { + *out = __builtin_amdgcn_prng_b32(a); +} diff --git a/clang/test/CodeGenOpenCL/builtins-amdgcn-mfma.cl b/clang/test/CodeGenOpenCL/builtins-amdgcn-mfma.cl index dcdeee6b6acc4..841d8fcad0fee 100644 --- a/clang/test/CodeGenOpenCL/builtins-amdgcn-mfma.cl +++ b/clang/test/CodeGenOpenCL/builtins-amdgcn-mfma.cl @@ -2,6 +2,7 @@ // RUN: %clang_cc1 -triple amdgcn-unknown-unknown -target-cpu gfx908 -DMFMA_GFX908_TESTS -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-GFX908 // RUN: %clang_cc1 -triple amdgcn-unknown-unknown -target-cpu gfx90a -DMFMA_GFX90A_TESTS -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-GFX90A // RUN: %clang_cc1 -triple amdgcn-unknown-unknown -target-cpu gfx940 -DMFMA_GFX940_TESTS -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-GFX940 +// RUN: %clang_cc1 -triple amdgcn-unknown-unknown -target-cpu gfx950 -DMFMA_GFX950_TESTS -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-GFX950 #pragma OPENCL EXTENSION cl_khr_fp64:enable @@ -23,6 +24,7 @@ typedef short v8s __attribute__((ext_vector_type(8))); typedef short v16s __attribute__((ext_vector_type(16))); typedef short v32s __attribute__((ext_vector_type(32))); typedef double v4d __attribute__((ext_vector_type(4))); +typedef __bf16 v8bf16 __attribute__((ext_vector_type(8))); #ifdef MFMA_GFX908_TESTS @@ -222,7 +224,7 @@ void test_mfma_f64_4x4x4f64(global double* out, double a, double b, double c) #endif // MFMA_GFX90A_TESTS -#ifdef MFMA_GFX940_TESTS +#if defined(MFMA_GFX940_TESTS) || defined(MFMA_GFX950_TESTS) // CHECK-GFX940-LABEL: @test_mfma_i32_16x16x32_i8 // CHECK-GFX940: call <4 x i32> @llvm.amdgcn.mfma.i32.16x16x32.i8(i64 %a, i64 %b, <4 x i32> %c, i32 0, i32 0, i32 0) void test_mfma_i32_16x16x32_i8(global v4i* out, long a, long b, v4i c) @@ -404,4 +406,29 @@ void test_smfmac_f32_32x32x32_fp8_fp8(global v16f* out, v2i a, v4i b, v16f c, in { *out = __builtin_amdgcn_smfmac_f32_32x32x32_fp8_fp8(a, b, c, idx, 0, 0); } -#endif // MFMA_GFX940_TESTS +#endif // defined(MFMA_GFX940_TESTS) || defined(MFMA_GFX950_TESTS) + +#ifdef MFMA_GFX950_TESTS + +// CHECK-GFX950-LABEL: @test_mfma_f32_16x16x32_f16( +// CHECK-GFX950: tail call <4 x float> @llvm.amdgcn.mfma.f32.16x16x32.f16(<8 x half> %a, <8 x half> %b, <4 x float> %c, i32 1, i32 2, i32 3) + +v4f test_mfma_f32_16x16x32_f16(v8h a, v8h b, v4f c) +{ + return __builtin_amdgcn_mfma_f32_16x16x32_f16(a, b, c, 1, 2, 3); +} + +// CHECK-GFX950-LABEL: @test_mfma_f32_32x32x16_f16 +// CHECK-GFX950: tail call <16 x float> @llvm.amdgcn.mfma.f32.32x32x16.f16(<8 x half> %a, <8 x half> %b, <16 x float> %c, i32 1, i32 2, i32 3) +v16f test_mfma_f32_32x32x16_f16(v8h a, v8h b, v16f c) +{ + return __builtin_amdgcn_mfma_f32_32x32x16_f16(a, b, c, 1, 2, 3); +} + +// CHECK-GFX950-LABEL: @test_mfma_f32_32x32x16_bf16( +// CHECK-GFX950: tail call <16 x float> @llvm.amdgcn.mfma.f32.32x32x16.bf16(<8 x bfloat> %a, <8 x bfloat> %b, <16 x float> %c, i32 1, i32 2, i32 3) +v16f test_mfma_f32_32x32x16_bf16(v8bf16 a, v8bf16 b, v16f c) { + return __builtin_amdgcn_mfma_f32_32x32x16_bf16(a, b, c, 1, 2, 3); +} + +#endif diff --git a/clang/test/Driver/amdgpu-macros.cl b/clang/test/Driver/amdgpu-macros.cl index d354f933c5ad7..d97b2ddb1fc66 100644 --- a/clang/test/Driver/amdgpu-macros.cl +++ b/clang/test/Driver/amdgpu-macros.cl @@ -110,6 +110,7 @@ // RUN: %clang -E -dM -target amdgcn -mcpu=gfx940 %s 2>&1 | FileCheck --check-prefixes=ARCH-GCN,FAST_FMAF %s -DWAVEFRONT_SIZE=64 -DCPU=gfx940 -DFAMILY=GFX9 // RUN: %clang -E -dM -target amdgcn -mcpu=gfx941 %s 2>&1 | FileCheck --check-prefixes=ARCH-GCN,FAST_FMAF %s -DWAVEFRONT_SIZE=64 -DCPU=gfx941 -DFAMILY=GFX9 // RUN: %clang -E -dM -target amdgcn -mcpu=gfx942 %s 2>&1 | FileCheck --check-prefixes=ARCH-GCN,FAST_FMAF %s -DWAVEFRONT_SIZE=64 -DCPU=gfx942 -DFAMILY=GFX9 +// RUN: %clang -E -dM -target amdgcn -mcpu=gfx950 %s 2>&1 | FileCheck --check-prefixes=ARCH-GCN,FAST_FMAF %s -DWAVEFRONT_SIZE=64 -DCPU=gfx950 -DFAMILY=GFX9 // RUN: %clang -E -dM -target amdgcn -mcpu=gfx1010 %s 2>&1 | FileCheck --check-prefixes=ARCH-GCN,FAST_FMAF %s -DWAVEFRONT_SIZE=32 -DCPU=gfx1010 -DFAMILY=GFX10 // RUN: %clang -E -dM -target amdgcn -mcpu=gfx1011 %s 2>&1 | FileCheck --check-prefixes=ARCH-GCN,FAST_FMAF %s -DWAVEFRONT_SIZE=32 -DCPU=gfx1011 -DFAMILY=GFX10 // RUN: %clang -E -dM -target amdgcn -mcpu=gfx1012 %s 2>&1 | FileCheck --check-prefixes=ARCH-GCN,FAST_FMAF %s -DWAVEFRONT_SIZE=32 -DCPU=gfx1012 -DFAMILY=GFX10 diff --git a/clang/test/Driver/amdgpu-mcpu.cl b/clang/test/Driver/amdgpu-mcpu.cl index ba57843507298..7c34d3ec6c63a 100644 --- a/clang/test/Driver/amdgpu-mcpu.cl +++ b/clang/test/Driver/amdgpu-mcpu.cl @@ -95,6 +95,7 @@ // RUN: %clang -### -target amdgcn -mcpu=gfx940 %s 2>&1 | FileCheck --check-prefix=GFX940 %s // RUN: %clang -### -target amdgcn -mcpu=gfx941 %s 2>&1 | FileCheck --check-prefix=GFX941 %s // RUN: %clang -### -target amdgcn -mcpu=gfx942 %s 2>&1 | FileCheck --check-prefix=GFX942 %s +// RUN: %clang -### -target amdgcn -mcpu=gfx950 %s 2>&1 | FileCheck --check-prefix=GFX950 %s // RUN: %clang -### -target amdgcn -mcpu=gfx1010 %s 2>&1 | FileCheck --check-prefix=GFX1010 %s // RUN: %clang -### -target amdgcn -mcpu=gfx1011 %s 2>&1 | FileCheck --check-prefix=GFX1011 %s // RUN: %clang -### -target amdgcn -mcpu=gfx1012 %s 2>&1 | FileCheck --check-prefix=GFX1012 %s @@ -150,6 +151,7 @@ // GFX940: "-target-cpu" "gfx940" // GFX941: "-target-cpu" "gfx941" // GFX942: "-target-cpu" "gfx942" +// GFX950: "-target-cpu" "gfx950" // GFX1010: "-target-cpu" "gfx1010" // GFX1011: "-target-cpu" "gfx1011" // GFX1012: "-target-cpu" "gfx1012" diff --git a/clang/test/Driver/clang_f_opts.c b/clang/test/Driver/clang_f_opts.c index 2cfbe256bc745..ddbf1fd951c84 100644 --- a/clang/test/Driver/clang_f_opts.c +++ b/clang/test/Driver/clang_f_opts.c @@ -71,9 +71,6 @@ // RUN: %clang -### -S -fauto-profile=%S/Inputs/file.prof -fno-auto-profile %s 2>&1 | FileCheck -check-prefix=CHECK-NO-AUTO-PROFILE %s // CHECK-NO-AUTO-PROFILE-NOT: "-fprofile-sample-use={{.*}}/file.prof" -// RUN: %clang -### -S -fauto-profile=%S/Inputs/file.prof -fno-profile-sample-use -fauto-profile %s 2>&1 | FileCheck -check-prefix=CHECK-AUTO-PROFILE %s -// RUN: %clang -### -S -fauto-profile=%S/Inputs/file.prof -fno-auto-profile -fprofile-sample-use %s 2>&1 | FileCheck -check-prefix=CHECK-AUTO-PROFILE %s - // RUN: %clang -### -S -fprofile-generate %s 2>&1 | FileCheck -check-prefix=CHECK-PROFILE-GENERATE-LLVM %s // RUN: %clang -### -S -fprofile-instr-generate %s 2>&1 | FileCheck -check-prefix=CHECK-PROFILE-GENERATE %s // RUN: %clang -### -S -fprofile-generate=/some/dir %s 2>&1 | FileCheck -check-prefix=CHECK-PROFILE-GENERATE-DIR %s diff --git a/clang/test/Driver/fprofile-sample-use.c b/clang/test/Driver/fprofile-sample-use.c new file mode 100644 index 0000000000000..7c8813a83785c --- /dev/null +++ b/clang/test/Driver/fprofile-sample-use.c @@ -0,0 +1,5 @@ +/// GCC -fauto-profile (without =) is rejected. +/// -fprofile-sample-use without = is rejected as well. +// RUN: not %clang -### -S -fauto-profile -fprofile-sample-use %s 2>&1 | FileCheck %s --check-prefix=ERR +// ERR: error: unknown argument: '-fauto-profile' +// ERR: error: unknown argument: '-fprofile-sample-use' diff --git a/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c index 6ecf0b57bee5c..15f190165a7d7 100644 --- a/clang/test/Driver/fsanitize.c +++ b/clang/test/Driver/fsanitize.c @@ -866,6 +866,13 @@ // CHECK-INTSAN-MINIMAL: "-fsanitize=integer-divide-by-zero,shift-base,shift-exponent,signed-integer-overflow,unsigned-integer-overflow,unsigned-shift-base,implicit-unsigned-integer-truncation,implicit-signed-integer-truncation,implicit-integer-sign-change" // CHECK-INTSAN-MINIMAL: "-fsanitize-minimal-runtime" +// RUN: %clang --target=x86_64-linux-gnu -fsanitize=implicit-conversion -fsanitize-trap=implicit-conversion %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-IMPL-CONV-TRAP +// CHECK-IMPL-CONV-TRAP: "-fsanitize-trap=implicit-unsigned-integer-truncation,implicit-signed-integer-truncation,implicit-integer-sign-change,implicit-bitfield-conversion" + +// RUN: %clang --target=x86_64-linux-gnu -fsanitize=implicit-conversion -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-IMPL-CONV-MINIMAL +// CHECK-IMPL-CONV-MINIMAL: "-fsanitize=implicit-unsigned-integer-truncation,implicit-signed-integer-truncation,implicit-integer-sign-change,implicit-bitfield-conversion" +// CHECK-IMPL-CONV-MINIMAL: "-fsanitize-minimal-runtime" + // RUN: %clang --target=aarch64-linux-android -march=armv8-a+memtag -fsanitize=memtag -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-MEMTAG-MINIMAL // CHECK-MEMTAG-MINIMAL: "-fsanitize=memtag-stack,memtag-heap,memtag-globals" // CHECK-MEMTAG-MINIMAL: "-fsanitize-minimal-runtime" diff --git a/clang/test/Driver/module-output.cppm b/clang/test/Driver/module-output.cppm index bf7bfbf3cb574..7cf0771f3d6ef 100644 --- a/clang/test/Driver/module-output.cppm +++ b/clang/test/Driver/module-output.cppm @@ -42,7 +42,7 @@ export module Hello; // CHECK: "-emit-module-interface" {{.*}}"-main-file-name" "Hello.cppm" {{.*}}"-o" "{{.*}}/output/Hello.pcm" "-x" "c++" "{{.*}}/Hello.cppm" // CHECK: "-emit-obj" {{.*}}"-main-file-name" "Hello.cppm" {{.*}}"-o" "{{.*}}/output/Hello.o" "-x" "pcm" "{{.*}}/output/Hello.pcm" -// MULTIPLE-ARCH: option '-fmodule-output' can't be used with multiple arch options +// MULTIPLE-ARCH: option '-fmodule-output' cannot be used with multiple arch options // CHECK-SPECIFIED: "-emit-module-interface" {{.*}}"-main-file-name" "Hello.cppm" {{.*}}"-o" "{{.*}}/pcm/Hello.pcm" "-x" "c++" "{{.*}}/Hello.cppm" // CHECK-SPECIFIED: "-emit-obj" {{.*}}"-main-file-name" "Hello.cppm" {{.*}}"-o" "{{.*}}/Hello.o" "-x" "pcm" "{{.*}}/pcm/Hello.pcm" diff --git a/clang/test/Driver/relax.s b/clang/test/Driver/relax.s index 0768a38834447..7b084de7e6be2 100644 --- a/clang/test/Driver/relax.s +++ b/clang/test/Driver/relax.s @@ -8,7 +8,7 @@ // RUN: llvm-readobj -r %t | FileCheck --check-prefix=REL %s // REL: R_X86_64_REX_GOTPCRELX foo -// REL: R_X86_64_REX2_GOTPCRELX foo +// REL: R_X86_64_CODE_4_GOTPCRELX foo movq foo@GOTPCREL(%rip), %rax movq foo@GOTPCREL(%rip), %r16 diff --git a/clang/test/Driver/riscv-cpus.c b/clang/test/Driver/riscv-cpus.c index d36639d16ad4c..249216612f7ee 100644 --- a/clang/test/Driver/riscv-cpus.c +++ b/clang/test/Driver/riscv-cpus.c @@ -104,6 +104,68 @@ // RUN: %clang --target=riscv32 -### -c %s 2>&1 -mtune=syntacore-scr1-max | FileCheck -check-prefix=MTUNE-SYNTACORE-SCR1-MAX %s // MTUNE-SYNTACORE-SCR1-MAX: "-tune-cpu" "syntacore-scr1-max" +// RUN: %clang --target=riscv64 -### -c %s 2>&1 -mtune=tt-ascalon-d8 | FileCheck -check-prefix=MTUNE-TT-ASCALON-D8 %s +// MTUNE-TT-ASCALON-D8: "-tune-cpu" "tt-ascalon-d8" + +// RUN: %clang --target=riscv64 -### -c %s 2>&1 -mcpu=tt-ascalon-d8 | FileCheck -check-prefix=MCPU-TT-ASCALON-D8 %s +// MCPU-TT-ASCALON-D8: "-target-cpu" "tt-ascalon-d8" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+m" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+a" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+f" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+d" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+c" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+v" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+h" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zicbom" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zicbop" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zicboz" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zicntr" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zicond" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zicsr" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zifencei" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zihintntl" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zihintpause" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zihpm" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zimop" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zmmul" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zawrs" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zfa" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zfbfmin" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zfh" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zfhmin" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zca" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zcb" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zba" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zbb" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zbs" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zkt" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zvbb" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zvbc" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zve32f" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zve32x" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zve64d" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zve64f" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zve64x" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zvfbfmin" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zvfbfwma" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zvfh" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zvfhmin" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zvkb" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zvkg" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zvkn" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zvknc" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zvkned" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zvkng" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zvknhb" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zvkt" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zvl128b" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zvl256b" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zvl32b" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+zvl64b" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+svinval" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+svnapot" +// MCPU-TT-ASCALON-D8-SAME: "-target-feature" "+svpbmt" + // RUN: %clang --target=riscv64 -### -c %s 2>&1 -mcpu=veyron-v1 | FileCheck -check-prefix=MCPU-VEYRON-V1 %s // MCPU-VEYRON-V1: "-target-cpu" "veyron-v1" // MCPU-VEYRON-V1: "-target-feature" "+m" diff --git a/clang/test/Misc/pragma-attribute-strict-subjects.c b/clang/test/Misc/pragma-attribute-strict-subjects.c index 7c2548c7dfc26..807977fb252aa 100644 --- a/clang/test/Misc/pragma-attribute-strict-subjects.c +++ b/clang/test/Misc/pragma-attribute-strict-subjects.c @@ -51,7 +51,7 @@ struct testRecoverStrictnessStruct { }; #pragma clang attribute pop #pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(function, record(unless(is_union)), variable, enum)) -// expected-error@-1 {{attribute 'abi_tag' can't be applied to 'enum'}} +// expected-error@-1 {{attribute 'abi_tag' cannot be applied to 'enum'}} int testRecoverExtraVar = 0; // CHECK-LABEL: VarDecl{{.*}} testRecoverExtraVar @@ -188,7 +188,7 @@ struct testSubset7Struct { }; #pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(record(unless(is_union)), function, variable, enum, enum_constant)) -// expected-error@-1 {{attribute 'abi_tag' can't be applied to 'enum_constant', and 'enum'}} +// expected-error@-1 {{attribute 'abi_tag' cannot be applied to 'enum_constant', and 'enum'}} int testSubsetRecoverVar; // CHECK-LABEL: VarDecl{{.*}} testSubsetRecoverVar @@ -205,7 +205,7 @@ struct testSubsetRecoverStruct { }; #pragma clang attribute pop #pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = enum) -// expected-error@-1 {{attribute 'abi_tag' can't be applied to 'enum'}} +// expected-error@-1 {{attribute 'abi_tag' cannot be applied to 'enum'}} int testSubsetNoVar; // CHECK-LABEL: VarDecl{{.*}} testSubsetNoVar diff --git a/clang/test/Misc/target-invalid-cpu-note/amdgcn.c b/clang/test/Misc/target-invalid-cpu-note/amdgcn.c index 4e675871f1e5b..642d2df211c21 100644 --- a/clang/test/Misc/target-invalid-cpu-note/amdgcn.c +++ b/clang/test/Misc/target-invalid-cpu-note/amdgcn.c @@ -48,6 +48,7 @@ // CHECK-SAME: {{^}}, gfx940 // CHECK-SAME: {{^}}, gfx941 // CHECK-SAME: {{^}}, gfx942 +// CHECK-SAME: {{^}}, gfx950 // CHECK-SAME: {{^}}, gfx1010 // CHECK-SAME: {{^}}, gfx1011 // CHECK-SAME: {{^}}, gfx1012 diff --git a/clang/test/Misc/target-invalid-cpu-note/nvptx.c b/clang/test/Misc/target-invalid-cpu-note/nvptx.c index 44fe07065b242..3ea6c02d6b384 100644 --- a/clang/test/Misc/target-invalid-cpu-note/nvptx.c +++ b/clang/test/Misc/target-invalid-cpu-note/nvptx.c @@ -54,6 +54,7 @@ // CHECK-SAME: {{^}}, gfx940 // CHECK-SAME: {{^}}, gfx941 // CHECK-SAME: {{^}}, gfx942 +// CHECK-SAME: {{^}}, gfx950 // CHECK-SAME: {{^}}, gfx10-1-generic // CHECK-SAME: {{^}}, gfx1010 // CHECK-SAME: {{^}}, gfx1011 diff --git a/clang/test/Misc/target-invalid-cpu-note/riscv.c b/clang/test/Misc/target-invalid-cpu-note/riscv.c index 7bbf3574af3c3..8c5df5884cd79 100644 --- a/clang/test/Misc/target-invalid-cpu-note/riscv.c +++ b/clang/test/Misc/target-invalid-cpu-note/riscv.c @@ -41,6 +41,7 @@ // RISCV64-SAME: {{^}}, syntacore-scr4-rv64 // RISCV64-SAME: {{^}}, syntacore-scr5-rv64 // RISCV64-SAME: {{^}}, syntacore-scr7 +// RISCV64-SAME: {{^}}, tt-ascalon-d8 // RISCV64-SAME: {{^}}, veyron-v1 // RISCV64-SAME: {{^}}, xiangshan-nanhu // RISCV64-SAME: {{$}} @@ -87,6 +88,7 @@ // TUNE-RISCV64-SAME: {{^}}, syntacore-scr4-rv64 // TUNE-RISCV64-SAME: {{^}}, syntacore-scr5-rv64 // TUNE-RISCV64-SAME: {{^}}, syntacore-scr7 +// TUNE-RISCV64-SAME: {{^}}, tt-ascalon-d8 // TUNE-RISCV64-SAME: {{^}}, veyron-v1 // TUNE-RISCV64-SAME: {{^}}, xiangshan-nanhu // TUNE-RISCV64-SAME: {{^}}, generic diff --git a/clang/test/Modules/no-eager-load.cppm b/clang/test/Modules/no-eager-load.cppm index c9eddaaed1555..aa6de44c998f3 100644 --- a/clang/test/Modules/no-eager-load.cppm +++ b/clang/test/Modules/no-eager-load.cppm @@ -44,7 +44,7 @@ void use() { // expected-note@* {{but in 'a' found a different body}} } -// expected-error@a.cppm:* {{declaration 'foo' attached to named module 'a' can't be attached to other modules}} +// expected-error@a.cppm:* {{declaration 'foo' attached to named module 'a' cannot be attached to other modules}} // expected-note@b.cppm:* {{}} //--- h.cppm @@ -59,5 +59,5 @@ void use() { // expected-note@* {{but in 'a' found a different body}} } -// expected-error@a.cppm:* {{declaration 'foo' attached to named module 'a' can't be attached to other modules}} +// expected-error@a.cppm:* {{declaration 'foo' attached to named module 'a' cannot be attached to other modules}} // expected-note@b.cppm:* {{}} diff --git a/clang/test/Modules/same-decl-in-different-modules.cppm b/clang/test/Modules/same-decl-in-different-modules.cppm index 2e8e90f7cd8e9..8ad9e29051d4e 100644 --- a/clang/test/Modules/same-decl-in-different-modules.cppm +++ b/clang/test/Modules/same-decl-in-different-modules.cppm @@ -32,11 +32,11 @@ void test() { S s; } -// expected-error@mod1.cppm:* {{declaration 'v' attached to named module 'mod1' can't be attached to other modules}} +// expected-error@mod1.cppm:* {{declaration 'v' attached to named module 'mod1' cannot be attached to other modules}} // expected-note@mod2.cppm:* {{}} -// expected-error@mod1.cppm:* {{declaration 'func' attached to named module 'mod1' can't be attached to other modules}} +// expected-error@mod1.cppm:* {{declaration 'func' attached to named module 'mod1' cannot be attached to other modules}} // expected-note@mod2.cppm:* {{}} -// expected-error@mod1.cppm:* {{declaration 'A' attached to named module 'mod1' can't be attached to other modules}} +// expected-error@mod1.cppm:* {{declaration 'A' attached to named module 'mod1' cannot be attached to other modules}} // expected-note@mod2.cppm:* {{}} -// expected-error@mod1.cppm:* 1+{{declaration 'S' attached to named module 'mod1' can't be attached to other modules}} +// expected-error@mod1.cppm:* 1+{{declaration 'S' attached to named module 'mod1' cannot be attached to other modules}} // expected-note@mod2.cppm:* 1+{{}} diff --git a/clang/test/OpenMP/for_simd_loop_messages.cpp b/clang/test/OpenMP/for_simd_loop_messages.cpp index 1cc5988ea8092..74a52f3f5d694 100644 --- a/clang/test/OpenMP/for_simd_loop_messages.cpp +++ b/clang/test/OpenMP/for_simd_loop_messages.cpp @@ -731,7 +731,7 @@ void test_ordered() { for (int i = 0; i < 16; ++i) ; #pragma omp parallel -// expected-error@+1 {{'ordered' clause with a parameter can not be specified in '#pragma omp for simd' directive}} +// expected-error@+1 {{'ordered' clause with a parameter cannot be specified in '#pragma omp for simd' directive}} #pragma omp for simd ordered(1) for (int i = 0; i < 16; ++i) ; diff --git a/clang/test/OpenMP/masked_taskloop_simd_linear_messages.cpp b/clang/test/OpenMP/masked_taskloop_simd_linear_messages.cpp index 50d2da7e8fd4d..6072ad1b92445 100644 --- a/clang/test/OpenMP/masked_taskloop_simd_linear_messages.cpp +++ b/clang/test/OpenMP/masked_taskloop_simd_linear_messages.cpp @@ -152,7 +152,7 @@ template int foomain(I argc, C **argv) { #pragma omp masked taskloop simd linear (S1) // expected-error {{'S1' does not refer to a value}} for (int k = 0; k < argc; ++k) ++k; #if defined(OMP52) - // omp52-error@+3{{step simple modifier is exclusive and can't be use with 'val', 'uval' or 'ref' modifier}} + // omp52-error@+3{{step simple modifier is exclusive and cannot be use with 'val', 'uval' or 'ref' modifier}} // expected-error@+2 {{linear variable with incomplete type 'S1'}} // expected-error@+1 {{argument of a linear clause should be of integral or pointer type, not 'S2'}} #pragma omp masked taskloop simd linear (a, b: val, B::ib) diff --git a/clang/test/OpenMP/master_taskloop_simd_linear_messages.cpp b/clang/test/OpenMP/master_taskloop_simd_linear_messages.cpp index ee29f63e110c0..c1bf61b8183ec 100644 --- a/clang/test/OpenMP/master_taskloop_simd_linear_messages.cpp +++ b/clang/test/OpenMP/master_taskloop_simd_linear_messages.cpp @@ -152,7 +152,7 @@ template int foomain(I argc, C **argv) { #pragma omp master taskloop simd linear (S1) // expected-error {{'S1' does not refer to a value}} for (int k = 0; k < argc; ++k) ++k; #if defined(OMP52) - // omp52-error@+3{{step simple modifier is exclusive and can't be use with 'val', 'uval' or 'ref' modifier}} + // omp52-error@+3{{step simple modifier is exclusive and cannot be use with 'val', 'uval' or 'ref' modifier}} // expected-error@+2 {{linear variable with incomplete type 'S1'}} // expected-error@+1 {{argument of a linear clause should be of integral or pointer type, not 'S2'}} #pragma omp master taskloop simd linear (a, b: val, B::ib) diff --git a/clang/test/OpenMP/parallel_for_simd_loop_messages.cpp b/clang/test/OpenMP/parallel_for_simd_loop_messages.cpp index f55453f6e8e15..4760a0281df54 100644 --- a/clang/test/OpenMP/parallel_for_simd_loop_messages.cpp +++ b/clang/test/OpenMP/parallel_for_simd_loop_messages.cpp @@ -638,7 +638,7 @@ void test_ordered() { #pragma omp parallel for simd ordered for (int i = 0; i < 16; ++i) ; -//expected-error@+1 {{'ordered' clause with a parameter can not be specified in '#pragma omp parallel for simd' directive}} +//expected-error@+1 {{'ordered' clause with a parameter cannot be specified in '#pragma omp parallel for simd' directive}} #pragma omp parallel for simd ordered(1) for (int i = 0; i < 16; ++i) ; diff --git a/clang/test/OpenMP/parallel_for_simd_messages.cpp b/clang/test/OpenMP/parallel_for_simd_messages.cpp index 8237406a1c068..b3408fab4417f 100644 --- a/clang/test/OpenMP/parallel_for_simd_messages.cpp +++ b/clang/test/OpenMP/parallel_for_simd_messages.cpp @@ -94,7 +94,7 @@ void test_ordered() { #pragma omp parallel for simd ordered for (int i = 0; i < 16; ++i) ; -// expected-error@+1 {{'ordered' clause with a parameter can not be specified in '#pragma omp parallel for simd' directive}} +// expected-error@+1 {{'ordered' clause with a parameter cannot be specified in '#pragma omp parallel for simd' directive}} #pragma omp parallel for simd ordered(1) for (int i = 0; i < 16; ++i) ; diff --git a/clang/test/OpenMP/parallel_masked_taskloop_simd_linear_messages.cpp b/clang/test/OpenMP/parallel_masked_taskloop_simd_linear_messages.cpp index a913a4e331964..bda3ef09181a6 100644 --- a/clang/test/OpenMP/parallel_masked_taskloop_simd_linear_messages.cpp +++ b/clang/test/OpenMP/parallel_masked_taskloop_simd_linear_messages.cpp @@ -152,7 +152,7 @@ template int foomain(I argc, C **argv) { #pragma omp parallel masked taskloop simd linear (S1) // expected-error {{'S1' does not refer to a value}} for (int k = 0; k < argc; ++k) ++k; #if defined(OMP52) - // omp52-error@+3{{step simple modifier is exclusive and can't be use with 'val', 'uval' or 'ref' modifier}} + // omp52-error@+3{{step simple modifier is exclusive and cannot be use with 'val', 'uval' or 'ref' modifier}} // expected-error@+2 {{linear variable with incomplete type 'S1'}} // expected-error@+1 {{argument of a linear clause should be of integral or pointer type, not 'S2'}} #pragma omp parallel masked taskloop simd linear (a, b: val, B::ib) diff --git a/clang/test/OpenMP/parallel_master_taskloop_simd_linear_messages.cpp b/clang/test/OpenMP/parallel_master_taskloop_simd_linear_messages.cpp index 2be29fdc6b929..01a734cd927e2 100644 --- a/clang/test/OpenMP/parallel_master_taskloop_simd_linear_messages.cpp +++ b/clang/test/OpenMP/parallel_master_taskloop_simd_linear_messages.cpp @@ -152,7 +152,7 @@ template int foomain(I argc, C **argv) { #pragma omp parallel master taskloop simd linear (S1) // expected-error {{'S1' does not refer to a value}} for (int k = 0; k < argc; ++k) ++k; #if defined(OMP52) - // omp52-error@+3{{step simple modifier is exclusive and can't be use with 'val', 'uval' or 'ref' modifier}} + // omp52-error@+3{{step simple modifier is exclusive and cannot be use with 'val', 'uval' or 'ref' modifier}} // expected-error@+2 {{linear variable with incomplete type 'S1'}} // expected-error@+1 {{argument of a linear clause should be of integral or pointer type, not 'S2'}} #pragma omp parallel master taskloop simd linear (a, b: val, B::ib) diff --git a/clang/test/OpenMP/simd_linear_messages.cpp b/clang/test/OpenMP/simd_linear_messages.cpp index a19fad9d7718a..68a2999fdf65a 100644 --- a/clang/test/OpenMP/simd_linear_messages.cpp +++ b/clang/test/OpenMP/simd_linear_messages.cpp @@ -142,7 +142,7 @@ template int foomain(I argc, C **argv) { #pragma omp simd linear (S1) // expected-error {{'S1' does not refer to a value}} for (int k = 0; k < argc; ++k) ++k; #if defined(OMP52) - // omp52-error@+3{{step simple modifier is exclusive and can't be use with 'val', 'uval' or 'ref' modifier}} + // omp52-error@+3{{step simple modifier is exclusive and cannot be use with 'val', 'uval' or 'ref' modifier}} // expected-error@+2 {{linear variable with incomplete type 'S1'}} // expected-error@+1 {{argument of a linear clause should be of integral or pointer type, not 'S2'}} #pragma omp simd linear (a, b: val, B::ib) diff --git a/clang/test/OpenMP/target_parallel_for_simd_ordered_messages.cpp b/clang/test/OpenMP/target_parallel_for_simd_ordered_messages.cpp index 8dd7f68c25fd8..73ea96eb24278 100644 --- a/clang/test/OpenMP/target_parallel_for_simd_ordered_messages.cpp +++ b/clang/test/OpenMP/target_parallel_for_simd_ordered_messages.cpp @@ -29,26 +29,26 @@ T tmain(T argc, S **argv) { #pragma omp target parallel for simd ordered() // expected-error {{expected expression}} for (int i = ST; i < N; i++) argv[0][i] = argv[0][i] - argv[0][i - ST]; -// expected-error@+2 {{'ordered' clause with a parameter can not be specified in '#pragma omp target parallel for simd' directive}} +// expected-error@+2 {{'ordered' clause with a parameter cannot be specified in '#pragma omp target parallel for simd' directive}} // expected-error@+1 {{expected ')'}} expected-note@+1 {{to match this '('}} #pragma omp target parallel for simd ordered(argc for (int i = ST; i < N; i++) argv[0][i] = argv[0][i] - argv[0][i - ST]; -// expected-error@+1 {{'ordered' clause with a parameter can not be specified in '#pragma omp target parallel for simd' directive}} +// expected-error@+1 {{'ordered' clause with a parameter cannot be specified in '#pragma omp target parallel for simd' directive}} #pragma omp target parallel for simd ordered(ST // expected-error {{expected ')'}} expected-note {{to match this '('}} for (int i = ST; i < N; i++) argv[0][i] = argv[0][i] - argv[0][i - ST]; -// expected-error@+1 {{'ordered' clause with a parameter can not be specified in '#pragma omp target parallel for simd' directive}} +// expected-error@+1 {{'ordered' clause with a parameter cannot be specified in '#pragma omp target parallel for simd' directive}} #pragma omp target parallel for simd ordered(1)) // expected-warning {{extra tokens at the end of '#pragma omp target parallel for simd' are ignored}} for (int i = ST; i < N; i++) argv[0][i] = argv[0][i] - argv[0][i - ST]; -// expected-error@+1 {{'ordered' clause with a parameter can not be specified in '#pragma omp target parallel for simd' directive}} +// expected-error@+1 {{'ordered' clause with a parameter cannot be specified in '#pragma omp target parallel for simd' directive}} #pragma omp target parallel for simd ordered((ST > 0) ? 1 + ST : 2) for (int i = ST; i < N; i++) argv[0][i] = argv[0][i] - argv[0][i - ST]; // expected-error@+3 {{argument to 'ordered' clause must be a strictly positive integer value}} // expected-error@+2 2 {{directive '#pragma omp target parallel for simd' cannot contain more than one 'ordered' clause}} -// expected-error@+1 {{'ordered' clause with a parameter can not be specified in '#pragma omp target parallel for simd' directive}} +// expected-error@+1 {{'ordered' clause with a parameter cannot be specified in '#pragma omp target parallel for simd' directive}} #pragma omp target parallel for simd ordered(foobool(argc)), ordered(true), ordered(-5) for (int i = ST; i < N; i++) argv[0][i] = argv[0][i] - argv[0][i - ST]; @@ -60,15 +60,15 @@ T tmain(T argc, S **argv) { #pragma omp target parallel for simd ordered(j = 2) // expected-error {{expected ')'}} expected-note {{to match this '('}} for (int i = ST; i < N; i++) argv[0][i] = argv[0][i] - argv[0][i - ST]; -// expected-error@+1 {{'ordered' clause with a parameter can not be specified in '#pragma omp target parallel for simd' directive}} +// expected-error@+1 {{'ordered' clause with a parameter cannot be specified in '#pragma omp target parallel for simd' directive}} #pragma omp target parallel for simd ordered(1) for (int i = ST; i < N; i++) argv[0][i] = argv[0][i] - argv[0][i - ST]; -// expected-error@+1 {{'ordered' clause with a parameter can not be specified in '#pragma omp target parallel for simd' directive}} +// expected-error@+1 {{'ordered' clause with a parameter cannot be specified in '#pragma omp target parallel for simd' directive}} #pragma omp target parallel for simd ordered(N) for (T i = ST; i < N; i++) argv[0][i] = argv[0][i] - argv[0][i - ST]; -// expected-error@+1 {{'ordered' clause with a parameter can not be specified in '#pragma omp target parallel for simd' directive}} +// expected-error@+1 {{'ordered' clause with a parameter cannot be specified in '#pragma omp target parallel for simd' directive}} #pragma omp target parallel for simd ordered(2) foo(); return argc; @@ -85,11 +85,11 @@ int main(int argc, char **argv) { #pragma omp target parallel for simd ordered() // expected-error {{expected expression}} for (int i = 4; i < 12; i++) argv[0][i] = argv[0][i] - argv[0][i - 4]; -// expected-error@+1 {{'ordered' clause with a parameter can not be specified in '#pragma omp target parallel for simd' directive}} +// expected-error@+1 {{'ordered' clause with a parameter cannot be specified in '#pragma omp target parallel for simd' directive}} #pragma omp target parallel for simd ordered(4 // expected-error {{expected ')'}} expected-note {{to match this '('}} for (int i = 4; i < 12; i++) argv[0][i] = argv[0][i] - argv[0][i - 4]; -// expected-error@+1 {{'ordered' clause with a parameter can not be specified in '#pragma omp target parallel for simd' directive}} +// expected-error@+1 {{'ordered' clause with a parameter cannot be specified in '#pragma omp target parallel for simd' directive}} #pragma omp target parallel for simd ordered(2 + 2)) // expected-warning {{extra tokens at the end of '#pragma omp target parallel for simd' are ignored}} for (int i = 4; i < 12; i++) argv[0][i] = argv[0][i] - argv[0][i - 4]; @@ -115,7 +115,7 @@ int main(int argc, char **argv) { // expected-error@+2 {{statement after '#pragma omp target parallel for simd' must be a for loop}} #pragma omp target parallel for simd ordered(ordered(tmain < int, char, -1, -2 > (argc, argv) // expected-error 2 {{expected ')'}} expected-note 2 {{to match this '('}} foo(); -// expected-error@+1 {{'ordered' clause with a parameter can not be specified in '#pragma omp target parallel for simd' directive}} +// expected-error@+1 {{'ordered' clause with a parameter cannot be specified in '#pragma omp target parallel for simd' directive}} #pragma omp target parallel for simd ordered(2) foo(); return tmain(argc, argv); diff --git a/clang/test/OpenMP/taskloop_simd_linear_messages.cpp b/clang/test/OpenMP/taskloop_simd_linear_messages.cpp index 22e2d26cb5561..5bf4785f14be4 100644 --- a/clang/test/OpenMP/taskloop_simd_linear_messages.cpp +++ b/clang/test/OpenMP/taskloop_simd_linear_messages.cpp @@ -152,7 +152,7 @@ template int foomain(I argc, C **argv) { #pragma omp taskloop simd linear (S1) // expected-error {{'S1' does not refer to a value}} for (int k = 0; k < argc; ++k) ++k; #if defined(OMP52) - // omp52-error@+3{{step simple modifier is exclusive and can't be use with 'val', 'uval' or 'ref' modifier}} + // omp52-error@+3{{step simple modifier is exclusive and cannot be use with 'val', 'uval' or 'ref' modifier}} // expected-error@+2 {{linear variable with incomplete type 'S1'}} // expected-error@+1 {{argument of a linear clause should be of integral or pointer type, not 'S2'}} #pragma omp taskloop simd linear (a, b: val, B::ib) diff --git a/clang/test/Parser/cxx2c-delete-with-message.cpp b/clang/test/Parser/cxx2c-delete-with-message.cpp index 1767a080a7dcd..d2d5ccf4623c9 100644 --- a/clang/test/Parser/cxx2c-delete-with-message.cpp +++ b/clang/test/Parser/cxx2c-delete-with-message.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify=expected,pre26 -pedantic %s +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify=expected,pre26 %s +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify=expected,pre26-pedantic -pedantic %s // RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify=expected,compat -Wpre-c++26-compat %s // RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify %s @@ -7,15 +8,15 @@ struct S { void b() = delete(; // expected-error {{expected string literal}} expected-error {{expected ')'}} expected-note {{to match this '('}} void c() = delete(); // expected-error {{expected string literal}} void d() = delete(42); // expected-error {{expected string literal}} - void e() = delete("foo"[0]); // expected-error {{expected ')'}} expected-note {{to match this '('}} // pre26-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}} - void f() = delete("foo"); // pre26-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}} - - S() = delete("foo"); // pre26-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}} - ~S() = delete("foo"); // pre26-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}} - S(const S&) = delete("foo"); // pre26-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}} - S(S&&) = delete("foo"); // pre26-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}} - S& operator=(const S&) = delete("foo"); // pre26-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}} - S& operator=(S&&) = delete("foo"); // pre26-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}} + void e() = delete("foo"[0]); // expected-error {{expected ')'}} expected-note {{to match this '('}} // pre26-pedantic-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}} + void f() = delete("foo"); // pre26-pedantic-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}} + + S() = delete("foo"); // pre26-pedantic-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}} + ~S() = delete("foo"); // pre26-pedantic-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}} + S(const S&) = delete("foo"); // pre26-pedantic-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}} + S(S&&) = delete("foo"); // pre26-pedantic-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}} + S& operator=(const S&) = delete("foo"); // pre26-pedantic-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}} + S& operator=(S&&) = delete("foo"); // pre26-pedantic-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}} }; struct T { @@ -31,8 +32,8 @@ void a() = delete; void b() = delete(; // expected-error {{expected string literal}} expected-error {{expected ')'}} expected-note {{to match this '('}} void c() = delete(); // expected-error {{expected string literal}} void d() = delete(42); // expected-error {{expected string literal}} -void e() = delete("foo"[0]); // expected-error {{expected ')'}} expected-note {{to match this '('}} // pre26-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}} -void f() = delete("foo"); // pre26-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}} +void e() = delete("foo"[0]); // expected-error {{expected ')'}} expected-note {{to match this '('}} // pre26-pedantic-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}} +void f() = delete("foo"); // pre26-pedantic-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}} constexpr const char *getMsg() { return "this is a message"; } void func() = delete(getMsg()); // expected-error {{expected string literal}} @@ -49,3 +50,12 @@ struct C { U f = delete ("hello"); // expected-error {{cannot delete expression of type 'const char[6]'}} }; } + +namespace GH109311 { +void f() = delete +#if __cpp_deleted_function >= 202403L + ("reason") // pre26-pedantic-warning {{'= delete' with a message is a C++2c extension}} \ + // compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2}} +#endif +; +} diff --git a/clang/test/Parser/pragma-attribute.cpp b/clang/test/Parser/pragma-attribute.cpp index 6377fc754352e..d5b1f848abd06 100644 --- a/clang/test/Parser/pragma-attribute.cpp +++ b/clang/test/Parser/pragma-attribute.cpp @@ -124,7 +124,7 @@ void function(); #pragma clang attribute push (__attribute__((annotate)), apply_to=function foo) // expected-error {{extra tokens after attribute in a '#pragma clang attribute push'}} #pragma clang attribute push (__attribute__((objc_bridge_related)), apply_to=function) -// expected-error@-1 {{attribute 'objc_bridge_related' can't be applied to 'function'}} +// expected-error@-1 {{attribute 'objc_bridge_related' cannot be applied to 'function'}} #pragma clang attribute pop #pragma clang attribute push (__attribute__((objc_bridge_related(1))), apply_to=function) // expected-error {{expected a related Objective-C class name, e.g., 'NSColor'}} @@ -182,15 +182,15 @@ _Pragma("clang attribute pop"); #pragma clang attribute push([[clang::uninitialized]], apply_to = variable(is_local)) #pragma clang attribute pop -#pragma clang attribute push([[clang::uninitialized]], apply_to = function) // expected-error {{attribute 'uninitialized' can't be applied to 'function'}} +#pragma clang attribute push([[clang::uninitialized]], apply_to = function) // expected-error {{attribute 'uninitialized' cannot be applied to 'function'}} #pragma clang attribute pop -#pragma clang attribute push([[clang::uninitialized]], apply_to = variable) // expected-error {{attribute 'uninitialized' can't be applied to 'variable'}} +#pragma clang attribute push([[clang::uninitialized]], apply_to = variable) // expected-error {{attribute 'uninitialized' cannot be applied to 'variable'}} #pragma clang attribute pop -#pragma clang attribute push([[clang::uninitialized]], apply_to = variable(is_thread_local)) // expected-error {{attribute 'uninitialized' can't be applied to 'variable(is_thread_local)'}} +#pragma clang attribute push([[clang::uninitialized]], apply_to = variable(is_thread_local)) // expected-error {{attribute 'uninitialized' cannot be applied to 'variable(is_thread_local)'}} #pragma clang attribute pop -#pragma clang attribute push([[clang::uninitialized]], apply_to = variable(is_global)) // expected-error {{attribute 'uninitialized' can't be applied to 'variable(is_global)'}} +#pragma clang attribute push([[clang::uninitialized]], apply_to = variable(is_global)) // expected-error {{attribute 'uninitialized' cannot be applied to 'variable(is_global)'}} #pragma clang attribute pop -#pragma clang attribute push([[clang::uninitialized]], apply_to = any(variable(is_parameter), variable(unless(is_parameter)))) // expected-error {{attribute 'uninitialized' can't be applied to 'variable(is_parameter)', and 'variable(unless(is_parameter))'}} +#pragma clang attribute push([[clang::uninitialized]], apply_to = any(variable(is_parameter), variable(unless(is_parameter)))) // expected-error {{attribute 'uninitialized' cannot be applied to 'variable(is_parameter)', and 'variable(unless(is_parameter))'}} #pragma clang attribute pop // We're allowed to apply attributes to subsets of allowed subjects. #pragma clang attribute push([[clang::no_destroy]], apply_to = variable) diff --git a/clang/test/ParserHLSL/hlsl_resource_handle_attrs.hlsl b/clang/test/ParserHLSL/hlsl_resource_handle_attrs.hlsl index 38d27bc21e4aa..c20515716ae6b 100644 --- a/clang/test/ParserHLSL/hlsl_resource_handle_attrs.hlsl +++ b/clang/test/ParserHLSL/hlsl_resource_handle_attrs.hlsl @@ -3,7 +3,7 @@ // CHECK: -ClassTemplateSpecializationDecl 0x{{[0-9a-f]+}} <> class RWBuffer definition implicit_instantiation // CHECK: -TemplateArgument type 'float' // CHECK: `-BuiltinType 0x{{[0-9a-f]+}} 'float' -// CHECK: -FieldDecl 0x{{[0-9a-f]+}} <> implicit h '__hlsl_resource_t +// CHECK: -FieldDecl 0x{{[0-9a-f]+}} <> implicit __handle '__hlsl_resource_t // CHECK-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] // CHECK-SAME{LITERAL}: [[hlsl::contained_type(float)]] // CHECK: -HLSLResourceAttr 0x{{[0-9a-f]+}} <> Implicit TypedBuffer @@ -13,7 +13,7 @@ RWBuffer Buffer1; // CHECK: -TemplateArgument type 'vector' // CHECK: `-ExtVectorType 0x{{[0-9a-f]+}} 'vector' 4 // CHECK: `-BuiltinType 0x{{[0-9a-f]+}} 'float' -// CHECK: -FieldDecl 0x{{[0-9a-f]+}} <> implicit h '__hlsl_resource_t +// CHECK: -FieldDecl 0x{{[0-9a-f]+}} <> implicit __handle '__hlsl_resource_t // CHECK-SAME{LITERAL}: [[hlsl::resource_class(UAV)] // CHECK-SAME{LITERAL}: [[hlsl::is_rov]] // CHECK-SAME{LITERAL}: [[hlsl::contained_type(vector)]] diff --git a/clang/test/Preprocessor/ptrauth_feature.c b/clang/test/Preprocessor/ptrauth_feature.c index 2a3edc23f4753..a440791d6cc69 100644 --- a/clang/test/Preprocessor/ptrauth_feature.c +++ b/clang/test/Preprocessor/ptrauth_feature.c @@ -2,34 +2,37 @@ //// For example, -fptrauth-init-fini will not affect codegen without -fptrauth-calls, but the preprocessor feature would be set anyway. // RUN: %clang_cc1 -E %s -triple=aarch64 -fptrauth-intrinsics | \ -// RUN: FileCheck %s --check-prefixes=INTRIN,NOCALLS,NORETS,NOVPTR_ADDR_DISCR,NOVPTR_TYPE_DISCR,NOTYPE_INFO_DISCR,NOFUNC,NOINITFINI,NOINITFINI_ADDR_DISCR,NOGOTOS +// RUN: FileCheck %s --check-prefixes=INTRIN,NOCALLS,NORETS,NOVPTR_ADDR_DISCR,NOVPTR_TYPE_DISCR,NOTYPE_INFO_DISCR,NOFUNC,NOINITFINI,NOINITFINI_ADDR_DISCR,NOGOTOS,NOELFGOT // RUN: %clang_cc1 -E %s -triple=aarch64 -fptrauth-calls | \ -// RUN: FileCheck %s --check-prefixes=NOINTRIN,CALLS,NORETS,NOVPTR_ADDR_DISCR,NOVPTR_TYPE_DISCR,NOTYPE_INFO_DISCR,NOFUNC,NOINITFINI,NOINITFINI_ADDR_DISCR,NOGOTOS +// RUN: FileCheck %s --check-prefixes=NOINTRIN,CALLS,NORETS,NOVPTR_ADDR_DISCR,NOVPTR_TYPE_DISCR,NOTYPE_INFO_DISCR,NOFUNC,NOINITFINI,NOINITFINI_ADDR_DISCR,NOGOTOS,NOELFGOT // RUN: %clang_cc1 -E %s -triple=aarch64 -fptrauth-returns | \ -// RUN: FileCheck %s --check-prefixes=NOINTRIN,NOCALLS,RETS,NOVPTR_ADDR_DISCR,NOVPTR_TYPE_DISCR,NOTYPE_INFO_DISCR,NOFUNC,NOINITFINI,NOINITFINI_ADDR_DISCR,NOGOTOS +// RUN: FileCheck %s --check-prefixes=NOINTRIN,NOCALLS,RETS,NOVPTR_ADDR_DISCR,NOVPTR_TYPE_DISCR,NOTYPE_INFO_DISCR,NOFUNC,NOINITFINI,NOINITFINI_ADDR_DISCR,NOGOTOS,NOELFGOT // RUN: %clang_cc1 -E %s -triple=aarch64 -fptrauth-vtable-pointer-address-discrimination | \ -// RUN: FileCheck %s --check-prefixes=NOINTRIN,NOCALLS,NORETS,VPTR_ADDR_DISCR,NOVPTR_TYPE_DISCR,NOTYPE_INFO_DISCR,NOFUNC,NOINITFINI,NOINITFINI_ADDR_DISCR,NOGOTOS +// RUN: FileCheck %s --check-prefixes=NOINTRIN,NOCALLS,NORETS,VPTR_ADDR_DISCR,NOVPTR_TYPE_DISCR,NOTYPE_INFO_DISCR,NOFUNC,NOINITFINI,NOINITFINI_ADDR_DISCR,NOGOTOS,NOELFGOT // RUN: %clang_cc1 -E %s -triple=aarch64 -fptrauth-vtable-pointer-type-discrimination | \ -// RUN: FileCheck %s --check-prefixes=NOINTRIN,NOCALLS,NORETS,NOVPTR_ADDR_DISCR,VPTR_TYPE_DISCR,NOTYPE_INFO_DISCR,NOFUNC,NOINITFINI,NOINITFINI_ADDR_DISCR,NOGOTOS +// RUN: FileCheck %s --check-prefixes=NOINTRIN,NOCALLS,NORETS,NOVPTR_ADDR_DISCR,VPTR_TYPE_DISCR,NOTYPE_INFO_DISCR,NOFUNC,NOINITFINI,NOINITFINI_ADDR_DISCR,NOGOTOS,NOELFGOT // RUN: %clang_cc1 -E %s -triple=aarch64 -fptrauth-type-info-vtable-pointer-discrimination | \ -// RUN: FileCheck %s --check-prefixes=NOINTRIN,NOCALLS,NORETS,NOVPTR_ADDR_DISCR,NOVPTR_TYPE_DISCR,TYPE_INFO_DISCR,NOFUNC,NOINITFINI,NOINITFINI_ADDR_DISCR,NOGOTOS +// RUN: FileCheck %s --check-prefixes=NOINTRIN,NOCALLS,NORETS,NOVPTR_ADDR_DISCR,NOVPTR_TYPE_DISCR,TYPE_INFO_DISCR,NOFUNC,NOINITFINI,NOINITFINI_ADDR_DISCR,NOGOTOS,NOELFGOT // RUN: %clang_cc1 -E %s -triple=aarch64 -fptrauth-function-pointer-type-discrimination | \ -// RUN: FileCheck %s --check-prefixes=NOINTRIN,NOCALLS,NORETS,NOVPTR_ADDR_DISCR,NOVPTR_TYPE_DISCR,NOTYPE_INFO_DISCR,FUNC,NOINITFINI,NOINITFINI_ADDR_DISCR,NOGOTOS +// RUN: FileCheck %s --check-prefixes=NOINTRIN,NOCALLS,NORETS,NOVPTR_ADDR_DISCR,NOVPTR_TYPE_DISCR,NOTYPE_INFO_DISCR,FUNC,NOINITFINI,NOINITFINI_ADDR_DISCR,NOGOTOS,NOELFGOT // RUN: %clang_cc1 -E %s -triple=aarch64 -fptrauth-init-fini | \ -// RUN: FileCheck %s --check-prefixes=NOINTRIN,NOCALLS,NORETS,NOVPTR_ADDR_DISCR,NOVPTR_TYPE_DISCR,NOTYPE_INFO_DISCR,NOFUNC,INITFINI,NOINITFINI_ADDR_DISCR,NOGOTOS +// RUN: FileCheck %s --check-prefixes=NOINTRIN,NOCALLS,NORETS,NOVPTR_ADDR_DISCR,NOVPTR_TYPE_DISCR,NOTYPE_INFO_DISCR,NOFUNC,INITFINI,NOINITFINI_ADDR_DISCR,NOGOTOS,NOELFGOT // RUN: %clang_cc1 -E %s -triple=aarch64 -fptrauth-init-fini-address-discrimination | \ -// RUN: FileCheck %s --check-prefixes=NOINTRIN,NOCALLS,NORETS,NOVPTR_ADDR_DISCR,NOVPTR_TYPE_DISCR,NOTYPE_INFO_DISCR,NOFUNC,NOINITFINI,INITFINI_ADDR_DISCR,NOGOTOS +// RUN: FileCheck %s --check-prefixes=NOINTRIN,NOCALLS,NORETS,NOVPTR_ADDR_DISCR,NOVPTR_TYPE_DISCR,NOTYPE_INFO_DISCR,NOFUNC,NOINITFINI,INITFINI_ADDR_DISCR,NOGOTOS,NOELFGOT // RUN: %clang_cc1 -E %s -triple=aarch64 -fptrauth-indirect-gotos | \ -// RUN: FileCheck %s --check-prefixes=NOINTRIN,NOCALLS,NORETS,NOVPTR_ADDR_DISCR,NOVPTR_TYPE_DISCR,NOTYPE_INFO_DISCR,NOFUNC,NOINITFINI,NOINITFINI_ADDR_DISCR,GOTOS +// RUN: FileCheck %s --check-prefixes=NOINTRIN,NOCALLS,NORETS,NOVPTR_ADDR_DISCR,NOVPTR_TYPE_DISCR,NOTYPE_INFO_DISCR,NOFUNC,NOINITFINI,NOINITFINI_ADDR_DISCR,GOTOS,NOELFGOT + +// RUN: %clang_cc1 -E %s -triple=aarch64 -fptrauth-elf-got | \ +// RUN: FileCheck %s --check-prefixes=NOINTRIN,NOCALLS,NORETS,NOVPTR_ADDR_DISCR,NOVPTR_TYPE_DISCR,NOTYPE_INFO_DISCR,NOFUNC,NOINITFINI,NOINITFINI_ADDR_DISCR,NOGOTOS,ELFGOT #if __has_feature(ptrauth_intrinsics) // INTRIN: has_ptrauth_intrinsics @@ -119,3 +122,11 @@ void has_ptrauth_indirect_gotos() {} // NOGOTOS: no_ptrauth_indirect_gotos void no_ptrauth_indirect_gotos() {} #endif + +#if __has_feature(ptrauth_elf_got) +// ELFGOT: has_ptrauth_elf_got +void has_ptrauth_elf_got() {} +#else +// NOELFGOT: no_ptrauth_elf_got +void no_ptrauth_elf_got() {} +#endif diff --git a/clang/test/Refactor/Extract/ObjCProperty.m b/clang/test/Refactor/Extract/ObjCProperty.m index 152ccb3484215..23c9a8941b7ab 100644 --- a/clang/test/Refactor/Extract/ObjCProperty.m +++ b/clang/test/Refactor/Extract/ObjCProperty.m @@ -36,6 +36,6 @@ - (void)prohibitSetterExtraction { /*range prohibit_setter=->+0:55*/self.implicitSetter = 0; } // CHECK: 2 'prohibit_setter' results: -// CHECK: the selected expression can't be extracted +// CHECK: the selected expression cannot be extracted @end diff --git a/clang/test/Sema/Inputs/lifetime-analysis.h b/clang/test/Sema/Inputs/lifetime-analysis.h new file mode 100644 index 0000000000000..41d1e2f074cc8 --- /dev/null +++ b/clang/test/Sema/Inputs/lifetime-analysis.h @@ -0,0 +1,138 @@ + +namespace __gnu_cxx { +template +struct basic_iterator { + basic_iterator operator++(); + T& operator*() const; + T* operator->() const; +}; + +template +bool operator!=(basic_iterator, basic_iterator); +} + +namespace std { +template struct remove_reference { typedef T type; }; +template struct remove_reference { typedef T type; }; +template struct remove_reference { typedef T type; }; + +template +typename remove_reference::type &&move(T &&t) noexcept; + +template +auto data(const C &c) -> decltype(c.data()); + +template +auto begin(C &c) -> decltype(c.begin()); + +template +T *begin(T (&array)[N]); + +using size_t = decltype(sizeof(0)); + +template +struct initializer_list { + const T* ptr; size_t sz; +}; +template class allocator {}; +template > +struct vector { + typedef __gnu_cxx::basic_iterator iterator; + iterator begin(); + iterator end(); + const T *data() const; + vector(); + vector(initializer_list __l, + const Alloc& alloc = Alloc()); + + template + vector(InputIterator first, InputIterator __last); + + T &at(int n); +}; + +template +struct basic_string_view { + basic_string_view(); + basic_string_view(const T *); + const T *begin() const; +}; +using string_view = basic_string_view; + +template struct iter { + iter& operator-=(int); + + iter operator-(int _Off) const { + iter _Tmp = *this; + return _Tmp -= _Off; + } +}; + +template +struct basic_string { + basic_string(); + basic_string(const T *); + const T *c_str() const; + operator basic_string_view () const; + using const_iterator = iter; +}; +using string = basic_string; + +template +struct unique_ptr { + T &operator*(); + T *get() const; +}; + +template +struct optional { + optional(); + optional(const T&); + + template + optional(U&& t); + + template + optional(optional&& __t); + + T &operator*() &; + T &&operator*() &&; + T &value() &; + T &&value() &&; +}; +template +optional<__decay(T)> make_optional(T&&); + + +template +struct stack { + T &top(); +}; + +struct any {}; + +template +T any_cast(const any& operand); + +template +struct reference_wrapper { + template + reference_wrapper(U &&); +}; + +template +reference_wrapper ref(T& t) noexcept; + +struct false_type { + static constexpr bool value = false; + constexpr operator bool() const noexcept { return value; } +}; +struct true_type { + static constexpr bool value = true; + constexpr operator bool() const noexcept { return value; } +}; + +template struct is_pointer : false_type {}; +template struct is_pointer : true_type {}; +template struct is_pointer : true_type {}; +} diff --git a/clang/test/Sema/asm.c b/clang/test/Sema/asm.c index 28ef3ec6ce09c..a9cff5947ef5d 100644 --- a/clang/test/Sema/asm.c +++ b/clang/test/Sema/asm.c @@ -90,7 +90,7 @@ int test7(unsigned long long b) { // PR3904 void test8(int i) { - // A number in an input constraint can't point to a read-write constraint. + // A number in an input constraint cannot point to a read-write constraint. asm("" : "+r" (i), "=r"(i) : "0" (i)); // expected-error{{invalid input constraint '0' in asm}} } @@ -359,7 +359,7 @@ void test19(long long x) asm ("" : "=rm" (x): "0" (a)); // expected-error {{unsupported inline asm: input with type 'st_size64' (aka 'struct _st_size64') matching output with type 'long long'}} // FIXME: This case is actually supported by codegen. asm ("" : "=rm" (a): "0" (d)); // expected-error {{unsupported inline asm: input with type 'st_size32' (aka 'struct _st_size32') matching output with type 'st_size64' (aka 'struct _st_size64')}} - asm ("" : "=rm" (b): "0" (1)); // expected-error {{impossible constraint in asm: can't store value into a register}} + asm ("" : "=rm" (b): "0" (1)); // expected-error {{impossible constraint in asm: cannot store value into a register}} // FIXME: This case should be supported by codegen, but it fails now. asm ("" : "=rm" (e): "0" (1)); // no-error // FIXME: This case should be supported by codegen, but it fails now. diff --git a/clang/test/Sema/attr-nonblocking-constraints.cpp b/clang/test/Sema/attr-nonblocking-constraints.cpp index cc9108c0a4fbd..bbc909f627f4c 100644 --- a/clang/test/Sema/attr-nonblocking-constraints.cpp +++ b/clang/test/Sema/attr-nonblocking-constraints.cpp @@ -144,6 +144,41 @@ void nb9() [[clang::nonblocking]] expected-note {{in template expansion here}} } +// Make sure we verify lambdas produced from template expansions. +struct HasTemplatedLambda { + void (*fptr)() [[clang::nonblocking]]; + + template + HasTemplatedLambda(const C&) + : fptr{ []() [[clang::nonblocking]] { + auto* y = new int; // expected-warning {{lambda with 'nonblocking' attribute must not allocate or deallocate memory}} + } } + {} +}; + +void nb9a() +{ + HasTemplatedLambda bad(42); +} + +// Templated function and lambda. +template +void TemplatedFunc(T x) [[clang::nonblocking]] { + auto* ptr = new T; // expected-warning {{function with 'nonblocking' attribute must not allocate or deallocate memory}} +} + +void nb9b() [[clang::nonblocking]] { + TemplatedFunc(42); // expected-note {{in template expansion here}} + + auto foo = [](auto x) [[clang::nonblocking]] { + auto* ptr = new int; // expected-warning {{lambda with 'nonblocking' attribute must not allocate or deallocate memory}} + return x; + }; + + // Note that foo() won't be validated unless instantiated. + foo(42); +} + void nb10( void (*fp1)(), // expected-note {{function pointer cannot be inferred 'nonblocking'}} void (*fp2)() [[clang::nonblocking]] diff --git a/clang/test/Sema/c2x-nodiscard.c b/clang/test/Sema/c2x-nodiscard.c index f8b0567366465..e2537bcf1d29d 100644 --- a/clang/test/Sema/c2x-nodiscard.c +++ b/clang/test/Sema/c2x-nodiscard.c @@ -31,10 +31,10 @@ enum E2 get_e(void); [[nodiscard]] int get_i(void); void f2(void) { - get_s(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} - get_s3(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute: Wrong}} + get_s(); // expected-warning {{ignoring return value of type 'S4' declared with 'nodiscard' attribute}} + get_s3(); // expected-warning {{ignoring return value of type 'S3' declared with 'nodiscard' attribute: Wrong}} get_i(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} - get_e(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + get_e(); // expected-warning {{ignoring return value of type 'E2' declared with 'nodiscard' attribute}} // Okay, warnings are not encouraged (void)get_s(); @@ -50,7 +50,7 @@ struct [[nodiscard]] error_info{ struct error_info enable_missile_safety_mode(void); void launch_missiles(void); void test_missiles(void) { - enable_missile_safety_mode(); // expected-warning {{ignoring return value of function declared with 'nodiscard'}} + enable_missile_safety_mode(); // expected-warning {{ignoring return value of type 'error_info' declared with 'nodiscard'}} launch_missiles(); } diff --git a/clang/test/Sema/constant_builtins_vector.cpp b/clang/test/Sema/constant_builtins_vector.cpp index d15c587cfffc4..7063c290479f6 100644 --- a/clang/test/Sema/constant_builtins_vector.cpp +++ b/clang/test/Sema/constant_builtins_vector.cpp @@ -745,3 +745,35 @@ constexpr long long reduceAddLong2 = __builtin_reduce_add((vector4long){(1LL << // expected-note@-1 {{outside the range of representable values of type 'long long'}} static_assert(__builtin_reduce_add((vector4uint){~0U, 0, 0, 1}) == 0); static_assert(__builtin_reduce_add((vector4ulong){~0ULL, 0, 0, 1}) == 0); + +static_assert(__builtin_reduce_mul((vector4char){}) == 0); +static_assert(__builtin_reduce_mul((vector4char){1, 2, 3, 4}) == 24); +static_assert(__builtin_reduce_mul((vector4short){1, 2, 30, 40}) == 2400); +static_assert(__builtin_reduce_mul((vector4int){10, 20, 300, 400}) == 24000000); +static_assert(__builtin_reduce_mul((vector4long){1000L, 2000L, 3000L, 4000L}) == 24000000000000L); +constexpr int reduceMulInt1 = __builtin_reduce_mul((vector4int){~(1 << 31), 1, 1, 2}); +// expected-error@-1 {{must be initialized by a constant expression}} \ +// expected-note@-1 {{outside the range of representable values of type 'int'}} +constexpr long long reduceMulLong1 = __builtin_reduce_mul((vector4long){~(1LL << 63), 1, 1, 2}); +// expected-error@-1 {{must be initialized by a constant expression}} \ +// expected-note@-1 {{outside the range of representable values of type 'long long'}} +constexpr int reduceMulInt2 = __builtin_reduce_mul((vector4int){(1 << 31), 1, 1, 2}); +// expected-error@-1 {{must be initialized by a constant expression}} \ +// expected-note@-1 {{outside the range of representable values of type 'int'}} +constexpr long long reduceMulLong2 = __builtin_reduce_mul((vector4long){(1LL << 63), 1, 1, 2}); +// expected-error@-1 {{must be initialized by a constant expression}} \ +// expected-note@-1 {{outside the range of representable values of type 'long long'}} +static_assert(__builtin_reduce_mul((vector4uint){~0U, 1, 1, 2}) == ~0U - 1); +static_assert(__builtin_reduce_mul((vector4ulong){~0ULL, 1, 1, 2}) == ~0ULL - 1); + +static_assert(__builtin_reduce_and((vector4char){}) == 0); +static_assert(__builtin_reduce_and((vector4char){(char)0x11, (char)0x22, (char)0x44, (char)0x88}) == 0); +static_assert(__builtin_reduce_and((vector4short){(short)0x1111, (short)0x2222, (short)0x4444, (short)0x8888}) == 0); +static_assert(__builtin_reduce_and((vector4int){(int)0x11111111, (int)0x22222222, (int)0x44444444, (int)0x88888888}) == 0); +static_assert(__builtin_reduce_and((vector4long){(long long)0x1111111111111111L, (long long)0x2222222222222222L, (long long)0x4444444444444444L, (long long)0x8888888888888888L}) == 0L); +static_assert(__builtin_reduce_and((vector4char){(char)-1, (char)~0x22, (char)~0x44, (char)~0x88}) == 0x11); +static_assert(__builtin_reduce_and((vector4short){(short)~0x1111, (short)-1, (short)~0x4444, (short)~0x8888}) == 0x2222); +static_assert(__builtin_reduce_and((vector4int){(int)~0x11111111, (int)~0x22222222, (int)-1, (int)~0x88888888}) == 0x44444444); +static_assert(__builtin_reduce_and((vector4long){(long long)~0x1111111111111111L, (long long)~0x2222222222222222L, (long long)~0x4444444444444444L, (long long)-1}) == 0x8888888888888888L); +static_assert(__builtin_reduce_and((vector4uint){0x11111111U, 0x22222222U, 0x44444444U, 0x88888888U}) == 0U); +static_assert(__builtin_reduce_and((vector4ulong){0x1111111111111111UL, 0x2222222222222222UL, 0x4444444444444444UL, 0x8888888888888888UL}) == 0L); diff --git a/clang/test/Sema/pragma-attribute-strict-subjects.c b/clang/test/Sema/pragma-attribute-strict-subjects.c index 4f37c271ce34a..85b484799529a 100644 --- a/clang/test/Sema/pragma-attribute-strict-subjects.c +++ b/clang/test/Sema/pragma-attribute-strict-subjects.c @@ -52,16 +52,16 @@ #pragma clang attribute pop #pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(function, record(unless(is_union)), variable, enum)) -// expected-error@-1 {{attribute 'abi_tag' can't be applied to 'enum'}} +// expected-error@-1 {{attribute 'abi_tag' cannot be applied to 'enum'}} #pragma clang attribute pop #pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(enum_constant, function, record(unless(is_union)), variable, variable(is_parameter), enum)) // FIXME: comma in this diagnostic is wrong. -// expected-error@-2 {{attribute 'abi_tag' can't be applied to 'enum_constant', and 'enum'}} +// expected-error@-2 {{attribute 'abi_tag' cannot be applied to 'enum_constant', and 'enum'}} #pragma clang attribute pop #pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(function, record(unless(is_union)), enum)) -// expected-error@-1 {{attribute 'abi_tag' can't be applied to 'enum'}} +// expected-error@-1 {{attribute 'abi_tag' cannot be applied to 'enum'}} #pragma clang attribute pop // Verify the non-strict subject set verification. @@ -96,12 +96,12 @@ #pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(record(unless(is_union)), function, variable, enum, enum_constant)) -// expected-error@-1 {{attribute 'abi_tag' can't be applied to 'enum_constant', and 'enum'}} +// expected-error@-1 {{attribute 'abi_tag' cannot be applied to 'enum_constant', and 'enum'}} #pragma clang attribute pop #pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = enum) -// expected-error@-1 {{attribute 'abi_tag' can't be applied to 'enum'}} +// expected-error@-1 {{attribute 'abi_tag' cannot be applied to 'enum'}} #pragma clang attribute pop @@ -124,21 +124,21 @@ #pragma clang attribute pop #pragma clang attribute push(__attribute__((objc_subclassing_restricted)), apply_to = any(objc_interface, objc_protocol)) -// expected-error@-1 {{attribute 'objc_subclassing_restricted' can't be applied to 'objc_protocol'}} +// expected-error@-1 {{attribute 'objc_subclassing_restricted' cannot be applied to 'objc_protocol'}} #pragma clang attribute pop #pragma clang attribute push(__attribute__((objc_subclassing_restricted)), apply_to = any(objc_protocol)) -// expected-error@-1 {{attribute 'objc_subclassing_restricted' can't be applied to 'objc_protocol'}} +// expected-error@-1 {{attribute 'objc_subclassing_restricted' cannot be applied to 'objc_protocol'}} // Don't report an error about missing 'objc_interface' as we aren't parsing // Objective-C. #pragma clang attribute pop #pragma clang attribute push(__attribute__((objc_subclassing_restricted)), apply_to = any(objc_interface, objc_protocol)) -// expected-error@-1 {{attribute 'objc_subclassing_restricted' can't be applied to 'objc_protocol'}} +// expected-error@-1 {{attribute 'objc_subclassing_restricted' cannot be applied to 'objc_protocol'}} #pragma clang attribute pop #pragma clang attribute push(__attribute__((objc_subclassing_restricted)), apply_to = any(objc_protocol)) -// expected-error@-1 {{attribute 'objc_subclassing_restricted' can't be applied to 'objc_protocol'}} +// expected-error@-1 {{attribute 'objc_subclassing_restricted' cannot be applied to 'objc_protocol'}} // Don't report an error about missing 'objc_interface' as we aren't parsing // Objective-C. #pragma clang attribute pop diff --git a/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp b/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp new file mode 100644 index 0000000000000..b3fde386b8616 --- /dev/null +++ b/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp @@ -0,0 +1,368 @@ +// RUN: %clang_cc1 --std=c++20 -fsyntax-only -verify -Wdangling-capture %s + +#include "Inputs/lifetime-analysis.h" + +// **************************************************************************** +// Capture an integer +// **************************************************************************** +namespace capture_int { +struct X {} x; +void captureInt(const int &i [[clang::lifetime_capture_by(x)]], X &x); +void captureRValInt(int &&i [[clang::lifetime_capture_by(x)]], X &x); +void noCaptureInt(int i [[clang::lifetime_capture_by(x)]], X &x); + +void use() { + int local; + captureInt(1, // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} + x); + captureRValInt(1, x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} + captureInt(local, x); + noCaptureInt(1, x); + noCaptureInt(local, x); +} +} // namespace capture_int + +// **************************************************************************** +// Capture std::string (gsl owner types) +// **************************************************************************** +namespace capture_string { +struct X {} x; +void captureString(const std::string &s [[clang::lifetime_capture_by(x)]], X &x); +void captureRValString(std::string &&s [[clang::lifetime_capture_by(x)]], X &x); + +void use() { + std::string local_string; + captureString(std::string(), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} + captureString(local_string, x); + captureRValString(std::move(local_string), x); + captureRValString(std::string(), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} +} +} // namespace capture_string + +// **************************************************************************** +// Capture std::string_view (gsl pointer types) +// **************************************************************************** +namespace capture_string_view { +struct X {} x; +void captureStringView(std::string_view s [[clang::lifetime_capture_by(x)]], X &x); +void captureRValStringView(std::string_view &&sv [[clang::lifetime_capture_by(x)]], X &x); +void noCaptureStringView(std::string_view sv, X &x); + +std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]); +std::string_view getNotLifetimeBoundView(const std::string& s); +const std::string& getLifetimeBoundString(const std::string &s [[clang::lifetimebound]]); +const std::string& getLifetimeBoundString(std::string_view sv [[clang::lifetimebound]]); + +void use() { + std::string_view local_string_view; + std::string local_string; + captureStringView(local_string_view, x); + captureStringView(std::string(), // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} + x); + + captureStringView(getLifetimeBoundView(local_string), x); + captureStringView(getNotLifetimeBoundView(std::string()), x); + captureRValStringView(std::move(local_string_view), x); + captureRValStringView(std::string(), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} + captureRValStringView(std::string_view{"abcd"}, x); + + noCaptureStringView(local_string_view, x); + noCaptureStringView(std::string(), x); + + // With lifetimebound functions. + captureStringView(getLifetimeBoundView( + std::string() // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} + ), x); + captureRValStringView(getLifetimeBoundView(local_string), x); + captureRValStringView(getLifetimeBoundView(std::string()), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} + captureRValStringView(getNotLifetimeBoundView(std::string()), x); + noCaptureStringView(getLifetimeBoundView(std::string()), x); + captureStringView(getLifetimeBoundString(std::string()), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} + captureStringView(getLifetimeBoundString(getLifetimeBoundView(std::string())), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} + captureStringView(getLifetimeBoundString(getLifetimeBoundString( + std::string() // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} + )), x); +} +} // namespace capture_string_view + +// **************************************************************************** +// Capture pointer (eg: std::string*) +// **************************************************************************** +const std::string* getLifetimeBoundPointer(const std::string &s [[clang::lifetimebound]]); +const std::string* getNotLifetimeBoundPointer(const std::string &s); + +namespace capture_pointer { +struct X {} x; +void capturePointer(const std::string* sp [[clang::lifetime_capture_by(x)]], X &x); +void use() { + capturePointer(getLifetimeBoundPointer(std::string()), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} + capturePointer(getLifetimeBoundPointer(*getLifetimeBoundPointer( + std::string() // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} + )), x); + capturePointer(getNotLifetimeBoundPointer(std::string()), x); + +} +} // namespace capture_pointer + +// **************************************************************************** +// Arrays and initializer lists. +// **************************************************************************** +namespace init_lists { +struct X {} x; +void captureVector(const std::vector &a [[clang::lifetime_capture_by(x)]], X &x); +void captureArray(int array [[clang::lifetime_capture_by(x)]] [2], X &x); +void captureInitList(std::initializer_list abc [[clang::lifetime_capture_by(x)]], X &x); + + +std::initializer_list getLifetimeBoundInitList(std::initializer_list abc [[clang::lifetimebound]]); + +void use() { + captureVector({1, 2, 3}, x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} + captureVector(std::vector{}, x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} + std::vector local_vector; + captureVector(local_vector, x); + int local_array[2]; + captureArray(local_array, x); + captureInitList({1, 2}, x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} + captureInitList(getLifetimeBoundInitList({1, 2}), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} +} +} // namespace init_lists + +// **************************************************************************** +// Implicit object param 'this' is captured +// **************************************************************************** +namespace this_is_captured { +struct X {} x; +struct S { + void capture(X &x) [[clang::lifetime_capture_by(x)]]; +}; +void use() { + S{}.capture(x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} + S s; + s.capture(x); +} +} // namespace this_is_captured + +// **************************************************************************** +// Capture by Global and Unknown. +// **************************************************************************** +namespace capture_by_global_unknown { +void captureByGlobal(std::string_view s [[clang::lifetime_capture_by(global)]]); +void captureByUnknown(std::string_view s [[clang::lifetime_capture_by(unknown)]]); + +std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]); + +void use() { + std::string_view local_string_view; + std::string local_string; + // capture by global. + captureByGlobal(std::string()); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}} + captureByGlobal(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}} + captureByGlobal(local_string); + captureByGlobal(local_string_view); + + // capture by unknown. + captureByUnknown(std::string()); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}} + captureByUnknown(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}} + captureByUnknown(local_string); + captureByUnknown(local_string_view); +} +} // namespace capture_by_global_unknown + +// **************************************************************************** +// Member functions: Capture by 'this' +// **************************************************************************** +namespace capture_by_this { +struct S { + void captureInt(const int& x [[clang::lifetime_capture_by(this)]]); + void captureView(std::string_view sv [[clang::lifetime_capture_by(this)]]); +}; +std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]); +std::string_view getNotLifetimeBoundView(const std::string& s); +const std::string& getLifetimeBoundString(const std::string &s [[clang::lifetimebound]]); + +void use() { + S s; + s.captureInt(1); // expected-warning {{object whose reference is captured by 's' will be destroyed at the end of the full-expression}} + s.captureView(std::string()); // expected-warning {{object whose reference is captured by 's' will be destroyed at the end of the full-expression}} + s.captureView(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured by 's' will be destroyed at the end of the full-expression}} + s.captureView(getLifetimeBoundString(std::string())); // expected-warning {{object whose reference is captured by 's' will be destroyed at the end of the full-expression}} + s.captureView(getNotLifetimeBoundView(std::string())); +} +} // namespace capture_by_this + +// **************************************************************************** +// Struct with field as a reference +// **************************************************************************** +namespace reference_field { +struct X {} x; +struct Foo { + const int& b; +}; +void captureField(Foo param [[clang::lifetime_capture_by(x)]], X &x); +void use() { + captureField(Foo{ + 1 // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} + }, x); + int local; + captureField(Foo{local}, x); +} +} // namespace reference_field + +// **************************************************************************** +// Capture default argument. +// **************************************************************************** +namespace default_arg { +struct X {} x; +void captureDefaultArg(X &x, std::string_view s [[clang::lifetime_capture_by(x)]] = std::string()); + +std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]); + +void useCaptureDefaultArg() { + X x; + captureDefaultArg(x); // FIXME: Diagnose temporary default arg. + captureDefaultArg(x, std::string("temp")); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} + captureDefaultArg(x, getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} + std::string local; + captureDefaultArg(x, local); +} +} // namespace default_arg + +// **************************************************************************** +// Container: *No* distinction between pointer-like and other element type +// **************************************************************************** +namespace containers_no_distinction { +template +struct MySet { + void insert(T&& t [[clang::lifetime_capture_by(this)]]); + void insert(const T& t [[clang::lifetime_capture_by(this)]]); +}; +void user_defined_containers() { + MySet set_of_int; + set_of_int.insert(1); // expected-warning {{object whose reference is captured by 'set_of_int' will be destroyed at the end of the full-expression}} + MySet set_of_sv; + set_of_sv.insert(std::string()); // expected-warning {{object whose reference is captured by 'set_of_sv' will be destroyed at the end of the full-expression}} + set_of_sv.insert(std::string_view()); +} +} // namespace containers_no_distinction + +// **************************************************************************** +// Container: Different for pointer-like and other element type. +// **************************************************************************** +namespace conatiners_with_different { +template struct IsPointerLikeTypeImpl : std::false_type {}; +template<> struct IsPointerLikeTypeImpl : std::true_type {}; +template concept IsPointerLikeType = std::is_pointer::value || IsPointerLikeTypeImpl::value; + +template struct MyVector { + void push_back(T&& t [[clang::lifetime_capture_by(this)]]) requires IsPointerLikeType; + void push_back(const T& t [[clang::lifetime_capture_by(this)]]) requires IsPointerLikeType; + + void push_back(T&& t) requires (!IsPointerLikeType); + void push_back(const T& t) requires (!IsPointerLikeType); +}; + +std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]); + +void use_container() { + std::string local; + + MyVector vector_of_string; + vector_of_string.push_back(std::string()); // Ok. + + MyVector vector_of_view; + vector_of_view.push_back(std::string()); // expected-warning {{object whose reference is captured by 'vector_of_view' will be destroyed at the end of the full-expression}} + vector_of_view.push_back(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured by 'vector_of_view' will be destroyed at the end of the full-expression}} + + MyVector vector_of_pointer; + vector_of_pointer.push_back(getLifetimeBoundPointer(std::string())); // expected-warning {{object whose reference is captured by 'vector_of_pointer' will be destroyed at the end of the full-expression}} + vector_of_pointer.push_back(getLifetimeBoundPointer(*getLifetimeBoundPointer(std::string()))); // expected-warning {{object whose reference is captured by 'vector_of_pointer' will be destroyed at the end of the full-expression}} + vector_of_pointer.push_back(getLifetimeBoundPointer(local)); + vector_of_pointer.push_back(getNotLifetimeBoundPointer(std::string())); +} + +// **************************************************************************** +// Container: For user defined view types +// **************************************************************************** +struct [[gsl::Pointer()]] MyStringView : public std::string_view { + MyStringView(); + MyStringView(std::string_view&&); + MyStringView(const MyStringView&); + MyStringView(const std::string&); +}; +template<> struct IsPointerLikeTypeImpl : std::true_type {}; + +std::optional getOptionalSV(); +std::optional getOptionalS(); +std::optional getOptionalMySV(); +MyStringView getMySV(); + +class MyStringViewNotPointer : public std::string_view {}; +std::optional getOptionalMySVNotP(); +MyStringViewNotPointer getMySVNotP(); + +std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]); +std::string_view getNotLifetimeBoundView(const std::string& s); +const std::string& getLifetimeBoundString(const std::string &s [[clang::lifetimebound]]); +const std::string& getLifetimeBoundString(std::string_view sv [[clang::lifetimebound]]); + +void use_my_view() { + std::string local; + MyVector vector_of_my_view; + vector_of_my_view.push_back(getMySV()); + vector_of_my_view.push_back(MyStringView{}); + vector_of_my_view.push_back(std::string_view{}); + vector_of_my_view.push_back(std::string{}); // expected-warning {{object whose reference is captured by 'vector_of_my_view' will be destroyed at the end of the full-expression}} + vector_of_my_view.push_back(getLifetimeBoundView(std::string{})); // expected-warning {{object whose reference is captured by 'vector_of_my_view' will be destroyed at the end of the full-expression}} + vector_of_my_view.push_back(getLifetimeBoundString(getLifetimeBoundView(std::string{}))); // expected-warning {{object whose reference is captured by 'vector_of_my_view' will be destroyed at the end of the full-expression}} + vector_of_my_view.push_back(getNotLifetimeBoundView(getLifetimeBoundString(getLifetimeBoundView(std::string{})))); + + // Use with container of other view types. + MyVector vector_of_view; + vector_of_view.push_back(getMySV()); + vector_of_view.push_back(getMySVNotP()); +} + +// **************************************************************************** +// Container: Use with std::optional (owner types) +// **************************************************************************** +void use_with_optional_view() { + MyVector vector_of_view; + + std::optional optional_of_view; + vector_of_view.push_back(optional_of_view.value()); + vector_of_view.push_back(getOptionalS().value()); // expected-warning {{object whose reference is captured by 'vector_of_view' will be destroyed at the end of the full-expression}} + + vector_of_view.push_back(getOptionalSV().value()); + vector_of_view.push_back(getOptionalMySV().value()); + vector_of_view.push_back(getOptionalMySVNotP().value()); +} +} // namespace conatiners_with_different + +// **************************************************************************** +// Capture 'temporary' views +// **************************************************************************** +namespace temporary_views { +void capture1(std::string_view s [[clang::lifetime_capture_by(x)]], std::vector& x); + +// Intended to capture the "string_view" itself +void capture2(const std::string_view& s [[clang::lifetime_capture_by(x)]], std::vector& x); +// Intended to capture the pointee of the "string_view" +void capture3(const std::string_view& s [[clang::lifetime_capture_by(x)]], std::vector& x); + +void use() { + std::vector x1; + capture1(std::string(), x1); // expected-warning {{object whose reference is captured by 'x1' will be destroyed at the end of the full-expression}} + capture1(std::string_view(), x1); + + std::vector x2; + // Clang considers 'const std::string_view&' to refer to the owner + // 'std::string' and not 'std::string_view'. Therefore no diagnostic here. + capture2(std::string_view(), x2); + capture2(std::string(), x2); // expected-warning {{object whose reference is captured by 'x2' will be destroyed at the end of the full-expression}} + + std::vector x3; + capture3(std::string_view(), x3); + capture3(std::string(), x3); // expected-warning {{object whose reference is captured by 'x3' will be destroyed at the end of the full-expression}} +} +} // namespace temporary_views diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp index 6a2af01ea5116..c18ecd86ad06f 100644 --- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp +++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -Wdangling -Wdangling-field -Wreturn-stack-address -verify %s +#include "Inputs/lifetime-analysis.h" struct [[gsl::Owner(int)]] MyIntOwner { MyIntOwner(); int &operator*(); @@ -129,130 +130,6 @@ void initLocalGslPtrWithTempOwner() { global2 = MyLongOwnerWithConversion{}; // expected-warning {{object backing the pointer global2 }} } -namespace __gnu_cxx { -template -struct basic_iterator { - basic_iterator operator++(); - T& operator*() const; - T* operator->() const; -}; - -template -bool operator!=(basic_iterator, basic_iterator); -} - -namespace std { -template struct remove_reference { typedef T type; }; -template struct remove_reference { typedef T type; }; -template struct remove_reference { typedef T type; }; - -template -typename remove_reference::type &&move(T &&t) noexcept; - -template -auto data(const C &c) -> decltype(c.data()); - -template -auto begin(C &c) -> decltype(c.begin()); - -template -T *begin(T (&array)[N]); - -using size_t = decltype(sizeof(0)); - -template -struct initializer_list { - const T* ptr; size_t sz; -}; -template class allocator {}; -template > -struct vector { - typedef __gnu_cxx::basic_iterator iterator; - iterator begin(); - iterator end(); - const T *data() const; - vector(); - vector(initializer_list __l, - const Alloc& alloc = Alloc()); - - template - vector(InputIterator first, InputIterator __last); - - T &at(int n); -}; - -template -struct basic_string_view { - basic_string_view(); - basic_string_view(const T *); - const T *begin() const; -}; -using string_view = basic_string_view; - -template struct iter { - iter& operator-=(int); - - iter operator-(int _Off) const { - iter _Tmp = *this; - return _Tmp -= _Off; - } -}; - -template -struct basic_string { - basic_string(); - basic_string(const T *); - const T *c_str() const; - operator basic_string_view () const; - using const_iterator = iter; -}; -using string = basic_string; - -template -struct unique_ptr { - T &operator*(); - T *get() const; -}; - -template -struct optional { - optional(); - optional(const T&); - - template - optional(U&& t); - - template - optional(optional&& __t); - - T &operator*() &; - T &&operator*() &&; - T &value() &; - T &&value() &&; -}; -template -optional<__decay(T)> make_optional(T&&); - - -template -struct stack { - T &top(); -}; - -struct any {}; - -template -T any_cast(const any& operand); - -template -struct reference_wrapper { - template - reference_wrapper(U &&); -}; - -template -reference_wrapper ref(T& t) noexcept; -} struct Unannotated { typedef std::vector::iterator iterator; diff --git a/clang/test/SemaCUDA/spirv-attrs.cu b/clang/test/SemaCUDA/spirv-attrs.cu new file mode 100644 index 0000000000000..6539421423ee1 --- /dev/null +++ b/clang/test/SemaCUDA/spirv-attrs.cu @@ -0,0 +1,18 @@ +// expected-no-diagnostics + +// RUN: %clang_cc1 -triple spirv64 -aux-triple x86_64-unknown-linux-gnu \ +// RUN: -fcuda-is-device -verify -fsyntax-only %s + +#include "Inputs/cuda.h" + +__attribute__((reqd_work_group_size(128, 1, 1))) +__global__ void reqd_work_group_size_128_1_1() {} + +__attribute__((work_group_size_hint(2, 2, 2))) +__global__ void work_group_size_hint_2_2_2() {} + +__attribute__((vec_type_hint(int))) +__global__ void vec_type_hint_int() {} + +__attribute__((intel_reqd_sub_group_size(64))) +__global__ void intel_reqd_sub_group_size_64() {} diff --git a/clang/test/SemaCXX/attr-lifetimebound.cpp b/clang/test/SemaCXX/attr-lifetimebound.cpp index 81e9193cf76a0..f89b556f5bba0 100644 --- a/clang/test/SemaCXX/attr-lifetimebound.cpp +++ b/clang/test/SemaCXX/attr-lifetimebound.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -std=c++23 -verify %s namespace usage_invalid { - void void_return(int ¶m [[clang::lifetimebound]]); // expected-error {{'lifetimebound' attribute cannot be applied to a parameter of a function that returns void}} + void void_return(int ¶m [[clang::lifetimebound]]); // expected-error {{'lifetimebound' attribute cannot be applied to a parameter of a function that returns void; did you mean 'lifetime_capture_by(X)'}} int *not_class_member() [[clang::lifetimebound]]; // expected-error {{non-member function has no implicit object parameter}} struct A { @@ -11,7 +11,7 @@ namespace usage_invalid { int *explicit_object(this A&) [[clang::lifetimebound]]; // expected-error {{explicit object member function has no implicit object parameter}} int not_function [[clang::lifetimebound]]; // expected-error {{only applies to parameters and implicit object parameters}} int [[clang::lifetimebound]] also_not_function; // expected-error {{cannot be applied to types}} - void void_return_member() [[clang::lifetimebound]]; // expected-error {{'lifetimebound' attribute cannot be applied to an implicit object parameter of a function that returns void}} + void void_return_member() [[clang::lifetimebound]]; // expected-error {{'lifetimebound' attribute cannot be applied to an implicit object parameter of a function that returns void; did you mean 'lifetime_capture_by(X)'}} }; int *attr_with_param(int ¶m [[clang::lifetimebound(42)]]); // expected-error {{takes no arguments}} } diff --git a/clang/test/SemaCXX/builtin-bit-cast.cpp b/clang/test/SemaCXX/builtin-bit-cast.cpp index d7f24c7939b55..8717371b941b0 100644 --- a/clang/test/SemaCXX/builtin-bit-cast.cpp +++ b/clang/test/SemaCXX/builtin-bit-cast.cpp @@ -32,10 +32,10 @@ struct not_trivially_copyable { virtual void foo() {} }; -// expected-error@+1{{__builtin_bit_cast source type must be trivially copyable}} +// expected-error@+1{{'__builtin_bit_cast' source type must be trivially copyable}} constexpr unsigned long ul = __builtin_bit_cast(unsigned long, not_trivially_copyable{}); -// expected-error@+1 {{__builtin_bit_cast destination type must be trivially copyable}} +// expected-error@+1 {{'__builtin_bit_cast' destination type must be trivially copyable}} constexpr long us = __builtin_bit_cast(unsigned long &, 0L); namespace PR42936 { diff --git a/clang/test/SemaCXX/constexpr-return-non-void-cxx2b.cpp b/clang/test/SemaCXX/constexpr-return-non-void-cxx2b.cpp index 25d1f8df7f716..19e7d4976428a 100644 --- a/clang/test/SemaCXX/constexpr-return-non-void-cxx2b.cpp +++ b/clang/test/SemaCXX/constexpr-return-non-void-cxx2b.cpp @@ -1,7 +1,36 @@ -// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++23 -fsyntax-only -Wimplicit-fallthrough -verify %s constexpr int f() { } // expected-warning {{non-void function does not return a value}} static_assert(__is_same(decltype([] constexpr -> int { }( )), int)); // expected-warning {{non-void lambda does not return a value}} consteval int g() { } // expected-warning {{non-void function does not return a value}} static_assert(__is_same(decltype([] consteval -> int { }( )), int)); // expected-warning {{non-void lambda does not return a value}} + +namespace GH116485 { +int h() { + if consteval { } +} // expected-warning {{non-void function does not return a value}} + +void i(int x) { + if consteval { + } + switch (x) { + case 1: + i(1); + case 2: // expected-warning {{unannotated fall-through between switch labels}} \ + // expected-note {{insert 'break;' to avoid fall-through}} + break; + } +} + +constexpr bool j() { + if !consteval { return true; } +} // expected-warning {{non-void function does not return a value in all control paths}} \ + // expected-note {{control reached end of constexpr function}} + +bool k = j(); +constinit bool l = j(); // expected-error {{variable does not have a constant initializer}} \ + // expected-note {{required by 'constinit' specifier here}} \ + // expected-note {{in call to 'j()'}} + +} diff --git a/clang/test/SemaCXX/warn-shadow.cpp b/clang/test/SemaCXX/warn-shadow.cpp index 2969bd39fed41..98a235a73c7e5 100644 --- a/clang/test/SemaCXX/warn-shadow.cpp +++ b/clang/test/SemaCXX/warn-shadow.cpp @@ -307,3 +307,17 @@ void test4() { } }; // namespace structured_binding_tests + +namespace GH62588 { +class Outer { +public: + char *foo(); // expected-note {{previous declaration is here}} \ + // expected-note {{previous definition is here}} + enum Outer_E { foo }; // expected-error {{redefinition of 'foo'}} \ + // expected-warning {{declaration shadows a static data member of 'GH62588::Outer'}} + class Inner { + public: + enum Inner_E { foo }; // ok + }; +}; +} // namespace GH62588 diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-warning-data-invocation.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-warning-data-invocation.cpp index 0228e42652bd9..a6a334d247023 100644 --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-warning-data-invocation.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-warning-data-invocation.cpp @@ -173,4 +173,21 @@ A false_negatives(std::span span_pt, span span_A) { return *a2; // TODO: Can cause OOB if span_pt is empty } + +void test_incomplete_type(std::span S) { + (struct IncompleteStruct *)S.data(); // expected-warning{{unsafe invocation of 'data'}} + (class IncompleteClass *)S.data(); // expected-warning{{unsafe invocation of 'data'}} + (union IncompleteUnion *)S.data(); // expected-warning{{unsafe invocation of 'data'}} +} + +void test_complete_type(std::span S) { + (struct CompleteStruct *)S.data(); // no warn as the struct size is smaller than long + (class CompleteClass *)S.data(); // no warn as the class size is smaller than long + (union CompleteUnion *)S.data(); // no warn as the union size is smaller than long + + struct CompleteStruct {}; + class CompleteClass {}; + union CompleteUnion {}; +} + #endif diff --git a/clang/test/SemaCXX/warn-unused-result.cpp b/clang/test/SemaCXX/warn-unused-result.cpp index 4b7a2503ecc0d..682c500dc1d96 100644 --- a/clang/test/SemaCXX/warn-unused-result.cpp +++ b/clang/test/SemaCXX/warn-unused-result.cpp @@ -108,7 +108,7 @@ void lazy() { (void)DoAnotherThing(); (void)DoYetAnotherThing(); - DoSomething(); // expected-warning {{ignoring return value}} + DoSomething(); // expected-warning {{ignoring return value of type 'Status' declared with 'warn_unused_result'}} DoSomethingElse(); DoAnotherThing(); DoYetAnotherThing(); @@ -120,11 +120,11 @@ class [[clang::warn_unused_result]] StatusOr { StatusOr doit(); void test() { Foo f; - f.doStuff(); // expected-warning {{ignoring return value}} - doit(); // expected-warning {{ignoring return value}} + f.doStuff(); // expected-warning {{ignoring return value of type 'Status' declared with 'warn_unused_result'}} + doit(); // expected-warning {{ignoring return value of type 'StatusOr' declared with 'warn_unused_result'}} auto func = []() { return Status(); }; - func(); // expected-warning {{ignoring return value}} + func(); // expected-warning {{ignoring return value of type 'Status' declared with 'warn_unused_result'}} } } @@ -139,7 +139,7 @@ struct Status {}; void Bar() { Foo f; - f.Bar(); // expected-warning {{ignoring return value}} + f.Bar(); // expected-warning {{ignoring return value of type 'Status' declared with 'warn_unused_result'}} }; } @@ -215,18 +215,18 @@ P operator--(const P &) { return {}; }; void f() { S s; P p; - s.DoThing(); // expected-warning {{ignoring return value}} - p.DoThing(); // expected-warning {{ignoring return value}} + s.DoThing(); // expected-warning {{ignoring return value of type 'S' declared with 'warn_unused_result'}} + p.DoThing(); // expected-warning {{ignoring return value of type 'P' declared with 'warn_unused_result'}} // Only postfix is expected to warn when written correctly. - s++; // expected-warning {{ignoring return value}} - s--; // expected-warning {{ignoring return value}} - p++; // expected-warning {{ignoring return value}} - p--; // expected-warning {{ignoring return value}} + s++; // expected-warning {{ignoring return value of type 'S' declared with 'warn_unused_result'}} + s--; // expected-warning {{ignoring return value of type 'S' declared with 'warn_unused_result'}} + p++; // expected-warning {{ignoring return value of type 'P' declared with 'warn_unused_result'}} + p--; // expected-warning {{ignoring return value of type 'P' declared with 'warn_unused_result'}} // Improperly written prefix operators should still warn. - ++s; // expected-warning {{ignoring return value}} - --s; // expected-warning {{ignoring return value}} - ++p; // expected-warning {{ignoring return value}} - --p; // expected-warning {{ignoring return value}} + ++s; // expected-warning {{ignoring return value of type 'S' declared with 'warn_unused_result'}} + --s; // expected-warning {{ignoring return value of type 'S' declared with 'warn_unused_result'}} + ++p; // expected-warning {{ignoring return value of type 'P' declared with 'warn_unused_result'}} + --p; // expected-warning {{ignoring return value of type 'P' declared with 'warn_unused_result'}} // Silencing the warning by cast to void still works. (void)s.DoThing(); @@ -243,7 +243,7 @@ namespace PR39837 { void g() { int a[2]; for (int b : a) - f(b); // expected-warning {{ignoring return value}} + f(b); // expected-warning {{ignoring return value of function declared with 'warn_unused_result'}} } } // namespace PR39837 @@ -261,12 +261,12 @@ typedef a indirect; a af1(); indirect indirectf1(); void af2() { - af1(); // expected-warning {{ignoring return value}} + af1(); // expected-warning {{ignoring return value of type 'a' declared with 'warn_unused_result'}} void *(*a1)(); a1(); // no warning a (*a2)(); - a2(); // expected-warning {{ignoring return value}} - indirectf1(); // expected-warning {{ignoring return value}} + a2(); // expected-warning {{ignoring return value of type 'a' declared with 'warn_unused_result'}} + indirectf1(); // expected-warning {{ignoring return value of type 'a' declared with 'warn_unused_result'}} } [[nodiscard]] typedef void *b1; // expected-warning {{'[[nodiscard]]' attribute ignored when applied to a typedef; consider using '__attribute__((warn_unused_result))' or '[[clang::warn_unused_result]]' instead}} [[gnu::warn_unused_result]] typedef void *b2; // expected-warning {{'[[gnu::warn_unused_result]]' attribute ignored when applied to a typedef; consider using '__attribute__((warn_unused_result))' or '[[clang::warn_unused_result]]' instead}} @@ -279,10 +279,79 @@ void bf2() { __attribute__((warn_unused_result)) typedef void *c; c cf1(); void cf2() { - cf1(); // expected-warning {{ignoring return value}} + cf1(); // expected-warning {{ignoring return value of type 'c' declared with 'warn_unused_result'}} void *(*c1)(); c1(); c (*c2)(); - c2(); // expected-warning {{ignoring return value}} + c2(); // expected-warning {{ignoring return value of type 'c' declared with 'warn_unused_result'}} } } + +namespace nodiscard_specialization { +// Test to only mark a specialization of class template as nodiscard +template struct S { S(int) {} }; +template<> struct [[nodiscard]] S { S(int) {} }; +template struct [[clang::warn_unused_result]] S { S(int) {} }; + +template +S obtain(const T&) { return {2}; } + +template +[[nodiscard]] S obtain2(const T&) { return {2}; } + +template +__attribute__((warn_unused_result)) S obtain3(const T&) { return {2}; } + +void use() { + obtain(1.0); // no warning + obtain(1); // expected-warning {{ignoring return value of type 'S' declared with 'nodiscard'}} + obtain(1); // expected-warning {{ignoring return value of type 'S' declared with 'warn_unused_result'}} + + S(2); // no warning + S(2); // expected-warning {{ignoring temporary of type 'S' declared with 'nodiscard'}} + S(2); // no warning (warn_unused_result does not diagnose constructor temporaries) + + // function should take precedence over type + obtain2(1.0); // expected-warning {{ignoring return value of function declared with 'nodiscard'}} + obtain2(1); // expected-warning {{ignoring return value of function declared with 'nodiscard'}} + obtain2(1); // expected-warning {{ignoring return value of function declared with 'nodiscard'}} + obtain3(1.0); // expected-warning {{ignoring return value of function declared with 'warn_unused_result'}} + obtain3(1); // expected-warning {{ignoring return value of function declared with 'warn_unused_result'}} + obtain3(1); // expected-warning {{ignoring return value of function declared with 'warn_unused_result'}} +} + +// Test on constructor nodiscard +struct H { + explicit H(int) {} + [[nodiscard]] explicit H(double) {} + __attribute__((warn_unused_result)) H(const char*) {} +}; + +struct [[nodiscard]] G { + explicit G(int) {} + [[nodiscard]] explicit G(double) {} + [[clang::warn_unused_result]] G(const char*) {} +}; + +void use2() { + H{2}; // no warning + H(2.0); // expected-warning {{ignoring temporary created by a constructor declared with 'nodiscard'}} + H("Hello"); // no warning (warn_unused_result does not diagnose constructor temporaries) + + // no warning for explicit cast to void + (void)H(2); + (void)H{2.0}; + (void)H{"Hello"}; + + // warns for all these invocations + // here, constructor/function should take precedence over type + G{2}; // expected-warning {{ignoring temporary of type 'G' declared with 'nodiscard'}} + G(2.0); // expected-warning {{ignoring temporary created by a constructor declared with 'nodiscard'}} + G("Hello"); // expected-warning {{ignoring temporary created by a constructor declared with 'warn_unused_result'}} + + // no warning for explicit cast to void + (void)G(2); + (void)G{2.0}; + (void)G{"Hello"}; +} +} // namespace nodiscard_specialization diff --git a/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl b/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl index 76b5d01b8036e..3c2ea557b1982 100644 --- a/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl +++ b/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl @@ -14,6 +14,6 @@ RWBuffer<> BufferErr2; [numthreads(1,1,1)] void main() { - (void)Buffer.h; // expected-error {{'h' is a private member of 'hlsl::RWBuffer>'}} + (void)Buffer.__handle; // expected-error {{'__handle' is a private member of 'hlsl::RWBuffer>'}} // expected-note@* {{implicitly declared private here}} } diff --git a/clang/test/SemaHLSL/BuiltIns/StructuredBuffers.hlsl b/clang/test/SemaHLSL/BuiltIns/StructuredBuffers.hlsl index a472d5519dc51..b0cf9453cecfc 100644 --- a/clang/test/SemaHLSL/BuiltIns/StructuredBuffers.hlsl +++ b/clang/test/SemaHLSL/BuiltIns/StructuredBuffers.hlsl @@ -14,6 +14,6 @@ StructuredBuffer<> BufferErr2; [numthreads(1,1,1)] void main() { - (void)Buffer.h; // expected-error {{'h' is a private member of 'hlsl::StructuredBuffer>'}} + (void)Buffer.__handle; // expected-error {{'__handle' is a private member of 'hlsl::StructuredBuffer>'}} // expected-note@* {{implicitly declared private here}} } diff --git a/clang/test/SemaObjC/comptypes-legal.m b/clang/test/SemaObjC/comptypes-legal.m index 09c3a7261bd58..8e332f42be842 100644 --- a/clang/test/SemaObjC/comptypes-legal.m +++ b/clang/test/SemaObjC/comptypes-legal.m @@ -41,7 +41,7 @@ @interface I - (void) Meth : (id )aKey; // expected-note {{passing argument to parameter 'aKey' here}} @end -@class ForwarClass; // expected-note 3 {{conformance of forward class 'ForwarClass' to protocol 'NSCopying' can not be confirmed}} +@class ForwarClass; // expected-note 3 {{conformance of forward class 'ForwarClass' to protocol 'NSCopying' cannot be confirmed}} ForwarClass *Test10751015 (I* pi, ForwarClass *ns_forward) { diff --git a/clang/test/SemaObjC/method-param-named-id.m b/clang/test/SemaObjC/method-param-named-id.m new file mode 100644 index 0000000000000..8269c31116c32 --- /dev/null +++ b/clang/test/SemaObjC/method-param-named-id.m @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class %s + + +@interface Foo +-(void)paramNamedID:(int)id usesIDType:(id)notShadowed; +-(void)paramNamedID:(int)id, id notShadowed; // expected-warning{{use of C-style parameters in Objective-C method declarations is deprecated}} +@end diff --git a/clang/test/SemaOpenCL/access-qualifier.cl b/clang/test/SemaOpenCL/access-qualifier.cl index 726253c0b1a23..d1c9b5e35af6c 100644 --- a/clang/test/SemaOpenCL/access-qualifier.cl +++ b/clang/test/SemaOpenCL/access-qualifier.cl @@ -36,7 +36,7 @@ void myRead(read_only image1d_t); #if (__OPENCL_C_VERSION__ == 200) || ((__OPENCL_CPP_VERSION__ == 202100 || __OPENCL_C_VERSION__ == 300) && defined(__opencl_c_read_write_images)) void myReadWrite(read_write image1d_t); #else -void myReadWrite(read_write image1d_t); // expected-error {{access qualifier 'read_write' can not be used for '__read_write image1d_t' prior to OpenCL C version 2.0 or in version 3.0 and without __opencl_c_read_write_images feature}} +void myReadWrite(read_write image1d_t); // expected-error {{access qualifier 'read_write' cannot be used for '__read_write image1d_t' prior to OpenCL C version 2.0 or in version 3.0 and without __opencl_c_read_write_images feature}} #endif @@ -94,9 +94,9 @@ kernel void k11(read_only write_only image1d_t i){} // expected-error{{multiple kernel void k12(read_only read_only image1d_t i){} // expected-warning {{duplicate 'read_only' declaration specifier}} #if (__OPENCL_C_VERSION__ == 200) || ((__OPENCL_CPP_VERSION__ == 202100 || __OPENCL_C_VERSION__ == 300) && defined(__opencl_c_read_write_images)) -kernel void k13(read_write pipe int i){} // expected-error{{access qualifier 'read_write' can not be used for 'read_only pipe int'}} +kernel void k13(read_write pipe int i){} // expected-error{{access qualifier 'read_write' cannot be used for 'read_only pipe int'}} #else -kernel void k13(__read_write image1d_t i){} // expected-error{{access qualifier '__read_write' can not be used for '__read_write image1d_t' prior to OpenCL C version 2.0 or in version 3.0 and without __opencl_c_read_write_images feature}} +kernel void k13(__read_write image1d_t i){} // expected-error{{access qualifier '__read_write' cannot be used for '__read_write image1d_t' prior to OpenCL C version 2.0 or in version 3.0 and without __opencl_c_read_write_images feature}} #endif #if defined(__OPENCL_C_VERSION__) && __OPENCL_C_VERSION__ < 200 @@ -116,7 +116,7 @@ kernel void k14(read_only pipe int p) { kernel void pipe_ro_twice(read_only read_only pipe int i){} // expected-warning{{duplicate 'read_only' declaration specifier}} // Conflicting access qualifiers -kernel void pipe_ro_twice_tw(read_write read_only read_only pipe int i){} // expected-error{{access qualifier 'read_write' can not be used for 'read_only pipe int'}} +kernel void pipe_ro_twice_tw(read_write read_only read_only pipe int i){} // expected-error{{access qualifier 'read_write' cannot be used for 'read_only pipe int'}} kernel void pipe_ro_wo(read_only write_only pipe int i){} // expected-error{{multiple access qualifiers}} typedef read_only pipe int ROPipeInt; diff --git a/clang/test/SemaOpenCL/builtins-amdgcn-error-gfx950-param.cl b/clang/test/SemaOpenCL/builtins-amdgcn-error-gfx950-param.cl new file mode 100644 index 0000000000000..4af67763c40dd --- /dev/null +++ b/clang/test/SemaOpenCL/builtins-amdgcn-error-gfx950-param.cl @@ -0,0 +1,28 @@ +// REQUIRES: amdgpu-registered-target +// RUN: %clang_cc1 -triple amdgcn-- -target-cpu gfx950 -verify -S -o - %s + +typedef float float4 __attribute__((ext_vector_type(4))); +typedef float float16 __attribute__((ext_vector_type(16))); +typedef half half8 __attribute__((ext_vector_type(8))); +typedef __bf16 bfloat8 __attribute__((ext_vector_type(8))); + + +void test_mfma_f32_16x16x32_f16(__global float4* out, half8 a, half8 b, float4 c, int X) { + + *out = __builtin_amdgcn_mfma_f32_16x16x32_f16(a, b, c, X, 0, 0); // expected-error{{argument to '__builtin_amdgcn_mfma_f32_16x16x32_f16' must be a constant integer}} + *out = __builtin_amdgcn_mfma_f32_16x16x32_f16(a, b, c, 0, X, 0); // expected-error{{argument to '__builtin_amdgcn_mfma_f32_16x16x32_f16' must be a constant integer}} + *out = __builtin_amdgcn_mfma_f32_16x16x32_f16(a, b, c, 0, 0, X); // expected-error{{argument to '__builtin_amdgcn_mfma_f32_16x16x32_f16' must be a constant integer}} +} + + +void test_mfma_f32_32x32x16_f16(__global float16* out, half8 a, half8 b, float16 c, int X) { + *out = __builtin_amdgcn_mfma_f32_32x32x16_f16(a, b, c, X, 0, 0); // expected-error{{argument to '__builtin_amdgcn_mfma_f32_32x32x16_f16' must be a constant integer}} + *out = __builtin_amdgcn_mfma_f32_32x32x16_f16(a, b, c, 0, X, 0); // expected-error{{argument to '__builtin_amdgcn_mfma_f32_32x32x16_f16' must be a constant integer}} + *out = __builtin_amdgcn_mfma_f32_32x32x16_f16(a, b, c, 0, 0, X); // expected-error{{argument to '__builtin_amdgcn_mfma_f32_32x32x16_f16' must be a constant integer}} +} + +void test_mfma_f32_32x32x16_bf16(__global float16* out, bfloat8 a, bfloat8 b, float16 c, int X) { + *out = __builtin_amdgcn_mfma_f32_32x32x16_bf16(a, b, c, X, 0, 0); // expected-error{{argument to '__builtin_amdgcn_mfma_f32_32x32x16_bf16' must be a constant integer}} + *out = __builtin_amdgcn_mfma_f32_32x32x16_bf16(a, b, c, 0, X, 0); // expected-error{{argument to '__builtin_amdgcn_mfma_f32_32x32x16_bf16' must be a constant integer}} + *out = __builtin_amdgcn_mfma_f32_32x32x16_bf16(a, b, c, 0, 0, X); // expected-error{{argument to '__builtin_amdgcn_mfma_f32_32x32x16_bf16' must be a constant integer}} +} diff --git a/clang/test/SemaOpenCL/builtins-amdgcn-error-gfx950.cl b/clang/test/SemaOpenCL/builtins-amdgcn-error-gfx950.cl new file mode 100644 index 0000000000000..e0fd2aa5c58a0 --- /dev/null +++ b/clang/test/SemaOpenCL/builtins-amdgcn-error-gfx950.cl @@ -0,0 +1,15 @@ +// REQUIRES: amdgpu-registered-target +// RUN: %clang_cc1 -triple amdgcn-- -target-cpu gfx940 -verify -S -o - %s + +typedef float float4 __attribute__((ext_vector_type(4))); +typedef float float16 __attribute__((ext_vector_type(16))); +typedef half half8 __attribute__((ext_vector_type(8))); +typedef __bf16 bfloat8 __attribute__((ext_vector_type(8))); + +void test(__global float4* out0, half8 a0, half8 b0, float4 c0, + __global float16* out1, half8 a1, half8 b1, float16 c1, + __global float16* out2, bfloat8 a2, bfloat8 b2, float16 c2) { + *out0 = __builtin_amdgcn_mfma_f32_16x16x32_f16(a0, b0, c0, 0, 0, 0); // expected-error{{'__builtin_amdgcn_mfma_f32_16x16x32_f16' needs target feature gfx950-insts}} + *out1 = __builtin_amdgcn_mfma_f32_32x32x16_f16(a1, b1, c1, 0, 0, 0); // expected-error{{'__builtin_amdgcn_mfma_f32_32x32x16_f16' needs target feature gfx950-insts}} + *out2 = __builtin_amdgcn_mfma_f32_32x32x16_bf16(a2, b2, c2, 0, 0, 0); // expected-error{{'__builtin_amdgcn_mfma_f32_32x32x16_bf16' needs target feature gfx950-insts}} +} diff --git a/clang/tools/amdgpu-arch/AMDGPUArch.cpp b/clang/tools/amdgpu-arch/AMDGPUArch.cpp index 7ae57b7877e1f..6c10cbc5c46a8 100644 --- a/clang/tools/amdgpu-arch/AMDGPUArch.cpp +++ b/clang/tools/amdgpu-arch/AMDGPUArch.cpp @@ -25,7 +25,7 @@ static void PrintVersion(raw_ostream &OS) { OS << clang::getClangToolFullVersion("amdgpu-arch") << '\n'; } -int printGPUsByHSA(); +int printGPUsByKFD(); int printGPUsByHIP(); int main(int argc, char *argv[]) { @@ -45,7 +45,7 @@ int main(int argc, char *argv[]) { } #ifndef _WIN32 - if (!printGPUsByHSA()) + if (!printGPUsByKFD()) return 0; #endif diff --git a/clang/tools/amdgpu-arch/AMDGPUArchByHSA.cpp b/clang/tools/amdgpu-arch/AMDGPUArchByHSA.cpp deleted file mode 100644 index 432f2c414ed24..0000000000000 --- a/clang/tools/amdgpu-arch/AMDGPUArchByHSA.cpp +++ /dev/null @@ -1,122 +0,0 @@ -//===- AMDGPUArchByHSA.cpp - list AMDGPU installed ------*- 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 -// -//===----------------------------------------------------------------------===// -// -// This file implements a tool for detecting name of AMDGPU installed in system -// using HSA on Linux. This tool is used by AMDGPU OpenMP and HIP driver. -// -//===----------------------------------------------------------------------===// - -#include "clang/Basic/Version.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/DynamicLibrary.h" -#include "llvm/Support/Error.h" -#include "llvm/Support/raw_ostream.h" -#include -#include -#include - -using namespace llvm; - -typedef enum { - HSA_STATUS_SUCCESS = 0x0, -} hsa_status_t; - -typedef enum { - HSA_DEVICE_TYPE_CPU = 0, - HSA_DEVICE_TYPE_GPU = 1, -} hsa_device_type_t; - -typedef enum { - HSA_AGENT_INFO_NAME = 0, - HSA_AGENT_INFO_DEVICE = 17, -} hsa_agent_info_t; - -typedef struct hsa_agent_s { - uint64_t handle; -} hsa_agent_t; - -hsa_status_t (*hsa_init)(); -hsa_status_t (*hsa_shut_down)(); -hsa_status_t (*hsa_agent_get_info)(hsa_agent_t, hsa_agent_info_t, void *); -hsa_status_t (*hsa_iterate_agents)(hsa_status_t (*)(hsa_agent_t, void *), - void *); - -constexpr const char *DynamicHSAPath = "libhsa-runtime64.so"; - -llvm::Error loadHSA() { - std::string ErrMsg; - auto DynlibHandle = std::make_unique( - llvm::sys::DynamicLibrary::getPermanentLibrary(DynamicHSAPath, &ErrMsg)); - if (!DynlibHandle->isValid()) { - return llvm::createStringError(llvm::inconvertibleErrorCode(), - "Failed to 'dlopen' %s", DynamicHSAPath); - } -#define DYNAMIC_INIT(SYMBOL) \ - { \ - void *SymbolPtr = DynlibHandle->getAddressOfSymbol(#SYMBOL); \ - if (!SymbolPtr) \ - return llvm::createStringError(llvm::inconvertibleErrorCode(), \ - "Failed to 'dlsym' " #SYMBOL); \ - SYMBOL = reinterpret_cast(SymbolPtr); \ - } - DYNAMIC_INIT(hsa_init); - DYNAMIC_INIT(hsa_shut_down); - DYNAMIC_INIT(hsa_agent_get_info); - DYNAMIC_INIT(hsa_iterate_agents); -#undef DYNAMIC_INIT - return llvm::Error::success(); -} - -static hsa_status_t iterateAgentsCallback(hsa_agent_t Agent, void *Data) { - hsa_device_type_t DeviceType; - hsa_status_t Status = - hsa_agent_get_info(Agent, HSA_AGENT_INFO_DEVICE, &DeviceType); - - // continue only if device type if GPU - if (Status != HSA_STATUS_SUCCESS || DeviceType != HSA_DEVICE_TYPE_GPU) { - return Status; - } - - std::vector *GPUs = - static_cast *>(Data); - char GPUName[64]; - Status = hsa_agent_get_info(Agent, HSA_AGENT_INFO_NAME, GPUName); - if (Status != HSA_STATUS_SUCCESS) { - return Status; - } - GPUs->push_back(GPUName); - return HSA_STATUS_SUCCESS; -} - -int printGPUsByHSA() { - // Attempt to load the HSA runtime. - if (llvm::Error Err = loadHSA()) { - logAllUnhandledErrors(std::move(Err), llvm::errs()); - return 1; - } - - hsa_status_t Status = hsa_init(); - if (Status != HSA_STATUS_SUCCESS) { - return 1; - } - - std::vector GPUs; - Status = hsa_iterate_agents(iterateAgentsCallback, &GPUs); - if (Status != HSA_STATUS_SUCCESS) { - return 1; - } - - for (const auto &GPU : GPUs) - llvm::outs() << GPU << '\n'; - - if (GPUs.size() < 1) - return 1; - - hsa_shut_down(); - return 0; -} diff --git a/clang/tools/amdgpu-arch/AMDGPUArchByKFD.cpp b/clang/tools/amdgpu-arch/AMDGPUArchByKFD.cpp new file mode 100644 index 0000000000000..94ebf9073e00e --- /dev/null +++ b/clang/tools/amdgpu-arch/AMDGPUArchByKFD.cpp @@ -0,0 +1,77 @@ +//===- AMDGPUArchByKFD.cpp - list AMDGPU installed ------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements a tool for detecting name of AMD GPUs installed in +// system using the Linux sysfs interface for the AMD KFD driver. This file does +// not respect ROCR_VISIBLE_DEVICES like the ROCm environment would. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/LineIterator.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include + +using namespace llvm; + +constexpr static const char *KFD_SYSFS_NODE_PATH = + "/sys/devices/virtual/kfd/kfd/topology/nodes"; + +// See the ROCm implementation for how this is handled. +// https://github.com/ROCm/ROCT-Thunk-Interface/blob/master/src/libhsakmt.h#L126 +constexpr static long getMajor(long Ver) { return (Ver / 10000) % 100; } +constexpr static long getMinor(long Ver) { return (Ver / 100) % 100; } +constexpr static long getStep(long Ver) { return Ver % 100; } + +int printGPUsByKFD() { + SmallVector> Devices; + std::error_code EC; + for (sys::fs::directory_iterator Begin(KFD_SYSFS_NODE_PATH, EC), End; + Begin != End; Begin.increment(EC)) { + if (EC) + return 1; + + long Node = 0; + if (sys::path::stem(Begin->path()).consumeInteger(10, Node)) + return 1; + + SmallString<0> Path(Begin->path()); + sys::path::append(Path, "properties"); + + ErrorOr> BufferOrErr = + MemoryBuffer::getFileOrSTDIN(Path); + if (std::error_code EC = BufferOrErr.getError()) + return 1; + + long GFXVersion = 0; + for (line_iterator Lines(**BufferOrErr, false); !Lines.is_at_end(); + ++Lines) { + StringRef Line(*Lines); + if (Line.consume_front("gfx_target_version")) { + if (Line.drop_while([](char C) { return std::isspace(C); }) + .consumeInteger(10, GFXVersion)) + return 1; + break; + } + } + + // If this is zero the node is a CPU. + if (GFXVersion == 0) + continue; + Devices.emplace_back(Node, GFXVersion); + } + + // Sort the devices by their node to make sure it prints in order. + llvm::sort(Devices, [](auto &L, auto &R) { return L.first < R.first; }); + for (const auto &[Node, GFXVersion] : Devices) + std::fprintf(stdout, "gfx%ld%ld%lx\n", getMajor(GFXVersion), + getMinor(GFXVersion), getStep(GFXVersion)); + + return 0; +} diff --git a/clang/tools/amdgpu-arch/CMakeLists.txt b/clang/tools/amdgpu-arch/CMakeLists.txt index 1657c70125130..c4c8de614565a 100644 --- a/clang/tools/amdgpu-arch/CMakeLists.txt +++ b/clang/tools/amdgpu-arch/CMakeLists.txt @@ -8,6 +8,6 @@ set(LLVM_LINK_COMPONENTS Support) -add_clang_tool(amdgpu-arch AMDGPUArch.cpp AMDGPUArchByHSA.cpp AMDGPUArchByHIP.cpp) +add_clang_tool(amdgpu-arch AMDGPUArch.cpp AMDGPUArchByKFD.cpp AMDGPUArchByHIP.cpp) target_link_libraries(amdgpu-arch PRIVATE clangBasic) diff --git a/clang/tools/clang-shlib/CMakeLists.txt b/clang/tools/clang-shlib/CMakeLists.txt index 298d3a9d18fec..d83c13fd394f4 100644 --- a/clang/tools/clang-shlib/CMakeLists.txt +++ b/clang/tools/clang-shlib/CMakeLists.txt @@ -48,6 +48,14 @@ add_clang_library(clang-cpp ${_OBJECTS} LINK_LIBS ${_DEPS}) + +configure_file(simple_version_script.map.in simple_version_script.map) + +if (NOT APPLE AND NOT MSVC AND NOT MINGW AND NOT LLVM_LINKER_IS_SOLARISLD) + # Solaris ld does not accept global: *; so there is no way to version *all* global symbols + target_link_options(clang-cpp PRIVATE LINKER:--version-script,${CMAKE_CURRENT_BINARY_DIR}/simple_version_script.map) +endif() + # Optimize function calls for default visibility definitions to avoid PLT and # reduce dynamic relocations. if (NOT APPLE AND NOT MINGW AND NOT LLVM_LINKER_IS_SOLARISLD_ILLUMOS) diff --git a/clang/tools/clang-shlib/simple_version_script.map.in b/clang/tools/clang-shlib/simple_version_script.map.in new file mode 100644 index 0000000000000..cb2306d1f5968 --- /dev/null +++ b/clang/tools/clang-shlib/simple_version_script.map.in @@ -0,0 +1 @@ +@LLVM_SHLIB_SYMBOL_VERSION@ { global: *; }; diff --git a/clang/unittests/Format/FormatTestVerilog.cpp b/clang/unittests/Format/FormatTestVerilog.cpp index 49d276fc78d81..e4a14ff754d1a 100644 --- a/clang/unittests/Format/FormatTestVerilog.cpp +++ b/clang/unittests/Format/FormatTestVerilog.cpp @@ -702,6 +702,18 @@ TEST_F(FormatTestVerilog, Hierarchy) { " generate\n" " endgenerate\n" "endfunction : x"); + // Type names with '::' should be recognized. + verifyFormat("function automatic x::x x\n" + " (input x);\n" + "endfunction : x"); + // Names having to do macros should be recognized. + verifyFormat("function automatic x::x x``x\n" + " (input x);\n" + "endfunction : x"); + verifyFormat("function automatic x::x `x\n" + " (input x);\n" + "endfunction : x"); + verifyNoCrash("x x(x x, x x);"); } TEST_F(FormatTestVerilog, Identifiers) { diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp index bb8ee416ea2db..e1ae1770e8ebe 100644 --- a/clang/unittests/Format/TokenAnnotatorTest.cpp +++ b/clang/unittests/Format/TokenAnnotatorTest.cpp @@ -2598,6 +2598,20 @@ TEST_F(TokenAnnotatorTest, UnderstandsVerilogOperators) { Tokens = Annotate("x = '{\"\"};"); ASSERT_EQ(Tokens.size(), 8u) << Tokens; EXPECT_TOKEN(Tokens[4], tok::string_literal, TT_Unknown); + + // Module headers. + Tokens = Annotate("module x();\nendmodule"); + ASSERT_EQ(Tokens.size(), 7u) << Tokens; + EXPECT_TOKEN(Tokens[2], tok::l_paren, TT_VerilogMultiLineListLParen); + Tokens = Annotate("function automatic `x x();\nendmodule"); + ASSERT_EQ(Tokens.size(), 10u) << Tokens; + EXPECT_TOKEN(Tokens[5], tok::l_paren, TT_VerilogMultiLineListLParen); + Tokens = Annotate("function automatic x``x x();\nendmodule"); + ASSERT_EQ(Tokens.size(), 11u) << Tokens; + EXPECT_TOKEN(Tokens[6], tok::l_paren, TT_VerilogMultiLineListLParen); + Tokens = Annotate("function automatic x::x x();\nendmodule"); + ASSERT_EQ(Tokens.size(), 11u) << Tokens; + EXPECT_TOKEN(Tokens[6], tok::l_paren, TT_VerilogMultiLineListLParen); } TEST_F(TokenAnnotatorTest, UnderstandTableGenTokens) { diff --git a/compiler-rt/lib/builtins/cpu_model/aarch64.c b/compiler-rt/lib/builtins/cpu_model/aarch64.c index def11f88c4854..74e5e01b66c54 100644 --- a/compiler-rt/lib/builtins/cpu_model/aarch64.c +++ b/compiler-rt/lib/builtins/cpu_model/aarch64.c @@ -48,6 +48,8 @@ _Bool __aarch64_have_lse_atomics #elif defined(__linux__) && __has_include() #include "aarch64/hwcap.inc" #include "aarch64/lse_atomics/getauxval.inc" +#elif defined(_WIN32) +#include "aarch64/lse_atomics/windows.inc" #else // When unimplemented, we leave __aarch64_have_lse_atomics initialized to false. #endif diff --git a/compiler-rt/lib/builtins/cpu_model/aarch64/lse_atomics/windows.inc b/compiler-rt/lib/builtins/cpu_model/aarch64/lse_atomics/windows.inc new file mode 100644 index 0000000000000..fff1593e1fac3 --- /dev/null +++ b/compiler-rt/lib/builtins/cpu_model/aarch64/lse_atomics/windows.inc @@ -0,0 +1,12 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include + +#ifndef PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE +#define PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE 34 +#endif + +static void CONSTRUCTOR_ATTRIBUTE init_have_lse_atomics(void) { + if (IsProcessorFeaturePresent(PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE)) + __aarch64_have_lse_atomics = true; +} diff --git a/compiler-rt/lib/hwasan/hwasan_platform_interceptors.h b/compiler-rt/lib/hwasan/hwasan_platform_interceptors.h index d92b510521942..e8011014c2331 100644 --- a/compiler-rt/lib/hwasan/hwasan_platform_interceptors.h +++ b/compiler-rt/lib/hwasan/hwasan_platform_interceptors.h @@ -200,6 +200,9 @@ #undef SANITIZER_INTERCEPT_CLOCK_GETCPUCLOCKID #define SANITIZER_INTERCEPT_CLOCK_GETCPUCLOCKID 0 +#undef SANITIZER_INTERCEPT_TIMER_CREATE +#define SANITIZER_INTERCEPT_TIMER_CREATE 0 + #undef SANITIZER_INTERCEPT_GETITIMER #define SANITIZER_INTERCEPT_GETITIMER 0 diff --git a/compiler-rt/lib/lsan/lsan_common.cpp b/compiler-rt/lib/lsan/lsan_common.cpp index 5c44c000ae577..7ab9e4ff2ac9f 100644 --- a/compiler-rt/lib/lsan/lsan_common.cpp +++ b/compiler-rt/lib/lsan/lsan_common.cpp @@ -569,7 +569,7 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads, PtraceRegistersStatus have_registers = suspended_threads.GetRegistersAndSP(i, ®isters, &sp); if (have_registers != REGISTERS_AVAILABLE) { - Report("Unable to get registers from thread %llu.\n", os_id); + VReport(1, "Unable to get registers from thread %llu.\n", os_id); // If unable to get SP, consider the entire stack to be reachable unless // GetRegistersAndSP failed with ESRCH. if (have_registers == REGISTERS_UNAVAILABLE_FATAL) diff --git a/compiler-rt/lib/msan/tests/msan_test.cpp b/compiler-rt/lib/msan/tests/msan_test.cpp index 41b99fabe84f4..a126dd4fdd55e 100644 --- a/compiler-rt/lib/msan/tests/msan_test.cpp +++ b/compiler-rt/lib/msan/tests/msan_test.cpp @@ -4881,4 +4881,32 @@ TEST(MemorySanitizer, throw_catch) { // pass } } + +#if defined(__GLIBC__) +TEST(MemorySanitizer, timer_create) { + timer_t timer; + EXPECT_POISONED(timer); + int res = timer_create(CLOCK_REALTIME, nullptr, &timer); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(timer); + + // Make sure the timer is usable. + struct itimerspec cur_value {}; + cur_value.it_value.tv_sec = 1; + EXPECT_EQ(0, timer_settime(timer, 0, &cur_value, nullptr)); + + struct itimerspec read_value; + EXPECT_POISONED(read_value); + EXPECT_EQ(0, timer_gettime(timer, &read_value)); + EXPECT_NOT_POISONED(read_value); + + timer_t timer2; + EXPECT_POISONED(timer2); + // Use an invalid clock_id to make timer_create fail. + res = timer_create(INT_MAX, nullptr, &timer2); + ASSERT_EQ(-1, res); + EXPECT_POISONED(timer2); + timer_delete(timer); +} +#endif } // namespace diff --git a/compiler-rt/lib/rtsan/rtsan.cpp b/compiler-rt/lib/rtsan/rtsan.cpp index e9f42d3760aa8..70edcc546219f 100644 --- a/compiler-rt/lib/rtsan/rtsan.cpp +++ b/compiler-rt/lib/rtsan/rtsan.cpp @@ -62,8 +62,12 @@ static void OnViolation(const BufferedStackTrace &stack, if (UNLIKELY(is_stack_novel)) { IncrementUniqueErrorCount(); - PrintDiagnostics(info); - stack.Print(); + { + ScopedErrorReportLock l; + PrintDiagnostics(info); + stack.Print(); + PrintErrorSummary(info, stack); + } handle.inc_use_count_unsafe(); } diff --git a/compiler-rt/lib/rtsan/rtsan_diagnostics.cpp b/compiler-rt/lib/rtsan/rtsan_diagnostics.cpp index ecba30d2ab8df..f13d3db101d48 100644 --- a/compiler-rt/lib/rtsan/rtsan_diagnostics.cpp +++ b/compiler-rt/lib/rtsan/rtsan_diagnostics.cpp @@ -39,20 +39,22 @@ class Decorator : public SanitizerCommonDecorator { }; } // namespace +static const char *GetErrorTypeStr(const DiagnosticsInfo &info) { + switch (info.type) { + case DiagnosticsInfoType::InterceptedCall: + return "unsafe-library-call"; + case DiagnosticsInfoType::BlockingCall: + return "blocking-call"; + } + CHECK(false); + return "(unknown error)"; +} + static void PrintError(const Decorator &decorator, const DiagnosticsInfo &info) { - const auto ErrorTypeStr = [&info]() -> const char * { - switch (info.type) { - case DiagnosticsInfoType::InterceptedCall: - return "unsafe-library-call"; - case DiagnosticsInfoType::BlockingCall: - return "blocking-call"; - } - return "(unknown error)"; - }; Printf("%s", decorator.Error()); - Report("ERROR: RealtimeSanitizer: %s\n", ErrorTypeStr()); + Report("ERROR: RealtimeSanitizer: %s\n", GetErrorTypeStr(info)); } static void PrintReason(const Decorator &decorator, @@ -78,10 +80,16 @@ static void PrintReason(const Decorator &decorator, } void __rtsan::PrintDiagnostics(const DiagnosticsInfo &info) { - ScopedErrorReportLock l; + ScopedErrorReportLock::CheckLocked(); Decorator d; PrintError(d, info); PrintReason(d, info); Printf("%s", d.Default()); } + +void __rtsan::PrintErrorSummary(const DiagnosticsInfo &info, + const BufferedStackTrace &stack) { + ScopedErrorReportLock::CheckLocked(); + ReportErrorSummary(GetErrorTypeStr(info), &stack); +} diff --git a/compiler-rt/lib/rtsan/rtsan_diagnostics.h b/compiler-rt/lib/rtsan/rtsan_diagnostics.h index f8a6b8a954a24..1138e61eb5df4 100644 --- a/compiler-rt/lib/rtsan/rtsan_diagnostics.h +++ b/compiler-rt/lib/rtsan/rtsan_diagnostics.h @@ -30,4 +30,6 @@ struct DiagnosticsInfo { }; void PrintDiagnostics(const DiagnosticsInfo &info); +void PrintErrorSummary(const DiagnosticsInfo &info, + const __sanitizer::BufferedStackTrace &stack); } // namespace __rtsan diff --git a/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp b/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp index 3a1b1f6524745..73448cfc11788 100644 --- a/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp +++ b/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp @@ -42,6 +42,7 @@ void OSSpinLockLock(volatile OSSpinLock *__lock); #endif #include +#include #include #include #include @@ -315,6 +316,31 @@ INTERCEPTOR(ssize_t, writev, int fd, const struct iovec *iov, int iovcnt) { return REAL(writev)(fd, iov, iovcnt); } +INTERCEPTOR(off_t, lseek, int fd, off_t offset, int whence) { + __rtsan_notify_intercepted_call("lseek"); + return REAL(lseek)(fd, offset, whence); +} + +#if SANITIZER_INTERCEPT_LSEEK64 +INTERCEPTOR(off64_t, lseek64, int fd, off64_t offset, int whence) { + __rtsan_notify_intercepted_call("lseek64"); + return REAL(lseek64)(fd, offset, whence); +} +#define RTSAN_MAYBE_INTERCEPT_LSEEK64 INTERCEPT_FUNCTION(lseek64) +#else +#define RTSAN_MAYBE_INTERCEPT_LSEEK64 +#endif // SANITIZER_INTERCEPT_LSEEK64 + +INTERCEPTOR(int, dup, int oldfd) { + __rtsan_notify_intercepted_call("dup"); + return REAL(dup)(oldfd); +} + +INTERCEPTOR(int, dup2, int oldfd, int newfd) { + __rtsan_notify_intercepted_call("dup2"); + return REAL(dup2)(oldfd, newfd); +} + // Concurrency #if SANITIZER_APPLE #pragma clang diagnostic push @@ -612,6 +638,114 @@ INTERCEPTOR(int, shutdown, int socket, int how) { return REAL(shutdown)(socket, how); } +// I/O Multiplexing + +INTERCEPTOR(int, poll, struct pollfd *fds, nfds_t nfds, int timeout) { + __rtsan_notify_intercepted_call("poll"); + return REAL(poll)(fds, nfds, timeout); +} + +#if !SANITIZER_APPLE +// FIXME: This should work on all unix systems, even Mac, but currently +// it is showing some weird error while linking +// error: declaration of 'select' has a different language linkage +INTERCEPTOR(int, select, int nfds, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, struct timeval *timeout) { + __rtsan_notify_intercepted_call("select"); + return REAL(select)(nfds, readfds, writefds, exceptfds, timeout); +} +#define RTSAN_MAYBE_INTERCEPT_SELECT INTERCEPT_FUNCTION(select) +#else +#define RTSAN_MAYBE_INTERCEPT_SELECT +#endif // !SANITIZER_APPLE + +INTERCEPTOR(int, pselect, int nfds, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, const struct timespec *timeout, + const sigset_t *sigmask) { + __rtsan_notify_intercepted_call("pselect"); + return REAL(pselect)(nfds, readfds, writefds, exceptfds, timeout, sigmask); +} + +#if SANITIZER_INTERCEPT_EPOLL +INTERCEPTOR(int, epoll_create, int size) { + __rtsan_notify_intercepted_call("epoll_create"); + return REAL(epoll_create)(size); +} + +INTERCEPTOR(int, epoll_create1, int flags) { + __rtsan_notify_intercepted_call("epoll_create1"); + return REAL(epoll_create1)(flags); +} + +INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, + struct epoll_event *event) { + __rtsan_notify_intercepted_call("epoll_ctl"); + return REAL(epoll_ctl)(epfd, op, fd, event); +} + +INTERCEPTOR(int, epoll_wait, int epfd, struct epoll_event *events, + int maxevents, int timeout) { + __rtsan_notify_intercepted_call("epoll_wait"); + return REAL(epoll_wait)(epfd, events, maxevents, timeout); +} + +INTERCEPTOR(int, epoll_pwait, int epfd, struct epoll_event *events, + int maxevents, int timeout, const sigset_t *sigmask) { + __rtsan_notify_intercepted_call("epoll_pwait"); + return REAL(epoll_pwait)(epfd, events, maxevents, timeout, sigmask); +} +#define RTSAN_MAYBE_INTERCEPT_EPOLL_CREATE INTERCEPT_FUNCTION(epoll_create) +#define RTSAN_MAYBE_INTERCEPT_EPOLL_CREATE1 INTERCEPT_FUNCTION(epoll_create1) +#define RTSAN_MAYBE_INTERCEPT_EPOLL_CTL INTERCEPT_FUNCTION(epoll_ctl) +#define RTSAN_MAYBE_INTERCEPT_EPOLL_WAIT INTERCEPT_FUNCTION(epoll_wait) +#define RTSAN_MAYBE_INTERCEPT_EPOLL_PWAIT INTERCEPT_FUNCTION(epoll_pwait) +#else +#define RTSAN_MAYBE_INTERCEPT_EPOLL_CREATE +#define RTSAN_MAYBE_INTERCEPT_EPOLL_CREATE1 +#define RTSAN_MAYBE_INTERCEPT_EPOLL_CTL +#define RTSAN_MAYBE_INTERCEPT_EPOLL_WAIT +#define RTSAN_MAYBE_INTERCEPT_EPOLL_PWAIT +#endif // SANITIZER_INTERCEPT_EPOLL + +#if SANITIZER_INTERCEPT_KQUEUE +INTERCEPTOR(int, kqueue, void) { + __rtsan_notify_intercepted_call("kqueue"); + return REAL(kqueue)(); +} + +INTERCEPTOR(int, kevent, int kq, const struct kevent *changelist, int nchanges, + struct kevent *eventlist, int nevents, + const struct timespec *timeout) { + __rtsan_notify_intercepted_call("kevent"); + return REAL(kevent)(kq, changelist, nchanges, eventlist, nevents, timeout); +} + +INTERCEPTOR(int, kevent64, int kq, const struct kevent64_s *changelist, + int nchanges, struct kevent64_s *eventlist, int nevents, + unsigned int flags, const struct timespec *timeout) { + __rtsan_notify_intercepted_call("kevent64"); + return REAL(kevent64)(kq, changelist, nchanges, eventlist, nevents, flags, + timeout); +} +#define RTSAN_MAYBE_INTERCEPT_KQUEUE INTERCEPT_FUNCTION(kqueue) +#define RTSAN_MAYBE_INTERCEPT_KEVENT INTERCEPT_FUNCTION(kevent) +#define RTSAN_MAYBE_INTERCEPT_KEVENT64 INTERCEPT_FUNCTION(kevent64) +#else +#define RTSAN_MAYBE_INTERCEPT_KQUEUE +#define RTSAN_MAYBE_INTERCEPT_KEVENT +#define RTSAN_MAYBE_INTERCEPT_KEVENT64 +#endif // SANITIZER_INTERCEPT_KQUEUE + +INTERCEPTOR(int, pipe, int pipefd[2]) { + __rtsan_notify_intercepted_call("pipe"); + return REAL(pipe)(pipefd); +} + +INTERCEPTOR(int, mkfifo, const char *pathname, mode_t mode) { + __rtsan_notify_intercepted_call("mkfifo"); + return REAL(mkfifo)(pathname, mode); +} + // Preinit void __rtsan::InitializeInterceptors() { INTERCEPT_FUNCTION(calloc); @@ -658,6 +792,10 @@ void __rtsan::InitializeInterceptors() { RTSAN_MAYBE_INTERCEPT_CREAT64; INTERCEPT_FUNCTION(puts); INTERCEPT_FUNCTION(fputs); + INTERCEPT_FUNCTION(lseek); + RTSAN_MAYBE_INTERCEPT_LSEEK64; + INTERCEPT_FUNCTION(dup); + INTERCEPT_FUNCTION(dup2); #if SANITIZER_APPLE INTERCEPT_FUNCTION(OSSpinLockLock); @@ -696,6 +834,21 @@ void __rtsan::InitializeInterceptors() { INTERCEPT_FUNCTION(sendto); INTERCEPT_FUNCTION(shutdown); INTERCEPT_FUNCTION(socket); + + RTSAN_MAYBE_INTERCEPT_SELECT; + INTERCEPT_FUNCTION(pselect); + INTERCEPT_FUNCTION(poll); + RTSAN_MAYBE_INTERCEPT_EPOLL_CREATE; + RTSAN_MAYBE_INTERCEPT_EPOLL_CREATE1; + RTSAN_MAYBE_INTERCEPT_EPOLL_CTL; + RTSAN_MAYBE_INTERCEPT_EPOLL_WAIT; + RTSAN_MAYBE_INTERCEPT_EPOLL_PWAIT; + RTSAN_MAYBE_INTERCEPT_KQUEUE; + RTSAN_MAYBE_INTERCEPT_KEVENT; + RTSAN_MAYBE_INTERCEPT_KEVENT64; + + INTERCEPT_FUNCTION(pipe); + INTERCEPT_FUNCTION(mkfifo); } #endif // SANITIZER_POSIX diff --git a/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp b/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp index d0ae12c9bea44..3e14346f33c7c 100644 --- a/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp +++ b/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp @@ -28,8 +28,18 @@ #include #endif +#if SANITIZER_INTERCEPT_EPOLL +#include +#endif + +#if SANITIZER_INTERCEPT_KQUEUE +#include +#include +#endif + #include #include +#include #include #include #include @@ -357,6 +367,24 @@ class RtsanOpenedFileTest : public RtsanFileTest { int fd = -1; }; +TEST_F(RtsanOpenedFileTest, LseekDiesWhenRealtime) { + auto Func = [this]() { lseek(GetOpenFd(), 0, SEEK_SET); }; + ExpectRealtimeDeath(Func, MAYBE_APPEND_64("lseek")); + ExpectNonRealtimeSurvival(Func); +} + +TEST_F(RtsanOpenedFileTest, DupDiesWhenRealtime) { + auto Func = [this]() { dup(GetOpenFd()); }; + ExpectRealtimeDeath(Func, "dup"); + ExpectNonRealtimeSurvival(Func); +} + +TEST_F(RtsanOpenedFileTest, Dup2DiesWhenRealtime) { + auto Func = [this]() { dup2(GetOpenFd(), 0); }; + ExpectRealtimeDeath(Func, "dup2"); + ExpectNonRealtimeSurvival(Func); +} + TEST_F(RtsanOpenedFileTest, FreadDiesWhenRealtime) { auto Func = [this]() { char c{}; @@ -779,4 +807,175 @@ TEST(TestRtsanInterceptors, ShutdownOnASocketDiesWhenRealtime) { ExpectNonRealtimeSurvival(Func); } +/* + I/O Multiplexing +*/ + +TEST(TestRtsanInterceptors, PollDiesWhenRealtime) { + struct pollfd fds[1]; + fds[0].fd = 0; + fds[0].events = POLLIN; + + auto Func = [&fds]() { poll(fds, 1, 0); }; + + ExpectRealtimeDeath(Func, "poll"); + ExpectNonRealtimeSurvival(Func); +} + +#if !SANITIZER_APPLE +// FIXME: This should work on Darwin as well +// see the comment near the interceptor +TEST(TestRtsanInterceptors, SelectDiesWhenRealtime) { + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(0, &readfds); + struct timeval timeout = {0, 0}; + + auto Func = [&readfds, &timeout]() { + select(1, &readfds, nullptr, nullptr, &timeout); + }; + ExpectRealtimeDeath(Func, "select"); + ExpectNonRealtimeSurvival(Func); +} +#endif + +TEST(TestRtsanInterceptors, PSelectDiesWhenRealtime) { + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(0, &readfds); + struct timespec timeout = {0, 0}; + + auto Func = [&]() { + pselect(1, &readfds, nullptr, nullptr, &timeout, nullptr); + }; + ExpectRealtimeDeath(Func, "pselect"); + ExpectNonRealtimeSurvival(Func); +} + +#if SANITIZER_INTERCEPT_EPOLL +TEST(TestRtsanInterceptors, EpollCreateDiesWhenRealtime) { + auto Func = []() { epoll_create(1); }; + ExpectRealtimeDeath(Func, "epoll_create"); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsanInterceptors, EpollCreate1DiesWhenRealtime) { + auto Func = []() { epoll_create1(EPOLL_CLOEXEC); }; + ExpectRealtimeDeath(Func, "epoll_create1"); + ExpectNonRealtimeSurvival(Func); +} + +class EpollTest : public ::testing::Test { +protected: + void SetUp() override { + epfd = epoll_create1(EPOLL_CLOEXEC); + ASSERT_GE(epfd, 0); + } + + void TearDown() override { + if (epfd >= 0) + close(epfd); + } + + int GetEpollFd() { return epfd; } + +private: + int epfd = -1; +}; + +TEST_F(EpollTest, EpollCtlDiesWhenRealtime) { + auto Func = [this]() { + struct epoll_event event = {.events = EPOLLIN, .data = {.fd = 0}}; + epoll_ctl(GetEpollFd(), EPOLL_CTL_ADD, 0, &event); + }; + ExpectRealtimeDeath(Func, "epoll_ctl"); + ExpectNonRealtimeSurvival(Func); +} + +TEST_F(EpollTest, EpollWaitDiesWhenRealtime) { + auto Func = [this]() { + struct epoll_event events[1]; + epoll_wait(GetEpollFd(), events, 1, 0); + }; + + ExpectRealtimeDeath(Func, "epoll_wait"); + ExpectNonRealtimeSurvival(Func); +} + +TEST_F(EpollTest, EpollPWaitDiesWhenRealtime) { + auto Func = [this]() { + struct epoll_event events[1]; + epoll_pwait(GetEpollFd(), events, 1, 0, nullptr); + }; + + ExpectRealtimeDeath(Func, "epoll_pwait"); + ExpectNonRealtimeSurvival(Func); +} +#endif // SANITIZER_INTERCEPT_EPOLL + +#if SANITIZER_INTERCEPT_KQUEUE +TEST(TestRtsanInterceptors, KqueueDiesWhenRealtime) { + auto Func = []() { kqueue(); }; + ExpectRealtimeDeath(Func, "kqueue"); + ExpectNonRealtimeSurvival(Func); +} + +class KqueueTest : public ::testing::Test { +protected: + void SetUp() override { + kq = kqueue(); + ASSERT_GE(kq, 0); + } + + void TearDown() override { + if (kq >= 0) + close(kq); + } + + int GetKqueueFd() { return kq; } + +private: + int kq = -1; +}; + +TEST_F(KqueueTest, KeventDiesWhenRealtime) { + struct kevent event; + EV_SET(&event, 0, EVFILT_READ, EV_ADD, 0, 0, nullptr); + struct timespec timeout = {0, 0}; + + auto Func = [this, event, timeout]() { + kevent(GetKqueueFd(), &event, 1, nullptr, 0, &timeout); + }; + + ExpectRealtimeDeath(Func, "kevent"); + ExpectNonRealtimeSurvival(Func); +} + +TEST_F(KqueueTest, Kevent64DiesWhenRealtime) { + struct kevent64_s event; + EV_SET64(&event, 0, EVFILT_READ, EV_ADD, 0, 0, 0, 0, 0); + struct timespec timeout = {0, 0}; + + auto Func = [this, event, timeout]() { + kevent64(GetKqueueFd(), &event, 1, nullptr, 0, 0, &timeout); + }; + + ExpectRealtimeDeath(Func, "kevent64"); + ExpectNonRealtimeSurvival(Func); +} +#endif // SANITIZER_INTERCEPT_KQUEUE + +TEST(TestRtsanInterceptors, MkfifoDiesWhenRealtime) { + auto Func = []() { mkfifo("/tmp/rtsan_test_fifo", 0); }; + ExpectRealtimeDeath(Func, "mkfifo"); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsanInterceptors, PipeDiesWhenRealtime) { + int fds[2]; + auto Func = [&fds]() { pipe(fds); }; + ExpectRealtimeDeath(Func, "pipe"); + ExpectNonRealtimeSurvival(Func); +} + #endif // SANITIZER_POSIX diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc index b8627f8557afe..99fa737adfaf2 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -2289,6 +2289,61 @@ INTERCEPTOR(int, pthread_getcpuclockid, uptr thread, #define INIT_CLOCK_GETCPUCLOCKID #endif +#if SANITIZER_INTERCEPT_TIMER_CREATE +INTERCEPTOR(int, timer_create, __sanitizer_clockid_t clockid, void *sevp, + __sanitizer_timer_t *timer) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, timer_create, clockid, sevp, timer); + int res = REAL(timer_create)(clockid, sevp, timer); + if (!res && timer) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, timer, sizeof *timer); + } + return res; +} + +INTERCEPTOR(int, timer_delete, __sanitizer_timer_t timer) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, timer_delete, timer); + int res = REAL(timer_delete)(timer); + return res; +} + +INTERCEPTOR(int, timer_gettime, __sanitizer_timer_t timer, + struct __sanitizer_itimerspec *curr_value) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, timer_gettime, timer, curr_value); + int res = REAL(timer_gettime)(timer, curr_value); + if (!res && curr_value) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, curr_value, sizeof *curr_value); + } + return res; +} + +INTERCEPTOR(int, timer_settime, __sanitizer_timer_t timer, int flags, + const struct __sanitizer_itimerspec *new_value, + struct __sanitizer_itimerspec *old_value) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, timer_settime, timer, flags, new_value, + old_value); + int res = REAL(timer_settime)(timer, flags, new_value, old_value); + if (!res) { + if (new_value) + COMMON_INTERCEPTOR_READ_RANGE(ctx, new_value, sizeof *new_value); + if (old_value) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, old_value, sizeof *old_value); + } + return res; +} + +# define INIT_TIMER_CREATE \ + COMMON_INTERCEPT_FUNCTION_GLIBC_VER_MIN(timer_create, "GLIBC_2.3.3"); \ + COMMON_INTERCEPT_FUNCTION_GLIBC_VER_MIN(timer_delete, "GLIBC_2.3.3"); \ + COMMON_INTERCEPT_FUNCTION_GLIBC_VER_MIN(timer_gettime, "GLIBC_2.3.3"); \ + COMMON_INTERCEPT_FUNCTION_GLIBC_VER_MIN(timer_settime, "GLIBC_2.3.3"); +#else +# define INIT_TIMER_CREATE +#endif + #if SANITIZER_INTERCEPT_GETITIMER INTERCEPTOR(int, getitimer, int which, void *curr_value) { void *ctx; @@ -10266,6 +10321,7 @@ static void InitializeCommonInterceptors() { INIT_SETPWENT; INIT_CLOCK_GETTIME; INIT_CLOCK_GETCPUCLOCKID; + INIT_TIMER_CREATE; INIT_GETITIMER; INIT_TIME; INIT_GLOB; diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h b/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h index 3fd6b595ef197..1f78b1af8e2c6 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h @@ -210,6 +210,8 @@ SANITIZER_WEAK_IMPORT void *aligned_alloc(__sanitizer::usize __alignment, #define SANITIZER_INTERCEPT_PREAD64 (SI_GLIBC || SI_SOLARIS32) #define SANITIZER_INTERCEPT_PWRITE64 (SI_GLIBC || SI_SOLARIS32) +#define SANITIZER_INTERCEPT_LSEEK64 (SI_GLIBC || SI_SOLARIS32) + #define SANITIZER_INTERCEPT_READV SI_POSIX #define SANITIZER_INTERCEPT_WRITEV SI_POSIX @@ -256,6 +258,9 @@ SANITIZER_WEAK_IMPORT void *aligned_alloc(__sanitizer::usize __alignment, (SI_FREEBSD || SI_NETBSD || SI_LINUX || SI_SOLARIS) #define SANITIZER_INTERCEPT_CLOCK_GETCPUCLOCKID \ (SI_LINUX || SI_FREEBSD || SI_NETBSD) +// TODO: This should be SI_POSIX, adding glibc first until I have time +// to verify all timer_t typedefs on other platforms. +#define SANITIZER_INTERCEPT_TIMER_CREATE SI_GLIBC #define SANITIZER_INTERCEPT_GETITIMER SI_POSIX #define SANITIZER_INTERCEPT_TIME SI_POSIX #define SANITIZER_INTERCEPT_GLOB (SI_GLIBC || SI_SOLARIS) @@ -339,6 +344,8 @@ SANITIZER_WEAK_IMPORT void *aligned_alloc(__sanitizer::usize __alignment, #define SANITIZER_INTERCEPT_GETGROUPS SI_POSIX #define SANITIZER_INTERCEPT_POLL SI_POSIX #define SANITIZER_INTERCEPT_PPOLL SI_LINUX_NOT_ANDROID || SI_SOLARIS +#define SANITIZER_INTERCEPT_EPOLL (SI_LINUX) +#define SANITIZER_INTERCEPT_KQUEUE (SI_FREEBSD || SI_NETBSD || SI_MAC) #define SANITIZER_INTERCEPT_WORDEXP \ (SI_FREEBSD || SI_NETBSD || (SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID || \ SI_SOLARIS) diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h b/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h index e8c81aa8e2816..7d98f8e9a9d80 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h @@ -389,6 +389,16 @@ typedef long __sanitizer_time_t; typedef long __sanitizer_suseconds_t; +struct __sanitizer_timespec { + __sanitizer_time_t tv_sec; /* seconds */ + u64 tv_nsec; /* nanoseconds */ +}; + +struct __sanitizer_itimerspec { + struct __sanitizer_timespec it_interval; /* timer period */ + struct __sanitizer_timespec it_value; /* timer expiration */ +}; + struct __sanitizer_timeval { __sanitizer_time_t tv_sec; __sanitizer_suseconds_t tv_usec; @@ -1517,6 +1527,10 @@ extern const int si_SEGV_ACCERR; #define SIGACTION_SYMNAME sigaction +# if SANITIZER_LINUX +typedef void *__sanitizer_timer_t; +# endif + #endif // SANITIZER_LINUX || SANITIZER_APPLE #endif diff --git a/compiler-rt/test/profile/ContinuousSyncMode/multi-threaded.cpp b/compiler-rt/test/profile/ContinuousSyncMode/multi-threaded.cpp new file mode 100644 index 0000000000000..ff05a69a5e7d4 --- /dev/null +++ b/compiler-rt/test/profile/ContinuousSyncMode/multi-threaded.cpp @@ -0,0 +1,29 @@ +// REQUIRES: target={{.*(darwin|aix).*}} + +// RUN: rm -f %t.profraw +// RUN: %clangxx_pgogen_cont -lpthread %s -o %t.exe -mllvm -disable-vp -fprofile-update=atomic +// RUN: env LLVM_PROFILE_FILE="%c%t.profraw" %run %t.exe +// RUN: llvm-profdata show --counts --function=accum %t.profraw | FileCheck %s +// CHECK: Block counts: [100000, 4] + +#include + +int x = 0; +void accum(int n) { + for (int i = 0; i < n; i++) + x += i; // don't care about accuracy, no need for atomic. +} + +int main() { + int init_value = 10000; + auto t1 = std::thread(accum, 1*init_value); + auto t2 = std::thread(accum, 2*init_value); + auto t3 = std::thread(accum, 3*init_value); + auto t4 = std::thread(accum, 4*init_value); + + t1.join(); + t2.join(); + t3.join(); + t4.join(); + return !x; +} diff --git a/compiler-rt/test/profile/ContinuousSyncMode/online-merging.c b/compiler-rt/test/profile/ContinuousSyncMode/online-merging.c index 35b0cd0b05d1f..54346487a5c79 100644 --- a/compiler-rt/test/profile/ContinuousSyncMode/online-merging.c +++ b/compiler-rt/test/profile/ContinuousSyncMode/online-merging.c @@ -8,9 +8,9 @@ // Create two DSOs and a driver program that uses them. // RUN: echo "void dso1(void) {}" > dso1.c // RUN: echo "void dso2(void) {}" > dso2.c -// RUN: %clang_pgogen_cont %shared_lib_flag -o %t.dir/dso1.dylib dso1.c -mllvm -instrprof-atomic-counter-update-all=1 -// RUN: %clang_pgogen_cont %shared_lib_flag -o %t.dir/dso2.dylib dso2.c -mllvm -instrprof-atomic-counter-update-all=1 -// RUN: %clang_pgogen_cont -o main.exe %s %t.dir/dso1.dylib %t.dir/dso2.dylib -mllvm -instrprof-atomic-counter-update-all=1 +// RUN: %clang_pgogen_cont %shared_lib_flag -o %t.dir/dso1.dylib dso1.c -fprofile-update=atomic +// RUN: %clang_pgogen_cont %shared_lib_flag -o %t.dir/dso2.dylib dso2.c -fprofile-update=atomic +// RUN: %clang_pgogen_cont -o main.exe %s %t.dir/dso1.dylib %t.dir/dso2.dylib -fprofile-update=atomic // // === Round 1 === // Test merging+continuous mode without any file contention. diff --git a/compiler-rt/test/profile/lit.cfg.py b/compiler-rt/test/profile/lit.cfg.py index 7a8877b9f4e50..72a389eaf0dfb 100644 --- a/compiler-rt/test/profile/lit.cfg.py +++ b/compiler-rt/test/profile/lit.cfg.py @@ -138,6 +138,14 @@ def exclude_unsupported_files_for_aix(dirname): config.substitutions.append( ("%clangxx_pgogen=", build_invocation(clang_cxxflags) + " -fprofile-generate=") ) +config.substitutions.append( + ( + "%clangxx_pgogen_cont ", + build_invocation(clang_cxxflags) + + " -fprofile-generate " + + ("-mllvm -runtime-counter-relocation " if runtime_reloc else ""), + ) +) config.substitutions.append( ("%clang_cspgogen ", build_invocation(clang_cflags) + " -fcs-profile-generate ") diff --git a/compiler-rt/test/rtsan/report_error_summary.cpp b/compiler-rt/test/rtsan/report_error_summary.cpp new file mode 100644 index 0000000000000..9da7f217f61bf --- /dev/null +++ b/compiler-rt/test/rtsan/report_error_summary.cpp @@ -0,0 +1,32 @@ +// RUN: %clangxx -fsanitize=realtime %s -o %t +// RUN: %env_rtsan_opts="halt_on_error=false" %run %t 2>&1 | FileCheck %s + +// RUN: %clangxx -DTEST_CUSTOM_HANDLER=1 -fsanitize=realtime %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK-CUSTOM-HANDLER + +// UNSUPPORTED: ios + +// Intent: Make sure we support ReporErrorSummary, including custom handlers + +#include +#include + +#ifdef TEST_CUSTOM_HANDLER +extern "C" void __sanitizer_report_error_summary(const char *error_summary) { + fprintf(stderr, "%s %s\n", "In custom handler! ", error_summary); +} +#endif + +int blocking_call() [[clang::blocking]] { return 0; } + +int main() [[clang::nonblocking]] { + void *ptr = malloc(2); + blocking_call(); + + printf("ptr: %p\n", ptr); // ensure we don't optimize out the malloc +} + +// CHECK: SUMMARY: RealtimeSanitizer: unsafe-library-call +// CHECK: SUMMARY: RealtimeSanitizer: blocking-call + +// CHECK-CUSTOM-HANDLER: In custom handler! SUMMARY: RealtimeSanitizer: unsafe-library-call diff --git a/flang/CODE_OWNERS.TXT b/flang/Maintainers.txt similarity index 100% rename from flang/CODE_OWNERS.TXT rename to flang/Maintainers.txt diff --git a/flang/examples/FeatureList/FeatureList.cpp b/flang/examples/FeatureList/FeatureList.cpp index dc68f160f5d92..fe7fee97bc78e 100644 --- a/flang/examples/FeatureList/FeatureList.cpp +++ b/flang/examples/FeatureList/FeatureList.cpp @@ -475,9 +475,9 @@ struct NodeVisitor { READ_FEATURE(OmpDoacross::Source) READ_FEATURE(OmpDoacrossClause) READ_FEATURE(OmpDependenceType) - READ_FEATURE(OmpDependenceType::Type) + READ_FEATURE(OmpDependenceType::Value) READ_FEATURE(OmpTaskDependenceType) - READ_FEATURE(OmpTaskDependenceType::Type) + READ_FEATURE(OmpTaskDependenceType::Value) READ_FEATURE(OmpIteration) READ_FEATURE(OmpIterationOffset) READ_FEATURE(OmpIterationVector) @@ -495,7 +495,7 @@ struct NodeVisitor { READ_FEATURE(OmpLinearClause::WithModifier) READ_FEATURE(OmpLinearClause::WithoutModifier) READ_FEATURE(OmpLinearModifier) - READ_FEATURE(OmpLinearModifier::Type) + READ_FEATURE(OmpLinearModifier::Value) READ_FEATURE(OmpLoopDirective) READ_FEATURE(OmpMapClause) READ_FEATURE(OmpMapClause::TypeModifier) @@ -515,7 +515,7 @@ struct NodeVisitor { READ_FEATURE(OmpReductionCombiner) READ_FEATURE(OmpReductionCombiner::FunctionCombiner) READ_FEATURE(OmpReductionInitializerClause) - READ_FEATURE(OmpReductionOperator) + READ_FEATURE(OmpReductionIdentifier) READ_FEATURE(OmpAllocateClause) READ_FEATURE(OmpAllocateClause::AllocateModifier) READ_FEATURE(OmpAllocateClause::AllocateModifier::Allocator) diff --git a/flang/examples/FlangOmpReport/FlangOmpReportVisitor.cpp b/flang/examples/FlangOmpReport/FlangOmpReportVisitor.cpp index d28ed0534d600..c184fdafb5c33 100644 --- a/flang/examples/FlangOmpReport/FlangOmpReportVisitor.cpp +++ b/flang/examples/FlangOmpReport/FlangOmpReportVisitor.cpp @@ -218,11 +218,11 @@ void OpenMPCounterVisitor::Post(const OmpScheduleModifierType::ModType &c) { clauseDetails += "modifier=" + std::string{OmpScheduleModifierType::EnumToString(c)} + ";"; } -void OpenMPCounterVisitor::Post(const OmpLinearModifier::Type &c) { +void OpenMPCounterVisitor::Post(const OmpLinearModifier::Value &c) { clauseDetails += "modifier=" + std::string{OmpLinearModifier::EnumToString(c)} + ";"; } -void OpenMPCounterVisitor::Post(const OmpTaskDependenceType::Type &c) { +void OpenMPCounterVisitor::Post(const OmpTaskDependenceType::Value &c) { clauseDetails += "type=" + std::string{OmpTaskDependenceType::EnumToString(c)} + ";"; } diff --git a/flang/examples/FlangOmpReport/FlangOmpReportVisitor.h b/flang/examples/FlangOmpReport/FlangOmpReportVisitor.h index 68c52db46e2f0..6c2d194a88e69 100644 --- a/flang/examples/FlangOmpReport/FlangOmpReportVisitor.h +++ b/flang/examples/FlangOmpReport/FlangOmpReportVisitor.h @@ -72,8 +72,8 @@ struct OpenMPCounterVisitor { void Post(const OmpDefaultmapClause::VariableCategory &c); void Post(const OmpDeviceTypeClause::Type &c); void Post(const OmpScheduleModifierType::ModType &c); - void Post(const OmpLinearModifier::Type &c); - void Post(const OmpTaskDependenceType::Type &c); + void Post(const OmpLinearModifier::Value &c); + void Post(const OmpTaskDependenceType::Value &c); void Post(const OmpMapClause::Type &c); void Post(const OmpScheduleClause::ScheduleType &c); void Post(const OmpIfClause::DirectiveNameModifier &c); diff --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h index a8a6eb922a045..6261a4eec4a55 100644 --- a/flang/include/flang/Evaluate/tools.h +++ b/flang/include/flang/Evaluate/tools.h @@ -1416,6 +1416,8 @@ common::IgnoreTKRSet GetIgnoreTKR(const Symbol &); std::optional GetDummyArgumentNumber(const Symbol *); +const Symbol *FindAncestorModuleProcedure(const Symbol *symInSubmodule); + } // namespace Fortran::semantics #endif // FORTRAN_EVALUATE_TOOLS_H_ diff --git a/flang/include/flang/Optimizer/Builder/HLFIRTools.h b/flang/include/flang/Optimizer/Builder/HLFIRTools.h index 6b41025eea078..f073f494b3fb2 100644 --- a/flang/include/flang/Optimizer/Builder/HLFIRTools.h +++ b/flang/include/flang/Optimizer/Builder/HLFIRTools.h @@ -357,8 +357,8 @@ hlfir::ElementalOp genElementalOp( /// Structure to describe a loop nest. struct LoopNest { - fir::DoLoopOp outerLoop; - fir::DoLoopOp innerLoop; + mlir::Operation *outerOp = nullptr; + mlir::Block *body = nullptr; llvm::SmallVector oneBasedIndices; }; @@ -366,11 +366,13 @@ struct LoopNest { /// \p isUnordered specifies whether the loops in the loop nest /// are unordered. LoopNest genLoopNest(mlir::Location loc, fir::FirOpBuilder &builder, - mlir::ValueRange extents, bool isUnordered = false); + mlir::ValueRange extents, bool isUnordered = false, + bool emitWorkshareLoop = false); inline LoopNest genLoopNest(mlir::Location loc, fir::FirOpBuilder &builder, - mlir::Value shape, bool isUnordered = false) { + mlir::Value shape, bool isUnordered = false, + bool emitWorkshareLoop = false) { return genLoopNest(loc, builder, getIndexExtents(loc, builder, shape), - isUnordered); + isUnordered, emitWorkshareLoop); } /// Inline the body of an hlfir.elemental at the current insertion point diff --git a/flang/include/flang/Optimizer/OpenMP/Passes.h b/flang/include/flang/Optimizer/OpenMP/Passes.h index 403d79667bf44..feb395f1a12db 100644 --- a/flang/include/flang/Optimizer/OpenMP/Passes.h +++ b/flang/include/flang/Optimizer/OpenMP/Passes.h @@ -25,6 +25,11 @@ namespace flangomp { #define GEN_PASS_REGISTRATION #include "flang/Optimizer/OpenMP/Passes.h.inc" +/// Impelements the logic specified in the 2.8.3 workshare Construct section of +/// the OpenMP standard which specifies what statements or constructs shall be +/// divided into units of work. +bool shouldUseWorkshareLowering(mlir::Operation *op); + } // namespace flangomp #endif // FORTRAN_OPTIMIZER_OPENMP_PASSES_H diff --git a/flang/include/flang/Optimizer/OpenMP/Passes.td b/flang/include/flang/Optimizer/OpenMP/Passes.td index c070bc22ff20c..37977334c1e9e 100644 --- a/flang/include/flang/Optimizer/OpenMP/Passes.td +++ b/flang/include/flang/Optimizer/OpenMP/Passes.td @@ -50,4 +50,9 @@ def FunctionFilteringPass : Pass<"omp-function-filtering"> { ]; } +// Needs to be scheduled on Module as we create functions in it +def LowerWorkshare : Pass<"lower-workshare", "::mlir::ModuleOp"> { + let summary = "Lower workshare construct"; +} + #endif //FORTRAN_OPTIMIZER_OPENMP_PASSES diff --git a/flang/include/flang/Optimizer/Passes/Pipelines.h b/flang/include/flang/Optimizer/Passes/Pipelines.h index 3b54ac3883858..55fafc2e6b36f 100644 --- a/flang/include/flang/Optimizer/Passes/Pipelines.h +++ b/flang/include/flang/Optimizer/Passes/Pipelines.h @@ -123,7 +123,8 @@ void createDefaultFIROptimizerPassPipeline(mlir::PassManager &pm, /// \param optLevel - optimization level used for creating FIR optimization /// passes pipeline void createHLFIRToFIRPassPipeline( - mlir::PassManager &pm, llvm::OptimizationLevel optLevel = defaultOptLevel); + mlir::PassManager &pm, bool enableOpenMP, + llvm::OptimizationLevel optLevel = defaultOptLevel); /// Create a pass pipeline for handling certain OpenMP transformations needed /// prior to FIR lowering. diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h index 5886e384b986b..63fddc424182b 100644 --- a/flang/include/flang/Parser/dump-parse-tree.h +++ b/flang/include/flang/Parser/dump-parse-tree.h @@ -477,7 +477,7 @@ class ParseTreeDumper { NODE(parser, ObjectDecl) NODE(parser, OldParameterStmt) NODE(parser, OmpIteratorSpecifier) - NODE(parser, OmpIteratorModifier) + NODE(parser, OmpIterator) NODE(parser, OmpAffinityClause) NODE(parser, OmpAlignedClause) NODE(parser, OmpAtomic) @@ -513,9 +513,9 @@ class ParseTreeDumper { NODE_ENUM(OmpDefaultmapClause, ImplicitBehavior) NODE_ENUM(OmpDefaultmapClause, VariableCategory) NODE(parser, OmpDependenceType) - NODE_ENUM(OmpDependenceType, Type) + NODE_ENUM(OmpDependenceType, Value) NODE(parser, OmpTaskDependenceType) - NODE_ENUM(OmpTaskDependenceType, Type) + NODE_ENUM(OmpTaskDependenceType, Value) NODE(parser, OmpIterationOffset) NODE(parser, OmpIteration) NODE(parser, OmpIterationVector) @@ -543,9 +543,10 @@ class ParseTreeDumper { NODE(OmpLinearClause, WithModifier) NODE(OmpLinearClause, WithoutModifier) NODE(parser, OmpLinearModifier) - NODE_ENUM(OmpLinearModifier, Type) + NODE_ENUM(OmpLinearModifier, Value) NODE(parser, OmpLoopDirective) NODE(parser, OmpMapClause) + NODE(parser, OmpMapperIdentifier) NODE_ENUM(OmpMapClause, TypeModifier) NODE_ENUM(OmpMapClause, Type) static std::string GetNodeName(const llvm::omp::Clause &x) { @@ -573,7 +574,7 @@ class ParseTreeDumper { NODE(parser, OmpReductionCombiner) NODE(OmpReductionCombiner, FunctionCombiner) NODE(parser, OmpReductionInitializerClause) - NODE(parser, OmpReductionOperator) + NODE(parser, OmpReductionIdentifier) NODE(parser, OmpAllocateClause) NODE(OmpAllocateClause, AllocateModifier) NODE(OmpAllocateClause::AllocateModifier, Allocator) diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h index 8f50809599a58..22b7f9acd1af5 100644 --- a/flang/include/flang/Parser/parse-tree.h +++ b/flang/include/flang/Parser/parse-tree.h @@ -3440,13 +3440,33 @@ struct OmpObject { WRAPPER_CLASS(OmpObjectList, std::list); +inline namespace modifier { +// For uniformity, in all keyword modifiers the name of the type defined +// by ENUM_CLASS is "Value", e.g. +// struct Foo { +// ENUM_CLASS(Value, Keyword1, Keyword2); +// }; + +// Ref: [5.0:47-49], [5.1:49-51], [5.2:67-69] +// +// iterator-specifier -> +// [iterator-type] iterator-identifier +// = range-specification | // since 5.0 +// [iterator-type ::] iterator-identifier +// = range-specification // since 5.2 +struct OmpIteratorSpecifier { + TUPLE_CLASS_BOILERPLATE(OmpIteratorSpecifier); + CharBlock source; + std::tuple t; +}; + // Ref: [4.5:169-170], [5.0:255-256], [5.1:288-289] // // dependence-type -> -// SINK | SOURCE | // since 4.5 -// IN | OUT | INOUT | // since 4.5, until 5.1 -// MUTEXINOUTSET | DEPOBJ | // since 5.0, until 5.1 -// INOUTSET // since 5.1, until 5.1 +// SINK | SOURCE | // since 4.5 +// IN | OUT | INOUT | // since 4.5, until 5.1 +// MUTEXINOUTSET | DEPOBJ | // since 5.0, until 5.1 +// INOUTSET // since 5.1, until 5.1 // // All of these, except SINK and SOURCE became task-dependence-type in 5.2. // @@ -3457,45 +3477,59 @@ WRAPPER_CLASS(OmpObjectList, std::list); // vector). This would accept the vector "i, j, k" (although interpreted // incorrectly), while flagging a syntax error for "i+1, j, k". struct OmpDependenceType { - ENUM_CLASS(Type, Sink, Source); - WRAPPER_CLASS_BOILERPLATE(OmpDependenceType, Type); + ENUM_CLASS(Value, Sink, Source); + WRAPPER_CLASS_BOILERPLATE(OmpDependenceType, Value); }; -// Ref: [4.5:169-170], [5.0:254-256], [5.1:287-289], [5.2:321] +// Ref: [5.0:47-49], [5.1:49-51], [5.2:67-69] // -// task-dependence-type -> // "dependence-type" in 5.1 and before -// IN | OUT | INOUT | // since 4.5 -// MUTEXINOUTSET | DEPOBJ | // since 5.0 -// INOUTSET // since 5.2 -struct OmpTaskDependenceType { - ENUM_CLASS(Type, In, Out, Inout, Inoutset, Mutexinoutset, Depobj) - WRAPPER_CLASS_BOILERPLATE(OmpTaskDependenceType, Type); +// iterator-modifier -> +// ITERATOR(iterator-specifier [, ...]) // since 5.0 +struct OmpIterator { + WRAPPER_CLASS_BOILERPLATE(OmpIterator, std::list); }; -// [5.0] 2.1.6 iterator-specifier -> type-declaration-stmt = subscript-triple -// iterator-modifier -> iterator-specifier-list -struct OmpIteratorSpecifier { - TUPLE_CLASS_BOILERPLATE(OmpIteratorSpecifier); - CharBlock source; - std::tuple t; +// Ref: [4.5:207-210], [5.0:290-293], [5.1:323-325], [5.2:117-120] +// +// linear-modifier -> +// REF | UVAL | VAL // since 4.5 +struct OmpLinearModifier { + ENUM_CLASS(Value, Ref, Uval, Val); + WRAPPER_CLASS_BOILERPLATE(OmpLinearModifier, Value); }; -WRAPPER_CLASS(OmpIteratorModifier, std::list); - -// 2.15.3.6 reduction-identifier -> + | - | * | .AND. | .OR. | .EQV. | .NEQV. | -// MAX | MIN | IAND | IOR | IEOR -struct OmpReductionOperator { - UNION_CLASS_BOILERPLATE(OmpReductionOperator); +// Ref: [4.5:201-207], [5.0:293-299], [5.1:325-331], [5.2:124] +// +// reduction-identifier -> +// base-language-identifier | // since 4.5 +// - | // since 4.5, until 5.2 +// + | * | .AND. | .OR. | .EQV. | .NEQV. | // since 4.5 +// MIN | MAX | IAND | IOR | IEOR // since 4.5 +// +struct OmpReductionIdentifier { + UNION_CLASS_BOILERPLATE(OmpReductionIdentifier); std::variant u; }; +// Ref: [4.5:169-170], [5.0:254-256], [5.1:287-289], [5.2:321] +// +// task-dependence-type -> // "dependence-type" in 5.1 and before +// IN | OUT | INOUT | // since 4.5 +// MUTEXINOUTSET | DEPOBJ | // since 5.0 +// INOUTSET // since 5.2 +struct OmpTaskDependenceType { + ENUM_CLASS(Value, In, Out, Inout, Inoutset, Mutexinoutset, Depobj) + WRAPPER_CLASS_BOILERPLATE(OmpTaskDependenceType, Value); +}; +} // namespace modifier + // --- Clauses // OMP 5.0 2.10.1 affinity([aff-modifier:] locator-list) // aff-modifier: interator-modifier struct OmpAffinityClause { TUPLE_CLASS_BOILERPLATE(OmpAffinityClause); - std::tuple, OmpObjectList> t; + std::tuple, OmpObjectList> t; }; // 2.8.1 aligned-clause -> ALIGNED (variable-name-list[ : scalar-constant]) @@ -3566,7 +3600,7 @@ WRAPPER_CLASS(OmpIterationVector, std::list); // OmpDoacrossClause), so that the context in TYPE_CONTEXT_PARSER can be set // separately for OmpDependClause and OmpDoacrossClause. struct OmpDoacross { - OmpDependenceType::Type GetDepType() const; + OmpDependenceType::Value GetDepType() const; WRAPPER_CLASS(Sink, OmpIterationVector); EMPTY_CLASS(Source); @@ -3586,10 +3620,9 @@ struct OmpDoacross { struct OmpDependClause { UNION_CLASS_BOILERPLATE(OmpDependClause); struct TaskDep { - OmpTaskDependenceType::Type GetTaskDepType() const; + OmpTaskDependenceType::Value GetTaskDepType() const; TUPLE_CLASS_BOILERPLATE(TaskDep); - std::tuple, OmpTaskDependenceType, - OmpObjectList> + std::tuple, OmpTaskDependenceType, OmpObjectList> t; }; std::variant u; @@ -3632,7 +3665,7 @@ struct OmpFromClause { // As in the case of MAP, modifiers are parsed as lists, even if they // are unique. These restrictions will be checked in semantic checks. std::tuple>, - std::optional>, OmpObjectList, + std::optional>, OmpObjectList, bool> // were the modifiers comma-separated? t; }; @@ -3661,7 +3694,7 @@ struct OmpDetachClause { // variable-name-list) struct OmpInReductionClause { TUPLE_CLASS_BOILERPLATE(OmpInReductionClause); - std::tuple t; + std::tuple t; }; // OMP 5.0 2.19.4.5 lastprivate-clause -> @@ -3673,12 +3706,6 @@ struct OmpLastprivateClause { std::tuple, OmpObjectList> t; }; -// 2.15.3.7 linear-modifier -> REF | VAL | UVAL -struct OmpLinearModifier { - ENUM_CLASS(Type, Ref, Val, Uval) - WRAPPER_CLASS_BOILERPLATE(OmpLinearModifier, Type); -}; - // 2.15.3.7 linear-clause -> LINEAR (linear-list[ : linear-step]) // linear-list -> list | linear-modifier(list) struct OmpLinearClause { @@ -3703,8 +3730,11 @@ struct OmpLinearClause { std::variant u; }; +WRAPPER_CLASS(OmpMapperIdentifier, std::optional); + // 2.15.5.1 map -> -// MAP ([[map-type-modifier-list [,]] [iterator-modifier [,]] map-type : ] +// MAP ([MAPPER(mapper-identifier)] [[map-type-modifier-list [,]] +// [iterator-modifier [,]] map-type : ] // variable-name-list) // map-type-modifier-list -> map-type-modifier [,] [...] // map-type-modifier -> ALWAYS | CLOSE | PRESENT | OMPX_HOLD @@ -3718,8 +3748,9 @@ struct OmpMapClause { // The checks for satisfying those constraints are deferred to semantics. // In OpenMP 5.2 the non-comma syntax has been deprecated: keep the // information about separator presence to emit a diagnostic if needed. - std::tuple>, - std::optional>, // unique + std::tuple>, + std::optional>, // unique std::optional>, // unique OmpObjectList, bool> // were the modifiers comma-separated? @@ -3749,7 +3780,7 @@ struct OmpProcBindClause { struct OmpReductionClause { TUPLE_CLASS_BOILERPLATE(OmpReductionClause); ENUM_CLASS(ReductionModifier, Inscan, Task, Default) - std::tuple, OmpReductionOperator, + std::tuple, OmpReductionIdentifier, OmpObjectList> t; }; @@ -3794,7 +3825,7 @@ struct OmpToClause { // As in the case of MAP, modifiers are parsed as lists, even if they // are unique. These restrictions will be checked in semantic checks. std::tuple>, - std::optional>, OmpObjectList, + std::optional>, OmpObjectList, bool> // were the modifiers comma-separated? t; }; @@ -3942,7 +3973,7 @@ WRAPPER_CLASS(OmpReductionInitializerClause, Expr); struct OpenMPDeclareReductionConstruct { TUPLE_CLASS_BOILERPLATE(OpenMPDeclareReductionConstruct); CharBlock source; - std::tuple, + std::tuple, OmpReductionCombiner, std::optional> t; }; diff --git a/flang/include/flang/Semantics/openmp-modifiers.h b/flang/include/flang/Semantics/openmp-modifiers.h new file mode 100644 index 0000000000000..65d28f71fbc72 --- /dev/null +++ b/flang/include/flang/Semantics/openmp-modifiers.h @@ -0,0 +1,391 @@ +//===-- flang/lib/Semantics/openmp-modifiers.h ------------------*- 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 FORTRAN_SEMANTICS_OPENMP_MODIFIERS_H_ +#define FORTRAN_SEMANTICS_OPENMP_MODIFIERS_H_ + +#include "flang/Common/enum-set.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Semantics/semantics.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Frontend/OpenMP/OMP.h" + +#include +#include +#include +#include + +namespace Fortran::semantics { + +// Ref: [5.2:58] +// +// Syntactic properties for Clauses, Arguments and Modifiers +// +// Inverse properties: +// not Required -> Optional +// not Unique -> Repeatable +// not Exclusive -> Compatible +// not Ultimate -> Free +// +// Clause defaults: Optional, Repeatable, Compatible, Free +// Argument defaults: Required, Unique, Compatible, Free +// Modifier defaults: Optional, Unique, Compatible, Free +// +// --- +// Each modifier is used as either pre-modifier (i.e. modifier: item), +// or post-modifier (i.e. item: modifier). The default is pre-. +// Add an additional property that reflects the type of modifier. + +ENUM_CLASS(OmpProperty, Required, Unique, Exclusive, Ultimate, Post) +using OmpProperties = common::EnumSet; +using OmpClauses = + common::EnumSet; + +struct OmpModifierDescriptor { + // Modifier name for use in diagnostic messages. + const OmpProperties &props(unsigned version) const; + const OmpClauses &clauses(unsigned version) const; + + const llvm::StringRef name; + // Version-dependent properties of the modifier. + const std::map props_; + // Version-dependent set of clauses to which the modifier can apply. + const std::map clauses_; +}; + +template const OmpModifierDescriptor &OmpGetDescriptor(); + +template <> +const OmpModifierDescriptor &OmpGetDescriptor(); +template <> +const OmpModifierDescriptor &OmpGetDescriptor(); +template <> +const OmpModifierDescriptor &OmpGetDescriptor(); +template <> +const OmpModifierDescriptor &OmpGetDescriptor(); +template <> +const OmpModifierDescriptor &OmpGetDescriptor(); + +// Explanation of terminology: +// +// A typical clause with modifier[s] looks like this (with parts that are +// not relevant here removed): +// struct OmpSomeClause { +// struct Modifier { +// using Variant = std::variant; +// Variant u; +// }; +// std::tuple>, ...> t; +// }; +// +// The Speficic1, etc. refer to parser classes that represent modifiers, +// e.g. OmpIterator or OmpTaskDependenceType. The Variant type contains +// all modifiers that are allowed for a given clause. The Modifier class +// is there to wrap the variant into the form that the parse tree visitor +// expects, i.e. with traits, member "u", etc. +// +// To avoid ambiguities with the word "modifier" (e.g. is it "any modifier", +// or "this specific modifier"?), the following code uses different terms: +// +// - UnionTy: refers to the nested "Modifier" class, i.e. +// "OmpSomeClause::Modifier" in the example above. +// - SpecificTy: refers to any of the alternatives, i.e. "Specific1" or +// "Specific2". + +template +const OmpModifierDescriptor &OmpGetDescriptor(const UnionTy &modifier) { + return common::visit( + [](auto &&m) -> decltype(auto) { + using SpecificTy = llvm::remove_cvref_t; + return OmpGetDescriptor(); + }, + modifier.u); +} + +/// Return the optional list of modifiers for a given `Omp[...]Clause`. +/// Specifically, the parameter type `ClauseTy` is the class that OmpClause::v +/// holds. +template +const std::optional> &OmpGetModifiers( + const ClauseTy &clause) { + using UnionTy = typename ClauseTy::Modifier; + return std::get>>(clause.t); +} + +namespace detail { +/// Finds the first entry in the iterator range that holds the `SpecificTy` +/// alternative, or the end iterator if it does not exist. +/// The `SpecificTy` should be provided, the `UnionTy` is expected to be +/// auto-deduced, e.g. +/// const std::optional> &modifiers = ... +/// ... = findInRange(modifiers->begin(), modifiers->end()); +template +typename std::list::const_iterator findInRange( + typename std::list::const_iterator begin, + typename std::list::const_iterator end) { + for (auto it{begin}; it != end; ++it) { + if (std::holds_alternative(it->u)) { + return it; + } + } + return end; +} +} // namespace detail + +/// Finds the entry in the list that holds the `SpecificTy` alternative, +/// and returns the pointer to that alternative. If such an entry does not +/// exist, it returns nullptr. +/// The list is assumed to contain at most one such item, with a check +/// whether the condition is met. +/// This function should only be called after the verification of modifier +/// properties has been performed, since it will assert if multiple items +/// are found. +template +const SpecificTy *OmpGetUniqueModifier( + const std::optional> &modifiers) { + const SpecificTy *found{nullptr}; + if (modifiers) { + auto end{modifiers->cend()}; + // typename std::list::iterator end{modifiers->end()}; + auto at{detail::findInRange(modifiers->cbegin(), end)}; + if (at != end) { + found = &std::get(at->u); +#ifndef NDEBUG + auto another{ + detail::findInRange(std::next(at), end)}; + assert(another == end && "repeated modifier"); +#endif + } + } + return found; +} + +namespace detail { +template constexpr const T *make_nullptr() { + return static_cast(nullptr); +} + +/// Helper function for verifying the Required property: +/// For a specific SpecificTy, if SpecificTy is has the Required property, +/// check if the list has an item that holds SpecificTy as an alternative. +/// If SpecificTy does not have the Required property, ignore it. +template +bool verifyIfRequired(const SpecificTy *, + const std::optional> &modifiers, + parser::CharBlock clauseSource, SemanticsContext &semaCtx) { + unsigned version{semaCtx.langOptions().OpenMPVersion}; + const OmpModifierDescriptor &desc{OmpGetDescriptor()}; + if (!desc.props(version).test(OmpProperty::Required)) { + // If the modifier is not required, there is nothing to do. + return true; + } + bool present{modifiers.has_value()}; + present = present && llvm::any_of(*modifiers, [](auto &&m) { + return std::holds_alternative(m.u); + }); + if (!present) { + semaCtx.Say( + clauseSource, "A %s modifier is required"_err_en_US, desc.name.str()); + } + return present; +} + +/// Helper function for verifying the Required property: +/// Visit all specific types in UnionTy, and verify the Required property +/// for each one of them. +template +bool verifyRequiredPack(const std::optional> &modifiers, + parser::CharBlock clauseSource, SemanticsContext &semaCtx, + std::integer_sequence) { + using VariantTy = typename UnionTy::Variant; + return (verifyIfRequired( + make_nullptr>(), + modifiers, clauseSource, semaCtx) && + ...); +} + +/// Verify the Required property for the given list. Return true if the +/// list is valid, or false otherwise. +template +bool verifyRequired(const std::optional> &modifiers, + parser::CharBlock clauseSource, SemanticsContext &semaCtx) { + using VariantTy = typename UnionTy::Variant; + return verifyRequiredPack(modifiers, clauseSource, semaCtx, + std::make_index_sequence>{}); +} + +/// Helper function to verify the Unique property. +/// If SpecificTy has the Unique property, and an item is found holding +/// it as the alternative, verify that none of the elements that follow +/// hold SpecificTy as the alternative. +template +bool verifyIfUnique(const SpecificTy *, + typename std::list::const_iterator specific, + typename std::list::const_iterator end, + SemanticsContext &semaCtx) { + // `specific` is the location of the modifier of type SpecificTy. + assert(specific != end && "`specific` must be a valid location"); + + unsigned version{semaCtx.langOptions().OpenMPVersion}; + const OmpModifierDescriptor &desc{OmpGetDescriptor()}; + // Ultimate implies Unique. + if (!desc.props(version).test(OmpProperty::Unique) && + !desc.props(version).test(OmpProperty::Ultimate)) { + return true; + } + if (std::next(specific) != end) { + auto next{ + detail::findInRange(std::next(specific), end)}; + if (next != end) { + semaCtx.Say(next->source, "A %s cannot occur multiple times"_err_en_US, + desc.name.str()); + } + } + return true; +} + +/// Verify the Unique property for the given list. Return true if the +/// list is valid, or false otherwise. +template +bool verifyUnique(const std::optional> &modifiers, + parser::CharBlock clauseSource, SemanticsContext &semaCtx) { + if (!modifiers) { + return true; + } + bool result{true}; + for (auto it{modifiers->cbegin()}, end{modifiers->cend()}; it != end; ++it) { + result = common::visit( + [&](auto &&m) { + return verifyIfUnique(&m, it, end, semaCtx); + }, + it->u) && + result; + } + return result; +} + +/// Verify the Ultimate property for the given list. Return true if the +/// list is valid, or false otherwise. +template +bool verifyUltimate(const std::optional> &modifiers, + parser::CharBlock clauseSource, SemanticsContext &semaCtx) { + if (!modifiers || modifiers->size() <= 1) { + return true; + } + unsigned version{semaCtx.langOptions().OpenMPVersion}; + bool result{true}; + auto first{modifiers->cbegin()}; + auto last{std::prev(modifiers->cend())}; + + // Any item that has the Ultimate property has to be either at the back + // or at the front of the list (depending on whether it's a pre- or a post- + // modifier). + // Walk over the list, and if a given item has the Ultimate property but is + // not at the right position, mark it as an error. + for (auto it{first}, end{modifiers->cend()}; it != end; ++it) { + result = + common::visit( + [&](auto &&m) { + using SpecificTy = llvm::remove_cvref_t; + const OmpModifierDescriptor &desc{OmpGetDescriptor()}; + auto &props{desc.props(version)}; + + if (props.test(OmpProperty::Ultimate)) { + bool isPre = !props.test(OmpProperty::Post); + if (it == (isPre ? last : first)) { + // Skip, since this is the correct place for this modifier. + return true; + } + llvm::StringRef where{isPre ? "last" : "first"}; + semaCtx.Say(it->source, + "The %s should be the %s modifier"_err_en_US, + desc.name.str(), where.str()); + return false; + } + return true; + }, + it->u) && + result; + } + return result; +} + +/// Verify the Exclusive property for the given list. Return true if the +/// list is valid, or false otherwise. +template +bool verifyExclusive(const std::optional> &modifiers, + parser::CharBlock clauseSource, SemanticsContext &semaCtx) { + if (!modifiers || modifiers->size() <= 1) { + return true; + } + unsigned version{semaCtx.langOptions().OpenMPVersion}; + const UnionTy &front{modifiers->front()}; + const OmpModifierDescriptor &frontDesc{OmpGetDescriptor(front)}; + + auto second{std::next(modifiers->cbegin())}; + auto end{modifiers->end()}; + + auto emitErrorMessage{[&](const UnionTy &excl, const UnionTy &other) { + const OmpModifierDescriptor &descExcl{OmpGetDescriptor(excl)}; + const OmpModifierDescriptor &descOther{OmpGetDescriptor(other)}; + parser::MessageFormattedText txt( + "An exclusive %s cannot be specified together with a modifier of a different type"_err_en_US, + descExcl.name.str()); + parser::Message message(excl.source, txt); + message.Attach( + other.source, "%s provided here"_en_US, descOther.name.str()); + semaCtx.Say(std::move(message)); + }}; + + if (frontDesc.props(version).test(OmpProperty::Exclusive)) { + // If the first item has the Exclusive property, then check if there is + // another item in the rest of the list with a different SpecificTy as + // the alternative, and mark it as an error. This allows multiple Exclusive + // items to coexist as long as they hold the same SpecificTy. + bool result{true}; + size_t frontIndex{front.u.index()}; + for (auto it{second}; it != end; ++it) { + if (it->u.index() != frontIndex) { + emitErrorMessage(front, *it); + result = false; + break; + } + } + return result; + } else { + // If the first item does not have the Exclusive property, then check + // if there is an item in the rest of the list that is Exclusive, and + // mark it as an error if so. + bool result{true}; + for (auto it{second}; it != end; ++it) { + const OmpModifierDescriptor &desc{OmpGetDescriptor(*it)}; + if (desc.props(version).test(OmpProperty::Exclusive)) { + emitErrorMessage(*it, front); + result = false; + break; + } + } + return result; + } +} +} // namespace detail + +template +bool OmpVerifyModifiers(const ClauseTy &clause, parser::CharBlock clauseSource, + SemanticsContext &semaCtx) { + auto &modifiers{OmpGetModifiers(clause)}; + bool result{detail::verifyRequired(modifiers, clauseSource, semaCtx)}; + result = detail::verifyUnique(modifiers, clauseSource, semaCtx) && result; + result = detail::verifyUltimate(modifiers, clauseSource, semaCtx) && result; + result = detail::verifyExclusive(modifiers, clauseSource, semaCtx) && result; + return result; +} +} // namespace Fortran::semantics + +#endif // FORTRAN_SEMANTICS_OPENMP_MODIFIERS_H_ diff --git a/flang/include/flang/Tools/CrossToolHelpers.h b/flang/include/flang/Tools/CrossToolHelpers.h index df4b21ada058f..d936b739e5815 100644 --- a/flang/include/flang/Tools/CrossToolHelpers.h +++ b/flang/include/flang/Tools/CrossToolHelpers.h @@ -123,6 +123,7 @@ struct MLIRToLLVMPassPipelineConfig : public FlangEPCallBacks { false; ///< Set no-signed-zeros-fp-math attribute for functions. bool UnsafeFPMath = false; ///< Set unsafe-fp-math attribute for functions. bool NSWOnLoopVarInc = false; ///< Add nsw flag to loop variable increments. + bool EnableOpenMP = false; ///< Enable OpenMP lowering. }; struct OffloadModuleOpts { diff --git a/flang/lib/Evaluate/characteristics.cpp b/flang/lib/Evaluate/characteristics.cpp index 78cc63d0fde40..324d6b8dde73b 100644 --- a/flang/lib/Evaluate/characteristics.cpp +++ b/flang/lib/Evaluate/characteristics.cpp @@ -731,11 +731,16 @@ static std::optional CharacterizeProcedure( return std::optional{}; } }, - [&](const semantics::EntityDetails &) { + [&](const semantics::EntityDetails &x) { CheckForNested(symbol); return std::optional{}; }, [&](const semantics::SubprogramNameDetails &) { + if (const semantics::Symbol * + ancestor{FindAncestorModuleProcedure(&symbol)}) { + return CharacterizeProcedure( + *ancestor, context, seenProcs, emitError); + } CheckForNested(symbol); return std::optional{}; }, diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp index 4d98220a7065c..15e3e9452894d 100644 --- a/flang/lib/Evaluate/tools.cpp +++ b/flang/lib/Evaluate/tools.cpp @@ -1990,4 +1990,37 @@ std::optional GetDummyArgumentNumber(const Symbol *symbol) { return std::nullopt; } +// Given a symbol that is a SubprogramNameDetails in a submodule, try to +// find its interface definition in its module or ancestor submodule. +const Symbol *FindAncestorModuleProcedure(const Symbol *symInSubmodule) { + if (symInSubmodule && symInSubmodule->owner().IsSubmodule()) { + if (const auto *nameDetails{ + symInSubmodule->detailsIf()}; + nameDetails && + nameDetails->kind() == semantics::SubprogramKind::Module) { + const Symbol *next{symInSubmodule->owner().symbol()}; + while (const Symbol * submodSym{next}) { + next = nullptr; + if (const auto *modDetails{ + submodSym->detailsIf()}; + modDetails && modDetails->isSubmodule() && modDetails->scope()) { + if (const semantics::Scope & parent{modDetails->scope()->parent()}; + parent.IsSubmodule() || parent.IsModule()) { + if (auto iter{parent.find(symInSubmodule->name())}; + iter != parent.end()) { + const Symbol &proc{iter->second->GetUltimate()}; + if (IsProcedure(proc)) { + return &proc; + } + } else if (parent.IsSubmodule()) { + next = parent.symbol(); + } + } + } + } + } + } + return nullptr; +} + } // namespace Fortran::semantics diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp index f2e460fc53a67..8c21fe18e67b4 100644 --- a/flang/lib/Frontend/FrontendActions.cpp +++ b/flang/lib/Frontend/FrontendActions.cpp @@ -715,7 +715,11 @@ void CodeGenAction::lowerHLFIRToFIR() { pm.enableVerifier(/*verifyPasses=*/true); // Create the pass pipeline - fir::createHLFIRToFIRPassPipeline(pm, level); + fir::createHLFIRToFIRPassPipeline( + pm, + ci.getInvocation().getFrontendOpts().features.IsEnabled( + Fortran::common::LanguageFeature::OpenMP), + level); (void)mlir::applyPassManagerCLOptions(pm); if (!mlir::succeeded(pm.run(*mlirModule))) { @@ -828,6 +832,10 @@ void CodeGenAction::generateLLVMIR() { config.VScaleMax = vsr->second; } + if (ci.getInvocation().getFrontendOpts().features.IsEnabled( + Fortran::common::LanguageFeature::OpenMP)) + config.EnableOpenMP = true; + if (ci.getInvocation().getLoweringOpts().getNSWOnLoopVarInc()) config.NSWOnLoopVarInc = true; diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp index 9f5b58590fb79..e84e7afbe82e0 100644 --- a/flang/lib/Lower/ConvertCall.cpp +++ b/flang/lib/Lower/ConvertCall.cpp @@ -2135,7 +2135,7 @@ class ElementalCallBuilder { hlfir::genLoopNest(loc, builder, shape, !mustBeOrdered); mlir::ValueRange oneBasedIndices = loopNest.oneBasedIndices; auto insPt = builder.saveInsertionPoint(); - builder.setInsertionPointToStart(loopNest.innerLoop.getBody()); + builder.setInsertionPointToStart(loopNest.body); callContext.stmtCtx.pushScope(); for (auto &preparedActual : loweredActuals) if (preparedActual) diff --git a/flang/lib/Lower/OpenMP/Clauses.cpp b/flang/lib/Lower/OpenMP/Clauses.cpp index f897022ef9512..eddc742d4c095 100644 --- a/flang/lib/Lower/OpenMP/Clauses.cpp +++ b/flang/lib/Lower/OpenMP/Clauses.cpp @@ -10,6 +10,7 @@ #include "flang/Common/idioms.h" #include "flang/Evaluate/expression.h" +#include "flang/Optimizer/Builder/Todo.h" #include "flang/Parser/parse-tree.h" #include "flang/Semantics/expression.h" #include "flang/Semantics/symbol.h" @@ -264,7 +265,7 @@ makeIteratorSpecifiers(const parser::OmpIteratorSpecifier &inp, return specifiers; } -Iterator makeIterator(const parser::OmpIteratorModifier &inp, +Iterator makeIterator(const parser::OmpIterator &inp, semantics::SemanticsContext &semaCtx) { Iterator iterator; for (auto &&spec : inp.v) @@ -324,8 +325,9 @@ makeProcedureDesignator(const parser::ProcedureDesignator &inp, inp.u)}; } -ReductionOperator makeReductionOperator(const parser::OmpReductionOperator &inp, - semantics::SemanticsContext &semaCtx) { +ReductionOperator +makeReductionOperator(const parser::OmpReductionIdentifier &inp, + semantics::SemanticsContext &semaCtx) { return Fortran::common::visit( common::visitors{ [&](const parser::DefinedOperator &s) { @@ -340,9 +342,9 @@ ReductionOperator makeReductionOperator(const parser::OmpReductionOperator &inp, clause::DependenceType makeDepType(const parser::OmpDependenceType &inp) { switch (inp.v) { - case parser::OmpDependenceType::Type::Sink: + case parser::OmpDependenceType::Value::Sink: return clause::DependenceType::Sink; - case parser::OmpDependenceType::Type::Source: + case parser::OmpDependenceType::Value::Source: return clause::DependenceType::Source; } llvm_unreachable("Unexpected dependence type"); @@ -350,17 +352,17 @@ clause::DependenceType makeDepType(const parser::OmpDependenceType &inp) { clause::DependenceType makeDepType(const parser::OmpTaskDependenceType &inp) { switch (inp.v) { - case parser::OmpTaskDependenceType::Type::Depobj: + case parser::OmpTaskDependenceType::Value::Depobj: return clause::DependenceType::Depobj; - case parser::OmpTaskDependenceType::Type::In: + case parser::OmpTaskDependenceType::Value::In: return clause::DependenceType::In; - case parser::OmpTaskDependenceType::Type::Inout: + case parser::OmpTaskDependenceType::Value::Inout: return clause::DependenceType::Inout; - case parser::OmpTaskDependenceType::Type::Inoutset: + case parser::OmpTaskDependenceType::Value::Inoutset: return clause::DependenceType::Inoutset; - case parser::OmpTaskDependenceType::Type::Mutexinoutset: + case parser::OmpTaskDependenceType::Value::Mutexinoutset: return clause::DependenceType::Mutexinoutset; - case parser::OmpTaskDependenceType::Type::Out: + case parser::OmpTaskDependenceType::Value::Out: return clause::DependenceType::Out; } llvm_unreachable("Unexpected task dependence type"); @@ -381,7 +383,7 @@ Absent make(const parser::OmpClause::Absent &inp, Affinity make(const parser::OmpClause::Affinity &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpAffinityClause - auto &t0 = std::get>(inp.v.t); + auto &t0 = std::get>(inp.v.t); auto &t1 = std::get(inp.v.t); auto &&maybeIter = @@ -626,7 +628,7 @@ Depend make(const parser::OmpClause::Depend &inp, using Variant = decltype(Depend::u); auto visitTaskDep = [&](const wrapped::TaskDep &s) -> Variant { - auto &t0 = std::get>(s.t); + auto &t0 = std::get>(s.t); auto &t1 = std::get(s.t); auto &t2 = std::get(s.t); @@ -769,8 +771,7 @@ From make(const parser::OmpClause::From &inp, ); auto &t0 = std::get>>(inp.v.t); - auto &t1 = - std::get>>(inp.v.t); + auto &t1 = std::get>>(inp.v.t); auto &t2 = std::get(inp.v.t); assert((!t0 || t0->size() == 1) && "Only one expectation modifier allowed"); @@ -881,7 +882,7 @@ Init make(const parser::OmpClause::Init &inp, InReduction make(const parser::OmpClause::InReduction &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpInReductionClause - auto &t0 = std::get(inp.v.t); + auto &t0 = std::get(inp.v.t); auto &t1 = std::get(inp.v.t); return InReduction{ {/*ReductionIdentifiers=*/{makeReductionOperator(t0, semaCtx)}, @@ -920,7 +921,7 @@ Linear make(const parser::OmpClause::Linear &inp, using wrapped = parser::OmpLinearClause; CLAUSET_ENUM_CONVERT( // - convert, parser::OmpLinearModifier::Type, Linear::LinearModifier, + convert, parser::OmpLinearModifier::Value, Linear::LinearModifier, // clang-format off MS(Ref, Ref) MS(Val, Val) @@ -984,10 +985,13 @@ Map make(const parser::OmpClause::Map &inp, ); auto &t0 = std::get>>(inp.v.t); - auto &t1 = - std::get>>(inp.v.t); + auto &t1 = std::get>>(inp.v.t); auto &t2 = std::get>>(inp.v.t); auto &t3 = std::get(inp.v.t); + auto &t4 = std::get(inp.v.t); + + if (t4.v) + TODO_NOLOC("OmpMapClause(MAPPER(...)): user defined mapper not supported"); // These should have been diagnosed already. assert((!t1 || t1->size() == 1) && "Only one iterator modifier is allowed"); @@ -1188,7 +1192,7 @@ Reduction make(const parser::OmpClause::Reduction &inp, auto &t0 = std::get>( inp.v.t); - auto &t1 = std::get(inp.v.t); + auto &t1 = std::get(inp.v.t); auto &t2 = std::get(inp.v.t); return Reduction{ {/*ReductionModifier=*/t0 @@ -1315,7 +1319,7 @@ Permutation make(const parser::OmpClause::Permutation &inp, TaskReduction make(const parser::OmpClause::TaskReduction &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpReductionClause - auto &t0 = std::get(inp.v.t); + auto &t0 = std::get(inp.v.t); auto &t1 = std::get(inp.v.t); return TaskReduction{ {/*ReductionIdentifiers=*/{makeReductionOperator(t0, semaCtx)}, @@ -1344,8 +1348,7 @@ To make(const parser::OmpClause::To &inp, ); auto &t0 = std::get>>(inp.v.t); - auto &t1 = - std::get>>(inp.v.t); + auto &t1 = std::get>>(inp.v.t); auto &t2 = std::get(inp.v.t); assert((!t0 || t0->size() == 1) && "Only one expectation modifier allowed"); diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp index 879a3e0ad7078..a2779213a1a15 100644 --- a/flang/lib/Lower/OpenMP/OpenMP.cpp +++ b/flang/lib/Lower/OpenMP/OpenMP.cpp @@ -1372,6 +1372,15 @@ static void genTaskwaitClauses(lower::AbstractConverter &converter, loc, llvm::omp::Directive::OMPD_taskwait); } +static void genWorkshareClauses(lower::AbstractConverter &converter, + semantics::SemanticsContext &semaCtx, + lower::StatementContext &stmtCtx, + const List &clauses, mlir::Location loc, + mlir::omp::WorkshareOperands &clauseOps) { + ClauseProcessor cp(converter, semaCtx, clauses); + cp.processNowait(clauseOps); +} + static void genTeamsClauses(lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx, lower::StatementContext &stmtCtx, @@ -2033,6 +2042,24 @@ genTaskyieldOp(lower::AbstractConverter &converter, lower::SymMap &symTable, return converter.getFirOpBuilder().create(loc); } +static mlir::omp::WorkshareOp +genWorkshareOp(lower::AbstractConverter &converter, lower::SymMap &symTable, + semantics::SemanticsContext &semaCtx, + lower::pft::Evaluation &eval, mlir::Location loc, + const ConstructQueue &queue, + ConstructQueue::const_iterator item) { + lower::StatementContext stmtCtx; + mlir::omp::WorkshareOperands clauseOps; + genWorkshareClauses(converter, semaCtx, stmtCtx, item->clauses, loc, + clauseOps); + + return genOpWithBody( + OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval, + llvm::omp::Directive::OMPD_workshare) + .setClauses(&item->clauses), + queue, item, clauseOps); +} + static mlir::omp::TeamsOp genTeamsOp(lower::AbstractConverter &converter, lower::SymMap &symTable, semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval, @@ -2631,10 +2658,7 @@ static void genOMPDispatch(lower::AbstractConverter &converter, llvm::omp::getOpenMPDirectiveName(dir) + ")"); // case llvm::omp::Directive::OMPD_workdistribute: case llvm::omp::Directive::OMPD_workshare: - // FIXME: Workshare is not a commonly used OpenMP construct, an - // implementation for this feature will come later. For the codes - // that use this construct, add a single construct for now. - genSingleOp(converter, symTable, semaCtx, eval, loc, queue, item); + genWorkshareOp(converter, symTable, semaCtx, eval, loc, queue, item); break; default: // Combined and composite constructs should have been split into a sequence diff --git a/flang/lib/Lower/OpenMP/ReductionProcessor.cpp b/flang/lib/Lower/OpenMP/ReductionProcessor.cpp index 6b98ea3d0615b..736de2ee511be 100644 --- a/flang/lib/Lower/OpenMP/ReductionProcessor.cpp +++ b/flang/lib/Lower/OpenMP/ReductionProcessor.cpp @@ -374,7 +374,7 @@ static void genBoxCombiner(fir::FirOpBuilder &builder, mlir::Location loc, // know this won't miss any opportuinties for clever elemental inlining hlfir::LoopNest nest = hlfir::genLoopNest( loc, builder, shapeShift.getExtents(), /*isUnordered=*/true); - builder.setInsertionPointToStart(nest.innerLoop.getBody()); + builder.setInsertionPointToStart(nest.body); mlir::Type refTy = fir::ReferenceType::get(seqTy.getEleTy()); auto lhsEleAddr = builder.create( loc, refTy, lhs, shapeShift, /*slice=*/mlir::Value{}, @@ -388,7 +388,7 @@ static void genBoxCombiner(fir::FirOpBuilder &builder, mlir::Location loc, builder, loc, redId, refTy, lhsEle, rhsEle); builder.create(loc, scalarReduction, lhsEleAddr); - builder.setInsertionPointAfter(nest.outerLoop); + builder.setInsertionPointAfter(nest.outerOp); builder.create(loc, lhsAddr); } diff --git a/flang/lib/Optimizer/Builder/HLFIRTools.cpp b/flang/lib/Optimizer/Builder/HLFIRTools.cpp index 8d0ae2f195178..7425ccf7fc0e3 100644 --- a/flang/lib/Optimizer/Builder/HLFIRTools.cpp +++ b/flang/lib/Optimizer/Builder/HLFIRTools.cpp @@ -20,6 +20,7 @@ #include "mlir/IR/IRMapping.h" #include "mlir/Support/LLVM.h" #include "llvm/ADT/TypeSwitch.h" +#include #include // Return explicit extents. If the base is a fir.box, this won't read it to @@ -855,26 +856,50 @@ mlir::Value hlfir::inlineElementalOp( hlfir::LoopNest hlfir::genLoopNest(mlir::Location loc, fir::FirOpBuilder &builder, - mlir::ValueRange extents, bool isUnordered) { + mlir::ValueRange extents, bool isUnordered, + bool emitWorkshareLoop) { + emitWorkshareLoop = emitWorkshareLoop && isUnordered; hlfir::LoopNest loopNest; assert(!extents.empty() && "must have at least one extent"); - auto insPt = builder.saveInsertionPoint(); + mlir::OpBuilder::InsertionGuard guard(builder); loopNest.oneBasedIndices.assign(extents.size(), mlir::Value{}); // Build loop nest from column to row. auto one = builder.create(loc, 1); mlir::Type indexType = builder.getIndexType(); - unsigned dim = extents.size() - 1; - for (auto extent : llvm::reverse(extents)) { - auto ub = builder.createConvert(loc, indexType, extent); - loopNest.innerLoop = - builder.create(loc, one, ub, one, isUnordered); - builder.setInsertionPointToStart(loopNest.innerLoop.getBody()); - // Reverse the indices so they are in column-major order. - loopNest.oneBasedIndices[dim--] = loopNest.innerLoop.getInductionVar(); - if (!loopNest.outerLoop) - loopNest.outerLoop = loopNest.innerLoop; + if (emitWorkshareLoop) { + auto wslw = builder.create(loc); + loopNest.outerOp = wslw; + builder.createBlock(&wslw.getRegion()); + mlir::omp::LoopNestOperands lnops; + lnops.loopInclusive = builder.getUnitAttr(); + for (auto extent : llvm::reverse(extents)) { + lnops.loopLowerBounds.push_back(one); + lnops.loopUpperBounds.push_back(extent); + lnops.loopSteps.push_back(one); + } + auto lnOp = builder.create(loc, lnops); + mlir::Block *block = builder.createBlock(&lnOp.getRegion()); + for (auto extent : llvm::reverse(extents)) + block->addArgument(extent.getType(), extent.getLoc()); + loopNest.body = block; + builder.create(loc); + for (unsigned dim = 0; dim < extents.size(); dim++) + loopNest.oneBasedIndices[extents.size() - dim - 1] = + lnOp.getRegion().front().getArgument(dim); + } else { + unsigned dim = extents.size() - 1; + for (auto extent : llvm::reverse(extents)) { + auto ub = builder.createConvert(loc, indexType, extent); + auto doLoop = + builder.create(loc, one, ub, one, isUnordered); + loopNest.body = doLoop.getBody(); + builder.setInsertionPointToStart(loopNest.body); + // Reverse the indices so they are in column-major order. + loopNest.oneBasedIndices[dim--] = doLoop.getInductionVar(); + if (!loopNest.outerOp) + loopNest.outerOp = doLoop; + } } - builder.restoreInsertionPoint(insPt); return loopNest; } diff --git a/flang/lib/Optimizer/CodeGen/Target.cpp b/flang/lib/Optimizer/CodeGen/Target.cpp index 5f746bf80e9d5..9ec055b1aecab 100644 --- a/flang/lib/Optimizer/CodeGen/Target.cpp +++ b/flang/lib/Optimizer/CodeGen/Target.cpp @@ -1127,6 +1127,30 @@ struct TargetLoongArch64 : public GenericTarget { } return marshal; } + + CodeGenSpecifics::Marshalling + integerArgumentType(mlir::Location loc, + mlir::IntegerType argTy) const override { + if (argTy.getWidth() == 32) { + // LA64 LP64D ABI requires unsigned 32 bit integers to be sign extended. + // Therefore, Flang also follows it if a function needs to be + // interoperable with C. + // + // Currently, it only adds `signext` attribute to the dummy arguments and + // return values in the function signatures, but it does not add the + // corresponding attribute to the actual arguments and return values in + // `fir.call` instruction. Thanks to LLVM's integration of all these + // attributes, the modification is still effective. + CodeGenSpecifics::Marshalling marshal; + AT::IntegerExtension intExt = AT::IntegerExtension::Sign; + marshal.emplace_back(argTy, AT{/*alignment=*/0, /*byval=*/false, + /*sret=*/false, /*append=*/false, + /*intExt=*/intExt}); + return marshal; + } + + return GenericTarget::integerArgumentType(loc, argTy); + } }; } // namespace diff --git a/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp b/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp index a70a6b388c4b1..07794828fce26 100644 --- a/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp +++ b/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp @@ -26,6 +26,7 @@ #include "flang/Optimizer/HLFIR/HLFIRDialect.h" #include "flang/Optimizer/HLFIR/HLFIROps.h" #include "flang/Optimizer/HLFIR/Passes.h" +#include "mlir/Dialect/OpenMP/OpenMPDialect.h" #include "mlir/IR/Dominance.h" #include "mlir/IR/PatternMatch.h" #include "mlir/Pass/Pass.h" @@ -793,7 +794,7 @@ struct ElementalOpConversion hlfir::LoopNest loopNest = hlfir::genLoopNest(loc, builder, extents, !elemental.isOrdered()); auto insPt = builder.saveInsertionPoint(); - builder.setInsertionPointToStart(loopNest.innerLoop.getBody()); + builder.setInsertionPointToStart(loopNest.body); auto yield = hlfir::inlineElementalOp(loc, builder, elemental, loopNest.oneBasedIndices); hlfir::Entity elementValue(yield.getElementValue()); diff --git a/flang/lib/Optimizer/HLFIR/Transforms/LowerHLFIROrderedAssignments.cpp b/flang/lib/Optimizer/HLFIR/Transforms/LowerHLFIROrderedAssignments.cpp index 85dd517cb5791..424566462e8fe 100644 --- a/flang/lib/Optimizer/HLFIR/Transforms/LowerHLFIROrderedAssignments.cpp +++ b/flang/lib/Optimizer/HLFIR/Transforms/LowerHLFIROrderedAssignments.cpp @@ -464,7 +464,7 @@ void OrderedAssignmentRewriter::pre(hlfir::RegionAssignOp regionAssignOp) { // if the LHS is not). mlir::Value shape = hlfir::genShape(loc, builder, lhsEntity); elementalLoopNest = hlfir::genLoopNest(loc, builder, shape); - builder.setInsertionPointToStart(elementalLoopNest->innerLoop.getBody()); + builder.setInsertionPointToStart(elementalLoopNest->body); lhsEntity = hlfir::getElementAt(loc, builder, lhsEntity, elementalLoopNest->oneBasedIndices); rhsEntity = hlfir::getElementAt(loc, builder, rhsEntity, @@ -484,7 +484,7 @@ void OrderedAssignmentRewriter::pre(hlfir::RegionAssignOp regionAssignOp) { for (auto &cleanupConversion : argConversionCleanups) cleanupConversion(); if (elementalLoopNest) - builder.setInsertionPointAfter(elementalLoopNest->outerLoop); + builder.setInsertionPointAfter(elementalLoopNest->outerOp); } else { // TODO: preserve allocatable assignment aspects for forall once // they are conveyed in hlfir.region_assign. @@ -492,8 +492,7 @@ void OrderedAssignmentRewriter::pre(hlfir::RegionAssignOp regionAssignOp) { } generateCleanupIfAny(loweredLhs.elementalCleanup); if (loweredLhs.vectorSubscriptLoopNest) - builder.setInsertionPointAfter( - loweredLhs.vectorSubscriptLoopNest->outerLoop); + builder.setInsertionPointAfter(loweredLhs.vectorSubscriptLoopNest->outerOp); generateCleanupIfAny(oldRhsYield); generateCleanupIfAny(loweredLhs.nonElementalCleanup); } @@ -518,8 +517,8 @@ void OrderedAssignmentRewriter::pre(hlfir::WhereOp whereOp) { hlfir::Entity savedMask{maybeSaved->first}; mlir::Value shape = hlfir::genShape(loc, builder, savedMask); whereLoopNest = hlfir::genLoopNest(loc, builder, shape); - constructStack.push_back(whereLoopNest->outerLoop.getOperation()); - builder.setInsertionPointToStart(whereLoopNest->innerLoop.getBody()); + constructStack.push_back(whereLoopNest->outerOp); + builder.setInsertionPointToStart(whereLoopNest->body); mlir::Value cdt = hlfir::getElementAt(loc, builder, savedMask, whereLoopNest->oneBasedIndices); generateMaskIfOp(cdt); @@ -527,7 +526,7 @@ void OrderedAssignmentRewriter::pre(hlfir::WhereOp whereOp) { // If this is the same run as the one that saved the value, the clean-up // was left-over to be done now. auto insertionPoint = builder.saveInsertionPoint(); - builder.setInsertionPointAfter(whereLoopNest->outerLoop); + builder.setInsertionPointAfter(whereLoopNest->outerOp); generateCleanupIfAny(maybeSaved->second); builder.restoreInsertionPoint(insertionPoint); } @@ -539,8 +538,8 @@ void OrderedAssignmentRewriter::pre(hlfir::WhereOp whereOp) { mask.generateNoneElementalPart(builder, mapper); mlir::Value shape = mask.generateShape(builder, mapper); whereLoopNest = hlfir::genLoopNest(loc, builder, shape); - constructStack.push_back(whereLoopNest->outerLoop.getOperation()); - builder.setInsertionPointToStart(whereLoopNest->innerLoop.getBody()); + constructStack.push_back(whereLoopNest->outerOp); + builder.setInsertionPointToStart(whereLoopNest->body); mlir::Value cdt = generateMaskedEntity(mask); generateMaskIfOp(cdt); return; @@ -754,7 +753,7 @@ OrderedAssignmentRewriter::generateYieldedLHS( loweredLhs.vectorSubscriptLoopNest = hlfir::genLoopNest( loc, builder, loweredLhs.vectorSubscriptShape.value()); builder.setInsertionPointToStart( - loweredLhs.vectorSubscriptLoopNest->innerLoop.getBody()); + loweredLhs.vectorSubscriptLoopNest->body); } loweredLhs.lhs = temp->second.fetch(loc, builder); return loweredLhs; @@ -771,8 +770,7 @@ OrderedAssignmentRewriter::generateYieldedLHS( loweredLhs.vectorSubscriptLoopNest = hlfir::genLoopNest(loc, builder, *loweredLhs.vectorSubscriptShape, !elementalAddrLhs.isOrdered()); - builder.setInsertionPointToStart( - loweredLhs.vectorSubscriptLoopNest->innerLoop.getBody()); + builder.setInsertionPointToStart(loweredLhs.vectorSubscriptLoopNest->body); mapper.map(elementalAddrLhs.getIndices(), loweredLhs.vectorSubscriptLoopNest->oneBasedIndices); for (auto &op : elementalAddrLhs.getBody().front().without_terminator()) @@ -798,11 +796,11 @@ OrderedAssignmentRewriter::generateMaskedEntity(MaskedArrayExpr &maskedExpr) { if (!maskedExpr.noneElementalPartWasGenerated) { // Generate none elemental part before the where loops (but inside the // current forall loops if any). - builder.setInsertionPoint(whereLoopNest->outerLoop); + builder.setInsertionPoint(whereLoopNest->outerOp); maskedExpr.generateNoneElementalPart(builder, mapper); } // Generate the none elemental part cleanup after the where loops. - builder.setInsertionPointAfter(whereLoopNest->outerLoop); + builder.setInsertionPointAfter(whereLoopNest->outerOp); maskedExpr.generateNoneElementalCleanupIfAny(builder, mapper); // Generate the value of the current element for the masked expression // at the current insertion point (inside the where loops, and any fir.if @@ -1242,7 +1240,7 @@ void OrderedAssignmentRewriter::saveLeftHandSide( LhsValueAndCleanUp loweredLhs = generateYieldedLHS(loc, region); fir::factory::TemporaryStorage *temp = nullptr; if (loweredLhs.vectorSubscriptLoopNest) - constructStack.push_back(loweredLhs.vectorSubscriptLoopNest->outerLoop); + constructStack.push_back(loweredLhs.vectorSubscriptLoopNest->outerOp); if (loweredLhs.vectorSubscriptLoopNest && !rhsIsArray(regionAssignOp)) { // Vector subscripted entity for which the shape must also be saved on top // of the element addresses (e.g. the shape may change in each forall @@ -1265,7 +1263,7 @@ void OrderedAssignmentRewriter::saveLeftHandSide( // subscripted LHS. auto &vectorTmp = temp->cast(); auto insertionPoint = builder.saveInsertionPoint(); - builder.setInsertionPoint(loweredLhs.vectorSubscriptLoopNest->outerLoop); + builder.setInsertionPoint(loweredLhs.vectorSubscriptLoopNest->outerOp); vectorTmp.pushShape(loc, builder, shape); builder.restoreInsertionPoint(insertionPoint); } else { @@ -1290,8 +1288,7 @@ void OrderedAssignmentRewriter::saveLeftHandSide( generateCleanupIfAny(loweredLhs.elementalCleanup); if (loweredLhs.vectorSubscriptLoopNest) { constructStack.pop_back(); - builder.setInsertionPointAfter( - loweredLhs.vectorSubscriptLoopNest->outerLoop); + builder.setInsertionPointAfter(loweredLhs.vectorSubscriptLoopNest->outerOp); } } diff --git a/flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp b/flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp index d05a3258cf293..166649d955dab 100644 --- a/flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp +++ b/flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp @@ -483,7 +483,7 @@ llvm::LogicalResult ElementalAssignBufferization::matchAndRewrite( // hlfir.elemental region inside the inner loop hlfir::LoopNest loopNest = hlfir::genLoopNest(loc, builder, extents, !elemental.isOrdered()); - builder.setInsertionPointToStart(loopNest.innerLoop.getBody()); + builder.setInsertionPointToStart(loopNest.body); auto yield = hlfir::inlineElementalOp(loc, builder, elemental, loopNest.oneBasedIndices); hlfir::Entity elementValue{yield.getElementValue()}; @@ -554,7 +554,7 @@ llvm::LogicalResult BroadcastAssignBufferization::matchAndRewrite( hlfir::getIndexExtents(loc, builder, shape); hlfir::LoopNest loopNest = hlfir::genLoopNest(loc, builder, extents, /*isUnordered=*/true); - builder.setInsertionPointToStart(loopNest.innerLoop.getBody()); + builder.setInsertionPointToStart(loopNest.body); auto arrayElement = hlfir::getElementAt(loc, builder, lhs, loopNest.oneBasedIndices); builder.create(loc, rhs, arrayElement); @@ -652,7 +652,7 @@ llvm::LogicalResult VariableAssignBufferization::matchAndRewrite( hlfir::getIndexExtents(loc, builder, shape); hlfir::LoopNest loopNest = hlfir::genLoopNest(loc, builder, extents, /*isUnordered=*/true); - builder.setInsertionPointToStart(loopNest.innerLoop.getBody()); + builder.setInsertionPointToStart(loopNest.body); auto rhsArrayElement = hlfir::getElementAt(loc, builder, rhs, loopNest.oneBasedIndices); rhsArrayElement = hlfir::loadTrivialScalar(loc, builder, rhsArrayElement); diff --git a/flang/lib/Optimizer/OpenMP/CMakeLists.txt b/flang/lib/Optimizer/OpenMP/CMakeLists.txt index 035d0d5ca46c7..b1e0dbf6e707e 100644 --- a/flang/lib/Optimizer/OpenMP/CMakeLists.txt +++ b/flang/lib/Optimizer/OpenMP/CMakeLists.txt @@ -5,6 +5,7 @@ add_flang_library(FlangOpenMPTransforms MapsForPrivatizedSymbols.cpp MapInfoFinalization.cpp MarkDeclareTarget.cpp + LowerWorkshare.cpp DEPENDS FIRDialect diff --git a/flang/lib/Optimizer/OpenMP/LowerWorkshare.cpp b/flang/lib/Optimizer/OpenMP/LowerWorkshare.cpp new file mode 100644 index 0000000000000..225c585a02d91 --- /dev/null +++ b/flang/lib/Optimizer/OpenMP/LowerWorkshare.cpp @@ -0,0 +1,527 @@ +//===- LowerWorkshare.cpp - special cases for bufferization -------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements the lowering of omp.workshare to other omp constructs. +// +// This pass is tasked with parallelizing the loops nested in +// workshare.loop_wrapper while both the Fortran to mlir lowering and the hlfir +// to fir lowering pipelines are responsible for emitting the +// workshare.loop_wrapper ops where appropriate according to the +// `shouldUseWorkshareLowering` function. +// +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace flangomp { +#define GEN_PASS_DEF_LOWERWORKSHARE +#include "flang/Optimizer/OpenMP/Passes.h.inc" +} // namespace flangomp + +#define DEBUG_TYPE "lower-workshare" + +using namespace mlir; + +namespace flangomp { + +// Checks for nesting pattern below as we need to avoid sharing the work of +// statements which are nested in some constructs such as omp.critical or +// another omp.parallel. +// +// omp.workshare { // `wsOp` +// ... +// omp.T { // `parent` +// ... +// `op` +// +template +static bool isNestedIn(omp::WorkshareOp wsOp, Operation *op) { + T parent = op->getParentOfType(); + if (!parent) + return false; + return wsOp->isProperAncestor(parent); +} + +bool shouldUseWorkshareLowering(Operation *op) { + auto parentWorkshare = op->getParentOfType(); + + if (!parentWorkshare) + return false; + + if (isNestedIn(parentWorkshare, op)) + return false; + + // 2.8.3 workshare Construct + // For a parallel construct, the construct is a unit of work with respect to + // the workshare construct. The statements contained in the parallel construct + // are executed by a new thread team. + if (isNestedIn(parentWorkshare, op)) + return false; + + // 2.8.2 single Construct + // Binding The binding thread set for a single region is the current team. A + // single region binds to the innermost enclosing parallel region. + // Description Only one of the encountering threads will execute the + // structured block associated with the single construct. + if (isNestedIn(parentWorkshare, op)) + return false; + + // Do not use workshare lowering until we support CFG in omp.workshare + if (parentWorkshare.getRegion().getBlocks().size() != 1) + return false; + + return true; +} + +} // namespace flangomp + +namespace { + +struct SingleRegion { + Block::iterator begin, end; +}; + +static bool mustParallelizeOp(Operation *op) { + return op + ->walk([&](Operation *nested) { + // We need to be careful not to pick up workshare.loop_wrapper in nested + // omp.parallel{omp.workshare} regions, i.e. make sure that `nested` + // binds to the workshare region we are currently handling. + // + // For example: + // + // omp.parallel { + // omp.workshare { // currently handling this + // omp.parallel { + // omp.workshare { // nested workshare + // omp.workshare.loop_wrapper {} + // + // Therefore, we skip if we encounter a nested omp.workshare. + if (isa(op)) + return WalkResult::skip(); + if (isa(op)) + return WalkResult::interrupt(); + return WalkResult::advance(); + }) + .wasInterrupted(); +} + +static bool isSafeToParallelize(Operation *op) { + return isa(op) || isa(op) || + isMemoryEffectFree(op); +} + +/// Simple shallow copies suffice for our purposes in this pass, so we implement +/// this simpler alternative to the full fledged `createCopyFunc` in the +/// frontend +static mlir::func::FuncOp createCopyFunc(mlir::Location loc, mlir::Type varType, + fir::FirOpBuilder builder) { + mlir::ModuleOp module = builder.getModule(); + auto rt = cast(varType); + mlir::Type eleTy = rt.getEleTy(); + std::string copyFuncName = + fir::getTypeAsString(eleTy, builder.getKindMap(), "_workshare_copy"); + + if (auto decl = module.lookupSymbol(copyFuncName)) + return decl; + // create function + mlir::OpBuilder::InsertionGuard guard(builder); + mlir::OpBuilder modBuilder(module.getBodyRegion()); + llvm::SmallVector argsTy = {varType, varType}; + auto funcType = mlir::FunctionType::get(builder.getContext(), argsTy, {}); + mlir::func::FuncOp funcOp = + modBuilder.create(loc, copyFuncName, funcType); + funcOp.setVisibility(mlir::SymbolTable::Visibility::Private); + builder.createBlock(&funcOp.getRegion(), funcOp.getRegion().end(), argsTy, + {loc, loc}); + builder.setInsertionPointToStart(&funcOp.getRegion().back()); + + Value loaded = builder.create(loc, funcOp.getArgument(1)); + builder.create(loc, loaded, funcOp.getArgument(0)); + + builder.create(loc); + return funcOp; +} + +static bool isUserOutsideSR(Operation *user, Operation *parentOp, + SingleRegion sr) { + while (user->getParentOp() != parentOp) + user = user->getParentOp(); + return sr.begin->getBlock() != user->getBlock() || + !(user->isBeforeInBlock(&*sr.end) && sr.begin->isBeforeInBlock(user)); +} + +static bool isTransitivelyUsedOutside(Value v, SingleRegion sr) { + Block *srBlock = sr.begin->getBlock(); + Operation *parentOp = srBlock->getParentOp(); + + for (auto &use : v.getUses()) { + Operation *user = use.getOwner(); + if (isUserOutsideSR(user, parentOp, sr)) + return true; + + // Now we know user is inside `sr`. + + // Results of nested users cannot be used outside of `sr`. + if (user->getBlock() != srBlock) + continue; + + // A non-safe to parallelize operation will be checked for uses outside + // separately. + if (!isSafeToParallelize(user)) + continue; + + // For safe to parallelize operations, we need to check if there is a + // transitive use of `v` through them. + for (auto res : user->getResults()) + if (isTransitivelyUsedOutside(res, sr)) + return true; + } + return false; +} + +/// We clone pure operations in both the parallel and single blocks. this +/// functions cleans them up if they end up with no uses +static void cleanupBlock(Block *block) { + for (Operation &op : llvm::make_early_inc_range( + llvm::make_range(block->rbegin(), block->rend()))) + if (isOpTriviallyDead(&op)) + op.erase(); +} + +static void parallelizeRegion(Region &sourceRegion, Region &targetRegion, + IRMapping &rootMapping, Location loc, + mlir::DominanceInfo &di) { + OpBuilder rootBuilder(sourceRegion.getContext()); + ModuleOp m = sourceRegion.getParentOfType(); + OpBuilder copyFuncBuilder(m.getBodyRegion()); + fir::FirOpBuilder firCopyFuncBuilder(copyFuncBuilder, m); + + auto mapReloadedValue = + [&](Value v, OpBuilder allocaBuilder, OpBuilder singleBuilder, + OpBuilder parallelBuilder, IRMapping singleMapping) -> Value { + if (auto reloaded = rootMapping.lookupOrNull(v)) + return nullptr; + Type ty = v.getType(); + Value alloc = allocaBuilder.create(loc, ty); + singleBuilder.create(loc, singleMapping.lookup(v), alloc); + Value reloaded = parallelBuilder.create(loc, ty, alloc); + rootMapping.map(v, reloaded); + return alloc; + }; + + auto moveToSingle = + [&](SingleRegion sr, OpBuilder allocaBuilder, OpBuilder singleBuilder, + OpBuilder parallelBuilder) -> std::pair> { + IRMapping singleMapping = rootMapping; + SmallVector copyPrivate; + bool allParallelized = true; + + for (Operation &op : llvm::make_range(sr.begin, sr.end)) { + if (isSafeToParallelize(&op)) { + singleBuilder.clone(op, singleMapping); + if (llvm::all_of(op.getOperands(), [&](Value opr) { + // Either we have already remapped it + bool remapped = rootMapping.contains(opr); + // Or it is available because it dominates `sr` + bool dominates = + di.properlyDominates(opr.getDefiningOp(), &*sr.begin); + return remapped || dominates; + })) { + // Safe to parallelize operations which have all operands available in + // the root parallel block can be executed there. + parallelBuilder.clone(op, rootMapping); + } else { + // If any operand was not available, it means that there was no + // transitive use of a non-safe-to-parallelize operation outside `sr`. + // This means that there should be no transitive uses outside `sr` of + // `op`. + assert(llvm::all_of(op.getResults(), [&](Value v) { + return !isTransitivelyUsedOutside(v, sr); + })); + allParallelized = false; + } + } else if (auto alloca = dyn_cast(&op)) { + auto hoisted = + cast(allocaBuilder.clone(*alloca, singleMapping)); + rootMapping.map(&*alloca, &*hoisted); + rootMapping.map(alloca.getResult(), hoisted.getResult()); + copyPrivate.push_back(hoisted); + allParallelized = false; + } else { + singleBuilder.clone(op, singleMapping); + // Prepare reloaded values for results of operations that cannot be + // safely parallelized and which are used after the region `sr`. + for (auto res : op.getResults()) { + if (isTransitivelyUsedOutside(res, sr)) { + auto alloc = mapReloadedValue(res, allocaBuilder, singleBuilder, + parallelBuilder, singleMapping); + if (alloc) + copyPrivate.push_back(alloc); + } + } + allParallelized = false; + } + } + singleBuilder.create(loc); + return {allParallelized, copyPrivate}; + }; + + for (Block &block : sourceRegion) { + Block *targetBlock = rootBuilder.createBlock( + &targetRegion, {}, block.getArgumentTypes(), + llvm::map_to_vector(block.getArguments(), + [](BlockArgument arg) { return arg.getLoc(); })); + rootMapping.map(&block, targetBlock); + rootMapping.map(block.getArguments(), targetBlock->getArguments()); + } + + auto handleOneBlock = [&](Block &block) { + Block &targetBlock = *rootMapping.lookup(&block); + rootBuilder.setInsertionPointToStart(&targetBlock); + Operation *terminator = block.getTerminator(); + SmallVector> regions; + + auto it = block.begin(); + auto getOneRegion = [&]() { + if (&*it == terminator) + return false; + if (mustParallelizeOp(&*it)) { + regions.push_back(&*it); + it++; + return true; + } + SingleRegion sr; + sr.begin = it; + while (&*it != terminator && !mustParallelizeOp(&*it)) + it++; + sr.end = it; + assert(sr.begin != sr.end); + regions.push_back(sr); + return true; + }; + while (getOneRegion()) + ; + + for (auto [i, opOrSingle] : llvm::enumerate(regions)) { + bool isLast = i + 1 == regions.size(); + if (std::holds_alternative(opOrSingle)) { + OpBuilder singleBuilder(sourceRegion.getContext()); + Block *singleBlock = new Block(); + singleBuilder.setInsertionPointToStart(singleBlock); + + OpBuilder allocaBuilder(sourceRegion.getContext()); + Block *allocaBlock = new Block(); + allocaBuilder.setInsertionPointToStart(allocaBlock); + + OpBuilder parallelBuilder(sourceRegion.getContext()); + Block *parallelBlock = new Block(); + parallelBuilder.setInsertionPointToStart(parallelBlock); + + auto [allParallelized, copyprivateVars] = + moveToSingle(std::get(opOrSingle), allocaBuilder, + singleBuilder, parallelBuilder); + if (allParallelized) { + // The single region was not required as all operations were safe to + // parallelize + assert(copyprivateVars.empty()); + assert(allocaBlock->empty()); + delete singleBlock; + } else { + omp::SingleOperands singleOperands; + if (isLast) + singleOperands.nowait = rootBuilder.getUnitAttr(); + singleOperands.copyprivateVars = copyprivateVars; + cleanupBlock(singleBlock); + for (auto var : singleOperands.copyprivateVars) { + mlir::func::FuncOp funcOp = + createCopyFunc(loc, var.getType(), firCopyFuncBuilder); + singleOperands.copyprivateSyms.push_back( + SymbolRefAttr::get(funcOp)); + } + omp::SingleOp singleOp = + rootBuilder.create(loc, singleOperands); + singleOp.getRegion().push_back(singleBlock); + targetRegion.front().getOperations().splice( + singleOp->getIterator(), allocaBlock->getOperations()); + } + rootBuilder.getInsertionBlock()->getOperations().splice( + rootBuilder.getInsertionPoint(), parallelBlock->getOperations()); + delete allocaBlock; + delete parallelBlock; + } else { + auto op = std::get(opOrSingle); + if (auto wslw = dyn_cast(op)) { + omp::WsloopOperands wsloopOperands; + if (isLast) + wsloopOperands.nowait = rootBuilder.getUnitAttr(); + auto wsloop = + rootBuilder.create(loc, wsloopOperands); + auto clonedWslw = cast( + rootBuilder.clone(*wslw, rootMapping)); + wsloop.getRegion().takeBody(clonedWslw.getRegion()); + clonedWslw->erase(); + } else { + assert(mustParallelizeOp(op)); + Operation *cloned = rootBuilder.cloneWithoutRegions(*op, rootMapping); + for (auto [region, clonedRegion] : + llvm::zip(op->getRegions(), cloned->getRegions())) + parallelizeRegion(region, clonedRegion, rootMapping, loc, di); + } + } + } + + rootBuilder.clone(*block.getTerminator(), rootMapping); + }; + + if (sourceRegion.hasOneBlock()) { + handleOneBlock(sourceRegion.front()); + } else { + auto &domTree = di.getDomTree(&sourceRegion); + for (auto node : llvm::breadth_first(domTree.getRootNode())) { + handleOneBlock(*node->getBlock()); + } + } + + for (Block &targetBlock : targetRegion) + cleanupBlock(&targetBlock); +} + +/// Lowers workshare to a sequence of single-thread regions and parallel loops +/// +/// For example: +/// +/// omp.workshare { +/// %a = fir.allocmem +/// omp.workshare.loop_wrapper {} +/// fir.call Assign %b %a +/// fir.freemem %a +/// } +/// +/// becomes +/// +/// %tmp = fir.alloca +/// omp.single copyprivate(%tmp) { +/// %a = fir.allocmem +/// fir.store %a %tmp +/// } +/// %a_reloaded = fir.load %tmp +/// omp.workshare.loop_wrapper {} +/// omp.single { +/// fir.call Assign %b %a_reloaded +/// fir.freemem %a_reloaded +/// } +/// +/// Note that we allocate temporary memory for values in omp.single's which need +/// to be accessed by all threads and broadcast them using single's copyprivate +LogicalResult lowerWorkshare(mlir::omp::WorkshareOp wsOp, DominanceInfo &di) { + Location loc = wsOp->getLoc(); + IRMapping rootMapping; + + OpBuilder rootBuilder(wsOp); + + // FIXME Currently, we only support workshare constructs with structured + // control flow. The transformation itself supports CFG, however, once we + // transform the MLIR region in the omp.workshare, we need to inline that + // region in the parent block. We have no guarantees at this point of the + // pipeline that the parent op supports CFG (e.g. fir.if), thus this is not + // generally possible. The alternative is to put the lowered region in an + // operation akin to scf.execute_region, which will get lowered at the same + // time when fir ops get lowered to CFG. However, SCF is not registered in + // flang so we cannot use it. Remove this requirement once we have + // scf.execute_region or an alternative operation available. + if (wsOp.getRegion().getBlocks().size() == 1) { + // This operation is just a placeholder which will be erased later. We need + // it because our `parallelizeRegion` function works on regions and not + // blocks. + omp::WorkshareOp newOp = + rootBuilder.create(loc, omp::WorkshareOperands()); + if (!wsOp.getNowait()) + rootBuilder.create(loc); + + parallelizeRegion(wsOp.getRegion(), newOp.getRegion(), rootMapping, loc, + di); + + // Inline the contents of the placeholder workshare op into its parent + // block. + Block *theBlock = &newOp.getRegion().front(); + Operation *term = theBlock->getTerminator(); + Block *parentBlock = wsOp->getBlock(); + parentBlock->getOperations().splice(newOp->getIterator(), + theBlock->getOperations()); + assert(term->getNumOperands() == 0); + term->erase(); + newOp->erase(); + wsOp->erase(); + } else { + // Otherwise just change the operation to an omp.single. + + wsOp->emitWarning( + "omp workshare with unstructured control flow is currently " + "unsupported and will be serialized."); + + // `shouldUseWorkshareLowering` should have guaranteed that there are no + // omp.workshare_loop_wrapper's that bind to this omp.workshare. + assert(!wsOp->walk([&](Operation *op) { + // Nested omp.workshare can have their own + // omp.workshare_loop_wrapper's. + if (isa(op)) + return WalkResult::skip(); + if (isa(op)) + return WalkResult::interrupt(); + return WalkResult::advance(); + }) + .wasInterrupted()); + + omp::SingleOperands operands; + operands.nowait = wsOp.getNowaitAttr(); + omp::SingleOp newOp = rootBuilder.create(loc, operands); + + newOp.getRegion().getBlocks().splice(newOp.getRegion().getBlocks().begin(), + wsOp.getRegion().getBlocks()); + wsOp->erase(); + } + return success(); +} + +class LowerWorksharePass + : public flangomp::impl::LowerWorkshareBase { +public: + void runOnOperation() override { + mlir::DominanceInfo &di = getAnalysis(); + getOperation()->walk([&](mlir::omp::WorkshareOp wsOp) { + if (failed(lowerWorkshare(wsOp, di))) + signalPassFailure(); + }); + } +}; +} // namespace diff --git a/flang/lib/Optimizer/Passes/Pipelines.cpp b/flang/lib/Optimizer/Passes/Pipelines.cpp index a914407991591..31af3531641dd 100644 --- a/flang/lib/Optimizer/Passes/Pipelines.cpp +++ b/flang/lib/Optimizer/Passes/Pipelines.cpp @@ -212,7 +212,7 @@ void createDefaultFIROptimizerPassPipeline(mlir::PassManager &pm, /// \param pm - MLIR pass manager that will hold the pipeline definition /// \param optLevel - optimization level used for creating FIR optimization /// passes pipeline -void createHLFIRToFIRPassPipeline(mlir::PassManager &pm, +void createHLFIRToFIRPassPipeline(mlir::PassManager &pm, bool enableOpenMP, llvm::OptimizationLevel optLevel) { if (optLevel.isOptimizingForSpeed()) { addCanonicalizerPassWithoutRegionSimplification(pm); @@ -230,6 +230,8 @@ void createHLFIRToFIRPassPipeline(mlir::PassManager &pm, pm.addPass(hlfir::createLowerHLFIRIntrinsics()); pm.addPass(hlfir::createBufferizeHLFIR()); pm.addPass(hlfir::createConvertHLFIRtoFIR()); + if (enableOpenMP) + pm.addPass(flangomp::createLowerWorkshare()); } /// Create a pass pipeline for handling certain OpenMP transformations needed @@ -303,7 +305,7 @@ void createDefaultFIRCodeGenPassPipeline(mlir::PassManager &pm, void createMLIRToLLVMPassPipeline(mlir::PassManager &pm, MLIRToLLVMPassPipelineConfig &config, llvm::StringRef inputFilename) { - fir::createHLFIRToFIRPassPipeline(pm, config.OptLevel); + fir::createHLFIRToFIRPassPipeline(pm, config.EnableOpenMP, config.OptLevel); // Add default optimizer pass pipeline. fir::createDefaultFIROptimizerPassPipeline(pm, config); diff --git a/flang/lib/Optimizer/Transforms/CUFCommon.cpp b/flang/lib/Optimizer/Transforms/CUFCommon.cpp index 5eca86529f9e1..162df8f9cab9c 100644 --- a/flang/lib/Optimizer/Transforms/CUFCommon.cpp +++ b/flang/lib/Optimizer/Transforms/CUFCommon.cpp @@ -22,9 +22,6 @@ mlir::gpu::GPUModuleOp cuf::getOrCreateGPUModule(mlir::ModuleOp mod, mlir::OpBuilder builder(ctx); auto gpuMod = builder.create(mod.getLoc(), cudaDeviceModuleName); - llvm::SmallVector targets; - targets.push_back(mlir::NVVM::NVVMTargetAttr::get(ctx)); - gpuMod.setTargetsAttr(builder.getArrayAttr(targets)); mlir::Block::iterator insertPt(mod.getBodyRegion().front().end()); symTab.insert(gpuMod, insertPt); return gpuMod; diff --git a/flang/lib/Optimizer/Transforms/CUFDeviceGlobal.cpp b/flang/lib/Optimizer/Transforms/CUFDeviceGlobal.cpp index a69b47ff74391..714b0b291be1e 100644 --- a/flang/lib/Optimizer/Transforms/CUFDeviceGlobal.cpp +++ b/flang/lib/Optimizer/Transforms/CUFDeviceGlobal.cpp @@ -26,25 +26,37 @@ namespace fir { namespace { +static void processAddrOfOp(fir::AddrOfOp addrOfOp, + mlir::SymbolTable &symbolTable, bool onlyConstant) { + if (auto globalOp = symbolTable.lookup( + addrOfOp.getSymbol().getRootReference().getValue())) { + bool isCandidate{(onlyConstant ? globalOp.getConstant() : true) && + !globalOp.getDataAttr()}; + if (isCandidate) + globalOp.setDataAttrAttr(cuf::DataAttributeAttr::get( + addrOfOp.getContext(), globalOp.getConstant() + ? cuf::DataAttribute::Constant + : cuf::DataAttribute::Device)); + } +} + static void prepareImplicitDeviceGlobals(mlir::func::FuncOp funcOp, mlir::SymbolTable &symbolTable, bool onlyConstant = true) { auto cudaProcAttr{ funcOp->getAttrOfType(cuf::getProcAttrName())}; - if (!cudaProcAttr || cudaProcAttr.getValue() == cuf::ProcAttribute::Host) - return; - for (auto addrOfOp : funcOp.getBody().getOps()) { - if (auto globalOp = symbolTable.lookup( - addrOfOp.getSymbol().getRootReference().getValue())) { - bool isCandidate{(onlyConstant ? globalOp.getConstant() : true) && - !globalOp.getDataAttr()}; - if (isCandidate) - globalOp.setDataAttrAttr(cuf::DataAttributeAttr::get( - funcOp.getContext(), globalOp.getConstant() - ? cuf::DataAttribute::Constant - : cuf::DataAttribute::Device)); + if (!cudaProcAttr || cudaProcAttr.getValue() == cuf::ProcAttribute::Host) { + // Look for globlas in CUF KERNEL DO operations. + for (auto cufKernelOp : funcOp.getBody().getOps()) { + cufKernelOp.walk([&](fir::AddrOfOp addrOfOp) { + processAddrOfOp(addrOfOp, symbolTable, onlyConstant); + }); } + return; } + funcOp.walk([&](fir::AddrOfOp addrOfOp) { + processAddrOfOp(addrOfOp, symbolTable, onlyConstant); + }); } class CUFDeviceGlobal : public fir::impl::CUFDeviceGlobalBase { diff --git a/flang/lib/Optimizer/Transforms/CUFOpConversion.cpp b/flang/lib/Optimizer/Transforms/CUFOpConversion.cpp index 9de20f0f0d45e..f1ebd08967b9a 100644 --- a/flang/lib/Optimizer/Transforms/CUFOpConversion.cpp +++ b/flang/lib/Optimizer/Transforms/CUFOpConversion.cpp @@ -628,6 +628,12 @@ struct CUFDataTransferOpConversion mlir::Value dst = getDeviceAddress(rewriter, op.getDstMutable(), symtab); mlir::Value src = getDeviceAddress(rewriter, op.getSrcMutable(), symtab); + // Materialize the src if constant. + if (matchPattern(src.getDefiningOp(), mlir::m_Constant())) { + mlir::Value temp = builder.createTemporary(loc, srcTy); + builder.create(loc, src, temp); + src = temp; + } llvm::SmallVector args{ fir::runtime::createArguments(builder, loc, fTy, dst, src, bytes, modeValue, sourceFile, sourceLine)}; @@ -654,7 +660,7 @@ struct CUFDataTransferOpConversion loc, builder); } auto materializeBoxIfNeeded = [&](mlir::Value val) -> mlir::Value { - if (mlir::isa(val.getDefiningOp())) { + if (mlir::isa(val.getDefiningOp())) { // Materialize the box to memory to be able to call the runtime. mlir::Value box = builder.createTemporary(loc, val.getType()); builder.create(loc, val, box); diff --git a/flang/lib/Optimizer/Transforms/ExternalNameConversion.cpp b/flang/lib/Optimizer/Transforms/ExternalNameConversion.cpp index 648628fd1c9af..cfd90ff723793 100644 --- a/flang/lib/Optimizer/Transforms/ExternalNameConversion.cpp +++ b/flang/lib/Optimizer/Transforms/ExternalNameConversion.cpp @@ -12,6 +12,7 @@ #include "flang/Optimizer/Dialect/FIROpsSupport.h" #include "flang/Optimizer/Support/InternalNames.h" #include "flang/Optimizer/Transforms/Passes.h" +#include "mlir/Dialect/GPU/IR/GPUDialect.h" #include "mlir/IR/Attributes.h" #include "mlir/IR/SymbolTable.h" #include "mlir/Pass/Pass.h" @@ -58,26 +59,36 @@ void ExternalNameConversionPass::runOnOperation() { auto *context = &getContext(); llvm::DenseMap remappings; - // Update names of external Fortran functions and names of Common Block - // globals. - for (auto &funcOrGlobal : op->getRegion(0).front()) { - if (llvm::isa(funcOrGlobal) || - llvm::isa(funcOrGlobal)) { - auto symName = funcOrGlobal.getAttrOfType( - mlir::SymbolTable::getSymbolAttrName()); - auto deconstructedName = fir::NameUniquer::deconstruct(symName); - if (fir::NameUniquer::isExternalFacingUniquedName(deconstructedName)) { - auto newName = - mangleExternalName(deconstructedName, appendUnderscoreOpt); - auto newAttr = mlir::StringAttr::get(context, newName); - mlir::SymbolTable::setSymbolName(&funcOrGlobal, newAttr); - auto newSymRef = mlir::FlatSymbolRefAttr::get(newAttr); - remappings.try_emplace(symName, newSymRef); - if (llvm::isa(funcOrGlobal)) - funcOrGlobal.setAttr(fir::getInternalFuncNameAttrName(), symName); + + auto renameFuncOrGlobalInModule = [&](mlir::Operation *module) { + for (auto &funcOrGlobal : module->getRegion(0).front()) { + if (llvm::isa(funcOrGlobal) || + llvm::isa(funcOrGlobal)) { + auto symName = funcOrGlobal.getAttrOfType( + mlir::SymbolTable::getSymbolAttrName()); + auto deconstructedName = fir::NameUniquer::deconstruct(symName); + if (fir::NameUniquer::isExternalFacingUniquedName(deconstructedName)) { + auto newName = + mangleExternalName(deconstructedName, appendUnderscoreOpt); + auto newAttr = mlir::StringAttr::get(context, newName); + mlir::SymbolTable::setSymbolName(&funcOrGlobal, newAttr); + auto newSymRef = mlir::FlatSymbolRefAttr::get(newAttr); + remappings.try_emplace(symName, newSymRef); + if (llvm::isa(funcOrGlobal)) + funcOrGlobal.setAttr(fir::getInternalFuncNameAttrName(), symName); + } } } - } + }; + + // Update names of external Fortran functions and names of Common Block + // globals. + renameFuncOrGlobalInModule(op); + + // Do the same in GPU modules. + if (auto mod = mlir::dyn_cast_or_null(*op)) + for (auto gpuMod : mod.getOps()) + renameFuncOrGlobalInModule(gpuMod); if (remappings.empty()) return; diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp index 5ead9a48fa896..630acf9a6b256 100644 --- a/flang/lib/Parser/openmp-parsers.cpp +++ b/flang/lib/Parser/openmp-parsers.cpp @@ -97,7 +97,7 @@ template struct MapModifiers { // Parsing of mappers is not supported yet. using TypeModParser = Parser; - using IterParser = Parser; + using IterParser = Parser; using TypeParser = Parser; using ModParser = ConcatSeparated; @@ -131,9 +131,8 @@ template struct MotionModifiers { constexpr MotionModifiers(const MotionModifiers &) = default; constexpr MotionModifiers(MotionModifiers &&) = default; - // Parsing of mappers if not implemented yet. using ExpParser = Parser; - using IterParser = Parser; + using IterParser = Parser; using ModParser = ConcatSeparated; using resultType = typename ModParser::resultType; @@ -191,6 +190,8 @@ static TypeDeclarationStmt makeIterSpecDecl(std::list &&names) { makeEntityList(std::move(names))); } +// --- Parsers for clause modifiers ----------------------------------- + TYPE_PARSER(construct( // Using Parser or Parser has the problem // that they will attempt to treat what follows the '=' as initialization. @@ -207,14 +208,40 @@ TYPE_PARSER(construct( makeIterSpecDecl, nonemptyList(Parser{}) / "="_tok)), subscriptTriplet)) +TYPE_PARSER(construct( + "SINK" >> pure(OmpDependenceType::Value::Sink) || + "SOURCE" >> pure(OmpDependenceType::Value::Source))) + // [5.0] 2.1.6 iterator -> iterator-specifier-list -TYPE_PARSER(construct("ITERATOR" >> +TYPE_PARSER(construct("ITERATOR" >> parenthesized(nonemptyList(sourced(Parser{}))))) +// 2.15.3.7 LINEAR (linear-list: linear-step) +// linear-list -> list | modifier(list) +// linear-modifier -> REF | VAL | UVAL +TYPE_PARSER(construct( // + "REF" >> pure(OmpLinearModifier::Value::Ref) || + "VAL" >> pure(OmpLinearModifier::Value::Val) || + "UVAL" >> pure(OmpLinearModifier::Value::Uval))) + +// 2.15.3.6 REDUCTION (reduction-identifier: variable-name-list) +TYPE_PARSER(construct(Parser{}) || + construct(Parser{})) + +TYPE_PARSER(construct( + "DEPOBJ" >> pure(OmpTaskDependenceType::Value::Depobj) || + "IN"_id >> pure(OmpTaskDependenceType::Value::In) || + "INOUT"_id >> pure(OmpTaskDependenceType::Value::Inout) || + "INOUTSET"_id >> pure(OmpTaskDependenceType::Value::Inoutset) || + "MUTEXINOUTSET" >> pure(OmpTaskDependenceType::Value::Mutexinoutset) || + "OUT" >> pure(OmpTaskDependenceType::Value::Out))) + +// --- Parsers for clauses -------------------------------------------- + // [5.0] 2.10.1 affinity([aff-modifier:] locator-list) // aff-modifier: interator-modifier TYPE_PARSER(construct( - maybe(Parser{} / ":"), Parser{})) + maybe(Parser{} / ":"), Parser{})) // 2.15.3.1 DEFAULT (PRIVATE | FIRSTPRIVATE | SHARED | NONE) TYPE_PARSER(construct( @@ -250,21 +277,26 @@ TYPE_PARSER( "TOFROM" >> pure(OmpMapClause::Type::Tofrom))) template -static inline OmpMapClause makeMapClause( +static inline OmpMapClause makeMapClause(OmpMapperIdentifier &&mm, std::tuple>, - std::optional>, + std::optional>, std::optional>> &&mods, OmpObjectList &&objs) { auto &&[tm, it, ty] = std::move(mods); - return OmpMapClause{std::move(tm), std::move(it), std::move(ty), - std::move(objs), CommasEverywhere}; + return OmpMapClause{std::move(mm), std::move(tm), std::move(it), + std::move(ty), std::move(objs), CommasEverywhere}; } +TYPE_PARSER(construct( + maybe("MAPPER"_tok >> parenthesized(name) / ","_tok))) + TYPE_PARSER(construct( - applyFunction( - makeMapClause, MapModifiers(","_tok), Parser{}) || + applyFunction(makeMapClause, + Parser{}, MapModifiers(","_tok), + Parser{}) || applyFunction(makeMapClause, - MapModifiers(maybe(","_tok)), Parser{}))) + Parser{}, MapModifiers(maybe(","_tok)), + Parser{}))) // [OpenMP 5.0] // 2.19.7.2 defaultmap(implicit-behavior[:variable-category]) @@ -346,21 +378,17 @@ TYPE_PARSER(construct( ":"), scalarLogicalExpr)) -// 2.15.3.6 REDUCTION (reduction-identifier: variable-name-list) -TYPE_PARSER(construct(Parser{}) || - construct(Parser{})) - TYPE_PARSER(construct( maybe( ("INSCAN" >> pure(OmpReductionClause::ReductionModifier::Inscan) || "TASK" >> pure(OmpReductionClause::ReductionModifier::Task) || "DEFAULT" >> pure(OmpReductionClause::ReductionModifier::Default)) / ","), - Parser{} / ":", Parser{})) + Parser{} / ":", Parser{})) // OMP 5.0 2.19.5.6 IN_REDUCTION (reduction-identifier: variable-name-list) TYPE_PARSER(construct( - Parser{} / ":", Parser{})) + Parser{} / ":", Parser{})) // OMP 5.0 2.11.4 allocate-clause -> ALLOCATE ([allocator:] variable-name-list) // OMP 5.2 2.13.4 allocate-clause -> ALLOCATE ([allocate-modifier @@ -393,18 +421,6 @@ TYPE_PARSER(construct( ":"), Parser{})) -TYPE_PARSER(construct( - "SINK" >> pure(OmpDependenceType::Type::Sink) || - "SOURCE" >> pure(OmpDependenceType::Type::Source))) - -TYPE_PARSER(construct( - "DEPOBJ" >> pure(OmpTaskDependenceType::Type::Depobj) || - "IN"_id >> pure(OmpTaskDependenceType::Type::In) || - "INOUT"_id >> pure(OmpTaskDependenceType::Type::Inout) || - "INOUTSET"_id >> pure(OmpTaskDependenceType::Type::Inoutset) || - "MUTEXINOUTSET" >> pure(OmpTaskDependenceType::Type::Mutexinoutset) || - "OUT" >> pure(OmpTaskDependenceType::Type::Out))) - // iteration-offset -> +/- non-negative-constant-expr TYPE_PARSER(construct( Parser{}, scalarIntConstantExpr)) @@ -422,7 +438,7 @@ TYPE_PARSER(construct( TYPE_CONTEXT_PARSER("Omp Depend clause"_en_US, construct( construct(construct( - maybe(Parser{} / ","_tok), + maybe(Parser{} / ","_tok), Parser{} / ":", Parser{})) || construct(Parser{}))) @@ -435,7 +451,7 @@ TYPE_PARSER(construct( template static inline MotionClause makeMotionClause( std::tuple>, - std::optional>> &&mods, + std::optional>> &&mods, OmpObjectList &&objs) { auto &&[exp, iter] = std::move(mods); return MotionClause( @@ -454,14 +470,6 @@ TYPE_PARSER(construct( applyFunction(makeMotionClause, MotionModifiers(maybe(","_tok)), Parser{}))) -// 2.15.3.7 LINEAR (linear-list: linear-step) -// linear-list -> list | modifier(list) -// linear-modifier -> REF | VAL | UVAL -TYPE_PARSER( - construct("REF" >> pure(OmpLinearModifier::Type::Ref) || - "VAL" >> pure(OmpLinearModifier::Type::Val) || - "UVAL" >> pure(OmpLinearModifier::Type::Uval))) - TYPE_CONTEXT_PARSER("Omp LINEAR clause"_en_US, construct( construct(construct( @@ -844,7 +852,7 @@ TYPE_PARSER(construct( // 2.16 Declare Reduction Construct TYPE_PARSER(sourced(construct( verbatim("DECLARE REDUCTION"_tok), - "(" >> Parser{} / ":", + "(" >> Parser{} / ":", nonemptyList(Parser{}) / ":", Parser{} / ")", maybe(Parser{})))) diff --git a/flang/lib/Parser/parse-tree.cpp b/flang/lib/Parser/parse-tree.cpp index 574e5fd84862e..24b2902f286f4 100644 --- a/flang/lib/Parser/parse-tree.cpp +++ b/flang/lib/Parser/parse-tree.cpp @@ -253,20 +253,20 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Name &x) { return os << x.ToString(); } -OmpDependenceType::Type OmpDoacross::GetDepType() const { +OmpDependenceType::Value OmpDoacross::GetDepType() const { return common::visit( // common::visitors{ [](const OmpDoacross::Sink &) { - return OmpDependenceType::Type::Sink; + return OmpDependenceType::Value::Sink; }, [](const OmpDoacross::Source &) { - return OmpDependenceType::Type::Source; + return OmpDependenceType::Value::Source; }, }, u); } -OmpTaskDependenceType::Type OmpDependClause::TaskDep::GetTaskDepType() const { +OmpTaskDependenceType::Value OmpDependClause::TaskDep::GetTaskDepType() const { return std::get(t).v; } diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp index a782dfb8d767a..4d6aaceb69c18 100644 --- a/flang/lib/Parser/unparse.cpp +++ b/flang/lib/Parser/unparse.cpp @@ -2079,7 +2079,7 @@ class UnparseVisitor { Put(" = "); Walk(std::get(x.t)); } - void Unparse(const OmpIteratorModifier &x) { + void Unparse(const OmpIterator &x) { Word("ITERATOR("); Walk(x.v); Put(")"); @@ -2093,14 +2093,24 @@ class UnparseVisitor { void Unparse(const OmpMapClause &x) { auto &typeMod = std::get>>(x.t); - auto &iter = std::get>>(x.t); + auto &iter = std::get>>(x.t); auto &type = std::get>>(x.t); + auto &mapper = std::get(x.t); // For a given list of items, if the item has a value, then walk it. // Print commas between items that have values. // Return 'true' if something did get printed, otherwise 'false'. bool needComma{false}; + if (mapper.v) { + Word("MAPPER("); + Walk(*mapper.v); + Put(")"); + needComma = true; + } if (typeMod) { + if (needComma) { + Put(", "); + } Walk(*typeMod); needComma = true; } @@ -2137,7 +2147,7 @@ class UnparseVisitor { Walk(std::get(x.t)); } void Unparse(const OmpAffinityClause &x) { - Walk(std::get>(x.t), ":"); + Walk(std::get>(x.t), ":"); Walk(std::get(x.t)); } void Unparse(const OmpAlignedClause &x) { @@ -2148,7 +2158,7 @@ class UnparseVisitor { void Unparse(const OmpFromClause &x) { auto &expect{ std::get>>(x.t)}; - auto &iter{std::get>>(x.t)}; + auto &iter{std::get>>(x.t)}; bool needComma{false}; if (expect) { Walk(*expect); @@ -2181,13 +2191,13 @@ class UnparseVisitor { void Unparse(const OmpReductionClause &x) { Walk(std::get>(x.t), ","); - Walk(std::get(x.t)); + Walk(std::get(x.t)); Put(":"); Walk(std::get(x.t)); } void Unparse(const OmpDetachClause &x) { Walk(x.v); } void Unparse(const OmpInReductionClause &x) { - Walk(std::get(x.t)); + Walk(std::get(x.t)); Put(":"); Walk(std::get(x.t)); } @@ -2253,7 +2263,7 @@ class UnparseVisitor { void Unparse(const OmpToClause &x) { auto &expect{ std::get>>(x.t)}; - auto &iter{std::get>>(x.t)}; + auto &iter{std::get>>(x.t)}; bool needComma{false}; if (expect) { Walk(*expect); @@ -2635,7 +2645,7 @@ class UnparseVisitor { } void Unparse(const OpenMPDeclareReductionConstruct &x) { Put("("); - Walk(std::get(x.t)), Put(" : "); + Walk(std::get(x.t)), Put(" : "); Walk(std::get>(x.t), ","), Put(" : "); Walk(std::get(x.t)); Put(")"); @@ -2900,8 +2910,8 @@ class UnparseVisitor { WALK_NESTED_ENUM( OmpLastprivateClause, LastprivateModifier) // OMP lastprivate-modifier WALK_NESTED_ENUM(OmpScheduleModifierType, ModType) // OMP schedule-modifier - WALK_NESTED_ENUM(OmpLinearModifier, Type) // OMP linear-modifier - WALK_NESTED_ENUM(OmpTaskDependenceType, Type) // OMP task-dependence-type + WALK_NESTED_ENUM(OmpLinearModifier, Value) // OMP linear-modifier + WALK_NESTED_ENUM(OmpTaskDependenceType, Value) // OMP task-dependence-type WALK_NESTED_ENUM(OmpScheduleClause, ScheduleType) // OMP schedule-type WALK_NESTED_ENUM(OmpDeviceClause, DeviceModifier) // OMP device modifier WALK_NESTED_ENUM(OmpDeviceTypeClause, Type) // OMP DEVICE_TYPE diff --git a/flang/lib/Semantics/CMakeLists.txt b/flang/lib/Semantics/CMakeLists.txt index 41406ecf50e00..7855ae7eed138 100644 --- a/flang/lib/Semantics/CMakeLists.txt +++ b/flang/lib/Semantics/CMakeLists.txt @@ -31,6 +31,7 @@ add_flang_library(FortranSemantics definable.cpp expression.cpp mod-file.cpp + openmp-modifiers.cpp pointer-assignment.cpp program-tree.cpp resolve-labels.cpp diff --git a/flang/lib/Semantics/check-cuda.cpp b/flang/lib/Semantics/check-cuda.cpp index eaf1d52a9fc1a..79b7a26ef222f 100644 --- a/flang/lib/Semantics/check-cuda.cpp +++ b/flang/lib/Semantics/check-cuda.cpp @@ -91,6 +91,37 @@ struct DeviceExprChecker } }; +struct FindHostArray + : public evaluate::AnyTraverse { + using Result = const Symbol *; + using Base = evaluate::AnyTraverse; + FindHostArray() : Base(*this) {} + using Base::operator(); + Result operator()(const evaluate::Component &x) const { + const Symbol &symbol{x.GetLastSymbol()}; + if (IsAllocatableOrPointer(symbol)) { + if (Result hostArray{(*this)(symbol)}) { + return hostArray; + } + } + return (*this)(x.base()); + } + Result operator()(const Symbol &symbol) const { + if (const auto *details{ + symbol.GetUltimate().detailsIf()}) { + if (details->IsArray() && + (!details->cudaDataAttr() || + (details->cudaDataAttr() && + *details->cudaDataAttr() != common::CUDADataAttr::Device && + *details->cudaDataAttr() != common::CUDADataAttr::Managed && + *details->cudaDataAttr() != common::CUDADataAttr::Unified))) { + return &symbol; + } + } + return nullptr; + } +}; + template static MaybeMsg CheckUnwrappedExpr(const A &x) { if (const auto *expr{parser::Unwrap(x)}) { return DeviceExprChecker{}(expr->typedExpr); @@ -306,22 +337,11 @@ template class DeviceContextChecker { } } template - void ErrorIfHostSymbol(const A &expr, const parser::CharBlock &source) { - for (const Symbol &sym : CollectCudaSymbols(expr)) { - if (const auto *details = - sym.GetUltimate().detailsIf()) { - if (details->IsArray() && - (!details->cudaDataAttr() || - (details->cudaDataAttr() && - *details->cudaDataAttr() != common::CUDADataAttr::Device && - *details->cudaDataAttr() != common::CUDADataAttr::Managed && - *details->cudaDataAttr() != - common::CUDADataAttr::Unified))) { - context_.Say(source, - "Host array '%s' cannot be present in CUF kernel"_err_en_US, - sym.name()); - } - } + void ErrorIfHostSymbol(const A &expr, parser::CharBlock source) { + if (const Symbol * hostArray{FindHostArray{}(expr)}) { + context_.Say(source, + "Host array '%s' cannot be present in CUF kernel"_err_en_US, + hostArray->name()); } } void Check(const parser::ActionStmt &stmt, const parser::CharBlock &source) { diff --git a/flang/lib/Semantics/check-declarations.cpp b/flang/lib/Semantics/check-declarations.cpp index 354594f3339df..c9656d031b2e1 100644 --- a/flang/lib/Semantics/check-declarations.cpp +++ b/flang/lib/Semantics/check-declarations.cpp @@ -1110,7 +1110,8 @@ void CheckHelper::CheckPointerInitialization(const Symbol &symbol) { if (proc->init() && *proc->init()) { // C1519 - must be nonelemental external or module procedure, // or an unrestricted specific intrinsic function. - const Symbol &ultimate{(*proc->init())->GetUltimate()}; + const Symbol &local{DEREF(*proc->init())}; + const Symbol &ultimate{local.GetUltimate()}; bool checkTarget{true}; if (ultimate.attrs().test(Attr::INTRINSIC)) { if (auto intrinsic{context_.intrinsics().IsSpecificIntrinsicFunction( @@ -1123,11 +1124,12 @@ void CheckHelper::CheckPointerInitialization(const Symbol &symbol) { ultimate.name(), symbol.name()); checkTarget = false; } - } else if ((!ultimate.attrs().test(Attr::EXTERNAL) && - ultimate.owner().kind() != Scope::Kind::Module) || + } else if (!(ultimate.attrs().test(Attr::EXTERNAL) || + ultimate.owner().kind() == Scope::Kind::Module || + ultimate.owner().IsTopLevel()) || IsDummy(ultimate) || IsPointer(ultimate)) { - context_.Say("Procedure pointer '%s' initializer '%s' is neither " - "an external nor a module procedure"_err_en_US, + context_.Say( + "Procedure pointer '%s' initializer '%s' is neither an external nor a module procedure"_err_en_US, symbol.name(), ultimate.name()); checkTarget = false; } else if (IsElementalProcedure(ultimate)) { diff --git a/flang/lib/Semantics/check-io.cpp b/flang/lib/Semantics/check-io.cpp index eeeda553d8a46..3c99163c1f134 100644 --- a/flang/lib/Semantics/check-io.cpp +++ b/flang/lib/Semantics/check-io.cpp @@ -860,6 +860,8 @@ void IoChecker::Leave(const parser::WriteStmt &writeStmt) { void IoChecker::LeaveReadWrite() const { CheckForRequiredSpecifier(IoSpecKind::Unit); // C1211 + CheckForRequiredSpecifier(flags_.test(Flag::InternalUnit), + "UNIT=internal-file", flags_.test(Flag::FmtOrNml), "FMT or NML"); CheckForProhibitedSpecifier(IoSpecKind::Nml, IoSpecKind::Rec); // C1216 CheckForProhibitedSpecifier(IoSpecKind::Nml, IoSpecKind::Fmt); // C1216 CheckForProhibitedSpecifier( diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp index 0b64a4a9801cc..9cac652216fcf 100644 --- a/flang/lib/Semantics/check-omp-structure.cpp +++ b/flang/lib/Semantics/check-omp-structure.cpp @@ -683,8 +683,7 @@ void OmpStructureChecker::CheckIteratorRange( } } -void OmpStructureChecker::CheckIteratorModifier( - const parser::OmpIteratorModifier &x) { +void OmpStructureChecker::CheckIteratorModifier(const parser::OmpIterator &x) { // Check if all iterator variables have integer type. for (auto &&iterSpec : x.v) { bool isInteger{true}; @@ -1859,21 +1858,21 @@ void OmpStructureChecker::CheckTargetUpdate() { } void OmpStructureChecker::CheckTaskDependenceType( - const parser::OmpTaskDependenceType::Type &x) { + const parser::OmpTaskDependenceType::Value &x) { // Common checks for task-dependence-type (DEPEND and UPDATE clauses). unsigned version{context_.langOptions().OpenMPVersion}; unsigned since{0}; switch (x) { - case parser::OmpTaskDependenceType::Type::In: - case parser::OmpTaskDependenceType::Type::Out: - case parser::OmpTaskDependenceType::Type::Inout: + case parser::OmpTaskDependenceType::Value::In: + case parser::OmpTaskDependenceType::Value::Out: + case parser::OmpTaskDependenceType::Value::Inout: break; - case parser::OmpTaskDependenceType::Type::Mutexinoutset: - case parser::OmpTaskDependenceType::Type::Depobj: + case parser::OmpTaskDependenceType::Value::Mutexinoutset: + case parser::OmpTaskDependenceType::Value::Depobj: since = 50; break; - case parser::OmpTaskDependenceType::Type::Inoutset: + case parser::OmpTaskDependenceType::Value::Inoutset: since = 52; break; } @@ -1888,14 +1887,14 @@ void OmpStructureChecker::CheckTaskDependenceType( } void OmpStructureChecker::CheckDependenceType( - const parser::OmpDependenceType::Type &x) { + const parser::OmpDependenceType::Value &x) { // Common checks for dependence-type (DEPEND and UPDATE clauses). unsigned version{context_.langOptions().OpenMPVersion}; unsigned deprecatedIn{~0u}; switch (x) { - case parser::OmpDependenceType::Type::Source: - case parser::OmpDependenceType::Type::Sink: + case parser::OmpDependenceType::Value::Source: + case parser::OmpDependenceType::Value::Sink: deprecatedIn = 52; break; } @@ -2864,7 +2863,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Reduction &x) { bool OmpStructureChecker::CheckReductionOperators( const parser::OmpClause::Reduction &x) { - const auto &definedOp{std::get(x.v.t)}; + const auto &definedOp{std::get(x.v.t)}; bool ok = false; common::visit( common::visitors{ @@ -2929,7 +2928,7 @@ bool OmpStructureChecker::CheckIntrinsicOperator( static bool IsReductionAllowedForType( const parser::OmpClause::Reduction &x, const DeclTypeSpec &type) { - const auto &definedOp{std::get(x.v.t)}; + const auto &definedOp{std::get(x.v.t)}; // TODO: user defined reduction operators. Just allow everything for now. bool ok{true}; @@ -3484,7 +3483,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) { CheckAllowedClause(llvm::omp::Clause::OMPC_map); using TypeMod = parser::OmpMapClause::TypeModifier; using Type = parser::OmpMapClause::Type; - using IterMod = parser::OmpIteratorModifier; + using IterMod = parser::OmpIterator; unsigned version{context_.langOptions().OpenMPVersion}; if (auto commas{std::get(x.v.t)}; !commas && version >= 52) { @@ -3637,7 +3636,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Depend &x) { if (taskDep) { if (version == 50) { invalidDep = taskDep->GetTaskDepType() == - parser::OmpTaskDependenceType::Type::Depobj; + parser::OmpTaskDependenceType::Value::Depobj; } } else { invalidDep = true; @@ -3684,7 +3683,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Depend &x) { } } } - if (std::get>(taskDep->t)) { + if (std::get>(taskDep->t)) { unsigned allowedInVersion{50}; if (version < allowedInVersion) { context_.Say(GetContext().clauseSource, @@ -3923,7 +3922,8 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Update &x) { if (version >= 51) { bool invalidDep{false}; if (taskType) { - invalidDep = taskType->v == parser::OmpTaskDependenceType::Type::Depobj; + invalidDep = + taskType->v == parser::OmpTaskDependenceType::Value::Depobj; } else { invalidDep = true; } @@ -4058,7 +4058,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::From &x) { CheckAllowedClause(llvm::omp::Clause::OMPC_from); unsigned version{context_.langOptions().OpenMPVersion}; using ExpMod = parser::OmpFromClause::Expectation; - using IterMod = parser::OmpIteratorModifier; + using IterMod = parser::OmpIterator; if (auto &expMod{std::get>>(x.v.t)}) { unsigned allowedInVersion{51}; @@ -4122,7 +4122,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::To &x) { } assert(GetContext().directive == llvm::omp::OMPD_target_update); using ExpMod = parser::OmpFromClause::Expectation; - using IterMod = parser::OmpIteratorModifier; + using IterMod = parser::OmpIterator; if (auto &expMod{std::get>>(x.v.t)}) { unsigned allowedInVersion{51}; diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h index 429e451c463e4..df21ebac0f6d7 100644 --- a/flang/lib/Semantics/check-omp-structure.h +++ b/flang/lib/Semantics/check-omp-structure.h @@ -202,7 +202,7 @@ class OmpStructureChecker void CheckWorkshareBlockStmts(const parser::Block &, parser::CharBlock); void CheckIteratorRange(const parser::OmpIteratorSpecifier &x); - void CheckIteratorModifier(const parser::OmpIteratorModifier &x); + void CheckIteratorModifier(const parser::OmpIterator &x); void CheckLoopItrVariableIsInt(const parser::OpenMPLoopConstruct &x); void CheckDoWhile(const parser::OpenMPLoopConstruct &x); void CheckAssociatedLoopConstraints(const parser::OpenMPLoopConstruct &x); @@ -218,8 +218,8 @@ class OmpStructureChecker void CheckSIMDNest(const parser::OpenMPConstruct &x); void CheckTargetNest(const parser::OpenMPConstruct &x); void CheckTargetUpdate(); - void CheckDependenceType(const parser::OmpDependenceType::Type &x); - void CheckTaskDependenceType(const parser::OmpTaskDependenceType::Type &x); + void CheckDependenceType(const parser::OmpDependenceType::Value &x); + void CheckTaskDependenceType(const parser::OmpTaskDependenceType::Value &x); void CheckCancellationNest( const parser::CharBlock &source, const parser::OmpCancelType::Type &type); std::int64_t GetOrdCollapseLevel(const parser::OpenMPLoopConstruct &x); diff --git a/flang/lib/Semantics/openmp-modifiers.cpp b/flang/lib/Semantics/openmp-modifiers.cpp new file mode 100644 index 0000000000000..70ca988cddce5 --- /dev/null +++ b/flang/lib/Semantics/openmp-modifiers.cpp @@ -0,0 +1,146 @@ +//===-- flang/lib/Semantics/openmp-modifiers.cpp --------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "flang/Semantics/openmp-modifiers.h" + +#include "flang/Parser/parse-tree.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Frontend/OpenMP/OMP.h" + +#include +#include +#include + +namespace Fortran::semantics { +using namespace llvm::omp; + +/// Find the highest version that exists as a key in the given map, +/// and is less than or equal to `version`. +/// Account for "version" not being a value from getOpenMPVersions(). +template +static unsigned findVersion( + unsigned version, const std::map &map) { + llvm::ArrayRef versions{llvm::omp::getOpenMPVersions()}; + assert(!versions.empty() && "getOpenMPVersions returned empty list"); + version = std::clamp(version, versions.front(), versions.back()); + + // std::map is sorted with respect to keys, by default in the ascending + // order. + unsigned found{0}; + for (auto &[v, _] : map) { + if (v <= version) { + found = v; + } else { + break; + } + } + + assert(found != 0 && "cannot locate entry for version in map"); + return found; +} + +const OmpProperties &OmpModifierDescriptor::props(unsigned version) const { + return props_.at(findVersion(version, props_)); +} + +const OmpClauses &OmpModifierDescriptor::clauses(unsigned version) const { + return clauses_.at(findVersion(version, clauses_)); +} + +// Note: The intent for these functions is to have them be automatically- +// generated in the future. + +template <> +const OmpModifierDescriptor &OmpGetDescriptor() { + static const OmpModifierDescriptor desc{ + /*name=*/"dependence-type", + /*props=*/ + { + {45, {OmpProperty::Required, OmpProperty::Ultimate}}, + }, + /*clauses=*/ + { + {45, {Clause::OMPC_depend}}, + {51, {Clause::OMPC_depend, Clause::OMPC_update}}, + {52, {Clause::OMPC_doacross}}, + }, + }; + return desc; +} + +template <> +const OmpModifierDescriptor &OmpGetDescriptor() { + static const OmpModifierDescriptor desc{ + /*name=*/"iterator", + /*props=*/ + { + {50, {OmpProperty::Unique}}, + }, + /*clauses=*/ + { + {50, {Clause::OMPC_affinity, Clause::OMPC_depend}}, + {51, + {Clause::OMPC_affinity, Clause::OMPC_depend, Clause::OMPC_from, + Clause::OMPC_map, Clause::OMPC_to}}, + }, + }; + return desc; +} + +template <> +const OmpModifierDescriptor &OmpGetDescriptor() { + static const OmpModifierDescriptor desc{ + /*name=*/"linear-modifier", + /*props=*/ + { + {45, {OmpProperty::Unique}}, + }, + /*clauses=*/ + { + {45, {Clause::OMPC_linear}}, + }, + }; + return desc; +} + +template <> +const OmpModifierDescriptor & +OmpGetDescriptor() { + static const OmpModifierDescriptor desc{ + /*name=*/"reduction-identifier", + /*props=*/ + { + {45, {OmpProperty::Required, OmpProperty::Ultimate}}, + }, + /*clauses=*/ + { + {45, {Clause::OMPC_reduction}}, + {50, + {Clause::OMPC_in_reduction, Clause::OMPC_reduction, + Clause::OMPC_task_reduction}}, + }, + }; + return desc; +} + +template <> +const OmpModifierDescriptor &OmpGetDescriptor() { + static const OmpModifierDescriptor desc{ + /*name=*/"task-dependence-type", + /*props=*/ + { + {52, {OmpProperty::Required, OmpProperty::Ultimate}}, + }, + /*clauses=*/ + { + {52, {Clause::OMPC_depend, Clause::OMPC_update}}, + }, + }; + return desc; +} +} // namespace Fortran::semantics diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp index a2059a1123b5e..80e238f3476ac 100644 --- a/flang/lib/Semantics/resolve-directives.cpp +++ b/flang/lib/Semantics/resolve-directives.cpp @@ -518,8 +518,8 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor { } bool Pre(const parser::OmpClause::Reduction &x) { - const parser::OmpReductionOperator &opr{ - std::get(x.v.t)}; + const parser::OmpReductionIdentifier &opr{ + std::get(x.v.t)}; auto createDummyProcSymbol = [&](const parser::Name *name) { // If name resolution failed, create a dummy symbol const auto namePair{ diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp index 09120e3ed0e97..929d35a4717dc 100644 --- a/flang/lib/Semantics/resolve-names.cpp +++ b/flang/lib/Semantics/resolve-names.cpp @@ -1471,6 +1471,8 @@ class OmpVisitor : public virtual DeclarationVisitor { bool Pre(const parser::OpenMPDeclareMapperConstruct &); + bool Pre(const parser::OmpMapClause &); + void Post(const parser::OmpBeginLoopDirective &) { messageHandler().set_currStmtSource(std::nullopt); } @@ -1639,6 +1641,33 @@ bool OmpVisitor::Pre(const parser::OpenMPDeclareMapperConstruct &x) { return false; } +bool OmpVisitor::Pre(const parser::OmpMapClause &x) { + const auto &mid{std::get(x.t)}; + if (const auto &mapperName{mid.v}) { + if (const auto symbol = FindSymbol(currScope(), *mapperName)) { + // TODO: Do we need a specific flag or type here, to distinghuish against + // other ConstructName things? Leaving this for the full implementation + // of mapper lowering. + auto *misc{symbol->detailsIf()}; + if (!misc || misc->kind() != MiscDetails::Kind::ConstructName) + context().Say(mapperName->source, + "Name '%s' should be a mapper name"_err_en_US, mapperName->source); + else + mapperName->symbol = symbol; + } else { + mapperName->symbol = &MakeSymbol( + *mapperName, MiscDetails{MiscDetails::Kind::ConstructName}); + // TODO: When completing the implementation, we probably want to error if + // the symbol is not declared, but right now, testing that the TODO for + // OmpMapclause happens is obscured by the TODO for declare mapper, so + // leaving this out. Remove the above line once the declare mapper is + // implemented. context().Say(mapperName->source, "'%s' not + // declared"_err_en_US, mapperName->source); + } + } + return true; +} + // Walk the parse tree and resolve names to symbols. class ResolveNamesVisitor : public virtual ScopeHandler, public ModuleVisitor, diff --git a/flang/test/Driver/dynamic-linker.f90 b/flang/test/Driver/dynamic-linker.f90 index 6d5c443ab75cb..e850939374568 100644 --- a/flang/test/Driver/dynamic-linker.f90 +++ b/flang/test/Driver/dynamic-linker.f90 @@ -17,7 +17,7 @@ ! GNU-LINKER-OPTIONS-SAME: "-static" ! GNU-LINKER-OPTIONS-SAME: "-rpath" "/path/to/dir" -! RDYNAMIC-LINKER-OPTION: "{{.*}}ld" +! RDYNAMIC-LINKER-OPTION: "{{.*}}ld{{(\.lld)?(\.exe)?}}" ! RDYNAMIC-LINKER-OPTION-SAME: "-export-dynamic" ! For MSVC, adding -static does not add any additional linker options. diff --git a/flang/test/Driver/isysroot.f90 b/flang/test/Driver/isysroot.f90 index 28b435cce08ed..07ffb68653147 100644 --- a/flang/test/Driver/isysroot.f90 +++ b/flang/test/Driver/isysroot.f90 @@ -8,7 +8,7 @@ ! RUN: %flang -### --target=aarch64-linux-gnu -isysroot /path/to/sysroot \ ! RUN: %s 2>&1 | FileCheck %s --check-prefix=CHECK-LINUX -! CHECK-DARWIN: "{{.*}}ld{{(64)?(\.lld)?}}" {{.*}}"-syslibroot" "/path/to/sysroot" +! CHECK-DARWIN: "{{.*}}ld{{(64)?(\.lld)?(\.exe)?}}" {{.*}}"-syslibroot" "/path/to/sysroot" ! Unused on Linux. ! CHECK-LINUX: warning: argument unused during compilation: '-isysroot /path/to/sysroot' ! CHECK-LINUX-NOT: /path/to/sysroot diff --git a/flang/test/Fir/CUDA/cuda-alloc-free.fir b/flang/test/Fir/CUDA/cuda-alloc-free.fir index 49bb5bdf5e6bc..abf2d56695b17 100644 --- a/flang/test/Fir/CUDA/cuda-alloc-free.fir +++ b/flang/test/Fir/CUDA/cuda-alloc-free.fir @@ -73,7 +73,7 @@ func.func @_QPtest_type() { // CHECK: %[[CONV_BYTES:.*]] = fir.convert %[[BYTES]] : (index) -> i64 // CHECK: fir.call @_FortranACUFMemAlloc(%[[CONV_BYTES]], %c0{{.*}}, %{{.*}}, %{{.*}}) : (i64, i32, !fir.ref, i32) -> !fir.llvm_ptr -gpu.module @cuda_device_mod [#nvvm.target] { +gpu.module @cuda_device_mod { gpu.func @_QMalloc() kernel { %0 = cuf.alloc !fir.box>> {bindc_name = "a", data_attr = #cuf.cuda, uniq_name = "_QMallocEa"} -> !fir.ref>>> gpu.return diff --git a/flang/test/Fir/CUDA/cuda-constructor-2.f90 b/flang/test/Fir/CUDA/cuda-constructor-2.f90 index 99386abc4fafd..901497e2cde55 100644 --- a/flang/test/Fir/CUDA/cuda-constructor-2.f90 +++ b/flang/test/Fir/CUDA/cuda-constructor-2.f90 @@ -10,11 +10,11 @@ module attributes {dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry>> } - gpu.module @cuda_device_mod [#nvvm.target] { + gpu.module @cuda_device_mod { } } -// CHECK: gpu.module @cuda_device_mod [#nvvm.target] +// CHECK: gpu.module @cuda_device_mod // CHECK: llvm.func internal @__cudaFortranConstructor() { // CHECK-DAG: %[[MODULE:.*]] = cuf.register_module @cuda_device_mod -> !llvm.ptr diff --git a/flang/test/Fir/CUDA/cuda-data-transfer.fir b/flang/test/Fir/CUDA/cuda-data-transfer.fir index 1ee44f3c6d97c..0f9ca6e640a80 100644 --- a/flang/test/Fir/CUDA/cuda-data-transfer.fir +++ b/flang/test/Fir/CUDA/cuda-data-transfer.fir @@ -466,4 +466,91 @@ func.func @_QPlogical_cst() { // CHECK: %[[BOX_NONE:.*]] = fir.convert %[[DESC]] : (!fir.ref>>) -> !fir.ref> // CHECK: fir.call @_FortranACUFDataTransferCstDesc(%{{.*}}, %[[BOX_NONE]], %{{.*}}, %{{.*}}, %{{.*}}) : (!fir.ref>, !fir.ref>, i32, !fir.ref, i32) -> none +func.func @_QPcallkernel(%arg0: !fir.box>> {fir.bindc_name = "a"}, %arg1: !fir.ref {fir.bindc_name = "b"}, %arg2: !fir.ref {fir.bindc_name = "c"}) { + %c0_i64 = arith.constant 0 : i64 + %c1_i32 = arith.constant 1 : i32 + %c0_i32 = arith.constant 0 : i32 + %c1 = arith.constant 1 : index + %c0 = arith.constant 0 : index + %0 = fir.dummy_scope : !fir.dscope + %1 = fir.declare %arg0 dummy_scope %0 {uniq_name = "_QFcallkernelEa"} : (!fir.box>>, !fir.dscope) -> !fir.box>> + %2 = fir.rebox %1 : (!fir.box>>) -> !fir.box>> + %3 = cuf.alloc !fir.box>>> {bindc_name = "adev", data_attr = #cuf.cuda, uniq_name = "_QFcallkernelEadev"} -> !fir.ref>>>> + %7 = fir.declare %3 {data_attr = #cuf.cuda, fortran_attrs = #fir.var_attrs, uniq_name = "_QFcallkernelEadev"} : (!fir.ref>>>>) -> !fir.ref>>>> + %8 = fir.declare %arg1 dummy_scope %0 {uniq_name = "_QFcallkernelEb"} : (!fir.ref, !fir.dscope) -> !fir.ref + %9 = fir.declare %arg2 dummy_scope %0 {uniq_name = "_QFcallkernelEc"} : (!fir.ref, !fir.dscope) -> !fir.ref + %10 = fir.alloca i32 {bindc_name = "m", uniq_name = "_QFcallkernelEm"} + %11 = fir.declare %10 {uniq_name = "_QFcallkernelEm"} : (!fir.ref) -> !fir.ref + %12 = fir.alloca i32 {bindc_name = "n", uniq_name = "_QFcallkernelEn"} + %13 = fir.declare %12 {uniq_name = "_QFcallkernelEn"} : (!fir.ref) -> !fir.ref + %14:3 = fir.box_dims %2, %c0 : (!fir.box>>, index) -> (index, index, index) + %15 = fir.convert %14#1 : (index) -> i32 + fir.store %15 to %13 : !fir.ref + %16:3 = fir.box_dims %2, %c1 : (!fir.box>>, index) -> (index, index, index) + %27 = fir.load %13 : !fir.ref + %28 = fir.convert %27 : (i32) -> index + %29 = arith.cmpi sgt, %28, %c0 : index + %30 = arith.select %29, %28, %c0 : index + %31 = fir.load %11 : !fir.ref + %32 = fir.convert %31 : (i32) -> index + %33 = arith.cmpi sgt, %32, %c0 : index + %34 = arith.select %33, %32, %c0 : index + %35 = fir.shape %30, %34 : (index, index) -> !fir.shape<2> + %36 = fir.undefined index + %37 = fir.slice %c1, %28, %c1, %c1, %32, %c1 : (index, index, index, index, index, index) -> !fir.slice<2> + %38 = fir.rebox %2 [%37] : (!fir.box>>, !fir.slice<2>) -> !fir.box>> + cuf.data_transfer %38 to %7 {transfer_kind = #cuf.cuda_transfer} : !fir.box>>, !fir.ref>>>> + return +} + +// CHECK-LABEL: func.func @_QPcallkernel( +// CHECK-SAME: %[[ARG0:.*]]: !fir.box>> {fir.bindc_name = "a"} +// CHECK: %[[ALLOCA:.*]] = fir.alloca !fir.box>> +// CHECK: %[[DECL_ARG0:.*]] = fir.declare %[[ARG0]] dummy_scope %{{.*}} {uniq_name = "_QFcallkernelEa"} : (!fir.box>>, !fir.dscope) -> !fir.box>> +// CHECK: %[[REBOX0:.*]] = fir.rebox %[[DECL_ARG0]] : (!fir.box>>) -> !fir.box>> +// CHECK: %[[REBOX1:.*]] = fir.rebox %[[REBOX0]] [%{{.*}}] : (!fir.box>>, !fir.slice<2>) -> !fir.box>> +// CHECK: fir.store %[[REBOX1]] to %[[ALLOCA]] : !fir.ref>>> +// CHECK: %[[BOX_NONE:.*]] = fir.convert %[[ALLOCA]] : (!fir.ref>>>) -> !fir.ref> +// CHECK: fir.call @_FortranACUFDataTransferDescDesc(%{{.*}}, %[[BOX_NONE]], %{{.*}}, %{{.*}}, %{{.*}}) : (!fir.ref>, !fir.ref>, i32, !fir.ref, i32) -> none + +func.func @_QPsrc_cst() { + %0 = fir.dummy_scope : !fir.dscope + %1 = cuf.alloc !fir.box>> {bindc_name = "d4", data_attr = #cuf.cuda, uniq_name = "_QFsub4Ed4"} -> !fir.ref>>> + %5:2 = hlfir.declare %1 {data_attr = #cuf.cuda, fortran_attrs = #fir.var_attrs, uniq_name = "_QFsub4Ed4"} : (!fir.ref>>>) -> (!fir.ref>>>, !fir.ref>>>) + %6 = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFsub4Ei"} + %7:2 = hlfir.declare %6 {uniq_name = "_QFsub4Ei"} : (!fir.ref) -> (!fir.ref, !fir.ref) + %c1 = arith.constant 1 : index + %c10_i32 = arith.constant 10 : i32 + %c0_i32 = arith.constant 0 : i32 + %9 = fir.convert %5#1 : (!fir.ref>>>) -> !fir.ref> + %c6_i32 = arith.constant 6 : i32 + %14 = fir.convert %c6_i32 : (i32) -> index + %c10_i32_0 = arith.constant 10 : i32 + %15 = fir.convert %c10_i32_0 : (i32) -> index + %c1_1 = arith.constant 1 : index + %16 = fir.convert %14 : (index) -> i32 + %17:2 = fir.do_loop %arg1 = %14 to %15 step %c1_1 iter_args(%arg2 = %16) -> (index, i32) { + fir.store %arg2 to %7#1 : !fir.ref + %cst = arith.constant -4.000000e+00 : f32 + %22 = fir.load %5#0 : !fir.ref>>> + %23 = fir.load %7#0 : !fir.ref + %24 = fir.convert %23 : (i32) -> i64 + %25 = hlfir.designate %22 (%24) : (!fir.box>>, i64) -> !fir.ref + cuf.data_transfer %cst to %25 {transfer_kind = #cuf.cuda_transfer} : f32, !fir.ref + %26 = arith.addi %arg1, %c1_1 : index + %27 = fir.convert %c1_1 : (index) -> i32 + %28 = fir.load %7#1 : !fir.ref + %29 = arith.addi %28, %27 : i32 + fir.result %26, %29 : index, i32 + } + return +} + +// CHECK-LABEL: func.func @_QPsrc_cst() +// CHECK: %[[ALLOCA:.*]] = fir.alloca f32 +// CHECK: %[[CST:.*]] = arith.constant -4.000000e+00 : f32 +// CHECK: fir.store %[[CST]] to %[[ALLOCA]] : !fir.ref +// CHECK: %[[CONV:.*]] = fir.convert %[[ALLOCA]] : (!fir.ref) -> !fir.llvm_ptr +// CHECK: fir.call @_FortranACUFDataTransferPtrPtr(%{{.*}}, %[[CONV]], %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}) : (!fir.llvm_ptr, !fir.llvm_ptr, i64, i32, !fir.ref, i32) -> none + } // end of module diff --git a/flang/test/Fir/CUDA/cuda-device-global.f90 b/flang/test/Fir/CUDA/cuda-device-global.f90 index c83a938d5af21..8cac643b27c34 100644 --- a/flang/test/Fir/CUDA/cuda-device-global.f90 +++ b/flang/test/Fir/CUDA/cuda-device-global.f90 @@ -5,9 +5,9 @@ module attributes {fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", gpu.container_module} { fir.global @_QMmtestsEn(dense<[3, 4, 5, 6, 7]> : tensor<5xi32>) {data_attr = #cuf.cuda} : !fir.array<5xi32> - gpu.module @cuda_device_mod [#nvvm.target] { + gpu.module @cuda_device_mod { } } -// CHECK: gpu.module @cuda_device_mod [#nvvm.target] +// CHECK: gpu.module @cuda_device_mo // CHECK-NEXT: fir.global @_QMmtestsEn(dense<[3, 4, 5, 6, 7]> : tensor<5xi32>) {data_attr = #cuf.cuda} : !fir.array<5xi32> diff --git a/flang/test/Fir/CUDA/cuda-extranal-mangling.mlir b/flang/test/Fir/CUDA/cuda-extranal-mangling.mlir new file mode 100644 index 0000000000000..551a89a7018c2 --- /dev/null +++ b/flang/test/Fir/CUDA/cuda-extranal-mangling.mlir @@ -0,0 +1,13 @@ +// RUN: fir-opt --split-input-file --external-name-interop %s | FileCheck %s + +gpu.module @cuda_device_mod { + gpu.func @_QPfoo() { + fir.call @_QPthreadfence() fastmath : () -> () + gpu.return + } + func.func private @_QPthreadfence() attributes {cuf.proc_attr = #cuf.cuda_proc} +} + +// CHECK-LABEL: gpu.func @_QPfoo +// CHECK: fir.call @threadfence_() +// CHECK: func.func private @threadfence_() diff --git a/flang/test/Fir/CUDA/cuda-implicit-device-global.f90 b/flang/test/Fir/CUDA/cuda-implicit-device-global.f90 index 18b56a491cd65..772e2696171a6 100644 --- a/flang/test/Fir/CUDA/cuda-implicit-device-global.f90 +++ b/flang/test/Fir/CUDA/cuda-implicit-device-global.f90 @@ -25,7 +25,7 @@ // Test that global used in device function are flagged with the correct // CHECK: fir.call @_FortranAioBeginExternalListOutput(%{{.*}}, %[[CONV]], %{{.*}}) fastmath : (i32, !fir.ref, i32) -> !fir.ref // CHECK: fir.global linkonce @_QQcl[[SYMBOL]] {data_attr = #cuf.cuda} constant : !fir.char<1,32> -// CHECK-LABEL: gpu.module @cuda_device_mod [#nvvm.target] +// CHECK-LABEL: gpu.module @cuda_device_mod // CHECK: fir.global linkonce @_QQclX6995815537abaf90e86ce166af128f3a // ----- @@ -51,5 +51,96 @@ // Test that global used in device function are flagged with the correct // CHECK: fir.call @_FortranAioBeginExternalListOutput(%{{.*}}, %[[CONV]], %{{.*}}) fastmath : (i32, !fir.ref, i32) -> !fir.ref // CHECK: fir.global linkonce @_QQcl[[SYMBOL]] constant : !fir.char<1,32> -// CHECK-LABEL: gpu.module @cuda_device_mod [#nvvm.target] +// CHECK-LABEL: gpu.module @cuda_device_mod // CHECK-NOT: fir.global linkonce @_QQclX6995815537abaf90e86ce166af128f3a + +// ----- + +func.func @_QPsub1() { + %0 = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFsub1Ei"} + %1:2 = hlfir.declare %0 {uniq_name = "_QFsub1Ei"} : (!fir.ref) -> (!fir.ref, !fir.ref) + %c1_i32 = arith.constant 1 : i32 + %2 = fir.convert %c1_i32 : (i32) -> index + %c100_i32 = arith.constant 100 : i32 + %3 = fir.convert %c100_i32 : (i32) -> index + %c1 = arith.constant 1 : index + cuf.kernel<<<*, *>>> (%arg0 : index) = (%2 : index) to (%3 : index) step (%c1 : index) { + %4 = fir.convert %arg0 : (index) -> i32 + fir.store %4 to %1#1 : !fir.ref + %5 = fir.load %1#0 : !fir.ref + %c1_i32_0 = arith.constant 1 : i32 + %6 = arith.cmpi eq, %5, %c1_i32_0 : i32 + fir.if %6 { + %c6_i32 = arith.constant 6 : i32 + %7 = fir.address_of(@_QQclX91d13f6e74caa2f03965d7a7c6a8fdd5) : !fir.ref> + %8 = fir.convert %7 : (!fir.ref>) -> !fir.ref + %c5_i32 = arith.constant 5 : i32 + %9 = fir.call @_FortranAioBeginExternalListOutput(%c6_i32, %8, %c5_i32) fastmath : (i32, !fir.ref, i32) -> !fir.ref + %10 = fir.address_of(@_QQclX5465737420504153534544) : !fir.ref> + %c11 = arith.constant 11 : index + %11:2 = hlfir.declare %10 typeparams %c11 {fortran_attrs = #fir.var_attrs, uniq_name = "_QQclX5465737420504153534544"} : (!fir.ref>, index) -> (!fir.ref>, !fir.ref>) + %12 = fir.convert %11#1 : (!fir.ref>) -> !fir.ref + %13 = fir.convert %c11 : (index) -> i64 + %14 = fir.call @_FortranAioOutputAscii(%9, %12, %13) fastmath : (!fir.ref, !fir.ref, i64) -> i1 + %15 = fir.call @_FortranAioEndIoStatement(%9) fastmath : (!fir.ref) -> i32 + } + "fir.end"() : () -> () + } + return +} +func.func private @_FortranAioBeginExternalListOutput(i32, !fir.ref, i32) -> !fir.ref attributes {fir.io, fir.runtime} +fir.global linkonce @_QQclX91d13f6e74caa2f03965d7a7c6a8fdd5 constant : !fir.char<1,10> { + %0 = fir.string_lit "dummy.cuf\00"(10) : !fir.char<1,10> + fir.has_value %0 : !fir.char<1,10> +} +func.func private @_FortranAioOutputAscii(!fir.ref, !fir.ref, i64) -> i1 attributes {fir.io, fir.runtime} +fir.global linkonce @_QQclX5465737420504153534544 constant : !fir.char<1,11> { + %0 = fir.string_lit "Test PASSED"(11) : !fir.char<1,11> + fir.has_value %0 : !fir.char<1,11> +} + +// CHECK: fir.global linkonce @_QQclX5465737420504153534544 {data_attr = #cuf.cuda} constant : !fir.char<1,11> + +// CHECK-LABEL: gpu.module @cuda_device_mod +// CHECK: fir.global linkonce @_QQclX5465737420504153534544 {data_attr = #cuf.cuda} constant + +// ----- + +func.func @_QPsub1() attributes {cuf.proc_attr = #cuf.cuda_proc} { + %6 = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFsub1Ei"} + %7:2 = hlfir.declare %6 {uniq_name = "_QFsub1Ei"} : (!fir.ref) -> (!fir.ref, !fir.ref) + %12 = fir.load %7#0 : !fir.ref + %c1_i32 = arith.constant 1 : i32 + %13 = arith.cmpi eq, %12, %c1_i32 : i32 + fir.if %13 { + %c6_i32 = arith.constant 6 : i32 + %14 = fir.address_of(@_QQclX91d13f6e74caa2f03965d7a7c6a8fdd5) : !fir.ref> + %15 = fir.convert %14 : (!fir.ref>) -> !fir.ref + %c3_i32 = arith.constant 3 : i32 + %16 = fir.call @_FortranAioBeginExternalListOutput(%c6_i32, %15, %c3_i32) fastmath : (i32, !fir.ref, i32) -> !fir.ref + %17 = fir.address_of(@_QQclX5465737420504153534544) : !fir.ref> + %c11 = arith.constant 11 : index + %18:2 = hlfir.declare %17 typeparams %c11 {fortran_attrs = #fir.var_attrs, uniq_name = "_QQclX5465737420504153534544"} : (!fir.ref>, index) -> (!fir.ref>, !fir.ref>) + %19 = fir.convert %18#1 : (!fir.ref>) -> !fir.ref + %20 = fir.convert %c11 : (index) -> i64 + %21 = fir.call @_FortranAioOutputAscii(%16, %19, %20) fastmath : (!fir.ref, !fir.ref, i64) -> i1 + %22 = fir.call @_FortranAioEndIoStatement(%16) fastmath : (!fir.ref) -> i32 + } + return +} +func.func private @_FortranAioBeginExternalListOutput(i32, !fir.ref, i32) -> !fir.ref attributes {fir.io, fir.runtime} +fir.global linkonce @_QQclX91d13f6e74caa2f03965d7a7c6a8fdd5 constant : !fir.char<1,10> { + %0 = fir.string_lit "dummy.cuf\00"(10) : !fir.char<1,10> + fir.has_value %0 : !fir.char<1,10> +} +func.func private @_FortranAioOutputAscii(!fir.ref, !fir.ref, i64) -> i1 attributes {fir.io, fir.runtime} +fir.global linkonce @_QQclX5465737420504153534544 constant : !fir.char<1,11> { + %0 = fir.string_lit "Test PASSED"(11) : !fir.char<1,11> + fir.has_value %0 : !fir.char<1,11> +} +func.func private @_FortranAioEndIoStatement(!fir.ref) -> i32 attributes {fir.io, fir.runtime} + +// CHECK: fir.global linkonce @_QQclX5465737420504153534544 {data_attr = #cuf.cuda} constant : !fir.char<1,11> + +// CHECK-LABEL: gpu.module @cuda_device_mod +// CHECK: fir.global linkonce @_QQclX5465737420504153534544 {data_attr = #cuf.cuda} constant diff --git a/flang/test/Fir/basic-program.fir b/flang/test/Fir/basic-program.fir index bca454c13ff9c..4b18acb7c2b43 100644 --- a/flang/test/Fir/basic-program.fir +++ b/flang/test/Fir/basic-program.fir @@ -47,6 +47,7 @@ func.func @_QQmain() { // PASSES-NEXT: LowerHLFIRIntrinsics // PASSES-NEXT: BufferizeHLFIR // PASSES-NEXT: ConvertHLFIRtoFIR +// PASSES-NEXT: LowerWorkshare // PASSES-NEXT: CSE // PASSES-NEXT: (S) 0 num-cse'd - Number of operations CSE'd // PASSES-NEXT: (S) 0 num-dce'd - Number of operations DCE'd diff --git a/flang/test/Fir/target-rewrite-integer-loongarch64.fir b/flang/test/Fir/target-rewrite-integer-loongarch64.fir new file mode 100644 index 0000000000000..8421cbbb41a9d --- /dev/null +++ b/flang/test/Fir/target-rewrite-integer-loongarch64.fir @@ -0,0 +1,27 @@ +/// Test i32 passing and returning on LoongArch64 +/// LoongArch64 LP64D ABI requires unsigned 32 bit integers to be sign extended. + +// REQUIRES: loongarch-registered-target +// RUN: fir-opt --target-rewrite="target=loongarch64-unknown-linux-gnu" %s | FileCheck %s --check-prefix=LOONGARCH64 +// RUN: tco -target="loongarch64-unknown-linux-gnu" %s | FileCheck %s --check-prefix=LOONGARCH64_LLVM + +// LOONGARCH64: func.func private @cfunc32(i32 {llvm.signext}) -> (i32 {llvm.signext}) attributes {fir.bindc_name = "cfunc32"} + +// LOONGARCH64_LLVM: declare signext i32 @cfunc32(i32 signext) +func.func private @cfunc32(i32) -> i32 attributes {fir.bindc_name = "cfunc32"} + +// LOONGARCH64-LABEL: func.func @foo( +// LOONGARCH64-SAME: %[[VAL_0:.*]]: i32 {llvm.signext}) -> (i32 {llvm.signext}) attributes {fir.bindc_name = "foo"} { +// LOONGARCH64: %[[VAL_1:.*]] = fir.call @cfunc32(%[[VAL_0]]) fastmath : (i32) -> i32 +// LOONGARCH64: return %[[VAL_1]] : i32 +// LOONGARCH64: } + +// LOONGARCH64_LLVM-LABEL: define signext i32 @foo( +// LOONGARCH64_LLVM: i32 signext %[[VAL_0:.*]]) { +// LOONGARCH64_LLVM: %[[VAL_1:.*]] = call i32 @cfunc32(i32 %[[VAL_0]]) +// LOONGARCH64_LLVM: ret i32 %[[VAL_1]] +// LOONGARCH64_LLVM: } +func.func @foo(%0: i32) -> i32 attributes {fir.bindc_name = "foo"} { + %1 = fir.call @cfunc32(%0) fastmath : (i32) -> i32 + return %1 : i32 +} diff --git a/flang/test/Lower/OpenMP/Todo/map-mapper.f90 b/flang/test/Lower/OpenMP/Todo/map-mapper.f90 new file mode 100644 index 0000000000000..d83c20db29307 --- /dev/null +++ b/flang/test/Lower/OpenMP/Todo/map-mapper.f90 @@ -0,0 +1,16 @@ +! RUN: not %flang_fc1 -emit-fir -fopenmp -fopenmp-version=50 %s 2>&1 | FileCheck %s +program p + integer, parameter :: n = 256 + real(8) :: a(256) + !! TODO: Add declare mapper, when it works to lower this construct + !!type t1 + !! integer :: x + !!end type t1 + !!!$omp declare mapper(xx : t1 :: nn) map(nn, nn%x) + !$omp target map(mapper(xx), from:a) +!CHECK: not yet implemented: OmpMapClause(MAPPER(...)) + do i=1,n + a(i) = 4.2 + end do + !$omp end target +end program p diff --git a/flang/test/Lower/OpenMP/Todo/scope-allocate.f90 b/flang/test/Lower/OpenMP/Todo/scope-allocate.f90 new file mode 100644 index 0000000000000..5a834c81a852c --- /dev/null +++ b/flang/test/Lower/OpenMP/Todo/scope-allocate.f90 @@ -0,0 +1,12 @@ +! RUN: %not_todo_cmd %flang_fc1 -emit-fir -fopenmp -o - %s -fopenmp-version=52 2>&1 | FileCheck %s + +! CHECK: not yet implemented: Scope construct +program omp_scope + integer i + i = 10 + + !$omp scope allocate(x) private(x) + print *, "omp scope", i + !$omp end scope + +end program omp_scope diff --git a/flang/test/Lower/OpenMP/Todo/scope-firstprivate.f90 b/flang/test/Lower/OpenMP/Todo/scope-firstprivate.f90 new file mode 100644 index 0000000000000..87bcecb817da4 --- /dev/null +++ b/flang/test/Lower/OpenMP/Todo/scope-firstprivate.f90 @@ -0,0 +1,12 @@ +! RUN: %not_todo_cmd %flang_fc1 -emit-fir -fopenmp -o - %s -fopenmp-version=52 2>&1 | FileCheck %s + +! CHECK: not yet implemented: Scope construct +program omp_scope + integer i + i = 10 + + !$omp scope firstprivate(x) + print *, "omp scope", i + !$omp end scope + +end program omp_scope diff --git a/flang/test/Lower/OpenMP/workshare.f90 b/flang/test/Lower/OpenMP/workshare.f90 index 1e11677a15e1f..8e771952f5b6d 100644 --- a/flang/test/Lower/OpenMP/workshare.f90 +++ b/flang/test/Lower/OpenMP/workshare.f90 @@ -6,7 +6,7 @@ subroutine sb1(arr) integer :: arr(:) !CHECK: omp.parallel { !$omp parallel -!CHECK: omp.single { +!CHECK: omp.workshare { !$omp workshare arr = 0 !$omp end workshare @@ -20,7 +20,7 @@ subroutine sb2(arr) integer :: arr(:) !CHECK: omp.parallel { !$omp parallel -!CHECK: omp.single nowait { +!CHECK: omp.workshare nowait { !$omp workshare arr = 0 !$omp end workshare nowait @@ -33,7 +33,7 @@ subroutine sb2(arr) subroutine sb3(arr) integer :: arr(:) !CHECK: omp.parallel { -!CHECK: omp.single { +!CHECK: omp.workshare { !$omp parallel workshare arr = 0 !$omp end parallel workshare diff --git a/flang/test/Parser/OpenMP/affinity-clause.f90 b/flang/test/Parser/OpenMP/affinity-clause.f90 index 804723cad7b2b..5e9e0a2194bab 100644 --- a/flang/test/Parser/OpenMP/affinity-clause.f90 +++ b/flang/test/Parser/OpenMP/affinity-clause.f90 @@ -63,7 +63,7 @@ subroutine f02(x) !PARSE-TREE: OmpBeginBlockDirective !PARSE-TREE: | OmpBlockDirective -> llvm::omp::Directive = task !PARSE-TREE: | OmpClauseList -> OmpClause -> Affinity -> OmpAffinityClause -!PARSE-TREE: | | OmpIteratorModifier -> OmpIteratorSpecifier +!PARSE-TREE: | | OmpIterator -> OmpIteratorSpecifier !PARSE-TREE: | | | TypeDeclarationStmt !PARSE-TREE: | | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec -> !PARSE-TREE: | | | | EntityDecl diff --git a/flang/test/Parser/OpenMP/depobj-construct.f90 b/flang/test/Parser/OpenMP/depobj-construct.f90 index 3de190c95bb73..51726a5adf99e 100644 --- a/flang/test/Parser/OpenMP/depobj-construct.f90 +++ b/flang/test/Parser/OpenMP/depobj-construct.f90 @@ -15,7 +15,7 @@ subroutine f00 !PARSE-TREE: | Verbatim !PARSE-TREE: | OmpObject -> Designator -> DataRef -> Name = 'x' !PARSE-TREE: | OmpClause -> Depend -> OmpDependClause -> TaskDep -!PARSE-TREE: | | OmpTaskDependenceType -> Type = In +!PARSE-TREE: | | OmpTaskDependenceType -> Value = In !PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'y' subroutine f01 @@ -31,7 +31,7 @@ subroutine f01 !PARSE-TREE: ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPStandaloneConstruct -> OpenMPDepobjConstruct !PARSE-TREE: | Verbatim !PARSE-TREE: | OmpObject -> Designator -> DataRef -> Name = 'x' -!PARSE-TREE: | OmpClause -> Update -> OmpUpdateClause -> OmpTaskDependenceType -> Type = Out +!PARSE-TREE: | OmpClause -> Update -> OmpUpdateClause -> OmpTaskDependenceType -> Value = Out subroutine f02 integer :: x diff --git a/flang/test/Parser/OpenMP/from-clause.f90 b/flang/test/Parser/OpenMP/from-clause.f90 index 1dcca0b611dfb..cff9c077c0a94 100644 --- a/flang/test/Parser/OpenMP/from-clause.f90 +++ b/flang/test/Parser/OpenMP/from-clause.f90 @@ -45,7 +45,7 @@ subroutine f02(x) !PARSE-TREE: OmpSimpleStandaloneDirective -> llvm::omp::Directive = target update !PARSE-TREE: OmpClauseList -> OmpClause -> From -> OmpFromClause !PARSE-TREE: | Expectation = Present -!PARSE-TREE: | OmpIteratorModifier -> OmpIteratorSpecifier +!PARSE-TREE: | OmpIterator -> OmpIteratorSpecifier !PARSE-TREE: | | TypeDeclarationStmt !PARSE-TREE: | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec -> !PARSE-TREE: | | | EntityDecl @@ -74,7 +74,7 @@ subroutine f03(x) !PARSE-TREE: OmpSimpleStandaloneDirective -> llvm::omp::Directive = target update !PARSE-TREE: OmpClauseList -> OmpClause -> From -> OmpFromClause !PARSE-TREE: | Expectation = Present -!PARSE-TREE: | OmpIteratorModifier -> OmpIteratorSpecifier +!PARSE-TREE: | OmpIterator -> OmpIteratorSpecifier !PARSE-TREE: | | TypeDeclarationStmt !PARSE-TREE: | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec -> !PARSE-TREE: | | | EntityDecl diff --git a/flang/test/Parser/OpenMP/in-reduction-clause.f90 b/flang/test/Parser/OpenMP/in-reduction-clause.f90 index 776ead3824b71..ab26ca2d9300f 100644 --- a/flang/test/Parser/OpenMP/in-reduction-clause.f90 +++ b/flang/test/Parser/OpenMP/in-reduction-clause.f90 @@ -37,14 +37,14 @@ end subroutine omp_in_reduction_taskgroup !PARSE-TREE-NEXT: OmpBeginBlockDirective !PARSE-TREE-NEXT: OmpBlockDirective -> llvm::omp::Directive = task !PARSE-TREE-NEXT: OmpClauseList -> OmpClause -> InReduction -> OmpInReductionClause -!PARSE-TREE-NEXT: OmpReductionOperator -> DefinedOperator -> IntrinsicOperator = Add +!PARSE-TREE-NEXT: OmpReductionIdentifier -> DefinedOperator -> IntrinsicOperator = Add !PARSE-TREE-NEXT: OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'z' !PARSE-TREE: OpenMPConstruct -> OpenMPLoopConstruct !PARSE-TREE-NEXT: OmpBeginLoopDirective !PARSE-TREE-NEXT: OmpLoopDirective -> llvm::omp::Directive = taskloop !PARSE-TREE-NEXT: OmpClauseList -> OmpClause -> InReduction -> OmpInReductionClause -!PARSE-TREE-NEXT: OmpReductionOperator -> DefinedOperator -> IntrinsicOperator = Add +!PARSE-TREE-NEXT: OmpReductionIdentifier -> DefinedOperator -> IntrinsicOperator = Add !PARSE-TREE-NEXT: OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'z' subroutine omp_in_reduction_parallel() @@ -74,6 +74,6 @@ end subroutine omp_in_reduction_parallel !PARSE-TREE-NEXT: OmpBeginLoopDirective !PARSE-TREE-NEXT: OmpLoopDirective -> llvm::omp::Directive = taskloop simd !PARSE-TREE-NEXT: OmpClauseList -> OmpClause -> InReduction -> OmpInReductionClause -!PARSE-TREE-NEXT: OmpReductionOperator -> DefinedOperator -> IntrinsicOperator = Add +!PARSE-TREE-NEXT: OmpReductionIdentifier -> DefinedOperator -> IntrinsicOperator = Add !PASRE-TREE-NEXT: OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'z' diff --git a/flang/test/Parser/OpenMP/map-modifiers.f90 b/flang/test/Parser/OpenMP/map-modifiers.f90 index 0c95f21c5e6a5..578512283c4dc 100644 --- a/flang/test/Parser/OpenMP/map-modifiers.f90 +++ b/flang/test/Parser/OpenMP/map-modifiers.f90 @@ -159,7 +159,7 @@ subroutine f10(x) !PARSE-TREE: | OmpBlockDirective -> llvm::omp::Directive = target !PARSE-TREE: | OmpClauseList -> OmpClause -> Map -> OmpMapClause !PARSE-TREE: | | TypeModifier = Present -!PARSE-TREE: | | OmpIteratorModifier -> OmpIteratorSpecifier +!PARSE-TREE: | | OmpIterator -> OmpIteratorSpecifier !PARSE-TREE: | | | TypeDeclarationStmt !PARSE-TREE: | | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec -> !PARSE-TREE: | | | | EntityDecl @@ -194,7 +194,7 @@ subroutine f11(x) !PARSE-TREE: | OmpBlockDirective -> llvm::omp::Directive = target !PARSE-TREE: | OmpClauseList -> OmpClause -> Map -> OmpMapClause !PARSE-TREE: | | TypeModifier = Present -!PARSE-TREE: | | OmpIteratorModifier -> OmpIteratorSpecifier +!PARSE-TREE: | | OmpIterator -> OmpIteratorSpecifier !PARSE-TREE: | | | TypeDeclarationStmt !PARSE-TREE: | | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec -> !PARSE-TREE: | | | | EntityDecl @@ -229,7 +229,7 @@ subroutine f12(x) !PARSE-TREE: | OmpBlockDirective -> llvm::omp::Directive = target !PARSE-TREE: | OmpClauseList -> OmpClause -> Map -> OmpMapClause !PARSE-TREE: | | TypeModifier = Present -!PARSE-TREE: | | OmpIteratorModifier -> OmpIteratorSpecifier +!PARSE-TREE: | | OmpIterator -> OmpIteratorSpecifier !PARSE-TREE: | | | TypeDeclarationStmt !PARSE-TREE: | | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec -> !PARSE-TREE: | | | | EntityDecl @@ -287,7 +287,7 @@ subroutine f90(x, y) !PARSE-TREE: | OmpBlockDirective -> llvm::omp::Directive = target !PARSE-TREE: | OmpClauseList -> OmpClause -> Map -> OmpMapClause !PARSE-TREE: | | TypeModifier = Present -!PARSE-TREE: | | OmpIteratorModifier -> OmpIteratorSpecifier +!PARSE-TREE: | | OmpIterator -> OmpIteratorSpecifier !PARSE-TREE: | | | TypeDeclarationStmt !PARSE-TREE: | | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec -> !PARSE-TREE: | | | | EntityDecl @@ -316,3 +316,28 @@ subroutine f90(x, y) !PARSE-TREE: | | | | Designator -> DataRef -> Name = 'k' !PARSE-TREE: | | bool = 'true' +subroutine f100(x, y) + integer :: x(10) + integer :: y + integer, parameter :: p = 23 + !$omp target map(mapper(xx), from: x) + x = x + 1 + !$omp end target +end + +!UNPARSE: SUBROUTINE f100 (x, y) +!UNPARSE: INTEGER x(10_4) +!UNPARSE: INTEGER y +!UNPARSE: INTEGER, PARAMETER :: p = 23_4 +!UNPARSE: !$OMP TARGET MAP(MAPPER(XX), FROM: X) +!UNPARSE: x=x+1_4 +!UNPARSE: !$OMP END TARGET +!UNPARSE: END SUBROUTINE + +!PARSE-TREE: OmpBeginBlockDirective +!PARSE-TREE: | OmpBlockDirective -> llvm::omp::Directive = target +!PARSE-TREE: | OmpClauseList -> OmpClause -> Map -> OmpMapClause +!PARSE-TREE: | | OmpMapperIdentifier -> Name = 'xx' +!PARSE-TREE: | | Type = From +!PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'x' + diff --git a/flang/test/Parser/OpenMP/reduction-modifier.f90 b/flang/test/Parser/OpenMP/reduction-modifier.f90 index d46aa70959592..4bba23bcf0611 100644 --- a/flang/test/Parser/OpenMP/reduction-modifier.f90 +++ b/flang/test/Parser/OpenMP/reduction-modifier.f90 @@ -10,7 +10,7 @@ subroutine foo() ! PARSE-TREE: | | | | OmpLoopDirective -> llvm::omp::Directive = do ! PARSE-TREE: | | | | OmpClauseList -> OmpClause -> Reduction -> OmpReductionClause ! PARSE-TREE: | | | | | ReductionModifier = Task -! PARSE-TREE: | | | | | OmpReductionOperator -> DefinedOperator -> IntrinsicOperator = Multiply +! PARSE-TREE: | | | | | OmpReductionIdentifier -> DefinedOperator -> IntrinsicOperator = Multiply ! PARSE-TREE: | | | | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'j !$omp do reduction (task, *: j) do i = 1, 10 diff --git a/flang/test/Parser/OpenMP/target-update-to-clause.f90 b/flang/test/Parser/OpenMP/target-update-to-clause.f90 index 2702575847924..bb57270fc0bf9 100644 --- a/flang/test/Parser/OpenMP/target-update-to-clause.f90 +++ b/flang/test/Parser/OpenMP/target-update-to-clause.f90 @@ -45,7 +45,7 @@ subroutine f02(x) !PARSE-TREE: OmpSimpleStandaloneDirective -> llvm::omp::Directive = target update !PARSE-TREE: OmpClauseList -> OmpClause -> To -> OmpToClause !PARSE-TREE: | Expectation = Present -!PARSE-TREE: | OmpIteratorModifier -> OmpIteratorSpecifier +!PARSE-TREE: | OmpIterator -> OmpIteratorSpecifier !PARSE-TREE: | | TypeDeclarationStmt !PARSE-TREE: | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec -> !PARSE-TREE: | | | EntityDecl @@ -74,7 +74,7 @@ subroutine f03(x) !PARSE-TREE: OmpSimpleStandaloneDirective -> llvm::omp::Directive = target update !PARSE-TREE: OmpClauseList -> OmpClause -> To -> OmpToClause !PARSE-TREE: | Expectation = Present -!PARSE-TREE: | OmpIteratorModifier -> OmpIteratorSpecifier +!PARSE-TREE: | OmpIterator -> OmpIteratorSpecifier !PARSE-TREE: | | TypeDeclarationStmt !PARSE-TREE: | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec -> !PARSE-TREE: | | | EntityDecl diff --git a/flang/test/Semantics/OpenMP/map-clause-symbols.f90 b/flang/test/Semantics/OpenMP/map-clause-symbols.f90 new file mode 100644 index 0000000000000..8f984fcd2fa7e --- /dev/null +++ b/flang/test/Semantics/OpenMP/map-clause-symbols.f90 @@ -0,0 +1,14 @@ +! RUN: %flang_fc1 -fdebug-dump-symbols -fopenmp -fopenmp-version=50 %s | FileCheck %s +program main +!CHECK-LABEL: MainProgram scope: main + integer, parameter :: n = 256 + real(8) :: a(256) + !$omp target map(mapper(xx), from:a) + do i=1,n + a(i) = 4.2 + end do + !$omp end target +!CHECK: OtherConstruct scope: size=0 alignment=1 sourceRange=74 bytes +!CHECK: OtherClause scope: size=0 alignment=1 sourceRange=0 bytes +!CHECK: xx: Misc ConstructName +end program main diff --git a/flang/test/Semantics/OpenMP/map-clause.f90 b/flang/test/Semantics/OpenMP/map-clause.f90 index a7430c3edeb94..efcef2571a04a 100644 --- a/flang/test/Semantics/OpenMP/map-clause.f90 +++ b/flang/test/Semantics/OpenMP/map-clause.f90 @@ -33,3 +33,11 @@ subroutine sb(arr) c = 2 !$omp end target end subroutine + +subroutine sb1 + integer :: xx + integer :: a + !ERROR: Name 'xx' should be a mapper name + !$omp target map(mapper(xx), from:a) + !$omp end target +end subroutine sb1 diff --git a/flang/test/Semantics/io03.f90 b/flang/test/Semantics/io03.f90 index 9cd672ee01696..6c05924f09dce 100644 --- a/flang/test/Semantics/io03.f90 +++ b/flang/test/Semantics/io03.f90 @@ -58,6 +58,13 @@ read(internal_file2, *) jj read(internal_file4, *) jj + !This is a valid statement but it's not what it looks like; "(internal-file)" + !must be parsed as a format expression, not as an internal unit. + read(internal_file) jj + + !ERROR: If UNIT=internal-file appears, FMT or NML must also appear + read(internal_file, iostat=stat1) jj + !ERROR: Internal file must not have a vector subscript read(internal_fileA(vv), *) jj @@ -106,11 +113,12 @@ !ERROR: If UNIT=* appears, POS must not appear read(*, pos=13) + !ERROR: If UNIT=internal-file appears, FMT or NML must also appear !ERROR: If UNIT=internal-file appears, REC must not appear read(internal_file, rec=13) !ERROR: If UNIT=internal-file appears, POS must not appear - read(internal_file, pos=13) + read(internal_file, *, pos=13) !ERROR: If REC appears, END must not appear read(10, fmt='(I4)', end=9, rec=13) jj @@ -135,7 +143,7 @@ read(*, asynchronous='yes') !ERROR: If ASYNCHRONOUS='YES' appears, UNIT=number must also appear - read(internal_file, asynchronous='y'//'es') + read(internal_file, *, asynchronous='y'//'es') !ERROR: If ID appears, ASYNCHRONOUS='YES' must also appear read(10, id=id) diff --git a/flang/test/Semantics/io04.f90 b/flang/test/Semantics/io04.f90 index 685e43dd6e401..7114f14a9488a 100644 --- a/flang/test/Semantics/io04.f90 +++ b/flang/test/Semantics/io04.f90 @@ -34,6 +34,7 @@ write(unit=10) 'Ok' write(*, nnn) write(10, nnn) + !ERROR: If UNIT=internal-file appears, FMT or NML must also appear write(internal_file) write(internal_file, *) write(internal_file, fmt=*) @@ -55,7 +56,7 @@ allocate(a(8), stat=stat8) !ERROR: Duplicate UNIT specifier - write(internal_file, unit=*) + write(internal_file, unit=*, fmt=*) !ERROR: WRITE statement must have a UNIT specifier write(nml=nnn) @@ -84,6 +85,7 @@ !ERROR: If UNIT=internal-file appears, POS must not appear write(internal_file, err=9, pos=n, nml=nnn) + !ERROR: If UNIT=internal-file appears, FMT or NML must also appear !ERROR: If UNIT=internal-file appears, REC must not appear write(internal_file, rec=n, err=9) @@ -106,7 +108,7 @@ write(*, asynchronous='yes') !ERROR: If ASYNCHRONOUS='YES' appears, UNIT=number must also appear - write(internal_file, asynchronous='yes') + write(internal_file, *, asynchronous='yes') !ERROR: If ID appears, ASYNCHRONOUS='YES' must also appear write(10, *, id=id) "Ok" diff --git a/flang/test/Semantics/pointer02.f90 b/flang/test/Semantics/pointer02.f90 new file mode 100644 index 0000000000000..90bb435855939 --- /dev/null +++ b/flang/test/Semantics/pointer02.f90 @@ -0,0 +1,53 @@ +! RUN: %python %S/test_errors.py %s %flang_fc1 +recursive subroutine sub(dp, dpp) + procedure(inner) dp + procedure(inner), pointer :: dpp + procedure(inner) ext + procedure(sub), pointer :: p1 => sub ! ok + procedure(inner), pointer :: p2 => ext ! ok + !ERROR: Procedure pointer 'p3' initializer 'inner' is neither an external nor a module procedure + procedure(inner), pointer :: p3 => inner + !ERROR: Procedure pointer 'p4' initializer 'dp' is neither an external nor a module procedure + procedure(inner), pointer :: p4 => dp + !ERROR: Procedure pointer 'p5' initializer 'dpp' is neither an external nor a module procedure + procedure(inner), pointer :: p5 => dpp + generic :: generic => ext + !ERROR: 'generic' must be an abstract interface or a procedure with an explicit interface + procedure(generic), pointer :: p6 ! => generic + contains + subroutine inner + end +end +recursive function fun() result(res) + procedure(fun), pointer :: p1 => fun ! ok + !ERROR: Procedure pointer 'p2' initializer 'inner' is neither an external nor a module procedure + procedure(inner), pointer :: p2 => inner + res = 0. + contains + function inner() + inner = 0. + end +end +module m + procedure(msub), pointer :: ps1 => msub ! ok + procedure(mfun), pointer :: pf1 => mfun ! ok + contains + recursive subroutine msub + procedure(msub), pointer :: ps2 => msub ! ok + !ERROR: Procedure pointer 'ps3' initializer 'inner' is neither an external nor a module procedure + procedure(inner), pointer :: ps3 => inner + contains + subroutine inner + end + end + recursive function mfun() result(res) + procedure(mfun), pointer :: pf2 => mfun ! ok + !ERROR: Procedure pointer 'pf3' initializer 'inner' is neither an external nor a module procedure + procedure(inner), pointer :: pf3 => inner + res = 0. + contains + function inner() + inner = 0. + end + end +end diff --git a/flang/test/Semantics/smp-def02.f90 b/flang/test/Semantics/smp-def02.f90 new file mode 100644 index 0000000000000..ef27f14edae0a --- /dev/null +++ b/flang/test/Semantics/smp-def02.f90 @@ -0,0 +1,42 @@ +!RUN: %flang -fsyntax-only %s 2>&1 | FileCheck --allow-empty %s +!Ensure no bogus error messages about insufficiently defined procedures +!CHECK-NOT: error + +module m + interface + module subroutine smp1(a1) + end + end interface +end + +submodule(m) sm1 + interface + module subroutine smp2(a1,a2) + end + end interface +end + +submodule(m:sm1) sm2 + interface generic + procedure smp1 + procedure smp2 + module subroutine smp3(a1,a2,a3) + end + end interface + contains + subroutine local1 + call generic(0.) + call generic(0., 1.) + call generic(0., 1., 2.) + end + subroutine local2(a1,a2,a3) + end + module procedure smp1 + end + module subroutine smp2(a1,a2) + end + module subroutine smp3(a1,a2,a3) + end +end + + diff --git a/flang/test/Semantics/undef-result01.f90 b/flang/test/Semantics/undef-result01.f90 index 08e7fe1e44899..e1ae58dae7c0a 100644 --- a/flang/test/Semantics/undef-result01.f90 +++ b/flang/test/Semantics/undef-result01.f90 @@ -117,7 +117,7 @@ function defdByNamelist() end character(4) function defdByWrite() - write(defdByWrite) 'abcd' + write(defdByWrite,*) 'abcd' end integer function defdBySize() diff --git a/flang/test/Transforms/OpenMP/lower-workshare-alloca.mlir b/flang/test/Transforms/OpenMP/lower-workshare-alloca.mlir new file mode 100644 index 0000000000000..12b0558d06ed5 --- /dev/null +++ b/flang/test/Transforms/OpenMP/lower-workshare-alloca.mlir @@ -0,0 +1,53 @@ +// RUN: fir-opt --lower-workshare --allow-unregistered-dialect %s | FileCheck %s + +// Checks that fir.alloca is hoisted out and copyprivate'd +func.func @wsfunc() { + omp.workshare { + %c1 = arith.constant 1 : index + %c42 = arith.constant 42 : index + %c1_i32 = arith.constant 1 : i32 + %alloc = fir.alloca i32 + fir.store %c1_i32 to %alloc : !fir.ref + omp.workshare.loop_wrapper { + omp.loop_nest (%arg1) : index = (%c1) to (%c42) inclusive step (%c1) { + "test.test1"(%alloc) : (!fir.ref) -> () + omp.yield + } + } + "test.test2"(%alloc) : (!fir.ref) -> () + omp.terminator + } + return +} + +// CHECK-LABEL: func.func private @_workshare_copy_i32( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref, +// CHECK-SAME: %[[VAL_1:.*]]: !fir.ref) { +// CHECK: %[[VAL_2:.*]] = fir.load %[[VAL_1]] : !fir.ref +// CHECK: fir.store %[[VAL_2]] to %[[VAL_0]] : !fir.ref +// CHECK: return +// CHECK: } + +// CHECK-LABEL: func.func @wsfunc() { +// CHECK: %[[VAL_0:.*]] = fir.alloca i32 +// CHECK: omp.single copyprivate(%[[VAL_0]] -> @_workshare_copy_i32 : !fir.ref) { +// CHECK: %[[VAL_1:.*]] = arith.constant 1 : i32 +// CHECK: fir.store %[[VAL_1]] to %[[VAL_0]] : !fir.ref +// CHECK: omp.terminator +// CHECK: } +// CHECK: %[[VAL_2:.*]] = arith.constant 1 : index +// CHECK: %[[VAL_3:.*]] = arith.constant 42 : index +// CHECK: omp.wsloop { +// CHECK: omp.loop_nest (%[[VAL_4:.*]]) : index = (%[[VAL_2]]) to (%[[VAL_3]]) inclusive step (%[[VAL_2]]) { +// CHECK: "test.test1"(%[[VAL_0]]) : (!fir.ref) -> () +// CHECK: omp.yield +// CHECK: } +// CHECK: } +// CHECK: omp.single nowait { +// CHECK: "test.test2"(%[[VAL_0]]) : (!fir.ref) -> () +// CHECK: omp.terminator +// CHECK: } +// CHECK: omp.barrier +// CHECK: return +// CHECK: } + diff --git a/flang/test/Transforms/OpenMP/lower-workshare-binding.mlir b/flang/test/Transforms/OpenMP/lower-workshare-binding.mlir new file mode 100644 index 0000000000000..f1d0e8e229614 --- /dev/null +++ b/flang/test/Transforms/OpenMP/lower-workshare-binding.mlir @@ -0,0 +1,49 @@ +// RUN: fir-opt --split-input-file --lower-workshare --allow-unregistered-dialect %s | FileCheck %s + +// Checks that the omp.workshare.loop_wrapper binds to the correct omp.workshare + +func.func @wsfunc() { + %c1 = arith.constant 1 : index + %c42 = arith.constant 42 : index + omp.parallel { + omp.workshare nowait { + omp.parallel { + omp.workshare nowait { + omp.workshare.loop_wrapper { + omp.loop_nest (%arg1) : index = (%c1) to (%c42) inclusive step (%c1) { + "test.test2"() : () -> () + omp.yield + } + } + omp.terminator + } + omp.terminator + } + omp.terminator + } + omp.terminator + } + return +} + +// CHECK-LABEL: func.func @wsfunc() { +// CHECK: %[[VAL_0:.*]] = arith.constant 1 : index +// CHECK: %[[VAL_1:.*]] = arith.constant 42 : index +// CHECK: omp.parallel { +// CHECK: omp.single nowait { +// CHECK: omp.parallel { +// CHECK: omp.wsloop nowait { +// CHECK: omp.loop_nest (%[[VAL_2:.*]]) : index = (%[[VAL_0]]) to (%[[VAL_1]]) inclusive step (%[[VAL_0]]) { +// CHECK: "test.test2"() : () -> () +// CHECK: omp.yield +// CHECK: } +// CHECK: } +// CHECK: omp.terminator +// CHECK: } +// CHECK: omp.terminator +// CHECK: } +// CHECK: omp.terminator +// CHECK: } +// CHECK: return +// CHECK: } + diff --git a/flang/test/Transforms/OpenMP/lower-workshare-cleanup.mlir b/flang/test/Transforms/OpenMP/lower-workshare-cleanup.mlir new file mode 100644 index 0000000000000..ca288917a3ac4 --- /dev/null +++ b/flang/test/Transforms/OpenMP/lower-workshare-cleanup.mlir @@ -0,0 +1,57 @@ +// RUN: fir-opt --split-input-file --lower-workshare --allow-unregistered-dialect %s | FileCheck %s + +// Check that we cleanup unused pure operations from the parallel and single +// regions + +// CHECK-LABEL: func.func @wsfunc() { +// CHECK: %[[VAL_0:.*]] = fir.alloca i32 +// CHECK: omp.parallel { +// CHECK: omp.single { +// CHECK: %[[VAL_1:.*]] = "test.test1"() : () -> i32 +// CHECK: %[[VAL_2:.*]] = arith.constant 2 : index +// CHECK: %[[VAL_3:.*]] = arith.constant 3 : index +// CHECK: %[[VAL_4:.*]] = arith.addi %[[VAL_2]], %[[VAL_3]] : index +// CHECK: "test.test3"(%[[VAL_4]]) : (index) -> () +// CHECK: omp.terminator +// CHECK: } +// CHECK: %[[VAL_5:.*]] = arith.constant 1 : index +// CHECK: %[[VAL_6:.*]] = arith.constant 42 : index +// CHECK: omp.wsloop nowait { +// CHECK: omp.loop_nest (%[[VAL_7:.*]]) : index = (%[[VAL_5]]) to (%[[VAL_6]]) inclusive step (%[[VAL_5]]) { +// CHECK: "test.test2"() : () -> () +// CHECK: omp.yield +// CHECK: } +// CHECK: } +// CHECK: omp.barrier +// CHECK: omp.terminator +// CHECK: } +// CHECK: return +// CHECK: } +func.func @wsfunc() { + %a = fir.alloca i32 + omp.parallel { + omp.workshare { + %t1 = "test.test1"() : () -> i32 + + %c1 = arith.constant 1 : index + %c42 = arith.constant 42 : index + + %c2 = arith.constant 2 : index + %c3 = arith.constant 3 : index + %add = arith.addi %c2, %c3 : index + "test.test3"(%add) : (index) -> () + + omp.workshare.loop_wrapper { + omp.loop_nest (%arg1) : index = (%c1) to (%c42) inclusive step (%c1) { + "test.test2"() : () -> () + omp.yield + } + } + omp.terminator + } + omp.terminator + } + return +} + + diff --git a/flang/test/Transforms/OpenMP/lower-workshare-copyprivate.mlir b/flang/test/Transforms/OpenMP/lower-workshare-copyprivate.mlir new file mode 100644 index 0000000000000..d7a04e198ceed --- /dev/null +++ b/flang/test/Transforms/OpenMP/lower-workshare-copyprivate.mlir @@ -0,0 +1,73 @@ +// RUN: fir-opt --split-input-file --lower-workshare --allow-unregistered-dialect %s | FileCheck %s + + +// Check if we store the correct values + +func.func @wsfunc() { + omp.parallel { + // CHECK: fir.alloca + // CHECK: fir.alloca + // CHECK: fir.alloca + // CHECK: fir.alloca + // CHECK: fir.alloca + // CHECK-NOT: fir.alloca + omp.workshare { + + %t1 = "test.test1"() : () -> i32 + // CHECK: %[[T1:.*]] = "test.test1" + // CHECK: fir.store %[[T1]] + %t2 = "test.test2"() : () -> i32 + // CHECK: %[[T2:.*]] = "test.test2" + // CHECK: fir.store %[[T2]] + %t3 = "test.test3"() : () -> i32 + // CHECK: %[[T3:.*]] = "test.test3" + // CHECK-NOT: fir.store %[[T3]] + %t4 = "test.test4"() : () -> i32 + // CHECK: %[[T4:.*]] = "test.test4" + // CHECK: fir.store %[[T4]] + %t5 = "test.test5"() : () -> i32 + // CHECK: %[[T5:.*]] = "test.test5" + // CHECK: fir.store %[[T5]] + %t6 = "test.test6"() : () -> i32 + // CHECK: %[[T6:.*]] = "test.test6" + // CHECK-NOT: fir.store %[[T6]] + + + "test.test1"(%t1) : (i32) -> () + "test.test1"(%t2) : (i32) -> () + "test.test1"(%t3) : (i32) -> () + + %true = arith.constant true + fir.if %true { + "test.test2"(%t3) : (i32) -> () + } + + %c1_i32 = arith.constant 1 : i32 + + %t5_pure_use = arith.addi %t5, %c1_i32 : i32 + + %t6_mem_effect_use = "test.test8"(%t6) : (i32) -> i32 + // CHECK: %[[T6_USE:.*]] = "test.test8" + // CHECK: fir.store %[[T6_USE]] + + %c42 = arith.constant 42 : index + %c1 = arith.constant 1 : index + omp.workshare.loop_wrapper { + omp.loop_nest (%arg1) : index = (%c1) to (%c42) inclusive step (%c1) { + "test.test10"(%t1) : (i32) -> () + "test.test10"(%t5_pure_use) : (i32) -> () + "test.test10"(%t6_mem_effect_use) : (i32) -> () + omp.yield + } + } + + "test.test10"(%t2) : (i32) -> () + fir.if %true { + "test.test10"(%t4) : (i32) -> () + } + omp.terminator + } + omp.terminator + } + return +} diff --git a/flang/test/Transforms/OpenMP/lower-workshare-correct-parallelize.mlir b/flang/test/Transforms/OpenMP/lower-workshare-correct-parallelize.mlir new file mode 100644 index 0000000000000..31db8213b5f00 --- /dev/null +++ b/flang/test/Transforms/OpenMP/lower-workshare-correct-parallelize.mlir @@ -0,0 +1,25 @@ +// RUN: fir-opt --lower-workshare --allow-unregistered-dialect %s | FileCheck %s + +// Check that the safe to parallelize `fir.declare` op will not be parallelized +// due to its operand %alloc not being reloaded outside the omp.single. + +func.func @foo() { + %c0 = arith.constant 0 : index + omp.workshare { + %alloc = fir.allocmem !fir.array, %c0 {bindc_name = ".tmp.forall", uniq_name = ""} + %shape = fir.shape %c0 : (index) -> !fir.shape<1> + %declare = fir.declare %alloc(%shape) {uniq_name = ".tmp.forall"} : (!fir.heap>, !fir.shape<1>) -> !fir.heap> + fir.freemem %alloc : !fir.heap> + omp.terminator + } + return +} + +// CHECK: omp.single nowait +// CHECK: fir.allocmem +// CHECK: fir.shape +// CHECK: fir.declare +// CHECK: fir.freemem +// CHECK: omp.terminator +// CHECK: } +// CHECK: omp.barrier diff --git a/flang/test/Transforms/OpenMP/lower-workshare-no-single.mlir b/flang/test/Transforms/OpenMP/lower-workshare-no-single.mlir new file mode 100644 index 0000000000000..1fd379a6e5eb4 --- /dev/null +++ b/flang/test/Transforms/OpenMP/lower-workshare-no-single.mlir @@ -0,0 +1,19 @@ +// RUN: fir-opt --split-input-file --lower-workshare --allow-unregistered-dialect %s | FileCheck %s + +// Check that we do not emit an omp.single for the constant operation + +func.func @foo() { + omp.workshare { + %c1 = arith.constant 1 : index + omp.workshare.loop_wrapper { + omp.loop_nest (%arg1) : index = (%c1) to (%c1) inclusive step (%c1) { + "test.test0"() : () -> () + omp.yield + } + } + omp.terminator + } + return +} + +// CHECK-NOT: omp.single diff --git a/flang/test/Transforms/OpenMP/lower-workshare-nowait.mlir b/flang/test/Transforms/OpenMP/lower-workshare-nowait.mlir new file mode 100644 index 0000000000000..940662e0bdccc --- /dev/null +++ b/flang/test/Transforms/OpenMP/lower-workshare-nowait.mlir @@ -0,0 +1,23 @@ +// RUN: fir-opt --split-input-file --lower-workshare --allow-unregistered-dialect %s | FileCheck %s + +// Check that we correctly handle nowait + +// CHECK-LABEL: func.func @nonowait +func.func @nonowait(%arg0: !fir.ref>) { + // CHECK: omp.barrier + omp.workshare { + omp.terminator + } + return +} + +// ----- + +// CHECK-LABEL: func.func @nowait +func.func @nowait(%arg0: !fir.ref>) { + // CHECK-NOT: omp.barrier + omp.workshare nowait { + omp.terminator + } + return +} diff --git a/flang/test/Transforms/OpenMP/lower-workshare-todo-cfg-dom.mlir b/flang/test/Transforms/OpenMP/lower-workshare-todo-cfg-dom.mlir new file mode 100644 index 0000000000000..83c49cd635d08 --- /dev/null +++ b/flang/test/Transforms/OpenMP/lower-workshare-todo-cfg-dom.mlir @@ -0,0 +1,26 @@ +// RUN: fir-opt --lower-workshare --allow-unregistered-dialect %s 2>&1 | FileCheck %s + +// CHECK: warning: omp workshare with unstructured control flow is currently unsupported and will be serialized. + +// CHECK: omp.parallel +// CHECK-NEXT: omp.single + +// TODO Check that the definition of %r dominates its use post-transform +func.func @wsfunc() { + %a = fir.alloca i32 + omp.parallel { + omp.workshare { + ^bb1: + %c1 = arith.constant 1 : i32 + cf.br ^bb3(%c1: i32) + ^bb2: + "test.test2"(%r) : (i32) -> () + omp.terminator + ^bb3(%arg1: i32): + %r = "test.test2"(%arg1) : (i32) -> i32 + cf.br ^bb2 + } + omp.terminator + } + return +} diff --git a/flang/test/Transforms/OpenMP/lower-workshare-todo-cfg.mlir b/flang/test/Transforms/OpenMP/lower-workshare-todo-cfg.mlir new file mode 100644 index 0000000000000..a27cf88069401 --- /dev/null +++ b/flang/test/Transforms/OpenMP/lower-workshare-todo-cfg.mlir @@ -0,0 +1,23 @@ +// RUN: fir-opt --lower-workshare --allow-unregistered-dialect %s 2>&1 | FileCheck %s + +// CHECK: warning: omp workshare with unstructured control flow is currently unsupported and will be serialized. + +// CHECK: omp.parallel +// CHECK-NEXT: omp.single + +// TODO Check transforming a simple CFG +func.func @wsfunc() { + %a = fir.alloca i32 + omp.parallel { + omp.workshare { + ^bb1: + %c1 = arith.constant 1 : i32 + cf.br ^bb3(%c1: i32) + ^bb3(%arg1: i32): + "test.test2"(%arg1) : (i32) -> () + omp.terminator + } + omp.terminator + } + return +} diff --git a/flang/tools/bbc/bbc.cpp b/flang/tools/bbc/bbc.cpp index fe5e36f704c76..1c24979bbcdaf 100644 --- a/flang/tools/bbc/bbc.cpp +++ b/flang/tools/bbc/bbc.cpp @@ -452,7 +452,8 @@ static llvm::LogicalResult convertFortranSourceToMLIR( if (emitFIR && useHLFIR) { // lower HLFIR to FIR - fir::createHLFIRToFIRPassPipeline(pm, llvm::OptimizationLevel::O2); + fir::createHLFIRToFIRPassPipeline(pm, enableOpenMP, + llvm::OptimizationLevel::O2); if (mlir::failed(pm.run(mlirModule))) { llvm::errs() << "FATAL: lowering from HLFIR to FIR failed"; return mlir::failure(); @@ -467,6 +468,8 @@ static llvm::LogicalResult convertFortranSourceToMLIR( // Add O2 optimizer pass pipeline. MLIRToLLVMPassPipelineConfig config(llvm::OptimizationLevel::O2); + if (enableOpenMP) + config.EnableOpenMP = true; config.NSWOnLoopVarInc = setNSW; fir::registerDefaultInlinerPass(config); fir::createDefaultFIROptimizerPassPipeline(pm, config); diff --git a/flang/tools/tco/tco.cpp b/flang/tools/tco/tco.cpp index 5c373c4e85258..eaf4bae088454 100644 --- a/flang/tools/tco/tco.cpp +++ b/flang/tools/tco/tco.cpp @@ -139,6 +139,7 @@ compileFIR(const mlir::PassPipelineCLParser &passPipeline) { return mlir::failure(); } else { MLIRToLLVMPassPipelineConfig config(llvm::OptimizationLevel::O2); + config.EnableOpenMP = true; // assume the input contains OpenMP config.AliasAnalysis = true; // enabled when optimizing for speed if (codeGenLLVM) { // Run only CodeGen passes. diff --git a/libc/docs/gpu/rpc.rst b/libc/docs/gpu/rpc.rst index ee5865d7f6407..e1244154341e9 100644 --- a/libc/docs/gpu/rpc.rst +++ b/libc/docs/gpu/rpc.rst @@ -302,6 +302,6 @@ associated with relocatable device code linking. Extensions ---------- -We describe which operation the RPC server should take with a 16-bit opcode. We -consider the first 32768 numbers to be reserved while the others are free to -use. +The opcode is a 32-bit integer that must be unique to the requested operation. +All opcodes used by ``libc`` internally have the character ``c`` in the most +significant byte. diff --git a/libc/fuzzing/__support/hashtable_fuzz.cpp b/libc/fuzzing/__support/hashtable_fuzz.cpp index 7d61e106c9c4a..8ab5e3b55cfd4 100644 --- a/libc/fuzzing/__support/hashtable_fuzz.cpp +++ b/libc/fuzzing/__support/hashtable_fuzz.cpp @@ -10,6 +10,7 @@ /// //===----------------------------------------------------------------------===// #include "include/llvm-libc-types/ENTRY.h" +#include "src/__support/CPP/bit.h" #include "src/__support/CPP/string_view.h" #include "src/__support/HashTable/table.h" #include "src/__support/macros/config.h" @@ -81,15 +82,14 @@ static struct { template T next() { static_assert(cpp::is_integral::value, "T must be an integral type"); - union { - T result; - char data[sizeof(T)]; - }; - for (size_t i = 0; i < sizeof(result); i++) + + char data[sizeof(T)]; + + for (size_t i = 0; i < sizeof(T); i++) data[i] = buffer[i]; - buffer += sizeof(result); - remaining -= sizeof(result); - return result; + buffer += sizeof(T); + remaining -= sizeof(T); + return cpp::bit_cast(data); } cpp::string_view next_string() { diff --git a/libc/include/llvm-libc-types/rpc_opcodes_t.h b/libc/include/llvm-libc-types/rpc_opcodes_t.h index 1a6c0cd9bc4a1..f3b35518935a5 100644 --- a/libc/include/llvm-libc-types/rpc_opcodes_t.h +++ b/libc/include/llvm-libc-types/rpc_opcodes_t.h @@ -9,38 +9,41 @@ #ifndef LLVM_LIBC_TYPES_RPC_OPCODES_T_H #define LLVM_LIBC_TYPES_RPC_OPCODES_T_H +#define LLVM_LIBC_RPC_BASE 'c' +#define LLVM_LIBC_OPCODE(n) (LLVM_LIBC_RPC_BASE << 24 | n) + typedef enum { - RPC_NOOP = 0, - RPC_EXIT, - RPC_WRITE_TO_STDOUT, - RPC_WRITE_TO_STDERR, - RPC_WRITE_TO_STREAM, - RPC_WRITE_TO_STDOUT_NEWLINE, - RPC_READ_FROM_STREAM, - RPC_READ_FGETS, - RPC_OPEN_FILE, - RPC_CLOSE_FILE, - RPC_MALLOC, - RPC_FREE, - RPC_HOST_CALL, - RPC_ABORT, - RPC_FEOF, - RPC_FERROR, - RPC_CLEARERR, - RPC_FSEEK, - RPC_FTELL, - RPC_FFLUSH, - RPC_UNGETC, - RPC_PRINTF_TO_STDOUT, - RPC_PRINTF_TO_STDERR, - RPC_PRINTF_TO_STREAM, - RPC_PRINTF_TO_STDOUT_PACKED, - RPC_PRINTF_TO_STDERR_PACKED, - RPC_PRINTF_TO_STREAM_PACKED, - RPC_REMOVE, - RPC_RENAME, - RPC_SYSTEM, - RPC_LAST = 0xFFFF, + RPC_NOOP = LLVM_LIBC_OPCODE(0), + RPC_EXIT = LLVM_LIBC_OPCODE(1), + RPC_WRITE_TO_STDOUT = LLVM_LIBC_OPCODE(2), + RPC_WRITE_TO_STDERR = LLVM_LIBC_OPCODE(3), + RPC_WRITE_TO_STREAM = LLVM_LIBC_OPCODE(4), + RPC_WRITE_TO_STDOUT_NEWLINE = LLVM_LIBC_OPCODE(5), + RPC_READ_FROM_STREAM = LLVM_LIBC_OPCODE(6), + RPC_READ_FGETS = LLVM_LIBC_OPCODE(7), + RPC_OPEN_FILE = LLVM_LIBC_OPCODE(8), + RPC_CLOSE_FILE = LLVM_LIBC_OPCODE(9), + RPC_MALLOC = LLVM_LIBC_OPCODE(10), + RPC_FREE = LLVM_LIBC_OPCODE(11), + RPC_HOST_CALL = LLVM_LIBC_OPCODE(12), + RPC_ABORT = LLVM_LIBC_OPCODE(13), + RPC_FEOF = LLVM_LIBC_OPCODE(14), + RPC_FERROR = LLVM_LIBC_OPCODE(15), + RPC_CLEARERR = LLVM_LIBC_OPCODE(16), + RPC_FSEEK = LLVM_LIBC_OPCODE(17), + RPC_FTELL = LLVM_LIBC_OPCODE(18), + RPC_FFLUSH = LLVM_LIBC_OPCODE(19), + RPC_UNGETC = LLVM_LIBC_OPCODE(20), + RPC_PRINTF_TO_STDOUT = LLVM_LIBC_OPCODE(21), + RPC_PRINTF_TO_STDERR = LLVM_LIBC_OPCODE(22), + RPC_PRINTF_TO_STREAM = LLVM_LIBC_OPCODE(23), + RPC_PRINTF_TO_STDOUT_PACKED = LLVM_LIBC_OPCODE(24), + RPC_PRINTF_TO_STDERR_PACKED = LLVM_LIBC_OPCODE(25), + RPC_PRINTF_TO_STREAM_PACKED = LLVM_LIBC_OPCODE(26), + RPC_REMOVE = LLVM_LIBC_OPCODE(27), + RPC_RENAME = LLVM_LIBC_OPCODE(28), + RPC_SYSTEM = LLVM_LIBC_OPCODE(29), + RPC_LAST = 0xFFFFFFFF, } rpc_opcode_t; #endif // LLVM_LIBC_TYPES_RPC_OPCODES_T_H diff --git a/libc/src/__support/HashTable/generic/bitmask_impl.inc b/libc/src/__support/HashTable/generic/bitmask_impl.inc index 469ddeeed8a85..d526dc1ece293 100644 --- a/libc/src/__support/HashTable/generic/bitmask_impl.inc +++ b/libc/src/__support/HashTable/generic/bitmask_impl.inc @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// +#include "src/__support/CPP/bit.h" #include "src/__support/common.h" #include "src/__support/endian_internal.h" #include "src/__support/macros/config.h" @@ -44,13 +45,11 @@ struct Group { // Load a group of control words from an arbitary address. LIBC_INLINE static Group load(const void *addr) { - union { - bitmask_t value; - char bytes[sizeof(bitmask_t)]; - } data; + char bytes[sizeof(bitmask_t)]; + for (size_t i = 0; i < sizeof(bitmask_t); ++i) - data.bytes[i] = static_cast(addr)[i]; - return {data.value}; + bytes[i] = static_cast(addr)[i]; + return Group{cpp::bit_cast(bytes)}; } // Load a group of control words from an aligned address. diff --git a/libc/src/__support/OSUtil/gpu/exit.cpp b/libc/src/__support/OSUtil/gpu/exit.cpp index 8aaa41b4e3eef..0cb266a42d180 100644 --- a/libc/src/__support/OSUtil/gpu/exit.cpp +++ b/libc/src/__support/OSUtil/gpu/exit.cpp @@ -8,6 +8,7 @@ #include "src/__support/OSUtil/exit.h" +#include "src/__support/GPU/utils.h" #include "src/__support/RPC/rpc_client.h" #include "src/__support/macros/config.h" #include "src/__support/macros/properties/architectures.h" diff --git a/libc/src/__support/RPC/rpc.h b/libc/src/__support/RPC/rpc.h index a257003a907de..30dd2c1a8125d 100644 --- a/libc/src/__support/RPC/rpc.h +++ b/libc/src/__support/RPC/rpc.h @@ -19,8 +19,7 @@ #define LLVM_LIBC_SRC___SUPPORT_RPC_RPC_H #include "rpc_util.h" -#include "src/__support/CPP/optional.h" -#include "src/__support/GPU/utils.h" +#include "src/__support/macros/attributes.h" #include "src/__support/macros/config.h" #include @@ -38,6 +37,9 @@ namespace rpc { #define __scoped_atomic_fetch_and(src, val, ord, scp) \ __atomic_fetch_and(src, val, ord) #endif +#if !__has_builtin(__scoped_atomic_thread_fence) +#define __scoped_atomic_thread_fence(ord, scp) __atomic_thread_fence(ord) +#endif /// A fixed size channel used to communicate between the RPC client and server. struct Buffer { @@ -49,11 +51,11 @@ static_assert(sizeof(Buffer) == 64, "Buffer size mismatch"); /// perform and which threads are active in the slots. struct Header { uint64_t mask; - uint16_t opcode; + uint32_t opcode; }; /// The maximum number of parallel ports that the RPC interface can support. -constexpr uint64_t MAX_PORT_COUNT = 4096; +constexpr static uint64_t MAX_PORT_COUNT = 4096; /// A common process used to synchronize communication between a client and a /// server. The process contains a read-only inbox and a write-only outbox used @@ -110,14 +112,14 @@ template struct Process { /// Retrieve the inbox state from memory shared between processes. LIBC_INLINE uint32_t load_inbox(uint64_t lane_mask, uint32_t index) const { - return gpu::broadcast_value( + return rpc::broadcast_value( lane_mask, __scoped_atomic_load_n(&inbox[index], __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM)); } /// Retrieve the outbox state from memory shared between processes. LIBC_INLINE uint32_t load_outbox(uint64_t lane_mask, uint32_t index) const { - return gpu::broadcast_value( + return rpc::broadcast_value( lane_mask, __scoped_atomic_load_n(&outbox[index], __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM)); } @@ -128,7 +130,7 @@ template struct Process { /// cheaper than calling load_outbox to get the value to store. LIBC_INLINE uint32_t invert_outbox(uint32_t index, uint32_t current_outbox) { uint32_t inverted_outbox = !current_outbox; - __atomic_thread_fence(__ATOMIC_RELEASE); + __scoped_atomic_thread_fence(__ATOMIC_RELEASE, __MEMORY_SCOPE_SYSTEM); __scoped_atomic_store_n(&outbox[index], inverted_outbox, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM); return inverted_outbox; @@ -142,7 +144,7 @@ template struct Process { sleep_briefly(); in = load_inbox(lane_mask, index); } - __atomic_thread_fence(__ATOMIC_ACQUIRE); + __scoped_atomic_thread_fence(__ATOMIC_ACQUIRE, __MEMORY_SCOPE_SYSTEM); } /// The packet is a linearly allocated array of buffers used to communicate @@ -162,7 +164,7 @@ template struct Process { /// Attempt to claim the lock at index. Return true on lock taken. /// lane_mask is a bitmap of the threads in the warp that would hold the - /// single lock on success, e.g. the result of gpu::get_lane_mask() + /// single lock on success, e.g. the result of rpc::get_lane_mask() /// The lock is held when the n-th bit of the lock bitfield is set. LIBC_INLINE bool try_lock(uint64_t lane_mask, uint32_t index) { // On amdgpu, test and set to the nth lock bit and a sync_lane would suffice @@ -173,12 +175,12 @@ template struct Process { // There may be threads active which are not in lane mask which must not // succeed in taking the lock, as otherwise it will leak. This is handled // by making threads which are not in lane_mask or with 0, a no-op. - uint32_t id = gpu::get_lane_id(); + uint32_t id = rpc::get_lane_id(); bool id_in_lane_mask = lane_mask & (1ul << id); // All threads in the warp call fetch_or. Possibly at the same time. bool before = set_nth(lock, index, id_in_lane_mask); - uint64_t packed = gpu::ballot(lane_mask, before); + uint64_t packed = rpc::ballot(lane_mask, before); // If every bit set in lane_mask is also set in packed, every single thread // in the warp failed to get the lock. Ballot returns unset for threads not @@ -198,7 +200,7 @@ template struct Process { // inlining the current function. bool holding_lock = lane_mask != packed; if (holding_lock) - __atomic_thread_fence(__ATOMIC_ACQUIRE); + __scoped_atomic_thread_fence(__ATOMIC_ACQUIRE, __MEMORY_SCOPE_DEVICE); return holding_lock; } @@ -206,14 +208,14 @@ template struct Process { /// convergent, otherwise the compiler will sink the store and deadlock. LIBC_INLINE void unlock(uint64_t lane_mask, uint32_t index) { // Do not move any writes past the unlock. - __atomic_thread_fence(__ATOMIC_RELEASE); + __scoped_atomic_thread_fence(__ATOMIC_RELEASE, __MEMORY_SCOPE_DEVICE); // Use exactly one thread to clear the nth bit in the lock array Must // restrict to a single thread to avoid one thread dropping the lock, then // an unrelated warp claiming the lock, then a second thread in this warp // dropping the lock again. - clear_nth(lock, index, gpu::is_first_lane(lane_mask)); - gpu::sync_lane(lane_mask); + clear_nth(lock, index, rpc::is_first_lane(lane_mask)); + rpc::sync_lane(lane_mask); } /// Number of bytes to allocate for an inbox or outbox. @@ -276,9 +278,9 @@ template LIBC_INLINE static void invoke_rpc(F &&fn, uint32_t lane_size, uint64_t lane_mask, Buffer *slot) { if constexpr (is_process_gpu()) { - fn(&slot[gpu::get_lane_id()], gpu::get_lane_id()); + fn(&slot[rpc::get_lane_id()], rpc::get_lane_id()); } else { - for (uint32_t i = 0; i < lane_size; i += gpu::get_lane_size()) + for (uint32_t i = 0; i < lane_size; i += rpc::get_num_lanes()) if (lane_mask & (1ul << i)) fn(&slot[i], i); } @@ -302,7 +304,7 @@ template struct Port { friend struct Client; friend struct Server; - friend class cpp::optional>; + friend class rpc::optional>; public: template LIBC_INLINE void recv(U use); @@ -315,15 +317,15 @@ template struct Port { template LIBC_INLINE void recv_n(void **dst, uint64_t *size, A &&alloc); - LIBC_INLINE uint16_t get_opcode() const { + LIBC_INLINE uint32_t get_opcode() const { return process.header[index].opcode; } - LIBC_INLINE uint16_t get_index() const { return index; } + LIBC_INLINE uint32_t get_index() const { return index; } LIBC_INLINE void close() { // Wait for all lanes to finish using the port. - gpu::sync_lane(lane_mask); + rpc::sync_lane(lane_mask); // The server is passive, if it own the buffer when it closes we need to // give ownership back to the client. @@ -353,14 +355,11 @@ struct Client { : process(port_count, buffer) {} using Port = rpc::Port; - template LIBC_INLINE Port open(); + template LIBC_INLINE Port open(); private: Process process; }; -static_assert(cpp::is_trivially_copyable::value && - sizeof(Process) == sizeof(Process), - "The client is not trivially copyable from the server"); /// The RPC server used to respond to the client. struct Server { @@ -373,7 +372,7 @@ struct Server { : process(port_count, buffer) {} using Port = rpc::Port; - LIBC_INLINE cpp::optional try_open(uint32_t lane_size, + LIBC_INLINE rpc::optional try_open(uint32_t lane_size, uint32_t start = 0); LIBC_INLINE Port open(uint32_t lane_size); @@ -466,7 +465,7 @@ LIBC_INLINE void Port::send_n(const void *const *src, uint64_t *size) { }); uint64_t idx = sizeof(Buffer::data) - sizeof(uint64_t); uint64_t mask = process.header[index].mask; - while (gpu::ballot(mask, idx < num_sends)) { + while (rpc::ballot(mask, idx < num_sends)) { send([=](Buffer *buffer, uint32_t id) { uint64_t len = lane_value(size, id) - idx > sizeof(Buffer::data) ? sizeof(Buffer::data) @@ -499,7 +498,7 @@ LIBC_INLINE void Port::recv_n(void **dst, uint64_t *size, A &&alloc) { }); uint64_t idx = sizeof(Buffer::data) - sizeof(uint64_t); uint64_t mask = process.header[index].mask; - while (gpu::ballot(mask, idx < num_recvs)) { + while (rpc::ballot(mask, idx < num_recvs)) { recv([=](Buffer *buffer, uint32_t id) { uint64_t len = lane_value(size, id) - idx > sizeof(Buffer::data) ? sizeof(Buffer::data) @@ -517,16 +516,16 @@ LIBC_INLINE void Port::recv_n(void **dst, uint64_t *size, A &&alloc) { /// port. Each port instance uses an associated \p opcode to tell the server /// what to do. The Client interface provides the appropriate lane size to the /// port using the platform's returned value. -template LIBC_INLINE Client::Port Client::open() { +template LIBC_INLINE Client::Port Client::open() { // Repeatedly perform a naive linear scan for a port that can be opened to // send data. - for (uint32_t index = gpu::get_cluster_id();; ++index) { + for (uint32_t index = 0;; ++index) { // Start from the beginning if we run out of ports to check. if (index >= process.port_count) index = 0; // Attempt to acquire the lock on this index. - uint64_t lane_mask = gpu::get_lane_mask(); + uint64_t lane_mask = rpc::get_lane_mask(); if (!process.try_lock(lane_mask, index)) continue; @@ -540,22 +539,22 @@ template LIBC_INLINE Client::Port Client::open() { continue; } - if (gpu::is_first_lane(lane_mask)) { + if (rpc::is_first_lane(lane_mask)) { process.header[index].opcode = opcode; process.header[index].mask = lane_mask; } - gpu::sync_lane(lane_mask); - return Port(process, lane_mask, gpu::get_lane_size(), index, out); + rpc::sync_lane(lane_mask); + return Port(process, lane_mask, rpc::get_num_lanes(), index, out); } } /// Attempts to open a port to use as the server. The server can only open a /// port if it has a pending receive operation -LIBC_INLINE cpp::optional +LIBC_INLINE rpc::optional Server::try_open(uint32_t lane_size, uint32_t start) { // Perform a naive linear scan for a port that has a pending request. for (uint32_t index = start; index < process.port_count; ++index) { - uint64_t lane_mask = gpu::get_lane_mask(); + uint64_t lane_mask = rpc::get_lane_mask(); uint32_t in = process.load_inbox(lane_mask, index); uint32_t out = process.load_outbox(lane_mask, index); @@ -578,13 +577,13 @@ Server::try_open(uint32_t lane_size, uint32_t start) { return Port(process, lane_mask, lane_size, index, out); } - return cpp::nullopt; + return rpc::nullopt; } LIBC_INLINE Server::Port Server::open(uint32_t lane_size) { for (;;) { - if (cpp::optional p = try_open(lane_size)) - return cpp::move(p.value()); + if (rpc::optional p = try_open(lane_size)) + return rpc::move(p.value()); sleep_briefly(); } } @@ -595,6 +594,9 @@ LIBC_INLINE Server::Port Server::open(uint32_t lane_size) { #undef __scoped_atomic_fetch_or #undef __scoped_atomic_fetch_and #endif +#if !__has_builtin(__scoped_atomic_thread_fence) +#undef __scoped_atomic_thread_fence +#endif } // namespace rpc } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/__support/RPC/rpc_client.h b/libc/src/__support/RPC/rpc_client.h index 695b6b7515bf7..7bd6d0b5e00b4 100644 --- a/libc/src/__support/RPC/rpc_client.h +++ b/libc/src/__support/RPC/rpc_client.h @@ -12,11 +12,16 @@ #include "rpc.h" #include "include/llvm-libc-types/rpc_opcodes_t.h" +#include "src/__support/CPP/type_traits.h" #include "src/__support/macros/config.h" namespace LIBC_NAMESPACE_DECL { namespace rpc { +static_assert(cpp::is_trivially_copyable::value && + sizeof(Process) == sizeof(Process), + "The client is not trivially copyable from the server"); + /// The libc client instance used to communicate with the server. extern Client client; diff --git a/libc/src/__support/RPC/rpc_util.h b/libc/src/__support/RPC/rpc_util.h index 93b8289617484..7067dfc974eb3 100644 --- a/libc/src/__support/RPC/rpc_util.h +++ b/libc/src/__support/RPC/rpc_util.h @@ -9,23 +9,230 @@ #ifndef LLVM_LIBC_SRC___SUPPORT_RPC_RPC_UTIL_H #define LLVM_LIBC_SRC___SUPPORT_RPC_RPC_UTIL_H -#include "src/__support/CPP/type_traits.h" #include "src/__support/macros/attributes.h" #include "src/__support/macros/config.h" -#include "src/__support/threads/sleep.h" + +#include +#include + +#if defined(__NVPTX__) || defined(__AMDGPU__) +#include +#define RPC_TARGET_IS_GPU +#endif namespace LIBC_NAMESPACE_DECL { namespace rpc { +template struct type_identity { + using type = T; +}; + +template struct type_constant { + static inline constexpr T value = v; +}; + +template struct remove_reference : type_identity {}; +template struct remove_reference : type_identity {}; +template struct remove_reference : type_identity {}; + +template struct is_const : type_constant {}; +template struct is_const : type_constant {}; + +/// Freestanding implementation of std::move. +template +LIBC_INLINE constexpr typename remove_reference::type &&move(T &&t) { + return static_cast::type &&>(t); +} + +/// Freestanding implementation of std::forward. +template +LIBC_INLINE constexpr T &&forward(typename remove_reference::type &value) { + return static_cast(value); +} +template +LIBC_INLINE constexpr T &&forward(typename remove_reference::type &&value) { + return static_cast(value); +} + +struct in_place_t { + LIBC_INLINE explicit in_place_t() = default; +}; + +struct nullopt_t { + LIBC_INLINE constexpr explicit nullopt_t() = default; +}; + +constexpr inline in_place_t in_place{}; +constexpr inline nullopt_t nullopt{}; + +/// Freestanding and minimal implementation of std::optional. +template class optional { + template struct OptionalStorage { + union { + char empty; + U stored_value; + }; + + bool in_use = false; + + LIBC_INLINE ~OptionalStorage() { reset(); } + + LIBC_INLINE constexpr OptionalStorage() : empty() {} + + template + LIBC_INLINE constexpr explicit OptionalStorage(in_place_t, Args &&...args) + : stored_value(forward(args)...) {} + + LIBC_INLINE constexpr void reset() { + if (in_use) + stored_value.~U(); + in_use = false; + } + }; + + OptionalStorage storage; + +public: + LIBC_INLINE constexpr optional() = default; + LIBC_INLINE constexpr optional(nullopt_t) {} + + LIBC_INLINE constexpr optional(const T &t) : storage(in_place, t) { + storage.in_use = true; + } + LIBC_INLINE constexpr optional(const optional &) = default; + + LIBC_INLINE constexpr optional(T &&t) : storage(in_place, move(t)) { + storage.in_use = true; + } + LIBC_INLINE constexpr optional(optional &&O) = default; + + LIBC_INLINE constexpr optional &operator=(T &&t) { + storage = move(t); + return *this; + } + LIBC_INLINE constexpr optional &operator=(optional &&) = default; + + LIBC_INLINE constexpr optional &operator=(const T &t) { + storage = t; + return *this; + } + LIBC_INLINE constexpr optional &operator=(const optional &) = default; + + LIBC_INLINE constexpr void reset() { storage.reset(); } + + LIBC_INLINE constexpr const T &value() const & { + return storage.stored_value; + } + + LIBC_INLINE constexpr T &value() & { return storage.stored_value; } + + LIBC_INLINE constexpr explicit operator bool() const { + return storage.in_use; + } + LIBC_INLINE constexpr bool has_value() const { return storage.in_use; } + LIBC_INLINE constexpr const T *operator->() const { + return &storage.stored_value; + } + LIBC_INLINE constexpr T *operator->() { return &storage.stored_value; } + LIBC_INLINE constexpr const T &operator*() const & { + return storage.stored_value; + } + LIBC_INLINE constexpr T &operator*() & { return storage.stored_value; } + + LIBC_INLINE constexpr T &&value() && { return move(storage.stored_value); } + LIBC_INLINE constexpr T &&operator*() && { + return move(storage.stored_value); + } +}; + +/// Suspend the thread briefly to assist the thread scheduler during busy loops. +LIBC_INLINE void sleep_briefly() { +#if defined(LIBC_TARGET_ARCH_IS_NVPTX) + if (__nvvm_reflect("__CUDA_ARCH") >= 700) + asm("nanosleep.u32 64;" ::: "memory"); +#elif defined(LIBC_TARGET_ARCH_IS_AMDGPU) + __builtin_amdgcn_s_sleep(2); +#elif defined(LIBC_TARGET_ARCH_IS_X86) + __builtin_ia32_pause(); +#elif defined(LIBC_TARGET_ARCH_IS_AARCH64) && __has_builtin(__builtin_arm_isb) + __builtin_arm_isb(0xf); +#elif defined(LIBC_TARGET_ARCH_IS_AARCH64) + asm volatile("isb\n" ::: "memory"); +#else + // Simply do nothing if sleeping isn't supported on this platform. +#endif +} + /// Conditional to indicate if this process is running on the GPU. LIBC_INLINE constexpr bool is_process_gpu() { -#if defined(__NVPTX__) || defined(__AMDGPU__) +#ifdef RPC_TARGET_IS_GPU return true; #else return false; #endif } +/// Wait for all lanes in the group to complete. +LIBC_INLINE void sync_lane(uint64_t lane_mask) { +#ifdef RPC_TARGET_IS_GPU + return __gpu_sync_lane(lane_mask); +#endif +} + +/// Copies the value from the first active thread to the rest. +LIBC_INLINE uint32_t broadcast_value(uint64_t lane_mask, uint32_t x) { +#ifdef RPC_TARGET_IS_GPU + return __gpu_read_first_lane_u32(lane_mask, x); +#else + return x; +#endif +} + +/// Returns the number lanes that participate in the RPC interface. +LIBC_INLINE uint32_t get_num_lanes() { +#ifdef RPC_TARGET_IS_GPU + return __gpu_num_lanes(); +#else + return 1; +#endif +} + +/// Returns the id of the thread inside of an AMD wavefront executing together. +LIBC_INLINE uint64_t get_lane_mask() { +#ifdef RPC_TARGET_IS_GPU + return __gpu_lane_mask(); +#else + return 1; +#endif +} + +/// Returns the id of the thread inside of an AMD wavefront executing together. +LIBC_INLINE uint32_t get_lane_id() { +#ifdef RPC_TARGET_IS_GPU + return __gpu_lane_id(); +#else + return 0; +#endif +} + +/// Conditional that is only true for a single thread in a lane. +LIBC_INLINE bool is_first_lane(uint64_t lane_mask) { +#ifdef RPC_TARGET_IS_GPU + return __gpu_is_first_in_lane(lane_mask); +#else + return true; +#endif +} + +/// Returns a bitmask of threads in the current lane for which \p x is true. +LIBC_INLINE uint64_t ballot(uint64_t lane_mask, bool x) { +#ifdef RPC_TARGET_IS_GPU + return __gpu_ballot(lane_mask, x); +#else + return x; +#endif +} + /// Return \p val aligned "upwards" according to \p align. template LIBC_INLINE constexpr V align_up(V val, A align) { @@ -44,7 +251,7 @@ template LIBC_INLINE V &lane_value(V *val, uint32_t id) { /// Advance the \p p by \p bytes. template LIBC_INLINE T *advance(T *ptr, U bytes) { - if constexpr (cpp::is_const_v) + if constexpr (is_const::value) return reinterpret_cast(reinterpret_cast(ptr) + bytes); else diff --git a/libc/src/__support/common.h b/libc/src/__support/common.h index 48c773fa02c17..79803a346f692 100644 --- a/libc/src/__support/common.h +++ b/libc/src/__support/common.h @@ -21,9 +21,25 @@ #define LLVM_LIBC_FUNCTION_ATTR #endif +// Allow each function `func` to have extra attributes specified by defining: +// `LLVM_LIBC_FUNCTION_ATTR_func` macro, which should always start with +// "LLVM_LIBC_EMPTY, " +// +// For examples: +// #define LLVM_LIBC_FUNCTION_ATTR_memcpy LLVM_LIBC_EMPTY, [[gnu::weak]] +// #define LLVM_LIBC_FUNCTION_ATTR_memchr LLVM_LIBC_EMPTY, [[gnu::weak]] \ +// [[gnu::visibility("default")]] +#define LLVM_LIBC_EMPTY + +#define GET_SECOND(first, second, ...) second +#define EXPAND_THEN_SECOND(name) GET_SECOND(name, LLVM_LIBC_EMPTY) + +#define LLVM_LIBC_ATTR(name) EXPAND_THEN_SECOND(LLVM_LIBC_FUNCTION_ATTR_##name) + // MacOS needs to be excluded because it does not support aliasing. #if defined(LIBC_COPT_PUBLIC_PACKAGING) && (!defined(__APPLE__)) #define LLVM_LIBC_FUNCTION_IMPL(type, name, arglist) \ + LLVM_LIBC_ATTR(name) \ LLVM_LIBC_FUNCTION_ATTR decltype(LIBC_NAMESPACE::name) \ __##name##_impl__ __asm__(#name); \ decltype(LIBC_NAMESPACE::name) name [[gnu::alias(#name)]]; \ diff --git a/libc/src/__support/hash.h b/libc/src/__support/hash.h index 527c83993fd59..49138b1f43b9e 100644 --- a/libc/src/__support/hash.h +++ b/libc/src/__support/hash.h @@ -13,8 +13,8 @@ #include "src/__support/CPP/limits.h" // numeric_limits #include "src/__support/macros/attributes.h" // LIBC_INLINE #include "src/__support/macros/config.h" -#include "src/__support/uint128.h" // UInt128 -#include // For uint64_t +#include "src/__support/uint128.h" // UInt128 +#include // For uint64_t namespace LIBC_NAMESPACE_DECL { namespace internal { @@ -34,25 +34,23 @@ LIBC_INLINE uint64_t folded_multiply(uint64_t x, uint64_t y) { // Therefore, we use a union to read the value. template LIBC_INLINE T read_little_endian(const void *ptr) { const uint8_t *bytes = static_cast(ptr); - union { - T value; - uint8_t buffer[sizeof(T)]; - } data; + uint8_t buffer[sizeof(T)]; #if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ - // Compiler should able to optimize this as a load followed by a byte swap. - // On aarch64 (-mbig-endian), this compiles to the following for int: + // Compiler should able to optimize this as a load followed by a byte + // swap. On aarch64 (-mbig-endian), this compiles to the following for + // int: // ldr w0, [x0] // rev w0, w0 // ret for (size_t i = 0; i < sizeof(T); ++i) { - data.buffer[i] = bytes[sizeof(T) - i - 1]; + buffer[i] = bytes[sizeof(T) - i - 1]; } #else for (size_t i = 0; i < sizeof(T); ++i) { - data.buffer[i] = bytes[i]; + buffer[i] = bytes[i]; } #endif - return data.value; + return cpp::bit_cast(buffer); } // Specialized read functions for small values. size must be <= 8. diff --git a/libc/src/math/generic/exp10m1f16.cpp b/libc/src/math/generic/exp10m1f16.cpp index 9f2c1959fa5ec..449aedf254ca5 100644 --- a/libc/src/math/generic/exp10m1f16.cpp +++ b/libc/src/math/generic/exp10m1f16.cpp @@ -119,6 +119,9 @@ LLVM_LIBC_FUNCTION(float16, exp10m1f16, (float16 x)) { // When |x| <= 2^(-3). if (x_abs <= 0x3000U) { + if (LIBC_UNLIKELY(x_abs == 0)) + return x; + if (auto r = EXP10M1F16_EXCEPTS_LO.lookup(x_u); LIBC_UNLIKELY(r.has_value())) return r.value(); diff --git a/libc/src/math/generic/tanhf16.cpp b/libc/src/math/generic/tanhf16.cpp index ae9b4be46f7cf..0266b5cfc2df1 100644 --- a/libc/src/math/generic/tanhf16.cpp +++ b/libc/src/math/generic/tanhf16.cpp @@ -64,6 +64,9 @@ LLVM_LIBC_FUNCTION(float16, tanhf16, (float16 x)) { // When |x| <= 0x1.d2p-4. if (x_abs <= 0x2f48U) { + if (LIBC_UNLIKELY(x_abs == 0)) + return x; + float xf = x; float xf_sq = xf * xf; // Degree-7 Taylor expansion generated by Sollya with the following diff --git a/libc/src/stdio/gpu/file.h b/libc/src/stdio/gpu/file.h index 16d64e8f37750..6ca792b454580 100644 --- a/libc/src/stdio/gpu/file.h +++ b/libc/src/stdio/gpu/file.h @@ -49,7 +49,7 @@ LIBC_INLINE ::FILE *to_stream(uintptr_t f) { return stream; } -template +template LIBC_INLINE uint64_t write_impl(::FILE *file, const void *data, size_t size) { uint64_t ret = 0; rpc::Client::Port port = rpc::client.open(); diff --git a/libc/src/stdio/gpu/vfprintf_utils.h b/libc/src/stdio/gpu/vfprintf_utils.h index 5010ee16d9607..a0a8c39781ad6 100644 --- a/libc/src/stdio/gpu/vfprintf_utils.h +++ b/libc/src/stdio/gpu/vfprintf_utils.h @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "hdr/types/FILE.h" +#include "src/__support/GPU/utils.h" #include "src/__support/RPC/rpc_client.h" #include "src/__support/arg_list.h" #include "src/__support/macros/config.h" @@ -15,7 +16,7 @@ namespace LIBC_NAMESPACE_DECL { -template +template LIBC_INLINE int vfprintf_impl(::FILE *__restrict file, const char *__restrict format, size_t format_size, va_list vlist) { diff --git a/libc/src/stdlib/gpu/abort.cpp b/libc/src/stdlib/gpu/abort.cpp index cfc7e9b8e228b..3a06fb38c3f64 100644 --- a/libc/src/stdlib/gpu/abort.cpp +++ b/libc/src/stdlib/gpu/abort.cpp @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// +#include "src/__support/GPU/utils.h" #include "src/__support/RPC/rpc_client.h" #include "src/__support/common.h" #include "src/__support/macros/config.h" diff --git a/libc/test/integration/scudo/CMakeLists.txt b/libc/test/integration/scudo/CMakeLists.txt index a5f7e3b63d24c..b4011e501b96b 100644 --- a/libc/test/integration/scudo/CMakeLists.txt +++ b/libc/test/integration/scudo/CMakeLists.txt @@ -13,14 +13,23 @@ endif() add_entrypoint_library( libc_for_scudo_integration_test DEPENDS - libc.src.stdlib.malloc - libc.src.stdlib.calloc - libc.src.stdlib.realloc + libc.src.errno.errno + libc.src.fcntl.open + libc.src.sched.__sched_getcpucount libc.src.stdlib.aligned_alloc + libc.src.stdlib.calloc libc.src.stdlib.free - libc.src.errno.errno + libc.src.stdlib.malloc + libc.src.stdlib.realloc + libc.src.sys.auxv.getauxval + libc.src.sys.mman.mmap + libc.src.sys.mman.munmap + libc.src.sys.prctl.prctl libc.src.unistd.__llvm_libc_syscall - libc.src.sched.__sched_getcpucount + libc.src.unistd.close + libc.src.unistd.read + libc.src.unistd.sysconf + libc.src.unistd.write ) add_executable( diff --git a/libc/test/src/__support/HashTable/group_test.cpp b/libc/test/src/__support/HashTable/group_test.cpp index 25b15312ad668..acdc58e205852 100644 --- a/libc/test/src/__support/HashTable/group_test.cpp +++ b/libc/test/src/__support/HashTable/group_test.cpp @@ -8,6 +8,7 @@ #include "src/__support/HashTable/bitmask.h" +#include "src/__support/CPP/bit.h" #include "src/__support/macros/config.h" #include "src/stdlib/rand.h" #include "test/UnitTest/Test.h" @@ -28,14 +29,13 @@ TEST(LlvmLibcHashTableBitMaskTest, Match) { size_t appearance[4][sizeof(Group)]; ByteArray array{}; - union { - uintptr_t random; - int data[sizeof(uintptr_t) / sizeof(int)]; - }; + int data[sizeof(uintptr_t) / sizeof(int)]; for (int &i : data) i = rand(); + uintptr_t random = cpp::bit_cast(data); + for (size_t i = 0; i < sizeof(Group); ++i) { size_t choice = random % 4; random /= 4; @@ -62,14 +62,13 @@ TEST(LlvmLibcHashTableBitMaskTest, MaskAvailable) { for (size_t i = 0; i < sizeof(Group); ++i) { ByteArray array{}; - union { - uintptr_t random; - int data[sizeof(uintptr_t) / sizeof(int)]; - }; + int data[sizeof(uintptr_t) / sizeof(int)]; for (int &j : data) j = rand(); + uintptr_t random = cpp::bit_cast(data); + ASSERT_FALSE(Group::load(array.data).mask_available().any_bit_set()); array.data[i] = 0x80; diff --git a/libc/test/src/__support/HashTable/table_test.cpp b/libc/test/src/__support/HashTable/table_test.cpp index f8ffa4d4123d3..c3b8697f2087a 100644 --- a/libc/test/src/__support/HashTable/table_test.cpp +++ b/libc/test/src/__support/HashTable/table_test.cpp @@ -82,7 +82,7 @@ TEST(LlvmLibcTableTest, GrowthSequence) { } TEST(LlvmLibcTableTest, Insertion) { - union key { + struct key { char bytes[2]; } keys[256]; for (size_t k = 0; k < 256; ++k) { diff --git a/libc/utils/gpu/server/llvmlibc_rpc_server.h b/libc/utils/gpu/server/llvmlibc_rpc_server.h index b0cf2f916b385..98df882afa21c 100644 --- a/libc/utils/gpu/server/llvmlibc_rpc_server.h +++ b/libc/utils/gpu/server/llvmlibc_rpc_server.h @@ -79,7 +79,7 @@ rpc_status_t rpc_handle_server(rpc_device_t rpc_device); /// Register a callback to handle an opcode from the RPC client. The associated /// data must remain accessible as long as the user intends to handle the server /// with this callback. -rpc_status_t rpc_register_callback(rpc_device_t rpc_device, uint16_t opcode, +rpc_status_t rpc_register_callback(rpc_device_t rpc_device, uint32_t opcode, rpc_opcode_callback_ty callback, void *data); /// Obtain a pointer to a local client buffer that can be copied directly to the diff --git a/libc/utils/gpu/server/rpc_server.cpp b/libc/utils/gpu/server/rpc_server.cpp index 11b6d0e27ab94..972601aaf1d5e 100644 --- a/libc/utils/gpu/server/rpc_server.cpp +++ b/libc/utils/gpu/server/rpc_server.cpp @@ -215,8 +215,8 @@ static void handle_printf(rpc::Server::Port &port, TempStorage &temp_storage) { template rpc_status_t handle_server_impl( rpc::Server &server, - const std::unordered_map &callbacks, - const std::unordered_map &callback_data, + const std::unordered_map &callbacks, + const std::unordered_map &callback_data, uint32_t &index) { auto port = server.try_open(lane_size, index); if (!port) @@ -477,8 +477,8 @@ struct Device { void *buffer; rpc::Server server; rpc::Client client; - std::unordered_map callbacks; - std::unordered_map callback_data; + std::unordered_map callbacks; + std::unordered_map callback_data; }; rpc_status_t rpc_server_init(rpc_device_t *rpc_device, uint64_t num_ports, @@ -528,7 +528,7 @@ rpc_status_t rpc_handle_server(rpc_device_t rpc_device) { } } -rpc_status_t rpc_register_callback(rpc_device_t rpc_device, uint16_t opcode, +rpc_status_t rpc_register_callback(rpc_device_t rpc_device, uint32_t opcode, rpc_opcode_callback_ty callback, void *data) { if (!rpc_device.handle) diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index 83168d153a7f4..0ae031e5365ae 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -205,7 +205,6 @@ set(files __assert __atomic/aliases.h __atomic/atomic.h - __atomic/atomic_base.h __atomic/atomic_flag.h __atomic/atomic_init.h __atomic/atomic_lock_free.h diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h index 113475cb1f007..ae0475693f22b 100644 --- a/libcxx/include/__atomic/atomic.h +++ b/libcxx/include/__atomic/atomic.h @@ -9,9 +9,10 @@ #ifndef _LIBCPP___ATOMIC_ATOMIC_H #define _LIBCPP___ATOMIC_ATOMIC_H -#include <__atomic/atomic_base.h> +#include <__atomic/atomic_sync.h> #include <__atomic/check_memory_order.h> #include <__atomic/cxx_atomic_impl.h> +#include <__atomic/is_always_lock_free.h> #include <__atomic/memory_order.h> #include <__config> #include <__cstddef/ptrdiff_t.h> @@ -21,6 +22,7 @@ #include <__type_traits/is_floating_point.h> #include <__type_traits/is_function.h> #include <__type_traits/is_integral.h> +#include <__type_traits/is_nothrow_constructible.h> #include <__type_traits/is_same.h> #include <__type_traits/remove_const.h> #include <__type_traits/remove_pointer.h> @@ -34,6 +36,197 @@ _LIBCPP_BEGIN_NAMESPACE_STD +template ::value && !is_same<_Tp, bool>::value> +struct __atomic_base // false +{ + mutable __cxx_atomic_impl<_Tp> __a_; + +#if _LIBCPP_STD_VER >= 17 + static constexpr bool is_always_lock_free = __libcpp_is_always_lock_free<__cxx_atomic_impl<_Tp> >::__value; +#endif + + _LIBCPP_HIDE_FROM_ABI bool is_lock_free() const volatile _NOEXCEPT { + return __cxx_atomic_is_lock_free(sizeof(__cxx_atomic_impl<_Tp>)); + } + _LIBCPP_HIDE_FROM_ABI bool is_lock_free() const _NOEXCEPT { + return static_cast<__atomic_base const volatile*>(this)->is_lock_free(); + } + _LIBCPP_HIDE_FROM_ABI void store(_Tp __d, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT + _LIBCPP_CHECK_STORE_MEMORY_ORDER(__m) { + std::__cxx_atomic_store(std::addressof(__a_), __d, __m); + } + _LIBCPP_HIDE_FROM_ABI void store(_Tp __d, memory_order __m = memory_order_seq_cst) _NOEXCEPT + _LIBCPP_CHECK_STORE_MEMORY_ORDER(__m) { + std::__cxx_atomic_store(std::addressof(__a_), __d, __m); + } + _LIBCPP_HIDE_FROM_ABI _Tp load(memory_order __m = memory_order_seq_cst) const volatile _NOEXCEPT + _LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m) { + return std::__cxx_atomic_load(std::addressof(__a_), __m); + } + _LIBCPP_HIDE_FROM_ABI _Tp load(memory_order __m = memory_order_seq_cst) const _NOEXCEPT + _LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m) { + return std::__cxx_atomic_load(std::addressof(__a_), __m); + } + _LIBCPP_HIDE_FROM_ABI operator _Tp() const volatile _NOEXCEPT { return load(); } + _LIBCPP_HIDE_FROM_ABI operator _Tp() const _NOEXCEPT { return load(); } + _LIBCPP_HIDE_FROM_ABI _Tp exchange(_Tp __d, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT { + return std::__cxx_atomic_exchange(std::addressof(__a_), __d, __m); + } + _LIBCPP_HIDE_FROM_ABI _Tp exchange(_Tp __d, memory_order __m = memory_order_seq_cst) _NOEXCEPT { + return std::__cxx_atomic_exchange(std::addressof(__a_), __d, __m); + } + _LIBCPP_HIDE_FROM_ABI bool + compare_exchange_weak(_Tp& __e, _Tp __d, memory_order __s, memory_order __f) volatile _NOEXCEPT + _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__s, __f) { + return std::__cxx_atomic_compare_exchange_weak(std::addressof(__a_), std::addressof(__e), __d, __s, __f); + } + _LIBCPP_HIDE_FROM_ABI bool compare_exchange_weak(_Tp& __e, _Tp __d, memory_order __s, memory_order __f) _NOEXCEPT + _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__s, __f) { + return std::__cxx_atomic_compare_exchange_weak(std::addressof(__a_), std::addressof(__e), __d, __s, __f); + } + _LIBCPP_HIDE_FROM_ABI bool + compare_exchange_strong(_Tp& __e, _Tp __d, memory_order __s, memory_order __f) volatile _NOEXCEPT + _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__s, __f) { + return std::__cxx_atomic_compare_exchange_strong(std::addressof(__a_), std::addressof(__e), __d, __s, __f); + } + _LIBCPP_HIDE_FROM_ABI bool compare_exchange_strong(_Tp& __e, _Tp __d, memory_order __s, memory_order __f) _NOEXCEPT + _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__s, __f) { + return std::__cxx_atomic_compare_exchange_strong(std::addressof(__a_), std::addressof(__e), __d, __s, __f); + } + _LIBCPP_HIDE_FROM_ABI bool + compare_exchange_weak(_Tp& __e, _Tp __d, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT { + return std::__cxx_atomic_compare_exchange_weak(std::addressof(__a_), std::addressof(__e), __d, __m, __m); + } + _LIBCPP_HIDE_FROM_ABI bool + compare_exchange_weak(_Tp& __e, _Tp __d, memory_order __m = memory_order_seq_cst) _NOEXCEPT { + return std::__cxx_atomic_compare_exchange_weak(std::addressof(__a_), std::addressof(__e), __d, __m, __m); + } + _LIBCPP_HIDE_FROM_ABI bool + compare_exchange_strong(_Tp& __e, _Tp __d, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT { + return std::__cxx_atomic_compare_exchange_strong(std::addressof(__a_), std::addressof(__e), __d, __m, __m); + } + _LIBCPP_HIDE_FROM_ABI bool + compare_exchange_strong(_Tp& __e, _Tp __d, memory_order __m = memory_order_seq_cst) _NOEXCEPT { + return std::__cxx_atomic_compare_exchange_strong(std::addressof(__a_), std::addressof(__e), __d, __m, __m); + } + +#if _LIBCPP_STD_VER >= 20 + _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void wait(_Tp __v, memory_order __m = memory_order_seq_cst) const + volatile _NOEXCEPT { + std::__atomic_wait(*this, __v, __m); + } + _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void + wait(_Tp __v, memory_order __m = memory_order_seq_cst) const _NOEXCEPT { + std::__atomic_wait(*this, __v, __m); + } + _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void notify_one() volatile _NOEXCEPT { + std::__atomic_notify_one(*this); + } + _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void notify_one() _NOEXCEPT { std::__atomic_notify_one(*this); } + _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void notify_all() volatile _NOEXCEPT { + std::__atomic_notify_all(*this); + } + _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void notify_all() _NOEXCEPT { std::__atomic_notify_all(*this); } +#endif // _LIBCPP_STD_VER >= 20 + +#if _LIBCPP_STD_VER >= 20 + _LIBCPP_HIDE_FROM_ABI constexpr __atomic_base() noexcept(is_nothrow_default_constructible_v<_Tp>) : __a_(_Tp()) {} +#else + _LIBCPP_HIDE_FROM_ABI __atomic_base() _NOEXCEPT = default; +#endif + + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __atomic_base(_Tp __d) _NOEXCEPT : __a_(__d) {} + + __atomic_base(const __atomic_base&) = delete; +}; + +// atomic + +template +struct __atomic_base<_Tp, true> : public __atomic_base<_Tp, false> { + using __base = __atomic_base<_Tp, false>; + + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __atomic_base() _NOEXCEPT = default; + + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __atomic_base(_Tp __d) _NOEXCEPT : __base(__d) {} + + _LIBCPP_HIDE_FROM_ABI _Tp fetch_add(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT { + return std::__cxx_atomic_fetch_add(std::addressof(this->__a_), __op, __m); + } + _LIBCPP_HIDE_FROM_ABI _Tp fetch_add(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT { + return std::__cxx_atomic_fetch_add(std::addressof(this->__a_), __op, __m); + } + _LIBCPP_HIDE_FROM_ABI _Tp fetch_sub(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT { + return std::__cxx_atomic_fetch_sub(std::addressof(this->__a_), __op, __m); + } + _LIBCPP_HIDE_FROM_ABI _Tp fetch_sub(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT { + return std::__cxx_atomic_fetch_sub(std::addressof(this->__a_), __op, __m); + } + _LIBCPP_HIDE_FROM_ABI _Tp fetch_and(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT { + return std::__cxx_atomic_fetch_and(std::addressof(this->__a_), __op, __m); + } + _LIBCPP_HIDE_FROM_ABI _Tp fetch_and(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT { + return std::__cxx_atomic_fetch_and(std::addressof(this->__a_), __op, __m); + } + _LIBCPP_HIDE_FROM_ABI _Tp fetch_or(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT { + return std::__cxx_atomic_fetch_or(std::addressof(this->__a_), __op, __m); + } + _LIBCPP_HIDE_FROM_ABI _Tp fetch_or(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT { + return std::__cxx_atomic_fetch_or(std::addressof(this->__a_), __op, __m); + } + _LIBCPP_HIDE_FROM_ABI _Tp fetch_xor(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT { + return std::__cxx_atomic_fetch_xor(std::addressof(this->__a_), __op, __m); + } + _LIBCPP_HIDE_FROM_ABI _Tp fetch_xor(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT { + return std::__cxx_atomic_fetch_xor(std::addressof(this->__a_), __op, __m); + } + + _LIBCPP_HIDE_FROM_ABI _Tp operator++(int) volatile _NOEXCEPT { return fetch_add(_Tp(1)); } + _LIBCPP_HIDE_FROM_ABI _Tp operator++(int) _NOEXCEPT { return fetch_add(_Tp(1)); } + _LIBCPP_HIDE_FROM_ABI _Tp operator--(int) volatile _NOEXCEPT { return fetch_sub(_Tp(1)); } + _LIBCPP_HIDE_FROM_ABI _Tp operator--(int) _NOEXCEPT { return fetch_sub(_Tp(1)); } + _LIBCPP_HIDE_FROM_ABI _Tp operator++() volatile _NOEXCEPT { return fetch_add(_Tp(1)) + _Tp(1); } + _LIBCPP_HIDE_FROM_ABI _Tp operator++() _NOEXCEPT { return fetch_add(_Tp(1)) + _Tp(1); } + _LIBCPP_HIDE_FROM_ABI _Tp operator--() volatile _NOEXCEPT { return fetch_sub(_Tp(1)) - _Tp(1); } + _LIBCPP_HIDE_FROM_ABI _Tp operator--() _NOEXCEPT { return fetch_sub(_Tp(1)) - _Tp(1); } + _LIBCPP_HIDE_FROM_ABI _Tp operator+=(_Tp __op) volatile _NOEXCEPT { return fetch_add(__op) + __op; } + _LIBCPP_HIDE_FROM_ABI _Tp operator+=(_Tp __op) _NOEXCEPT { return fetch_add(__op) + __op; } + _LIBCPP_HIDE_FROM_ABI _Tp operator-=(_Tp __op) volatile _NOEXCEPT { return fetch_sub(__op) - __op; } + _LIBCPP_HIDE_FROM_ABI _Tp operator-=(_Tp __op) _NOEXCEPT { return fetch_sub(__op) - __op; } + _LIBCPP_HIDE_FROM_ABI _Tp operator&=(_Tp __op) volatile _NOEXCEPT { return fetch_and(__op) & __op; } + _LIBCPP_HIDE_FROM_ABI _Tp operator&=(_Tp __op) _NOEXCEPT { return fetch_and(__op) & __op; } + _LIBCPP_HIDE_FROM_ABI _Tp operator|=(_Tp __op) volatile _NOEXCEPT { return fetch_or(__op) | __op; } + _LIBCPP_HIDE_FROM_ABI _Tp operator|=(_Tp __op) _NOEXCEPT { return fetch_or(__op) | __op; } + _LIBCPP_HIDE_FROM_ABI _Tp operator^=(_Tp __op) volatile _NOEXCEPT { return fetch_xor(__op) ^ __op; } + _LIBCPP_HIDE_FROM_ABI _Tp operator^=(_Tp __op) _NOEXCEPT { return fetch_xor(__op) ^ __op; } +}; + +// Here we need _IsIntegral because the default template argument is not enough +// e.g __atomic_base is __atomic_base, which inherits from +// __atomic_base and the caller of the wait function is +// __atomic_base. So specializing __atomic_base<_Tp> does not work +template +struct __atomic_waitable_traits<__atomic_base<_Tp, _IsIntegral> > { + static _LIBCPP_HIDE_FROM_ABI _Tp __atomic_load(const __atomic_base<_Tp, _IsIntegral>& __a, memory_order __order) { + return __a.load(__order); + } + + static _LIBCPP_HIDE_FROM_ABI _Tp + __atomic_load(const volatile __atomic_base<_Tp, _IsIntegral>& __this, memory_order __order) { + return __this.load(__order); + } + + static _LIBCPP_HIDE_FROM_ABI const __cxx_atomic_impl<_Tp>* + __atomic_contention_address(const __atomic_base<_Tp, _IsIntegral>& __a) { + return std::addressof(__a.__a_); + } + + static _LIBCPP_HIDE_FROM_ABI const volatile __cxx_atomic_impl<_Tp>* + __atomic_contention_address(const volatile __atomic_base<_Tp, _IsIntegral>& __this) { + return std::addressof(__this.__a_); + } +}; + template struct atomic : public __atomic_base<_Tp> { using __base = __atomic_base<_Tp>; @@ -123,6 +316,9 @@ struct atomic<_Tp*> : public __atomic_base<_Tp*> { atomic& operator=(const atomic&) volatile = delete; }; +template +struct __atomic_waitable_traits > : __atomic_waitable_traits<__atomic_base<_Tp> > {}; + #if _LIBCPP_STD_VER >= 20 template requires is_floating_point_v<_Tp> diff --git a/libcxx/include/__atomic/atomic_base.h b/libcxx/include/__atomic/atomic_base.h deleted file mode 100644 index 93f5c4cff0d1b..0000000000000 --- a/libcxx/include/__atomic/atomic_base.h +++ /dev/null @@ -1,223 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 _LIBCPP___ATOMIC_ATOMIC_BASE_H -#define _LIBCPP___ATOMIC_ATOMIC_BASE_H - -#include <__atomic/atomic_sync.h> -#include <__atomic/check_memory_order.h> -#include <__atomic/cxx_atomic_impl.h> -#include <__atomic/is_always_lock_free.h> -#include <__atomic/memory_order.h> -#include <__config> -#include <__memory/addressof.h> -#include <__type_traits/is_integral.h> -#include <__type_traits/is_nothrow_constructible.h> -#include <__type_traits/is_same.h> -#include - -#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) -# pragma GCC system_header -#endif - -_LIBCPP_BEGIN_NAMESPACE_STD - -template ::value && !is_same<_Tp, bool>::value> -struct __atomic_base // false -{ - mutable __cxx_atomic_impl<_Tp> __a_; - -#if _LIBCPP_STD_VER >= 17 - static constexpr bool is_always_lock_free = __libcpp_is_always_lock_free<__cxx_atomic_impl<_Tp> >::__value; -#endif - - _LIBCPP_HIDE_FROM_ABI bool is_lock_free() const volatile _NOEXCEPT { - return __cxx_atomic_is_lock_free(sizeof(__cxx_atomic_impl<_Tp>)); - } - _LIBCPP_HIDE_FROM_ABI bool is_lock_free() const _NOEXCEPT { - return static_cast<__atomic_base const volatile*>(this)->is_lock_free(); - } - _LIBCPP_HIDE_FROM_ABI void store(_Tp __d, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT - _LIBCPP_CHECK_STORE_MEMORY_ORDER(__m) { - std::__cxx_atomic_store(std::addressof(__a_), __d, __m); - } - _LIBCPP_HIDE_FROM_ABI void store(_Tp __d, memory_order __m = memory_order_seq_cst) _NOEXCEPT - _LIBCPP_CHECK_STORE_MEMORY_ORDER(__m) { - std::__cxx_atomic_store(std::addressof(__a_), __d, __m); - } - _LIBCPP_HIDE_FROM_ABI _Tp load(memory_order __m = memory_order_seq_cst) const volatile _NOEXCEPT - _LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m) { - return std::__cxx_atomic_load(std::addressof(__a_), __m); - } - _LIBCPP_HIDE_FROM_ABI _Tp load(memory_order __m = memory_order_seq_cst) const _NOEXCEPT - _LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m) { - return std::__cxx_atomic_load(std::addressof(__a_), __m); - } - _LIBCPP_HIDE_FROM_ABI operator _Tp() const volatile _NOEXCEPT { return load(); } - _LIBCPP_HIDE_FROM_ABI operator _Tp() const _NOEXCEPT { return load(); } - _LIBCPP_HIDE_FROM_ABI _Tp exchange(_Tp __d, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT { - return std::__cxx_atomic_exchange(std::addressof(__a_), __d, __m); - } - _LIBCPP_HIDE_FROM_ABI _Tp exchange(_Tp __d, memory_order __m = memory_order_seq_cst) _NOEXCEPT { - return std::__cxx_atomic_exchange(std::addressof(__a_), __d, __m); - } - _LIBCPP_HIDE_FROM_ABI bool - compare_exchange_weak(_Tp& __e, _Tp __d, memory_order __s, memory_order __f) volatile _NOEXCEPT - _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__s, __f) { - return std::__cxx_atomic_compare_exchange_weak(std::addressof(__a_), std::addressof(__e), __d, __s, __f); - } - _LIBCPP_HIDE_FROM_ABI bool compare_exchange_weak(_Tp& __e, _Tp __d, memory_order __s, memory_order __f) _NOEXCEPT - _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__s, __f) { - return std::__cxx_atomic_compare_exchange_weak(std::addressof(__a_), std::addressof(__e), __d, __s, __f); - } - _LIBCPP_HIDE_FROM_ABI bool - compare_exchange_strong(_Tp& __e, _Tp __d, memory_order __s, memory_order __f) volatile _NOEXCEPT - _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__s, __f) { - return std::__cxx_atomic_compare_exchange_strong(std::addressof(__a_), std::addressof(__e), __d, __s, __f); - } - _LIBCPP_HIDE_FROM_ABI bool compare_exchange_strong(_Tp& __e, _Tp __d, memory_order __s, memory_order __f) _NOEXCEPT - _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__s, __f) { - return std::__cxx_atomic_compare_exchange_strong(std::addressof(__a_), std::addressof(__e), __d, __s, __f); - } - _LIBCPP_HIDE_FROM_ABI bool - compare_exchange_weak(_Tp& __e, _Tp __d, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT { - return std::__cxx_atomic_compare_exchange_weak(std::addressof(__a_), std::addressof(__e), __d, __m, __m); - } - _LIBCPP_HIDE_FROM_ABI bool - compare_exchange_weak(_Tp& __e, _Tp __d, memory_order __m = memory_order_seq_cst) _NOEXCEPT { - return std::__cxx_atomic_compare_exchange_weak(std::addressof(__a_), std::addressof(__e), __d, __m, __m); - } - _LIBCPP_HIDE_FROM_ABI bool - compare_exchange_strong(_Tp& __e, _Tp __d, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT { - return std::__cxx_atomic_compare_exchange_strong(std::addressof(__a_), std::addressof(__e), __d, __m, __m); - } - _LIBCPP_HIDE_FROM_ABI bool - compare_exchange_strong(_Tp& __e, _Tp __d, memory_order __m = memory_order_seq_cst) _NOEXCEPT { - return std::__cxx_atomic_compare_exchange_strong(std::addressof(__a_), std::addressof(__e), __d, __m, __m); - } - -#if _LIBCPP_STD_VER >= 20 - _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void wait(_Tp __v, memory_order __m = memory_order_seq_cst) const - volatile _NOEXCEPT { - std::__atomic_wait(*this, __v, __m); - } - _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void - wait(_Tp __v, memory_order __m = memory_order_seq_cst) const _NOEXCEPT { - std::__atomic_wait(*this, __v, __m); - } - _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void notify_one() volatile _NOEXCEPT { - std::__atomic_notify_one(*this); - } - _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void notify_one() _NOEXCEPT { std::__atomic_notify_one(*this); } - _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void notify_all() volatile _NOEXCEPT { - std::__atomic_notify_all(*this); - } - _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void notify_all() _NOEXCEPT { std::__atomic_notify_all(*this); } -#endif // _LIBCPP_STD_VER >= 20 - -#if _LIBCPP_STD_VER >= 20 - _LIBCPP_HIDE_FROM_ABI constexpr __atomic_base() noexcept(is_nothrow_default_constructible_v<_Tp>) : __a_(_Tp()) {} -#else - _LIBCPP_HIDE_FROM_ABI __atomic_base() _NOEXCEPT = default; -#endif - - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __atomic_base(_Tp __d) _NOEXCEPT : __a_(__d) {} - - __atomic_base(const __atomic_base&) = delete; -}; - -// atomic - -template -struct __atomic_base<_Tp, true> : public __atomic_base<_Tp, false> { - using __base = __atomic_base<_Tp, false>; - - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __atomic_base() _NOEXCEPT = default; - - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __atomic_base(_Tp __d) _NOEXCEPT : __base(__d) {} - - _LIBCPP_HIDE_FROM_ABI _Tp fetch_add(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT { - return std::__cxx_atomic_fetch_add(std::addressof(this->__a_), __op, __m); - } - _LIBCPP_HIDE_FROM_ABI _Tp fetch_add(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT { - return std::__cxx_atomic_fetch_add(std::addressof(this->__a_), __op, __m); - } - _LIBCPP_HIDE_FROM_ABI _Tp fetch_sub(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT { - return std::__cxx_atomic_fetch_sub(std::addressof(this->__a_), __op, __m); - } - _LIBCPP_HIDE_FROM_ABI _Tp fetch_sub(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT { - return std::__cxx_atomic_fetch_sub(std::addressof(this->__a_), __op, __m); - } - _LIBCPP_HIDE_FROM_ABI _Tp fetch_and(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT { - return std::__cxx_atomic_fetch_and(std::addressof(this->__a_), __op, __m); - } - _LIBCPP_HIDE_FROM_ABI _Tp fetch_and(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT { - return std::__cxx_atomic_fetch_and(std::addressof(this->__a_), __op, __m); - } - _LIBCPP_HIDE_FROM_ABI _Tp fetch_or(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT { - return std::__cxx_atomic_fetch_or(std::addressof(this->__a_), __op, __m); - } - _LIBCPP_HIDE_FROM_ABI _Tp fetch_or(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT { - return std::__cxx_atomic_fetch_or(std::addressof(this->__a_), __op, __m); - } - _LIBCPP_HIDE_FROM_ABI _Tp fetch_xor(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT { - return std::__cxx_atomic_fetch_xor(std::addressof(this->__a_), __op, __m); - } - _LIBCPP_HIDE_FROM_ABI _Tp fetch_xor(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT { - return std::__cxx_atomic_fetch_xor(std::addressof(this->__a_), __op, __m); - } - - _LIBCPP_HIDE_FROM_ABI _Tp operator++(int) volatile _NOEXCEPT { return fetch_add(_Tp(1)); } - _LIBCPP_HIDE_FROM_ABI _Tp operator++(int) _NOEXCEPT { return fetch_add(_Tp(1)); } - _LIBCPP_HIDE_FROM_ABI _Tp operator--(int) volatile _NOEXCEPT { return fetch_sub(_Tp(1)); } - _LIBCPP_HIDE_FROM_ABI _Tp operator--(int) _NOEXCEPT { return fetch_sub(_Tp(1)); } - _LIBCPP_HIDE_FROM_ABI _Tp operator++() volatile _NOEXCEPT { return fetch_add(_Tp(1)) + _Tp(1); } - _LIBCPP_HIDE_FROM_ABI _Tp operator++() _NOEXCEPT { return fetch_add(_Tp(1)) + _Tp(1); } - _LIBCPP_HIDE_FROM_ABI _Tp operator--() volatile _NOEXCEPT { return fetch_sub(_Tp(1)) - _Tp(1); } - _LIBCPP_HIDE_FROM_ABI _Tp operator--() _NOEXCEPT { return fetch_sub(_Tp(1)) - _Tp(1); } - _LIBCPP_HIDE_FROM_ABI _Tp operator+=(_Tp __op) volatile _NOEXCEPT { return fetch_add(__op) + __op; } - _LIBCPP_HIDE_FROM_ABI _Tp operator+=(_Tp __op) _NOEXCEPT { return fetch_add(__op) + __op; } - _LIBCPP_HIDE_FROM_ABI _Tp operator-=(_Tp __op) volatile _NOEXCEPT { return fetch_sub(__op) - __op; } - _LIBCPP_HIDE_FROM_ABI _Tp operator-=(_Tp __op) _NOEXCEPT { return fetch_sub(__op) - __op; } - _LIBCPP_HIDE_FROM_ABI _Tp operator&=(_Tp __op) volatile _NOEXCEPT { return fetch_and(__op) & __op; } - _LIBCPP_HIDE_FROM_ABI _Tp operator&=(_Tp __op) _NOEXCEPT { return fetch_and(__op) & __op; } - _LIBCPP_HIDE_FROM_ABI _Tp operator|=(_Tp __op) volatile _NOEXCEPT { return fetch_or(__op) | __op; } - _LIBCPP_HIDE_FROM_ABI _Tp operator|=(_Tp __op) _NOEXCEPT { return fetch_or(__op) | __op; } - _LIBCPP_HIDE_FROM_ABI _Tp operator^=(_Tp __op) volatile _NOEXCEPT { return fetch_xor(__op) ^ __op; } - _LIBCPP_HIDE_FROM_ABI _Tp operator^=(_Tp __op) _NOEXCEPT { return fetch_xor(__op) ^ __op; } -}; - -// Here we need _IsIntegral because the default template argument is not enough -// e.g __atomic_base is __atomic_base, which inherits from -// __atomic_base and the caller of the wait function is -// __atomic_base. So specializing __atomic_base<_Tp> does not work -template -struct __atomic_waitable_traits<__atomic_base<_Tp, _IsIntegral> > { - static _LIBCPP_HIDE_FROM_ABI _Tp __atomic_load(const __atomic_base<_Tp, _IsIntegral>& __a, memory_order __order) { - return __a.load(__order); - } - - static _LIBCPP_HIDE_FROM_ABI _Tp - __atomic_load(const volatile __atomic_base<_Tp, _IsIntegral>& __this, memory_order __order) { - return __this.load(__order); - } - - static _LIBCPP_HIDE_FROM_ABI const __cxx_atomic_impl<_Tp>* - __atomic_contention_address(const __atomic_base<_Tp, _IsIntegral>& __a) { - return std::addressof(__a.__a_); - } - - static _LIBCPP_HIDE_FROM_ABI const volatile __cxx_atomic_impl<_Tp>* - __atomic_contention_address(const volatile __atomic_base<_Tp, _IsIntegral>& __this) { - return std::addressof(__this.__a_); - } -}; - -_LIBCPP_END_NAMESPACE_STD - -#endif // _LIBCPP___ATOMIC_ATOMIC_BASE_H diff --git a/libcxx/include/atomic b/libcxx/include/atomic index 716d198bc236b..d4adf277c49c7 100644 --- a/libcxx/include/atomic +++ b/libcxx/include/atomic @@ -591,7 +591,6 @@ template #include <__atomic/aliases.h> #include <__atomic/atomic.h> -#include <__atomic/atomic_base.h> #include <__atomic/atomic_flag.h> #include <__atomic/atomic_init.h> #include <__atomic/atomic_lock_free.h> diff --git a/libcxx/include/barrier b/libcxx/include/barrier index c7df0e9e6e8d4..980eae06ab140 100644 --- a/libcxx/include/barrier +++ b/libcxx/include/barrier @@ -50,7 +50,7 @@ namespace std #if _LIBCPP_HAS_THREADS # include <__assert> -# include <__atomic/atomic_base.h> +# include <__atomic/atomic.h> # include <__atomic/memory_order.h> # include <__cstddef/ptrdiff_t.h> # include <__memory/unique_ptr.h> @@ -109,9 +109,9 @@ template class __barrier_base { ptrdiff_t __expected_; unique_ptr<__barrier_algorithm_base, void (*)(__barrier_algorithm_base*)> __base_; - __atomic_base __expected_adjustment_; + atomic __expected_adjustment_; _CompletionF __completion_; - __atomic_base<__barrier_phase_t> __phase_; + atomic<__barrier_phase_t> __phase_; public: using arrival_token = __barrier_phase_t; @@ -167,10 +167,10 @@ Two versions of this algorithm are provided: template class __barrier_base { - __atomic_base __expected; - __atomic_base __arrived; + atomic __expected; + atomic __arrived; _CompletionF __completion; - __atomic_base __phase; + atomic __phase; public: using arrival_token = bool; @@ -212,7 +212,7 @@ class __barrier_base<__empty_completion> { static constexpr uint64_t __phase_bit = 1ull << 63; static constexpr uint64_t __arrived_mask = (__phase_bit - 1) & ~__expected_mask; - __atomic_base __phase_arrived_expected; + atomic __phase_arrived_expected; static _LIBCPP_HIDE_FROM_ABI constexpr uint64_t __init(ptrdiff_t __count) _NOEXCEPT { return ((uint64_t(1u << 31) - __count) << 32) | (uint64_t(1u << 31) - __count); diff --git a/libcxx/include/bit b/libcxx/include/bit index 94387d101a398..092aebca26a31 100644 --- a/libcxx/include/bit +++ b/libcxx/include/bit @@ -87,10 +87,6 @@ namespace std { # pragma GCC system_header #endif -#if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 17 -# include -#endif - #if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20 # include # include diff --git a/libcxx/include/charconv b/libcxx/include/charconv index 8f5e697eec439..a65b3d3527080 100644 --- a/libcxx/include/charconv +++ b/libcxx/include/charconv @@ -101,13 +101,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD _LIBCPP_END_NAMESPACE_STD -#if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 14 -# include -# include -# include -# include -#endif - #if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20 # include # include diff --git a/libcxx/include/compare b/libcxx/include/compare index de0e4c7ec2280..440d4c4b4dd26 100644 --- a/libcxx/include/compare +++ b/libcxx/include/compare @@ -164,12 +164,6 @@ namespace std { # pragma GCC system_header #endif -#if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 17 -# include -# include -# include -#endif - #if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20 # include # include diff --git a/libcxx/include/expected b/libcxx/include/expected index 6a2f12f2bf3b5..3c7ef336432a1 100644 --- a/libcxx/include/expected +++ b/libcxx/include/expected @@ -53,10 +53,4 @@ namespace std { # pragma GCC system_header #endif -#if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20 -# include -# include -# include -#endif - #endif // _LIBCPP_EXPECTED diff --git a/libcxx/include/future b/libcxx/include/future index 9f7c95e542fd6..cbf3ed9346417 100644 --- a/libcxx/include/future +++ b/libcxx/include/future @@ -384,6 +384,7 @@ template struct uses_allocator, Alloc>; # include <__system_error/error_category.h> # include <__system_error/error_code.h> # include <__system_error/error_condition.h> +# include <__thread/thread.h> # include <__type_traits/add_lvalue_reference.h> # include <__type_traits/aligned_storage.h> # include <__type_traits/conditional.h> @@ -397,7 +398,6 @@ template struct uses_allocator, Alloc>; # include # include # include -# include # include # if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -2071,6 +2071,7 @@ _LIBCPP_POP_MACROS # include # include # include +# include #endif #endif // _LIBCPP_FUTURE diff --git a/libcxx/include/latch b/libcxx/include/latch index 90cca27c50c37..1860ed816c856 100644 --- a/libcxx/include/latch +++ b/libcxx/include/latch @@ -45,7 +45,7 @@ namespace std #if _LIBCPP_HAS_THREADS # include <__assert> -# include <__atomic/atomic_base.h> +# include <__atomic/atomic.h> # include <__atomic/atomic_sync.h> # include <__atomic/memory_order.h> # include <__cstddef/ptrdiff_t.h> @@ -64,7 +64,7 @@ _LIBCPP_PUSH_MACROS _LIBCPP_BEGIN_NAMESPACE_STD class latch { - __atomic_base __a_; + atomic __a_; public: static _LIBCPP_HIDE_FROM_ABI constexpr ptrdiff_t max() noexcept { return numeric_limits::max(); } diff --git a/libcxx/include/mdspan b/libcxx/include/mdspan index 29190e4a9953e..d6191a197e15c 100644 --- a/libcxx/include/mdspan +++ b/libcxx/include/mdspan @@ -426,13 +426,4 @@ namespace std { # pragma GCC system_header #endif -#if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20 -# include -# include -# include -# include -# include -# include -#endif - #endif // _LIBCPP_MDSPAN diff --git a/libcxx/include/memory_resource b/libcxx/include/memory_resource index e98ca20aa058c..7de69e67b7c06 100644 --- a/libcxx/include/memory_resource +++ b/libcxx/include/memory_resource @@ -66,15 +66,6 @@ namespace std::pmr { # pragma GCC system_header #endif -#if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 14 -# include -# include -# include -# include -# include -# include -#endif - #if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20 # include #endif diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap index 139c0a8366643..4e06a68c6a6b6 100644 --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -844,7 +844,6 @@ module std [system] { module atomic { module aliases { header "__atomic/aliases.h" } - module atomic_base { header "__atomic/atomic_base.h" } module atomic_flag { header "__atomic/atomic_flag.h" } module atomic_init { header "__atomic/atomic_init.h" } module atomic_lock_free { header "__atomic/atomic_lock_free.h" } diff --git a/libcxx/include/ranges b/libcxx/include/ranges index b17a399e0ed65..d8ee6f75e8b23 100644 --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -446,14 +446,6 @@ namespace std { # pragma GCC system_header #endif -#if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 17 -# include -# include -# include -# include -# include -#endif - #if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20 # include # include diff --git a/libcxx/include/semaphore b/libcxx/include/semaphore index 05c85bc810603..c594df459c93f 100644 --- a/libcxx/include/semaphore +++ b/libcxx/include/semaphore @@ -50,7 +50,7 @@ using binary_semaphore = counting_semaphore<1>; // since C++20 #if _LIBCPP_HAS_THREADS # include <__assert> -# include <__atomic/atomic_base.h> +# include <__atomic/atomic.h> # include <__atomic/atomic_sync.h> # include <__atomic/memory_order.h> # include <__chrono/time_point.h> @@ -83,7 +83,7 @@ functions. It avoids contention against users' own use of those facilities. # define _LIBCPP_SEMAPHORE_MAX (numeric_limits::max()) class __atomic_semaphore_base { - __atomic_base __a_; + atomic __a_; public: _LIBCPP_HIDE_FROM_ABI constexpr explicit __atomic_semaphore_base(ptrdiff_t __count) : __a_(__count) {} diff --git a/libcxx/include/string b/libcxx/include/string index a994f65a9a6e4..bf7fc3c37ecd7 100644 --- a/libcxx/include/string +++ b/libcxx/include/string @@ -3374,7 +3374,7 @@ template inline _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::__shrink_or_extend(size_type __target_capacity) { __annotate_delete(); - auto __guard = std::__make_scope_guard(__annotate_new_size(*this)); + auto __guard = std::__make_scope_guard(__annotate_new_size(*this)); size_type __cap = capacity(); size_type __sz = size(); diff --git a/libcxx/include/thread b/libcxx/include/thread index bfe7e4a4c51e5..d7c3f704ad672 100644 --- a/libcxx/include/thread +++ b/libcxx/include/thread @@ -90,11 +90,17 @@ void sleep_for(const chrono::duration& rel_time); #if _LIBCPP_HAS_THREADS -# include <__thread/formatter.h> -# include <__thread/jthread.h> -# include <__thread/support.h> # include <__thread/this_thread.h> # include <__thread/thread.h> + +# if _LIBCPP_STD_VER >= 20 +# include <__thread/jthread.h> +# endif + +# if _LIBCPP_STD_VER >= 23 +# include <__thread/formatter.h> +# endif + # include // standard-mandated includes @@ -108,13 +114,6 @@ void sleep_for(const chrono::duration& rel_time); #endif // _LIBCPP_HAS_THREADS -#if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) -# include -# include -# include -# include -#endif - #if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 17 # include #endif diff --git a/libcxx/src/barrier.cpp b/libcxx/src/barrier.cpp index 69601bfeec054..b97c7bd73b74c 100644 --- a/libcxx/src/barrier.cpp +++ b/libcxx/src/barrier.cpp @@ -17,7 +17,7 @@ class __barrier_algorithm_base { public: struct alignas(64) /* naturally-align the heap state */ __state_t { struct { - __atomic_base<__barrier_phase_t> __phase{0}; + atomic<__barrier_phase_t> __phase{0}; } __tickets[64]; }; diff --git a/libcxx/test/benchmarks/CMakeLists.txt b/libcxx/test/benchmarks/CMakeLists.txt index b5a4aae82c06a..b0fe600623d96 100644 --- a/libcxx/test/benchmarks/CMakeLists.txt +++ b/libcxx/test/benchmarks/CMakeLists.txt @@ -35,13 +35,14 @@ ExternalProject_Add(google-benchmark SOURCE_DIR ${LLVM_THIRD_PARTY_DIR}/benchmark INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/google-benchmark CMAKE_CACHE_ARGS - -DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER} - -DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER} + -DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER} + -DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER} + -DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM} -DCMAKE_BUILD_TYPE:STRING=RELEASE -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_CXX_FLAGS:STRING=${BENCHMARK_COMPILE_FLAGS} -DBENCHMARK_USE_LIBCXX:BOOL=ON -DBENCHMARK_ENABLE_TESTING:BOOL=OFF - -DBENCHMARK_CXX_LIBRARIES:STRING="${BENCHMARK_CXX_LIBRARIES}") + -DBENCHMARK_CXX_LIBRARIES:STRING=${BENCHMARK_CXX_LIBRARIES}) add_dependencies(cxx-test-depends google-benchmark) diff --git a/libcxx/test/benchmarks/ContainerBenchmarks.h b/libcxx/test/benchmarks/ContainerBenchmarks.h index 742c848328604..38e11777f488b 100644 --- a/libcxx/test/benchmarks/ContainerBenchmarks.h +++ b/libcxx/test/benchmarks/ContainerBenchmarks.h @@ -11,6 +11,8 @@ #define BENCHMARK_CONTAINER_BENCHMARKS_H #include +#include +#include #include "Utilities.h" #include "benchmark/benchmark.h" @@ -149,6 +151,34 @@ void BM_EmplaceDuplicate(benchmark::State& st, Container c, GenInputs gen) { } } +template +void BM_erase_iter_in_middle(benchmark::State& st, Container, GenInputs gen) { + auto in = gen(st.range(0)); + Container c(in.begin(), in.end()); + assert(c.size() > 2); + for (auto _ : st) { + auto mid = std::next(c.begin(), c.size() / 2); + auto tmp = *mid; + auto result = c.erase(mid); // erase an element in the middle + benchmark::DoNotOptimize(result); + c.push_back(std::move(tmp)); // and then push it back at the end to avoid needing a new container + } +} + +template +void BM_erase_iter_at_start(benchmark::State& st, Container, GenInputs gen) { + auto in = gen(st.range(0)); + Container c(in.begin(), in.end()); + assert(c.size() > 2); + for (auto _ : st) { + auto it = c.begin(); + auto tmp = *it; + auto result = c.erase(it); // erase the first element + benchmark::DoNotOptimize(result); + c.push_back(std::move(tmp)); // and then push it back at the end to avoid needing a new container + } +} + template void BM_Find(benchmark::State& st, Container c, GenInputs gen) { auto in = gen(st.range(0)); diff --git a/libcxx/test/benchmarks/atomic_wait.bench.cpp b/libcxx/test/benchmarks/atomic_wait.bench.cpp index 49503a318fda1..d19f5fbed8ad6 100644 --- a/libcxx/test/benchmarks/atomic_wait.bench.cpp +++ b/libcxx/test/benchmarks/atomic_wait.bench.cpp @@ -9,6 +9,7 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17 #include +#include #include #include #include diff --git a/libcxx/test/benchmarks/atomic_wait_vs_mutex_lock.bench.cpp b/libcxx/test/benchmarks/atomic_wait_vs_mutex_lock.bench.cpp index 221fc086d2a62..a554c721df017 100644 --- a/libcxx/test/benchmarks/atomic_wait_vs_mutex_lock.bench.cpp +++ b/libcxx/test/benchmarks/atomic_wait_vs_mutex_lock.bench.cpp @@ -9,8 +9,8 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17 #include +#include #include -#include #include #include diff --git a/libcxx/test/benchmarks/deque.bench.cpp b/libcxx/test/benchmarks/deque.bench.cpp index b8f3b76dd27ee..ab0ba96b12ffc 100644 --- a/libcxx/test/benchmarks/deque.bench.cpp +++ b/libcxx/test/benchmarks/deque.bench.cpp @@ -9,6 +9,7 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 #include +#include #include "benchmark/benchmark.h" @@ -41,4 +42,14 @@ BENCHMARK_CAPTURE(BM_ConstructFromRange, deque_size_t, std::deque{}, get BENCHMARK_CAPTURE(BM_ConstructFromRange, deque_string, std::deque{}, getRandomStringInputs) ->Arg(TestNumInputs); +BENCHMARK_CAPTURE(BM_erase_iter_in_middle, deque_int, std::deque{}, getRandomIntegerInputs) + ->Range(TestNumInputs, TestNumInputs * 10); +BENCHMARK_CAPTURE(BM_erase_iter_in_middle, deque_string, std::deque{}, getRandomStringInputs) + ->Range(TestNumInputs, TestNumInputs * 10); + +BENCHMARK_CAPTURE(BM_erase_iter_at_start, deque_int, std::deque{}, getRandomIntegerInputs) + ->Range(TestNumInputs, TestNumInputs * 10); +BENCHMARK_CAPTURE(BM_erase_iter_at_start, deque_string, std::deque{}, getRandomStringInputs) + ->Range(TestNumInputs, TestNumInputs * 10); + BENCHMARK_MAIN(); diff --git a/libcxx/test/benchmarks/stop_token.bench.cpp b/libcxx/test/benchmarks/stop_token.bench.cpp index 6149f91c6fc38..a627f80697dd5 100644 --- a/libcxx/test/benchmarks/stop_token.bench.cpp +++ b/libcxx/test/benchmarks/stop_token.bench.cpp @@ -8,7 +8,7 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17 -#include +#include #include #include #include diff --git a/libcxx/test/benchmarks/vector_operations.bench.cpp b/libcxx/test/benchmarks/vector_operations.bench.cpp index ce8ab233fc981..1855861263324 100644 --- a/libcxx/test/benchmarks/vector_operations.bench.cpp +++ b/libcxx/test/benchmarks/vector_operations.bench.cpp @@ -54,6 +54,16 @@ BENCHMARK_CAPTURE(BM_ConstructFromRange, vector_string, std::vector BENCHMARK_CAPTURE(BM_Pushback_no_grow, vector_int, std::vector{})->Arg(TestNumInputs); +BENCHMARK_CAPTURE(BM_erase_iter_in_middle, vector_int, std::vector{}, getRandomIntegerInputs) + ->Range(TestNumInputs, TestNumInputs * 10); +BENCHMARK_CAPTURE(BM_erase_iter_in_middle, vector_string, std::vector{}, getRandomStringInputs) + ->Range(TestNumInputs, TestNumInputs * 10); + +BENCHMARK_CAPTURE(BM_erase_iter_at_start, vector_int, std::vector{}, getRandomIntegerInputs) + ->Range(TestNumInputs, TestNumInputs * 10); +BENCHMARK_CAPTURE(BM_erase_iter_at_start, vector_string, std::vector{}, getRandomStringInputs) + ->Range(TestNumInputs, TestNumInputs * 10); + template void bm_grow(benchmark::State& state) { for (auto _ : state) { diff --git a/libcxx/test/libcxx/thread/thread.stoptoken/atomic_unique_lock.pass.cpp b/libcxx/test/libcxx/thread/thread.stoptoken/atomic_unique_lock.pass.cpp index 44d51921ac74a..a8093ae22b38d 100644 --- a/libcxx/test/libcxx/thread/thread.stoptoken/atomic_unique_lock.pass.cpp +++ b/libcxx/test/libcxx/thread/thread.stoptoken/atomic_unique_lock.pass.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "make_test_thread.h" diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv index d70541290023b..72fccfd364932 100644 --- a/libcxx/test/libcxx/transitive_includes/cxx03.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv @@ -242,17 +242,14 @@ ccomplex utility ccomplex variant ccomplex vector ccomplex version -charconv cerrno charconv cmath charconv concepts charconv cstddef charconv cstdint charconv cstdlib charconv cstring -charconv initializer_list charconv iosfwd charconv limits -charconv new charconv type_traits charconv version chrono algorithm @@ -571,12 +568,6 @@ exception typeinfo exception version execution cstddef execution version -expected cstddef -expected cstdint -expected cstdlib -expected initializer_list -expected new -expected type_traits expected version experimental/iterator algorithm experimental/iterator atomic @@ -1340,53 +1331,6 @@ map utility map variant map vector map version -mdspan algorithm -mdspan array -mdspan atomic -mdspan bit -mdspan cctype -mdspan cerrno -mdspan cinttypes -mdspan climits -mdspan clocale -mdspan cmath -mdspan compare -mdspan concepts -mdspan cstdarg -mdspan cstddef -mdspan cstdint -mdspan cstdio -mdspan cstdlib -mdspan cstring -mdspan ctime -mdspan cwchar -mdspan cwctype -mdspan exception -mdspan functional -mdspan initializer_list -mdspan ios -mdspan iosfwd -mdspan iterator -mdspan limits -mdspan locale -mdspan memory -mdspan mutex -mdspan new -mdspan optional -mdspan ratio -mdspan span -mdspan stdexcept -mdspan streambuf -mdspan string -mdspan string_view -mdspan system_error -mdspan tuple -mdspan type_traits -mdspan typeinfo -mdspan unordered_map -mdspan utility -mdspan variant -mdspan vector mdspan version memory atomic memory cctype @@ -1416,42 +1360,15 @@ memory typeinfo memory utility memory variant memory version -memory_resource algorithm -memory_resource atomic -memory_resource bit -memory_resource cctype -memory_resource cerrno -memory_resource climits -memory_resource cmath -memory_resource compare -memory_resource concepts memory_resource cstddef memory_resource cstdint -memory_resource cstdio memory_resource cstdlib -memory_resource cstring -memory_resource ctime -memory_resource cwchar -memory_resource cwctype memory_resource exception -memory_resource initializer_list memory_resource iosfwd -memory_resource iterator -memory_resource limits -memory_resource memory -memory_resource mutex memory_resource new -memory_resource optional -memory_resource ratio memory_resource stdexcept -memory_resource string -memory_resource string_view -memory_resource system_error -memory_resource tuple memory_resource type_traits memory_resource typeinfo -memory_resource utility -memory_resource variant memory_resource version mutex algorithm mutex atomic @@ -1772,52 +1689,28 @@ random utility random variant random vector random version -ranges algorithm -ranges array -ranges atomic -ranges bit ranges cctype -ranges cerrno -ranges climits -ranges clocale ranges cmath ranges compare ranges concepts -ranges cstdarg ranges cstddef ranges cstdint ranges cstdio ranges cstdlib ranges cstring -ranges ctime ranges cwchar ranges cwctype ranges exception -ranges functional ranges initializer_list -ranges ios ranges iosfwd ranges iterator ranges limits -ranges locale -ranges memory -ranges mutex ranges new -ranges optional -ranges ratio -ranges span -ranges stdexcept -ranges streambuf -ranges string -ranges string_view -ranges system_error ranges tuple ranges type_traits ranges typeinfo -ranges unordered_map ranges utility ranges variant -ranges vector ranges version ratio climits ratio cstdint diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv index d70541290023b..72fccfd364932 100644 --- a/libcxx/test/libcxx/transitive_includes/cxx11.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv @@ -242,17 +242,14 @@ ccomplex utility ccomplex variant ccomplex vector ccomplex version -charconv cerrno charconv cmath charconv concepts charconv cstddef charconv cstdint charconv cstdlib charconv cstring -charconv initializer_list charconv iosfwd charconv limits -charconv new charconv type_traits charconv version chrono algorithm @@ -571,12 +568,6 @@ exception typeinfo exception version execution cstddef execution version -expected cstddef -expected cstdint -expected cstdlib -expected initializer_list -expected new -expected type_traits expected version experimental/iterator algorithm experimental/iterator atomic @@ -1340,53 +1331,6 @@ map utility map variant map vector map version -mdspan algorithm -mdspan array -mdspan atomic -mdspan bit -mdspan cctype -mdspan cerrno -mdspan cinttypes -mdspan climits -mdspan clocale -mdspan cmath -mdspan compare -mdspan concepts -mdspan cstdarg -mdspan cstddef -mdspan cstdint -mdspan cstdio -mdspan cstdlib -mdspan cstring -mdspan ctime -mdspan cwchar -mdspan cwctype -mdspan exception -mdspan functional -mdspan initializer_list -mdspan ios -mdspan iosfwd -mdspan iterator -mdspan limits -mdspan locale -mdspan memory -mdspan mutex -mdspan new -mdspan optional -mdspan ratio -mdspan span -mdspan stdexcept -mdspan streambuf -mdspan string -mdspan string_view -mdspan system_error -mdspan tuple -mdspan type_traits -mdspan typeinfo -mdspan unordered_map -mdspan utility -mdspan variant -mdspan vector mdspan version memory atomic memory cctype @@ -1416,42 +1360,15 @@ memory typeinfo memory utility memory variant memory version -memory_resource algorithm -memory_resource atomic -memory_resource bit -memory_resource cctype -memory_resource cerrno -memory_resource climits -memory_resource cmath -memory_resource compare -memory_resource concepts memory_resource cstddef memory_resource cstdint -memory_resource cstdio memory_resource cstdlib -memory_resource cstring -memory_resource ctime -memory_resource cwchar -memory_resource cwctype memory_resource exception -memory_resource initializer_list memory_resource iosfwd -memory_resource iterator -memory_resource limits -memory_resource memory -memory_resource mutex memory_resource new -memory_resource optional -memory_resource ratio memory_resource stdexcept -memory_resource string -memory_resource string_view -memory_resource system_error -memory_resource tuple memory_resource type_traits memory_resource typeinfo -memory_resource utility -memory_resource variant memory_resource version mutex algorithm mutex atomic @@ -1772,52 +1689,28 @@ random utility random variant random vector random version -ranges algorithm -ranges array -ranges atomic -ranges bit ranges cctype -ranges cerrno -ranges climits -ranges clocale ranges cmath ranges compare ranges concepts -ranges cstdarg ranges cstddef ranges cstdint ranges cstdio ranges cstdlib ranges cstring -ranges ctime ranges cwchar ranges cwctype ranges exception -ranges functional ranges initializer_list -ranges ios ranges iosfwd ranges iterator ranges limits -ranges locale -ranges memory -ranges mutex ranges new -ranges optional -ranges ratio -ranges span -ranges stdexcept -ranges streambuf -ranges string -ranges string_view -ranges system_error ranges tuple ranges type_traits ranges typeinfo -ranges unordered_map ranges utility ranges variant -ranges vector ranges version ratio climits ratio cstdint diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv index 90bff887eb278..fd36dace19c76 100644 --- a/libcxx/test/libcxx/transitive_includes/cxx14.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv @@ -247,17 +247,14 @@ ccomplex utility ccomplex variant ccomplex vector ccomplex version -charconv cerrno charconv cmath charconv concepts charconv cstddef charconv cstdint charconv cstdlib charconv cstring -charconv initializer_list charconv iosfwd charconv limits -charconv new charconv type_traits charconv version chrono algorithm @@ -582,12 +579,6 @@ exception typeinfo exception version execution cstddef execution version -expected cstddef -expected cstdint -expected cstdlib -expected initializer_list -expected new -expected type_traits expected version experimental/iterator algorithm experimental/iterator atomic @@ -1370,54 +1361,6 @@ map utility map variant map vector map version -mdspan algorithm -mdspan array -mdspan atomic -mdspan bit -mdspan cctype -mdspan cerrno -mdspan cinttypes -mdspan climits -mdspan clocale -mdspan cmath -mdspan compare -mdspan concepts -mdspan cstdarg -mdspan cstddef -mdspan cstdint -mdspan cstdio -mdspan cstdlib -mdspan cstring -mdspan ctime -mdspan cwchar -mdspan cwctype -mdspan exception -mdspan execution -mdspan functional -mdspan initializer_list -mdspan ios -mdspan iosfwd -mdspan iterator -mdspan limits -mdspan locale -mdspan memory -mdspan mutex -mdspan new -mdspan optional -mdspan ratio -mdspan span -mdspan stdexcept -mdspan streambuf -mdspan string -mdspan string_view -mdspan system_error -mdspan tuple -mdspan type_traits -mdspan typeinfo -mdspan unordered_map -mdspan utility -mdspan variant -mdspan vector mdspan version memory atomic memory cctype @@ -1447,43 +1390,15 @@ memory typeinfo memory utility memory variant memory version -memory_resource algorithm -memory_resource atomic -memory_resource bit -memory_resource cctype -memory_resource cerrno -memory_resource climits -memory_resource cmath -memory_resource compare -memory_resource concepts memory_resource cstddef memory_resource cstdint -memory_resource cstdio memory_resource cstdlib -memory_resource cstring -memory_resource ctime -memory_resource cwchar -memory_resource cwctype memory_resource exception -memory_resource execution -memory_resource initializer_list memory_resource iosfwd -memory_resource iterator -memory_resource limits -memory_resource memory -memory_resource mutex memory_resource new -memory_resource optional -memory_resource ratio memory_resource stdexcept -memory_resource string -memory_resource string_view -memory_resource system_error -memory_resource tuple memory_resource type_traits memory_resource typeinfo -memory_resource utility -memory_resource variant memory_resource version mutex algorithm mutex atomic @@ -1808,53 +1723,28 @@ random utility random variant random vector random version -ranges algorithm -ranges array -ranges atomic -ranges bit ranges cctype -ranges cerrno -ranges climits -ranges clocale ranges cmath ranges compare ranges concepts -ranges cstdarg ranges cstddef ranges cstdint ranges cstdio ranges cstdlib ranges cstring -ranges ctime ranges cwchar ranges cwctype ranges exception -ranges execution -ranges functional ranges initializer_list -ranges ios ranges iosfwd ranges iterator ranges limits -ranges locale -ranges memory -ranges mutex ranges new -ranges optional -ranges ratio -ranges span -ranges stdexcept -ranges streambuf -ranges string -ranges string_view -ranges system_error ranges tuple ranges type_traits ranges typeinfo -ranges unordered_map ranges utility ranges variant -ranges vector ranges version ratio climits ratio cstdint diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv index 2f908e7f78ec1..eaec25f81e582 100644 --- a/libcxx/test/libcxx/transitive_includes/cxx17.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv @@ -571,12 +571,6 @@ exception typeinfo exception version execution cstddef execution version -expected cstddef -expected cstdint -expected cstdlib -expected initializer_list -expected new -expected type_traits expected version experimental/iterator algorithm experimental/iterator atomic @@ -1364,53 +1358,6 @@ map utility map variant map vector map version -mdspan algorithm -mdspan array -mdspan atomic -mdspan bit -mdspan cctype -mdspan cerrno -mdspan cinttypes -mdspan climits -mdspan clocale -mdspan cmath -mdspan compare -mdspan concepts -mdspan cstdarg -mdspan cstddef -mdspan cstdint -mdspan cstdio -mdspan cstdlib -mdspan cstring -mdspan ctime -mdspan cwchar -mdspan cwctype -mdspan exception -mdspan functional -mdspan initializer_list -mdspan ios -mdspan iosfwd -mdspan iterator -mdspan limits -mdspan locale -mdspan memory -mdspan mutex -mdspan new -mdspan optional -mdspan ratio -mdspan span -mdspan stdexcept -mdspan streambuf -mdspan string -mdspan string_view -mdspan system_error -mdspan tuple -mdspan type_traits -mdspan typeinfo -mdspan unordered_map -mdspan utility -mdspan variant -mdspan vector mdspan version memory atomic memory cctype @@ -1796,52 +1743,28 @@ random utility random variant random vector random version -ranges algorithm -ranges array -ranges atomic -ranges bit ranges cctype -ranges cerrno -ranges climits -ranges clocale ranges cmath ranges compare ranges concepts -ranges cstdarg ranges cstddef ranges cstdint ranges cstdio ranges cstdlib ranges cstring -ranges ctime ranges cwchar ranges cwctype ranges exception -ranges functional ranges initializer_list -ranges ios ranges iosfwd ranges iterator ranges limits -ranges locale -ranges memory -ranges mutex ranges new -ranges optional -ranges ratio -ranges span -ranges stdexcept -ranges streambuf -ranges string -ranges string_view -ranges system_error ranges tuple ranges type_traits ranges typeinfo -ranges unordered_map ranges utility ranges variant -ranges vector ranges version ratio climits ratio cstdint diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv index 1a198aa4562fd..89c28e49d6c9d 100644 --- a/libcxx/test/libcxx/transitive_includes/cxx20.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv @@ -567,12 +567,6 @@ exception typeinfo exception version execution cstddef execution version -expected cstddef -expected cstdint -expected cstdlib -expected initializer_list -expected new -expected type_traits expected version experimental/iterator algorithm experimental/iterator atomic @@ -1358,53 +1352,6 @@ map utility map variant map vector map version -mdspan algorithm -mdspan array -mdspan atomic -mdspan bit -mdspan cctype -mdspan cerrno -mdspan cinttypes -mdspan climits -mdspan clocale -mdspan cmath -mdspan compare -mdspan concepts -mdspan cstdarg -mdspan cstddef -mdspan cstdint -mdspan cstdio -mdspan cstdlib -mdspan cstring -mdspan ctime -mdspan cwchar -mdspan cwctype -mdspan exception -mdspan functional -mdspan initializer_list -mdspan ios -mdspan iosfwd -mdspan iterator -mdspan limits -mdspan locale -mdspan memory -mdspan mutex -mdspan new -mdspan optional -mdspan ratio -mdspan span -mdspan stdexcept -mdspan streambuf -mdspan string -mdspan string_view -mdspan system_error -mdspan tuple -mdspan type_traits -mdspan typeinfo -mdspan unordered_map -mdspan utility -mdspan variant -mdspan vector mdspan version memory atomic memory cctype diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv index 791aad29710b5..a008b4d76edde 100644 --- a/libcxx/test/libcxx/transitive_includes/cxx23.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv @@ -445,8 +445,6 @@ functional tuple functional typeinfo functional unordered_map functional version -future array -future atomic future bitset future cctype future cerrno @@ -475,7 +473,6 @@ future stdexcept future streambuf future string future string_view -future thread future tuple future typeinfo future version diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv index 78c457a22c31d..d5321da32b3d4 100644 --- a/libcxx/test/libcxx/transitive_includes/cxx26.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv @@ -444,8 +444,6 @@ functional tuple functional typeinfo functional unordered_map functional version -future array -future atomic future bitset future cctype future cerrno @@ -474,7 +472,6 @@ future stdexcept future streambuf future string future string_view -future thread future tuple future typeinfo future version diff --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/common.h b/libcxx/test/std/containers/sequences/vector/vector.modifiers/common.h new file mode 100644 index 0000000000000..72cd47a50b2c0 --- /dev/null +++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/common.h @@ -0,0 +1,86 @@ +//===----------------------------------------------------------------------===// +// +// 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 TEST_STD_CONTAINERS_SEQUENCES_VECTOR_VECTOR_MODIFIERS_COMMON_H +#define TEST_STD_CONTAINERS_SEQUENCES_VECTOR_VECTOR_MODIFIERS_COMMON_H + +#include "test_macros.h" + +#include // for __libcpp_is_trivially_relocatable + +#ifndef TEST_HAS_NO_EXCEPTIONS +struct Throws { + Throws() : v_(0) {} + Throws(int v) : v_(v) {} + Throws(const Throws& rhs) : v_(rhs.v_) { + if (sThrows) + throw 1; + } + Throws(Throws&& rhs) : v_(rhs.v_) { + if (sThrows) + throw 1; + } + Throws& operator=(const Throws& rhs) { + v_ = rhs.v_; + return *this; + } + Throws& operator=(Throws&& rhs) { + v_ = rhs.v_; + return *this; + } + int v_; + static bool sThrows; +}; + +bool Throws::sThrows = false; +#endif + +struct Tracker { + int copy_assignments = 0; + int move_assignments = 0; +}; + +struct TrackedAssignment { + Tracker* tracker_; + TEST_CONSTEXPR_CXX14 explicit TrackedAssignment(Tracker* tracker) : tracker_(tracker) {} + + TrackedAssignment(TrackedAssignment const&) = default; + TrackedAssignment(TrackedAssignment&&) = default; + + TEST_CONSTEXPR_CXX14 TrackedAssignment& operator=(TrackedAssignment const&) { + tracker_->copy_assignments++; + return *this; + } + TEST_CONSTEXPR_CXX14 TrackedAssignment& operator=(TrackedAssignment&&) { + tracker_->move_assignments++; + return *this; + } +}; + +struct NonTriviallyRelocatable { + int value_; + TEST_CONSTEXPR NonTriviallyRelocatable() : value_(0) {} + TEST_CONSTEXPR explicit NonTriviallyRelocatable(int v) : value_(v) {} + TEST_CONSTEXPR NonTriviallyRelocatable(NonTriviallyRelocatable const& other) : value_(other.value_) {} + TEST_CONSTEXPR NonTriviallyRelocatable(NonTriviallyRelocatable&& other) : value_(other.value_) {} + TEST_CONSTEXPR_CXX14 NonTriviallyRelocatable& operator=(NonTriviallyRelocatable const& other) { + value_ = other.value_; + return *this; + } + TEST_CONSTEXPR_CXX14 NonTriviallyRelocatable& operator=(NonTriviallyRelocatable&& other) { + value_ = other.value_; + return *this; + } + + TEST_CONSTEXPR_CXX14 friend bool operator==(NonTriviallyRelocatable const& a, NonTriviallyRelocatable const& b) { + return a.value_ == b.value_; + } +}; +LIBCPP_STATIC_ASSERT(!std::__libcpp_is_trivially_relocatable::value, ""); + +#endif // TEST_STD_CONTAINERS_SEQUENCES_VECTOR_VECTOR_MODIFIERS_COMMON_H diff --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/erase_iter.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/erase_iter.pass.cpp index 549f29a8f7ba1..f0157eb74b90f 100644 --- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/erase_iter.pass.cpp +++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/erase_iter.pass.cpp @@ -11,135 +11,79 @@ // iterator erase(const_iterator position); #include -#include #include +#include #include "asan_testing.h" +#include "common.h" #include "min_allocator.h" #include "MoveOnly.h" #include "test_macros.h" -#ifndef TEST_HAS_NO_EXCEPTIONS -struct Throws { - Throws() : v_(0) {} - Throws(int v) : v_(v) {} - Throws(const Throws& rhs) : v_(rhs.v_) { - if (sThrows) - throw 1; - } - Throws(Throws&& rhs) : v_(rhs.v_) { - if (sThrows) - throw 1; - } - Throws& operator=(const Throws& rhs) { - v_ = rhs.v_; - return *this; - } - Throws& operator=(Throws&& rhs) { - v_ = rhs.v_; - return *this; - } - int v_; - static bool sThrows; -}; - -bool Throws::sThrows = false; -#endif - -TEST_CONSTEXPR_CXX20 bool tests() { - { - int a1[] = {1, 2, 3, 4, 5}; - std::vector l1(a1, a1 + 5); - l1.erase(l1.begin()); - assert(is_contiguous_container_asan_correct(l1)); - assert(l1 == std::vector(a1 + 1, a1 + 5)); - } +template