diff --git a/.ci/generate-buildkite-pipeline-premerge b/.ci/generate-buildkite-pipeline-premerge index 98a8b8fff3687a..53a43070bf1ca3 100755 --- a/.ci/generate-buildkite-pipeline-premerge +++ b/.ci/generate-buildkite-pipeline-premerge @@ -198,7 +198,7 @@ function check-targets() { echo "check-clang-tools" ;; compiler-rt) - echo "check-all" + echo "check-compiler-rt" ;; cross-project-tests) echo "check-cross-project" @@ -219,7 +219,7 @@ function check-targets() { echo "check-all" ;; libclc) - echo "check-all" + # Currently there is no testing for libclc. ;; *) echo "check-${project}" diff --git a/bolt/include/bolt/Passes/ContinuityStats.h b/bolt/include/bolt/Passes/ContinuityStats.h new file mode 100644 index 00000000000000..bd4d491ad4a55b --- /dev/null +++ b/bolt/include/bolt/Passes/ContinuityStats.h @@ -0,0 +1,61 @@ +//===- bolt/Passes/ContinuityStats.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 +// +//===----------------------------------------------------------------------===// +// +// This pass checks how well the BOLT input profile satisfies the following +// "CFG continuity" property of a perfect profile: +// +// Each positive-execution-count block in the function’s CFG +// should be *reachable* from a positive-execution-count function +// entry block through a positive-execution-count path. +// +// More specifically, for each of the hottest 1000 functions, the pass +// calculates the function’s fraction of basic block execution counts +// that is *unreachable*. It then reports the 95th percentile of the +// distribution of the 1000 unreachable fractions in a single BOLT-INFO line. +// The smaller the reported value is, the better the BOLT profile +// satisfies the CFG continuity property. + +// The default value of 1000 above can be changed via the hidden BOLT option +// `-num-functions-for-continuity-check=[N]`. +// If more detailed stats are needed, `-v=1` can be used: the hottest N +// functions will be grouped into 5 equally-sized buckets, from the hottest +// to the coldest; for each bucket, various summary statistics of the +// distribution of the unreachable fractions and the raw unreachable execution +// counts will be reported. +// +//===----------------------------------------------------------------------===// + +#ifndef BOLT_PASSES_CONTINUITYSTATS_H +#define BOLT_PASSES_CONTINUITYSTATS_H + +#include "bolt/Passes/BinaryPasses.h" +#include + +namespace llvm { + +class raw_ostream; + +namespace bolt { +class BinaryContext; + +/// Compute and report to the user the function CFG continuity quality +class PrintContinuityStats : public BinaryFunctionPass { +public: + explicit PrintContinuityStats(const cl::opt &PrintPass) + : BinaryFunctionPass(PrintPass) {} + + bool shouldOptimize(const BinaryFunction &BF) const override; + const char *getName() const override { return "continuity-stats"; } + bool shouldPrint(const BinaryFunction &) const override { return false; } + Error runOnFunctions(BinaryContext &BC) override; +}; + +} // namespace bolt +} // namespace llvm + +#endif // BOLT_PASSES_CONTINUITYSTATS_H diff --git a/bolt/lib/Passes/ADRRelaxationPass.cpp b/bolt/lib/Passes/ADRRelaxationPass.cpp index 256034a841c706..52811edcb82731 100644 --- a/bolt/lib/Passes/ADRRelaxationPass.cpp +++ b/bolt/lib/Passes/ADRRelaxationPass.cpp @@ -56,13 +56,14 @@ void ADRRelaxationPass::runOnFunction(BinaryFunction &BF) { continue; } - // Don't relax adr if it points to the same function and it is not split - // and BF initial size is < 1MB. + // Don't relax ADR if it points to the same function and is in the main + // fragment and BF initial size is < 1MB. const unsigned OneMB = 0x100000; if (BF.getSize() < OneMB) { BinaryFunction *TargetBF = BC.getFunctionForSymbol(Symbol); - if (TargetBF == &BF && !BF.isSplit()) + if (TargetBF == &BF && !BB.isSplit()) continue; + // No relaxation needed if ADR references a basic block in the same // fragment. if (BinaryBasicBlock *TargetBB = BF.getBasicBlockForLabel(Symbol)) diff --git a/bolt/lib/Passes/CMakeLists.txt b/bolt/lib/Passes/CMakeLists.txt index 407d8b03f73977..1c1273b3d2420d 100644 --- a/bolt/lib/Passes/CMakeLists.txt +++ b/bolt/lib/Passes/CMakeLists.txt @@ -26,6 +26,7 @@ add_llvm_library(LLVMBOLTPasses PatchEntries.cpp PettisAndHansen.cpp PLTCall.cpp + ContinuityStats.cpp RegAnalysis.cpp RegReAssign.cpp ReorderAlgorithm.cpp diff --git a/bolt/lib/Passes/ContinuityStats.cpp b/bolt/lib/Passes/ContinuityStats.cpp new file mode 100644 index 00000000000000..b32365b59065dc --- /dev/null +++ b/bolt/lib/Passes/ContinuityStats.cpp @@ -0,0 +1,250 @@ +//===- bolt/Passes/ContinuityStats.cpp --------------------------*- 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 the continuity stats calculation pass. +// +//===----------------------------------------------------------------------===// + +#include "bolt/Passes/ContinuityStats.h" +#include "bolt/Core/BinaryBasicBlock.h" +#include "bolt/Core/BinaryFunction.h" +#include "bolt/Utils/CommandLineOpts.h" +#include "llvm/Support/CommandLine.h" +#include +#include +#include + +#define DEBUG_TYPE "bolt-opts" + +using namespace llvm; +using namespace bolt; + +namespace opts { +extern cl::opt Verbosity; +cl::opt NumFunctionsForContinuityCheck( + "num-functions-for-continuity-check", + cl::desc("number of hottest functions to print aggregated " + "CFG discontinuity stats of."), + cl::init(1000), cl::ZeroOrMore, cl::Hidden, cl::cat(BoltOptCategory)); +} // namespace opts + +namespace { +using FunctionListType = std::vector; +using function_iterator = FunctionListType::iterator; + +template +void printDistribution(raw_ostream &OS, std::vector &values, + bool Fraction = false) { + if (values.empty()) + return; + // Sort values from largest to smallest and print the MAX, TOP 1%, 5%, 10%, + // 20%, 50%, 80%, MIN. If Fraction is true, then values are printed as + // fractions instead of integers. + std::sort(values.begin(), values.end()); + + auto printLine = [&](std::string Text, double Percent) { + int Rank = int(values.size() * (1.0 - Percent / 100)); + if (Percent == 0) + Rank = values.size() - 1; + if (Fraction) + OS << " " << Text << std::string(9 - Text.length(), ' ') << ": " + << format("%.2lf%%", values[Rank] * 100) << "\n"; + else + OS << " " << Text << std::string(9 - Text.length(), ' ') << ": " + << values[Rank] << "\n"; + }; + + printLine("MAX", 0); + const int percentages[] = {1, 5, 10, 20, 50, 80}; + for (size_t i = 0; i < sizeof(percentages) / sizeof(percentages[0]); ++i) { + printLine("TOP " + std::to_string(percentages[i]) + "%", percentages[i]); + } + printLine("MIN", 100); +} + +void printCFGContinuityStats(raw_ostream &OS, + iterator_range &Functions) { + // Given a perfect profile, every positive-execution-count BB should be + // connected to an entry of the function through a positive-execution-count + // directed path in the control flow graph. + std::vector NumUnreachables; + std::vector SumECUnreachables; + std::vector FractionECUnreachables; + + for (auto it = Functions.begin(); it != Functions.end(); ++it) { + const BinaryFunction *Function = *it; + if (Function->size() <= 1) + continue; + + // Compute the sum of all BB execution counts (ECs). + size_t NumPosECBBs = 0; + size_t SumAllBBEC = 0; + for (const BinaryBasicBlock &BB : *Function) { + const size_t BBEC = BB.getKnownExecutionCount(); + NumPosECBBs += BBEC > 0 ? 1 : 0; + SumAllBBEC += BBEC; + } + + // Perform BFS on subgraph of CFG induced by positive weight edges. + // Compute the number of BBs reachable from the entry(s) of the function and + // the sum of their execution counts (ECs). + std::unordered_map IndexToBB; + std::unordered_set Visited; + std::queue Queue; + for (const BinaryBasicBlock &BB : *Function) { + // Make sure BB.getIndex() is not already in IndexToBB. + assert(IndexToBB.find(BB.getIndex()) == IndexToBB.end()); + IndexToBB[BB.getIndex()] = &BB; + if (BB.isEntryPoint() && BB.getKnownExecutionCount() > 0) { + Queue.push(BB.getIndex()); + Visited.insert(BB.getIndex()); + } + } + while (!Queue.empty()) { + const unsigned BBIndex = Queue.front(); + const BinaryBasicBlock *BB = IndexToBB[BBIndex]; + Queue.pop(); + auto SuccBIIter = BB->branch_info_begin(); + for (const BinaryBasicBlock *Succ : BB->successors()) { + const uint64_t Count = SuccBIIter->Count; + if (Count == BinaryBasicBlock::COUNT_NO_PROFILE || Count == 0) { + ++SuccBIIter; + continue; + } + if (!Visited.insert(Succ->getIndex()).second) { + ++SuccBIIter; + continue; + } + Queue.push(Succ->getIndex()); + ++SuccBIIter; + } + } + + const size_t NumReachableBBs = Visited.size(); + + // Loop through Visited, and sum the corresponding BBs' execution counts + // (ECs). + size_t SumReachableBBEC = 0; + for (const unsigned BBIndex : Visited) { + const BinaryBasicBlock *BB = IndexToBB[BBIndex]; + SumReachableBBEC += BB->getKnownExecutionCount(); + } + + const size_t NumPosECBBsUnreachableFromEntry = + NumPosECBBs - NumReachableBBs; + const size_t SumUnreachableBBEC = SumAllBBEC - SumReachableBBEC; + const double FractionECUnreachable = + (double)SumUnreachableBBEC / SumAllBBEC; + + if (opts::Verbosity >= 2 && FractionECUnreachable >= 0.05) { + OS << "Non-trivial CFG discontinuity observed in function " + << Function->getPrintName() << "\n"; + LLVM_DEBUG(Function->dump()); + } + + NumUnreachables.push_back(NumPosECBBsUnreachableFromEntry); + SumECUnreachables.push_back(SumUnreachableBBEC); + FractionECUnreachables.push_back(FractionECUnreachable); + } + + if (FractionECUnreachables.empty()) + return; + + std::sort(FractionECUnreachables.begin(), FractionECUnreachables.end()); + const int Rank = int(FractionECUnreachables.size() * 0.95); + OS << format("top 5%% function CFG discontinuity is %.2lf%%\n", + FractionECUnreachables[Rank] * 100); + + if (opts::Verbosity >= 1) { + OS << "abbreviations: EC = execution count, POS BBs = positive EC BBs\n" + << "distribution of NUM(unreachable POS BBs) among all focal " + "functions\n"; + printDistribution(OS, NumUnreachables); + + OS << "distribution of SUM_EC(unreachable POS BBs) among all focal " + "functions\n"; + printDistribution(OS, SumECUnreachables); + + OS << "distribution of [(SUM_EC(unreachable POS BBs) / SUM_EC(all " + "POS BBs))] among all focal functions\n"; + printDistribution(OS, FractionECUnreachables, /*Fraction=*/true); + } +} + +void printAll(BinaryContext &BC, FunctionListType &ValidFunctions, + size_t NumTopFunctions) { + // Sort the list of functions by execution counts (reverse). + llvm::sort(ValidFunctions, + [&](const BinaryFunction *A, const BinaryFunction *B) { + return A->getKnownExecutionCount() > B->getKnownExecutionCount(); + }); + + const size_t RealNumTopFunctions = + std::min(NumTopFunctions, ValidFunctions.size()); + + iterator_range Functions( + ValidFunctions.begin(), ValidFunctions.begin() + RealNumTopFunctions); + + BC.outs() << format("BOLT-INFO: among the hottest %zu functions ", + RealNumTopFunctions); + printCFGContinuityStats(BC.outs(), Functions); + + // Print more detailed bucketed stats if requested. + if (opts::Verbosity >= 1 && RealNumTopFunctions >= 5) { + const size_t PerBucketSize = RealNumTopFunctions / 5; + BC.outs() << format( + "Detailed stats for 5 buckets, each with %zu functions:\n", + PerBucketSize); + + // For each bucket, print the CFG continuity stats of the functions in the + // bucket. + for (size_t BucketIndex = 0; BucketIndex < 5; ++BucketIndex) { + const size_t StartIndex = BucketIndex * PerBucketSize; + const size_t EndIndex = StartIndex + PerBucketSize; + iterator_range Functions( + ValidFunctions.begin() + StartIndex, + ValidFunctions.begin() + EndIndex); + const size_t MaxFunctionExecutionCount = + ValidFunctions[StartIndex]->getKnownExecutionCount(); + const size_t MinFunctionExecutionCount = + ValidFunctions[EndIndex - 1]->getKnownExecutionCount(); + BC.outs() << format("----------------\n| Bucket %zu: " + "|\n----------------\n", + BucketIndex + 1) + << format( + "execution counts of the %zu functions in the bucket: " + "%zu-%zu\n", + EndIndex - StartIndex, MinFunctionExecutionCount, + MaxFunctionExecutionCount); + printCFGContinuityStats(BC.outs(), Functions); + } + } +} +} // namespace + +bool PrintContinuityStats::shouldOptimize(const BinaryFunction &BF) const { + if (BF.empty() || !BF.hasValidProfile()) + return false; + + return BinaryFunctionPass::shouldOptimize(BF); +} + +Error PrintContinuityStats::runOnFunctions(BinaryContext &BC) { + // Create a list of functions with valid profiles. + FunctionListType ValidFunctions; + for (const auto &BFI : BC.getBinaryFunctions()) { + const BinaryFunction *Function = &BFI.second; + if (PrintContinuityStats::shouldOptimize(*Function)) + ValidFunctions.push_back(Function); + } + if (ValidFunctions.empty() || opts::NumFunctionsForContinuityCheck == 0) + return Error::success(); + + printAll(BC, ValidFunctions, opts::NumFunctionsForContinuityCheck); + return Error::success(); +} diff --git a/bolt/lib/Rewrite/BinaryPassManager.cpp b/bolt/lib/Rewrite/BinaryPassManager.cpp index 5dfef0b71cc79f..b0906041833484 100644 --- a/bolt/lib/Rewrite/BinaryPassManager.cpp +++ b/bolt/lib/Rewrite/BinaryPassManager.cpp @@ -12,6 +12,7 @@ #include "bolt/Passes/AllocCombiner.h" #include "bolt/Passes/AsmDump.h" #include "bolt/Passes/CMOVConversion.h" +#include "bolt/Passes/ContinuityStats.h" #include "bolt/Passes/FixRISCVCallsPass.h" #include "bolt/Passes/FixRelaxationPass.h" #include "bolt/Passes/FrameOptimizer.h" @@ -373,6 +374,8 @@ Error BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) { if (opts::PrintProfileStats) Manager.registerPass(std::make_unique(NeverPrint)); + Manager.registerPass(std::make_unique(NeverPrint)); + Manager.registerPass(std::make_unique(NeverPrint)); Manager.registerPass(std::make_unique(NeverPrint)); diff --git a/bolt/test/AArch64/adr-relaxation.s b/bolt/test/AArch64/adr-relaxation.s new file mode 100644 index 00000000000000..0aa3c71f29aaab --- /dev/null +++ b/bolt/test/AArch64/adr-relaxation.s @@ -0,0 +1,49 @@ +## Check that llvm-bolt will not unnecessarily relax ADR instruction. + +# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %s -o %t.o +# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q -static +# RUN: llvm-bolt %t.exe -o %t.bolt --split-functions --split-strategy=random2 +# RUN: llvm-objdump -d --disassemble-symbols=_start %t.bolt | FileCheck %s +# RUN: llvm-objdump -d --disassemble-symbols=foo.cold.0 %t.bolt \ +# RUN: | FileCheck --check-prefix=CHECK-FOO %s + +## ADR below references its containing function that is split. But ADR is always +## in the main fragment, thus there is no need to relax it. + .text + .globl _start + .type _start, %function +_start: +# CHECK: <_start>: + .cfi_startproc + adr x1, _start +# CHECK-NOT: adrp +# CHECK: adr + cmp x1, x11 + b.hi .L1 + mov x0, #0x0 +.L1: + ret x30 + .cfi_endproc +.size _start, .-_start + + +## In foo, ADR is in the split fragment but references the main one. Thus, it +## needs to be relaxed into ADRP + ADD. + .globl foo + .type foo, %function +foo: + .cfi_startproc + cmp x1, x11 + b.hi .L2 + mov x0, #0x0 +.L2: +# CHECK-FOO: : + adr x1, foo +# CHECK-FOO: adrp +# CHECK-FOO-NEXT: add + ret x30 + .cfi_endproc +.size foo, .-foo + +## Force relocation mode. + .reloc 0, R_AARCH64_NONE diff --git a/bolt/test/X86/cfg-discontinuity-reporting.test b/bolt/test/X86/cfg-discontinuity-reporting.test new file mode 100644 index 00000000000000..4d7d3305cdb751 --- /dev/null +++ b/bolt/test/X86/cfg-discontinuity-reporting.test @@ -0,0 +1,4 @@ +## Check profile discontinuity reporting +RUN: yaml2obj %p/Inputs/blarge_new.yaml &> %t.exe +RUN: llvm-bolt %t.exe -o %t.out --pa -p %p/Inputs/blarge_new.preagg.txt | FileCheck %s +CHECK: among the hottest 5 functions top 5% function CFG discontinuity is 100.00% diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp index 9c3c7cc70c187b..225e867c9b24f7 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp @@ -474,10 +474,8 @@ void ProTypeMemberInitCheck::checkMissingMemberInitializer( // It only includes fields that have not been fixed SmallPtrSet AllFieldsToInit; forEachField(ClassDecl, FieldsToInit, [&](const FieldDecl *F) { - if (!HasRecordClassMemberSet.contains(F)) { + if (HasRecordClassMemberSet.insert(F).second) AllFieldsToInit.insert(F); - HasRecordClassMemberSet.insert(F); - } }); if (FieldsToInit.empty()) return; diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index c1122e1180ab91..a4bb303a2bc42b 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -174,10 +174,6 @@ C++23 Feature Support C++20 Feature Support ^^^^^^^^^^^^^^^^^^^^^ -C++17 Feature Support -^^^^^^^^^^^^^^^^^^^^^ -- The implementation of the relaxed template template argument matching rules is - more complete and reliable, and should provide more accurate diagnostics. Resolutions to C++ Defect Reports ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -335,10 +331,6 @@ Improvements to Clang's diagnostics - Clang now diagnoses when the result of a [[nodiscard]] function is discarded after being cast in C. Fixes #GH104391. -- Clang now properly explains the reason a template template argument failed to - match a template template parameter, in terms of the C++17 relaxed matching rules - instead of the old ones. - - Don't emit duplicated dangling diagnostics. (#GH93386). - Improved diagnostic when trying to befriend a concept. (#GH45182). @@ -380,6 +372,10 @@ Improvements to Clang's diagnostics - Clang now omits warnings for extra parentheses in fold expressions with single expansion (#GH101863). +- The warning for an unsupported type for a named register variable is now phrased ``unsupported type for named register variable``, + instead of ``bad type for named register variable``. This makes it clear that the type is not supported at all, rather than being + suboptimal in some way the error fails to mention (#GH111550). + Improvements to Clang's time-trace ---------------------------------- @@ -444,8 +440,6 @@ Bug Fixes to C++ Support - Correctly check constraints of explicit instantiations of member functions. (#GH46029) - When performing partial ordering of function templates, clang now checks that the deduction was consistent. Fixes (#GH18291). -- Fixes to several issues in partial ordering of template template parameters, which - were documented in the test suite. - Fixed an assertion failure about a constraint of a friend function template references to a value with greater template depth than the friend function template. (#GH98258) - Clang now rebuilds the template parameters of out-of-line declarations and specializations in the context @@ -474,6 +468,7 @@ Bug Fixes to C++ Support - Fixed an assertion failure in debug mode, and potential crashes in release mode, when diagnosing a failed cast caused indirectly by a failed implicit conversion to the type of the constructor parameter. - Fixed an assertion failure by adjusting integral to boolean vector conversions (#GH108326) +- Clang is now better at keeping track of friend function template instance contexts. (#GH55509) - Fixed an issue deducing non-type template arguments of reference type. (#GH73460) - Fixed an issue in constraint evaluation, where type constraints on the lambda expression containing outer unexpanded parameters were not correctly expanded. (#GH101754) @@ -486,6 +481,7 @@ Bug Fixes to C++ Support - Clang now uses the correct set of template argument lists when comparing the constraints of out-of-line definitions and member templates explicitly specialized for a given implicit instantiation of a class template. (#GH102320) +- Fix a crash when parsing a pseudo destructor involving an invalid type. (#GH111460) Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 7ff35d73df5997..6afc86710a8137 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -2299,6 +2299,13 @@ class FunctionDecl : public DeclaratorDecl, FunctionDeclBits.IsLateTemplateParsed = ILT; } + bool isInstantiatedFromMemberTemplate() const { + return FunctionDeclBits.IsInstantiatedFromMemberTemplate; + } + void setInstantiatedFromMemberTemplate(bool Val = true) { + FunctionDeclBits.IsInstantiatedFromMemberTemplate = Val; + } + /// Whether this function is "trivial" in some specialized C++ senses. /// Can only be true for default constructors, copy constructors, /// copy assignment operators, and destructors. Not meaningful until diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index ee662ed73d7e0e..eb67dc03157e64 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -1763,6 +1763,8 @@ class DeclContext { uint64_t HasImplicitReturnZero : 1; LLVM_PREFERRED_TYPE(bool) uint64_t IsLateTemplateParsed : 1; + LLVM_PREFERRED_TYPE(bool) + uint64_t IsInstantiatedFromMemberTemplate : 1; /// Kind of contexpr specifier as defined by ConstexprSpecKind. LLVM_PREFERRED_TYPE(ConstexprSpecKind) @@ -1813,7 +1815,7 @@ class DeclContext { }; /// Number of inherited and non-inherited bits in FunctionDeclBitfields. - enum { NumFunctionDeclBits = NumDeclContextBits + 31 }; + enum { NumFunctionDeclBits = NumDeclContextBits + 32 }; /// Stores the bits used by CXXConstructorDecl. If modified /// NumCXXConstructorDeclBits and the accessor @@ -1824,12 +1826,12 @@ class DeclContext { LLVM_PREFERRED_TYPE(FunctionDeclBitfields) uint64_t : NumFunctionDeclBits; - /// 20 bits to fit in the remaining available space. + /// 19 bits to fit in the remaining available space. /// Note that this makes CXXConstructorDeclBitfields take /// exactly 64 bits and thus the width of NumCtorInitializers /// will need to be shrunk if some bit is added to NumDeclContextBitfields, /// NumFunctionDeclBitfields or CXXConstructorDeclBitfields. - uint64_t NumCtorInitializers : 17; + uint64_t NumCtorInitializers : 16; LLVM_PREFERRED_TYPE(bool) uint64_t IsInheritingConstructor : 1; @@ -1843,7 +1845,7 @@ class DeclContext { }; /// Number of inherited and non-inherited bits in CXXConstructorDeclBitfields. - enum { NumCXXConstructorDeclBits = NumFunctionDeclBits + 20 }; + enum { NumCXXConstructorDeclBits = NumFunctionDeclBits + 19 }; /// Stores the bits used by ObjCMethodDecl. /// If modified NumObjCMethodDeclBits and the accessor diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 05739f39d2a496..2fb49ec1aea0d0 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -1008,6 +1008,15 @@ class FunctionTemplateDecl : public RedeclarableTemplateDecl { return getTemplatedDecl()->isThisDeclarationADefinition(); } + bool isCompatibleWithDefinition() const { + return getTemplatedDecl()->isInstantiatedFromMemberTemplate() || + isThisDeclarationADefinition(); + } + void setInstantiatedFromMemberTemplate(FunctionTemplateDecl *D) { + getTemplatedDecl()->setInstantiatedFromMemberTemplate(); + RedeclarableTemplateDecl::setInstantiatedFromMemberTemplate(D); + } + /// Return the specialization with the provided arguments if it exists, /// otherwise return the insertion point. FunctionDecl *findSpecialization(ArrayRef Args, diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h index 3a1d6852d2a708..2e48c1c3c72c8e 100644 --- a/clang/include/clang/AST/OpenMPClause.h +++ b/clang/include/clang/AST/OpenMPClause.h @@ -930,6 +930,105 @@ class OMPSizesClause final } }; +/// This class represents the 'permutation' clause in the +/// '#pragma omp interchange' directive. +/// +/// \code{.c} +/// #pragma omp interchange permutation(2,1) +/// for (int i = 0; i < 64; ++i) +/// for (int j = 0; j < 64; ++j) +/// \endcode +class OMPPermutationClause final + : public OMPClause, + private llvm::TrailingObjects { + friend class OMPClauseReader; + friend class llvm::TrailingObjects; + + /// Location of '('. + SourceLocation LParenLoc; + + /// Number of arguments in the clause, and hence also the number of loops to + /// be permuted. + unsigned NumLoops; + + /// Sets the permutation index expressions. + void setArgRefs(ArrayRef VL) { + assert(VL.size() == NumLoops && "Expecting one expression per loop"); + llvm::copy(VL, static_cast(this) + ->template getTrailingObjects()); + } + + /// Build an empty clause. + explicit OMPPermutationClause(int NumLoops) + : OMPClause(llvm::omp::OMPC_permutation, SourceLocation(), + SourceLocation()), + NumLoops(NumLoops) {} + +public: + /// Build a 'permutation' clause AST node. + /// + /// \param C Context of the AST. + /// \param StartLoc Location of the 'permutation' identifier. + /// \param LParenLoc Location of '('. + /// \param EndLoc Location of ')'. + /// \param Args Content of the clause. + static OMPPermutationClause * + Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation LParenLoc, + SourceLocation EndLoc, ArrayRef Args); + + /// Build an empty 'permutation' AST node for deserialization. + /// + /// \param C Context of the AST. + /// \param NumLoops Number of arguments in the clause. + static OMPPermutationClause *CreateEmpty(const ASTContext &C, + unsigned NumLoops); + + /// Sets the location of '('. + void setLParenLoc(SourceLocation Loc) { LParenLoc = Loc; } + + /// Returns the location of '('. + SourceLocation getLParenLoc() const { return LParenLoc; } + + /// Returns the number of list items. + unsigned getNumLoops() const { return NumLoops; } + + /// Returns the permutation index expressions. + ///@{ + MutableArrayRef getArgsRefs() { + return MutableArrayRef(static_cast(this) + ->template getTrailingObjects(), + NumLoops); + } + ArrayRef getArgsRefs() const { + return ArrayRef(static_cast(this) + ->template getTrailingObjects(), + NumLoops); + } + ///@} + + child_range children() { + MutableArrayRef Args = getArgsRefs(); + return child_range(reinterpret_cast(Args.begin()), + reinterpret_cast(Args.end())); + } + const_child_range children() const { + ArrayRef Args = getArgsRefs(); + return const_child_range(reinterpret_cast(Args.begin()), + reinterpret_cast(Args.end())); + } + + child_range used_children() { + return child_range(child_iterator(), child_iterator()); + } + const_child_range used_children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } + + static bool classof(const OMPClause *T) { + return T->getClauseKind() == llvm::omp::OMPC_permutation; + } +}; + /// Representation of the 'full' clause of the '#pragma omp unroll' directive. /// /// \code diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index cbbba9e88b7f5b..b2dd51319ba9ea 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -3348,6 +3348,14 @@ bool RecursiveASTVisitor::VisitOMPSizesClause(OMPSizesClause *C) { return true; } +template +bool RecursiveASTVisitor::VisitOMPPermutationClause( + OMPPermutationClause *C) { + for (Expr *E : C->getArgsRefs()) + TRY_TO(TraverseStmt(E)); + return true; +} + template bool RecursiveASTVisitor::VisitOMPFullClause(OMPFullClause *C) { return true; diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td index a6b17ccb6799d2..1ed379c76c8ea2 100644 --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -335,7 +335,7 @@ def warn_atomic_op_misaligned : Warning< def warn_atomic_op_oversized : Warning< "large atomic operation may incur " "significant performance penalty" - "; the access size (%0 bytes) exceeds the max lock-free size (%1 bytes)">, + "; the access size (%0 bytes) exceeds the max lock-free size (%1 bytes)">, InGroup; def warn_sync_op_misaligned : Warning< diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 583475327c5227..777ea1f37cea46 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5262,13 +5262,6 @@ def note_template_arg_refers_here_func : Note< def err_template_arg_template_params_mismatch : Error< "template template argument has different template parameters than its " "corresponding template template parameter">; -def note_template_arg_template_params_mismatch : Note< - "template template argument has different template parameters than its " - "corresponding template template parameter">; -def err_non_deduced_mismatch : Error< - "could not match %diff{$ against $|types}0,1">; -def err_inconsistent_deduction : Error< - "conflicting deduction %diff{$ against $|types}0,1 for parameter">; def err_template_arg_not_integral_or_enumeral : Error< "non-type template argument of type %0 must have an integral or enumeration" " type">; @@ -9386,7 +9379,8 @@ let CategoryName = "Inline Assembly Issue" in { "global register variables on this target">; def err_asm_register_size_mismatch : Error<"size of register '%0' does not " "match variable size">; - def err_asm_bad_register_type : Error<"bad type for named register variable">; + def err_asm_unsupported_register_type : Error< + "unsupported type for named register variable">; def err_asm_invalid_input_size : Error< "invalid input size for constraint '%0'">; def err_asm_invalid_output_size : Error< @@ -11708,6 +11702,10 @@ def err_omp_dispatch_statement_call " to a target function or an assignment to one">; def err_omp_unroll_full_variable_trip_count : Error< "loop to be fully unrolled must have a constant trip count">; +def err_omp_interchange_permutation_value_range : Error< + "permutation index must be at least 1 and at most %0">; +def err_omp_interchange_permutation_value_repeated : Error< + "index %0 must appear exactly once in the permutation clause">; def note_omp_directive_here : Note<"'%0' directive found here">; def err_omp_instantiation_not_supported : Error<"instantiation of '%0' not supported yet">; diff --git a/clang/include/clang/CIR/CIRGenerator.h b/clang/include/clang/CIR/CIRGenerator.h new file mode 100644 index 00000000000000..9a8930ac46ea9c --- /dev/null +++ b/clang/include/clang/CIR/CIRGenerator.h @@ -0,0 +1,60 @@ +//===- CIRGenerator.h - CIR Generation from Clang AST ---------------------===// +// +// 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 simple interface to perform CIR generation from Clang +// AST +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CIR_CIRGENERATOR_H +#define LLVM_CLANG_CIR_CIRGENERATOR_H + +#include "clang/AST/ASTConsumer.h" +#include "clang/Basic/CodeGenOptions.h" + +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/Support/VirtualFileSystem.h" + +#include + +namespace clang { +class DeclGroupRef; +class DiagnosticsEngine; +} // namespace clang + +namespace mlir { +class MLIRContext; +} // namespace mlir +namespace cir { +class CIRGenModule; + +class CIRGenerator : public clang::ASTConsumer { + virtual void anchor(); + clang::DiagnosticsEngine &diags; + clang::ASTContext *astCtx; + // Only used for debug info. + llvm::IntrusiveRefCntPtr fs; + + const clang::CodeGenOptions &codeGenOpts; + +protected: + std::unique_ptr mlirCtx; + std::unique_ptr cgm; + +public: + CIRGenerator(clang::DiagnosticsEngine &diags, + llvm::IntrusiveRefCntPtr fs, + const clang::CodeGenOptions &cgo); + ~CIRGenerator() override; + void Initialize(clang::ASTContext &astCtx) override; + bool HandleTopLevelDecl(clang::DeclGroupRef group) override; +}; + +} // namespace cir + +#endif // LLVM_CLANG_CIR_CIRGENERATOR_H diff --git a/clang/include/clang/CIR/FrontendAction/CIRGenAction.h b/clang/include/clang/CIR/FrontendAction/CIRGenAction.h new file mode 100644 index 00000000000000..2ab612613b73da --- /dev/null +++ b/clang/include/clang/CIR/FrontendAction/CIRGenAction.h @@ -0,0 +1,60 @@ +//===---- CIRGenAction.h - CIR Code Generation Frontend Action -*- C++ -*--===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CIR_CIRGENACTION_H +#define LLVM_CLANG_CIR_CIRGENACTION_H + +#include "clang/Frontend/FrontendAction.h" + +#include "mlir/IR/BuiltinOps.h" +#include "mlir/IR/OwningOpRef.h" + +namespace mlir { +class MLIRContext; +class ModuleOp; +} // namespace mlir + +namespace cir { +class CIRGenConsumer; + +class CIRGenAction : public clang::ASTFrontendAction { +public: + enum class OutputType { + EmitCIR, + }; + +private: + friend class CIRGenConsumer; + + mlir::OwningOpRef MLIRMod; + + mlir::MLIRContext *MLIRCtx; + +protected: + CIRGenAction(OutputType Action, mlir::MLIRContext *MLIRCtx = nullptr); + + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &CI, + llvm::StringRef InFile) override; + +public: + ~CIRGenAction() override; + + OutputType Action; +}; + +class EmitCIRAction : public CIRGenAction { + virtual void anchor(); + +public: + EmitCIRAction(mlir::MLIRContext *MLIRCtx = nullptr); +}; + +} // namespace cir + +#endif diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 90f0c4f2df2130..9adc0b15f2ea82 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2996,7 +2996,7 @@ defm clangir : BoolFOption<"clangir", PosFlag, NegFlag LLVM pipeline to compile">, BothFlags<[], [ClangOption, CC1Option], "">>; -def emit_cir : Flag<["-"], "emit-cir">, Visibility<[CC1Option]>, +def emit_cir : Flag<["-"], "emit-cir">, Visibility<[ClangOption, CC1Option]>, Group, HelpText<"Build ASTs and then lower to ClangIR">; /// ClangIR-specific options - END diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 93e49d395388a6..dbcb545058a026 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -3595,6 +3595,9 @@ class Parser : public CodeCompletionHandler { /// Parses the 'sizes' clause of a '#pragma omp tile' directive. OMPClause *ParseOpenMPSizesClause(); + /// Parses the 'permutation' clause of a '#pragma omp interchange' directive. + OMPClause *ParseOpenMPPermutationClause(); + /// Parses clause without any additional arguments. /// /// \param Kind Kind of current clause. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 7ff9c2754a6fe0..86053bd7da1725 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -12417,9 +12417,8 @@ class Sema final : public SemaBase { sema::TemplateDeductionInfo &Info); bool isTemplateTemplateParameterAtLeastAsSpecializedAs( - TemplateParameterList *PParam, TemplateDecl *PArg, TemplateDecl *AArg, - const DefaultArguments &DefaultArgs, SourceLocation ArgLoc, - bool IsDeduced); + TemplateParameterList *PParam, TemplateDecl *AArg, + const DefaultArguments &DefaultArgs, SourceLocation Loc, bool IsDeduced); /// Mark which template parameters are used in a given expression. /// @@ -12728,9 +12727,6 @@ class Sema final : public SemaBase { /// We are instantiating a type alias template declaration. TypeAliasTemplateInstantiation, - - /// We are performing partial ordering for template template parameters. - PartialOrderingTTP, } Kind; /// Was the enclosing context a non-instantiation SFINAE context? @@ -12952,12 +12948,6 @@ class Sema final : public SemaBase { TemplateDecl *Entity, BuildingDeductionGuidesTag, SourceRange InstantiationRange = SourceRange()); - struct PartialOrderingTTP {}; - /// \brief Note that we are partial ordering template template parameters. - InstantiatingTemplate(Sema &SemaRef, SourceLocation ArgLoc, - PartialOrderingTTP, TemplateDecl *PArg, - SourceRange InstantiationRange = SourceRange()); - /// Note that we have finished instantiating this template. void Clear(); @@ -13027,6 +13017,12 @@ class Sema final : public SemaBase { std::optional> Innermost = std::nullopt, bool RelativeToPrimary = false, bool ForConstraintInstantiation = false); + void getTemplateInstantiationArgs( + MultiLevelTemplateArgumentList &Result, const NamedDecl *D, + const DeclContext *DC = nullptr, bool Final = false, + std::optional> Innermost = std::nullopt, + bool RelativeToPrimary = false, bool ForConstraintInstantiation = false); + /// RAII object to handle the state changes required to synthesize /// a function body. class SynthesizedFunctionScope { diff --git a/clang/include/clang/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h index 53191e7bb4272b..80ad30b0f99efc 100644 --- a/clang/include/clang/Sema/SemaOpenMP.h +++ b/clang/include/clang/Sema/SemaOpenMP.h @@ -891,6 +891,11 @@ class SemaOpenMP : public SemaBase { SourceLocation StartLoc, SourceLocation LParenLoc, SourceLocation EndLoc); + /// Called on well-form 'permutation' clause after parsing its arguments. + OMPClause *ActOnOpenMPPermutationClause(ArrayRef PermExprs, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc); /// Called on well-form 'full' clauses. OMPClause *ActOnOpenMPFullClause(SourceLocation StartLoc, SourceLocation EndLoc); diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index e1326bba37269f..f270de1054c61b 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -3586,8 +3586,9 @@ bool Compiler::VisitShuffleVectorExpr(const ShuffleVectorExpr *E) { } for (unsigned I = 0; I != NumOutputElems; ++I) { APSInt ShuffleIndex = E->getShuffleMaskIdx(Ctx.getASTContext(), I); + assert(ShuffleIndex >= -1); if (ShuffleIndex == -1) - return this->emitInvalid(E); // FIXME: Better diagnostic. + return this->emitInvalidShuffleVectorIndex(I, E); assert(ShuffleIndex < (NumInputElems * 2)); if (!this->emitGetLocal(PT_Ptr, diff --git a/clang/lib/AST/ByteCode/EvaluationResult.cpp b/clang/lib/AST/ByteCode/EvaluationResult.cpp index 627d4b2f65be9d..c0d116cdf26c48 100644 --- a/clang/lib/AST/ByteCode/EvaluationResult.cpp +++ b/clang/lib/AST/ByteCode/EvaluationResult.cpp @@ -130,8 +130,9 @@ static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc, const Descriptor *Desc = BasePtr.getDeclDesc(); if (const auto *CD = dyn_cast_if_present(R->getDecl())) { const auto &BS = *std::next(CD->bases_begin(), I); - S.FFDiag(BS.getBaseTypeLoc(), diag::note_constexpr_uninitialized_base) - << B.Desc->getType() << BS.getSourceRange(); + SourceLocation TypeBeginLoc = BS.getBaseTypeLoc(); + S.FFDiag(TypeBeginLoc, diag::note_constexpr_uninitialized_base) + << B.Desc->getType() << SourceRange(TypeBeginLoc, BS.getEndLoc()); } else { S.FFDiag(Desc->getLocation(), diag::note_constexpr_uninitialized_base) << B.Desc->getType(); diff --git a/clang/lib/AST/ByteCode/Function.h b/clang/lib/AST/ByteCode/Function.h index 640bfa65644f0f..7fe9aeb1101204 100644 --- a/clang/lib/AST/ByteCode/Function.h +++ b/clang/lib/AST/ByteCode/Function.h @@ -222,6 +222,10 @@ class Function final { return ParamOffsets[ParamIndex]; } + PrimType getParamType(unsigned ParamIndex) const { + return ParamTypes[ParamIndex]; + } + private: /// Construct a function representing an actual function. Function(Program &P, FunctionDeclTy Source, unsigned ArgSize, diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index b3ba81f04ff1ff..82e11743cc5296 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -21,6 +21,8 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" +#include "clang/Basic/DiagnosticSema.h" +#include "clang/Basic/TargetInfo.h" #include "llvm/ADT/APSInt.h" #include "llvm/ADT/StringExtras.h" #include @@ -1406,6 +1408,54 @@ bool handleFixedPointOverflow(InterpState &S, CodePtr OpPC, return S.noteUndefinedBehavior(); } +bool InvalidShuffleVectorIndex(InterpState &S, CodePtr OpPC, uint32_t Index) { + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.FFDiag(Loc, + diag::err_shufflevector_minus_one_is_undefined_behavior_constexpr) + << Index; + return false; +} + +bool CheckPointerToIntegralCast(InterpState &S, CodePtr OpPC, + const Pointer &Ptr, unsigned BitWidth) { + if (Ptr.isDummy()) + return false; + + const SourceInfo &E = S.Current->getSource(OpPC); + S.CCEDiag(E, diag::note_constexpr_invalid_cast) + << 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC); + + if (Ptr.isBlockPointer() && !Ptr.isZero()) { + // Only allow based lvalue casts if they are lossless. + if (S.getASTContext().getTargetInfo().getPointerWidth(LangAS::Default) != + BitWidth) + return Invalid(S, OpPC); + } + return true; +} + +bool CastPointerIntegralAP(InterpState &S, CodePtr OpPC, uint32_t BitWidth) { + const Pointer &Ptr = S.Stk.pop(); + + if (!CheckPointerToIntegralCast(S, OpPC, Ptr, BitWidth)) + return false; + + S.Stk.push>( + IntegralAP::from(Ptr.getIntegerRepresentation(), BitWidth)); + return true; +} + +bool CastPointerIntegralAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth) { + const Pointer &Ptr = S.Stk.pop(); + + if (!CheckPointerToIntegralCast(S, OpPC, Ptr, BitWidth)) + return false; + + S.Stk.push>( + IntegralAP::from(Ptr.getIntegerRepresentation(), BitWidth)); + return true; +} + // https://github.com/llvm/llvm-project/issues/102513 #if defined(_WIN32) && !defined(__clang__) && !defined(NDEBUG) #pragma optimize("", off) diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index 5c3ee5e689f1c3..41708910024476 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -161,6 +161,7 @@ bool CallBI(InterpState &S, CodePtr OpPC, const Function *Func, bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize, const CallExpr *CE); bool CheckLiteralType(InterpState &S, CodePtr OpPC, const Type *T); +bool InvalidShuffleVectorIndex(InterpState &S, CodePtr OpPC, uint32_t Index); template static bool handleOverflow(InterpState &S, CodePtr OpPC, const T &SrcValue) { @@ -2288,53 +2289,22 @@ static inline bool CastFloatingIntegralAPS(InterpState &S, CodePtr OpPC, return CheckFloatResult(S, OpPC, F, Status, FPO); } +bool CheckPointerToIntegralCast(InterpState &S, CodePtr OpPC, + const Pointer &Ptr, unsigned BitWidth); +bool CastPointerIntegralAP(InterpState &S, CodePtr OpPC, uint32_t BitWidth); +bool CastPointerIntegralAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth); + template ::T> bool CastPointerIntegral(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.pop(); - if (Ptr.isDummy()) + if (!CheckPointerToIntegralCast(S, OpPC, Ptr, T::bitWidth())) return false; - const SourceInfo &E = S.Current->getSource(OpPC); - S.CCEDiag(E, diag::note_constexpr_invalid_cast) - << 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC); - S.Stk.push(T::from(Ptr.getIntegerRepresentation())); return true; } -static inline bool CastPointerIntegralAP(InterpState &S, CodePtr OpPC, - uint32_t BitWidth) { - const Pointer &Ptr = S.Stk.pop(); - - if (Ptr.isDummy()) - return false; - - const SourceInfo &E = S.Current->getSource(OpPC); - S.CCEDiag(E, diag::note_constexpr_invalid_cast) - << 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC); - - S.Stk.push>( - IntegralAP::from(Ptr.getIntegerRepresentation(), BitWidth)); - return true; -} - -static inline bool CastPointerIntegralAPS(InterpState &S, CodePtr OpPC, - uint32_t BitWidth) { - const Pointer &Ptr = S.Stk.pop(); - - if (Ptr.isDummy()) - return false; - - const SourceInfo &E = S.Current->getSource(OpPC); - S.CCEDiag(E, diag::note_constexpr_invalid_cast) - << 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC); - - S.Stk.push>( - IntegralAP::from(Ptr.getIntegerRepresentation(), BitWidth)); - return true; -} - template ::T> static inline bool CastIntegralFixedPoint(InterpState &S, CodePtr OpPC, uint32_t FPS) { diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index 98381254886e29..7d811b7baea7ce 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -38,6 +38,15 @@ static T getParam(const InterpFrame *Frame, unsigned Index) { return Frame->getParam(Offset); } +// static APSInt getAPSIntParam(InterpStack &Stk, size_t Offset = 0) { +static APSInt getAPSIntParam(const InterpFrame *Frame, unsigned Index) { + APSInt R; + unsigned Offset = Frame->getFunction()->getParamOffset(Index); + INT_TYPE_SWITCH(Frame->getFunction()->getParamType(Index), + R = Frame->getParam(Offset).toAPSInt()); + return R; +} + PrimType getIntPrimType(const InterpState &S) { const TargetInfo &TI = S.getASTContext().getTargetInfo(); unsigned IntWidth = TI.getIntWidth(); @@ -1273,6 +1282,39 @@ static bool interp__builtin_ia32_pext(InterpState &S, CodePtr OpPC, return true; } +static bool interp__builtin_ia32_addcarry_subborrow(InterpState &S, + CodePtr OpPC, + const InterpFrame *Frame, + const Function *Func, + const CallExpr *Call) { + unsigned BuiltinOp = Func->getBuiltinID(); + APSInt CarryIn = getAPSIntParam(Frame, 0); + APSInt LHS = getAPSIntParam(Frame, 1); + APSInt RHS = getAPSIntParam(Frame, 2); + + bool IsAdd = BuiltinOp == clang::X86::BI__builtin_ia32_addcarryx_u32 || + BuiltinOp == clang::X86::BI__builtin_ia32_addcarryx_u64; + + unsigned BitWidth = LHS.getBitWidth(); + unsigned CarryInBit = CarryIn.ugt(0) ? 1 : 0; + APInt ExResult = + IsAdd ? (LHS.zext(BitWidth + 1) + (RHS.zext(BitWidth + 1) + CarryInBit)) + : (LHS.zext(BitWidth + 1) - (RHS.zext(BitWidth + 1) + CarryInBit)); + + APInt Result = ExResult.extractBits(BitWidth, 0); + APSInt CarryOut = + APSInt(ExResult.extractBits(1, BitWidth), /*IsUnsigned=*/true); + + Pointer &CarryOutPtr = S.Stk.peek(); + QualType CarryOutType = Call->getArg(3)->getType()->getPointeeType(); + PrimType CarryOutT = *S.getContext().classify(CarryOutType); + assignInteger(CarryOutPtr, CarryOutT, APSInt(Result, true)); + + pushInteger(S, CarryOut, Call->getType()); + + return true; +} + static bool interp__builtin_os_log_format_buffer_size(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, @@ -1898,6 +1940,14 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F, return false; break; + case clang::X86::BI__builtin_ia32_addcarryx_u32: + case clang::X86::BI__builtin_ia32_addcarryx_u64: + case clang::X86::BI__builtin_ia32_subborrow_u32: + case clang::X86::BI__builtin_ia32_subborrow_u64: + if (!interp__builtin_ia32_addcarry_subborrow(S, OpPC, Frame, F, Call)) + return false; + break; + case Builtin::BI__builtin_os_log_format_buffer_size: if (!interp__builtin_os_log_format_buffer_size(S, OpPC, Frame, F, Call)) return false; diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td index 61b6f2e8daa2fb..7b65138e5a3c94 100644 --- a/clang/lib/AST/ByteCode/Opcodes.td +++ b/clang/lib/AST/ByteCode/Opcodes.td @@ -773,6 +773,9 @@ def InvalidDeclRef : Opcode { } def SizelessVectorElementSize : Opcode; +def InvalidShuffleVectorIndex : Opcode { + let Args = [ArgUint32]; +} def Assume : Opcode; diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 58d11a0312c505..8f54b5f1589d4f 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -3067,6 +3067,7 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC, FunctionDeclBits.IsIneligibleOrNotSelected = false; FunctionDeclBits.HasImplicitReturnZero = false; FunctionDeclBits.IsLateTemplateParsed = false; + FunctionDeclBits.IsInstantiatedFromMemberTemplate = false; FunctionDeclBits.ConstexprKind = static_cast(ConstexprKind); FunctionDeclBits.BodyContainsImmediateEscalatingExpression = false; FunctionDeclBits.InstantiationIsPending = false; diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp index eb15aa84406901..985c844362d951 100644 --- a/clang/lib/AST/OpenMPClause.cpp +++ b/clang/lib/AST/OpenMPClause.cpp @@ -971,6 +971,25 @@ OMPSizesClause *OMPSizesClause::CreateEmpty(const ASTContext &C, return new (Mem) OMPSizesClause(NumSizes); } +OMPPermutationClause *OMPPermutationClause::Create(const ASTContext &C, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc, + ArrayRef Args) { + OMPPermutationClause *Clause = CreateEmpty(C, Args.size()); + Clause->setLocStart(StartLoc); + Clause->setLParenLoc(LParenLoc); + Clause->setLocEnd(EndLoc); + Clause->setArgRefs(Args); + return Clause; +} + +OMPPermutationClause *OMPPermutationClause::CreateEmpty(const ASTContext &C, + unsigned NumLoops) { + void *Mem = C.Allocate(totalSizeToAlloc(NumLoops)); + return new (Mem) OMPPermutationClause(NumLoops); +} + OMPFullClause *OMPFullClause::Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc) { @@ -1841,6 +1860,14 @@ void OMPClausePrinter::VisitOMPSizesClause(OMPSizesClause *Node) { OS << ")"; } +void OMPClausePrinter::VisitOMPPermutationClause(OMPPermutationClause *Node) { + OS << "permutation("; + llvm::interleaveComma(Node->getArgsRefs(), OS, [&](const Expr *E) { + E->printPretty(OS, nullptr, Policy, 0); + }); + OS << ")"; +} + void OMPClausePrinter::VisitOMPFullClause(OMPFullClause *Node) { OS << "full"; } void OMPClausePrinter::VisitOMPPartialClause(OMPPartialClause *Node) { diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 299ac005c7fdb9..4d177fd6c5968c 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -493,6 +493,13 @@ void OMPClauseProfiler::VisitOMPSizesClause(const OMPSizesClause *C) { Profiler->VisitExpr(E); } +void OMPClauseProfiler::VisitOMPPermutationClause( + const OMPPermutationClause *C) { + for (Expr *E : C->getArgsRefs()) + if (E) + Profiler->VisitExpr(E); +} + void OMPClauseProfiler::VisitOMPFullClause(const OMPFullClause *C) {} void OMPClauseProfiler::VisitOMPPartialClause(const OMPPartialClause *C) { diff --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp index 630a8898aa2293..8d2460bc74fa39 100644 --- a/clang/lib/Basic/OpenMPKinds.cpp +++ b/clang/lib/Basic/OpenMPKinds.cpp @@ -188,6 +188,7 @@ unsigned clang::getOpenMPSimpleClauseType(OpenMPClauseKind Kind, StringRef Str, case OMPC_safelen: case OMPC_simdlen: case OMPC_sizes: + case OMPC_permutation: case OMPC_allocator: case OMPC_allocate: case OMPC_collapse: @@ -512,6 +513,7 @@ const char *clang::getOpenMPSimpleClauseTypeName(OpenMPClauseKind Kind, case OMPC_safelen: case OMPC_simdlen: case OMPC_sizes: + case OMPC_permutation: case OMPC_allocator: case OMPC_allocate: case OMPC_collapse: diff --git a/clang/lib/CIR/CMakeLists.txt b/clang/lib/CIR/CMakeLists.txt index d2ff200e0da5f5..11cca734808dfa 100644 --- a/clang/lib/CIR/CMakeLists.txt +++ b/clang/lib/CIR/CMakeLists.txt @@ -2,3 +2,5 @@ include_directories(${LLVM_MAIN_SRC_DIR}/../mlir/include) include_directories(${CMAKE_BINARY_DIR}/tools/mlir/include) add_subdirectory(Dialect) +add_subdirectory(CodeGen) +add_subdirectory(FrontendAction) diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp new file mode 100644 index 00000000000000..95e62326939fc2 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -0,0 +1,32 @@ +//===- CIRGenModule.cpp - Per-Module state for CIR generation -------------===// +// +// 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 is the internal per-translation-unit state used for CIR translation. +// +//===----------------------------------------------------------------------===// + +#include "CIRGenModule.h" + +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclBase.h" + +#include "mlir/IR/BuiltinOps.h" +#include "mlir/IR/Location.h" +#include "mlir/IR/MLIRContext.h" + +using namespace cir; +CIRGenModule::CIRGenModule(mlir::MLIRContext &context, + clang::ASTContext &astctx, + const clang::CodeGenOptions &cgo, + DiagnosticsEngine &diags) + : astCtx(astctx), langOpts(astctx.getLangOpts()), + theModule{mlir::ModuleOp::create(mlir::UnknownLoc())}, + target(astCtx.getTargetInfo()) {} + +// Emit code for a single top level declaration. +void CIRGenModule::buildTopLevelDecl(Decl *decl) {} diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h new file mode 100644 index 00000000000000..ab2a1d8864659a --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -0,0 +1,62 @@ +//===--- CIRGenModule.h - Per-Module state for CIR gen ----------*- 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 is the internal per-translation-unit state used for CIR translation. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENMODULE_H +#define LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENMODULE_H + +#include "CIRGenTypeCache.h" + +#include "mlir/IR/BuiltinOps.h" +#include "mlir/IR/MLIRContext.h" + +namespace clang { +class ASTContext; +class CodeGenOptions; +class Decl; +class DiagnosticsEngine; +class LangOptions; +class TargetInfo; +} // namespace clang + +using namespace clang; +namespace cir { + +/// This class organizes the cross-function state that is used while generating +/// CIR code. +class CIRGenModule : public CIRGenTypeCache { + CIRGenModule(CIRGenModule &) = delete; + CIRGenModule &operator=(CIRGenModule &) = delete; + +public: + CIRGenModule(mlir::MLIRContext &context, clang::ASTContext &astctx, + const clang::CodeGenOptions &cgo, + clang::DiagnosticsEngine &diags); + + ~CIRGenModule() = default; + +private: + /// Hold Clang AST information. + clang::ASTContext &astCtx; + + const clang::LangOptions &langOpts; + + /// A "module" matches a c/cpp source file: containing a list of functions. + mlir::ModuleOp theModule; + + const clang::TargetInfo ⌖ + +public: + void buildTopLevelDecl(clang::Decl *decl); +}; +} // namespace cir + +#endif // LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENMODULE_H diff --git a/clang/lib/CIR/CodeGen/CIRGenTypeCache.h b/clang/lib/CIR/CodeGen/CIRGenTypeCache.h new file mode 100644 index 00000000000000..6478e0a0780994 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenTypeCache.h @@ -0,0 +1,27 @@ +//===--- CIRGenTypeCache.h - Commonly used LLVM types and info -*- 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 structure provides a set of common types useful during CIR emission. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_CIR_CIRGENTYPECACHE_H +#define LLVM_CLANG_LIB_CIR_CIRGENTYPECACHE_H + +namespace cir { + +/// This structure provides a set of types that are commonly used +/// during IR emission. It's initialized once in CodeGenModule's +/// constructor and then copied around into new CIRGenFunction's. +struct CIRGenTypeCache { + CIRGenTypeCache() = default; +}; + +} // namespace cir + +#endif // LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENTYPECACHE_H diff --git a/clang/lib/CIR/CodeGen/CIRGenerator.cpp b/clang/lib/CIR/CodeGen/CIRGenerator.cpp new file mode 100644 index 00000000000000..159355a99ece80 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenerator.cpp @@ -0,0 +1,43 @@ +//===--- CIRGenerator.cpp - Emit CIR from ASTs ----------------------------===// +// +// 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 builds an AST and converts it to CIR. +// +//===----------------------------------------------------------------------===// + +#include "CIRGenModule.h" + +#include "clang/AST/DeclGroup.h" +#include "clang/CIR/CIRGenerator.h" + +using namespace cir; +using namespace clang; + +void CIRGenerator::anchor() {} + +CIRGenerator::CIRGenerator(clang::DiagnosticsEngine &diags, + llvm::IntrusiveRefCntPtr vfs, + const CodeGenOptions &cgo) + : diags(diags), fs(std::move(vfs)), codeGenOpts{cgo} {} +CIRGenerator::~CIRGenerator() = default; + +void CIRGenerator::Initialize(ASTContext &astCtx) { + using namespace llvm; + + this->astCtx = &astCtx; + + cgm = std::make_unique(*mlirCtx, astCtx, codeGenOpts, diags); +} + +bool CIRGenerator::HandleTopLevelDecl(DeclGroupRef group) { + + for (Decl *decl : group) + cgm->buildTopLevelDecl(decl); + + return true; +} diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt new file mode 100644 index 00000000000000..17a3aabfbd7f0e --- /dev/null +++ b/clang/lib/CIR/CodeGen/CMakeLists.txt @@ -0,0 +1,23 @@ +set( + LLVM_LINK_COMPONENTS + Core + Support +) + +get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) + +add_clang_library(clangCIR + CIRGenerator.cpp + CIRGenModule.cpp + + DEPENDS + MLIRCIR + ${dialect_libs} + + LINK_LIBS + clangAST + clangBasic + clangLex + ${dialect_libs} + MLIRCIR +) diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp new file mode 100644 index 00000000000000..72b9fa0c13c595 --- /dev/null +++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp @@ -0,0 +1,72 @@ +//===--- CIRGenAction.cpp - LLVM Code generation Frontend Action ---------===// +// +// 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 "clang/CIR/FrontendAction/CIRGenAction.h" +#include "clang/CIR/CIRGenerator.h" +#include "clang/Frontend/CompilerInstance.h" + +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/OwningOpRef.h" + +using namespace cir; +using namespace clang; + +namespace cir { + +class CIRGenConsumer : public clang::ASTConsumer { + + virtual void anchor(); + + std::unique_ptr OutputStream; + + IntrusiveRefCntPtr FS; + std::unique_ptr Gen; + +public: + CIRGenConsumer(CIRGenAction::OutputType Action, + DiagnosticsEngine &DiagnosticsEngine, + IntrusiveRefCntPtr VFS, + const HeaderSearchOptions &HeaderSearchOptions, + const CodeGenOptions &CodeGenOptions, + const TargetOptions &TargetOptions, + const LangOptions &LangOptions, + const FrontendOptions &FEOptions, + std::unique_ptr OS) + : OutputStream(std::move(OS)), FS(VFS), + Gen(std::make_unique(DiagnosticsEngine, std::move(VFS), + CodeGenOptions)) {} + + bool HandleTopLevelDecl(DeclGroupRef D) override { + Gen->HandleTopLevelDecl(D); + return true; + } +}; +} // namespace cir + +void CIRGenConsumer::anchor() {} + +CIRGenAction::CIRGenAction(OutputType Act, mlir::MLIRContext *MLIRCtx) + : MLIRCtx(MLIRCtx ? MLIRCtx : new mlir::MLIRContext), Action(Act) {} + +CIRGenAction::~CIRGenAction() { MLIRMod.release(); } + +std::unique_ptr +CIRGenAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + std::unique_ptr Out = CI.takeOutputStream(); + + auto Result = std::make_unique( + Action, CI.getDiagnostics(), &CI.getVirtualFileSystem(), + CI.getHeaderSearchOpts(), CI.getCodeGenOpts(), CI.getTargetOpts(), + CI.getLangOpts(), CI.getFrontendOpts(), std::move(Out)); + + return Result; +} + +void EmitCIRAction::anchor() {} +EmitCIRAction::EmitCIRAction(mlir::MLIRContext *MLIRCtx) + : CIRGenAction(OutputType::EmitCIR, MLIRCtx) {} diff --git a/clang/lib/CIR/FrontendAction/CMakeLists.txt b/clang/lib/CIR/FrontendAction/CMakeLists.txt new file mode 100644 index 00000000000000..b0616ab5d64b09 --- /dev/null +++ b/clang/lib/CIR/FrontendAction/CMakeLists.txt @@ -0,0 +1,17 @@ +set(LLVM_LINK_COMPONENTS + Core + Support + ) + +get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) + +add_clang_library(clangCIRFrontendAction + CIRGenAction.cpp + + LINK_LIBS + clangAST + clangFrontend + clangCIR + MLIRCIR + MLIRIR + ) diff --git a/clang/lib/CodeGen/Targets/AMDGPU.cpp b/clang/lib/CodeGen/Targets/AMDGPU.cpp index 37e6af3d4196a8..b852dcffb295c9 100644 --- a/clang/lib/CodeGen/Targets/AMDGPU.cpp +++ b/clang/lib/CodeGen/Targets/AMDGPU.cpp @@ -452,9 +452,6 @@ void AMDGPUTargetCodeGenInfo::setTargetAttributes( if (FD) setFunctionDeclAttributes(FD, F, M); - if (M.getContext().getTargetInfo().allowAMDGPUUnsafeFPAtomics()) - F->addFnAttr("amdgpu-unsafe-fp-atomics", "true"); - if (!getABIInfo().getCodeGenOpts().EmitIEEENaNCompliantInsts) F->addFnAttr("amdgpu-ieee", "false"); } diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 66ec0a7fd32f99..49b07322a21a52 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -5139,6 +5139,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, } } + if (Args.hasArg(options::OPT_fclangir)) + CmdArgs.push_back("-fclangir"); + if (IsOpenMPDevice) { // We have to pass the triple of the host if compiling for an OpenMP device. std::string NormalizedTriple = @@ -9107,13 +9110,6 @@ void OffloadPackager::ConstructJob(Compilation &C, const JobAction &JA, llvm::copy_if(Features, std::back_inserter(FeatureArgs), [](StringRef Arg) { return !Arg.starts_with("-target"); }); - if (TC->getTriple().isAMDGPU()) { - for (StringRef Feature : llvm::split(Arch.split(':').second, ':')) { - FeatureArgs.emplace_back( - Args.MakeArgString(Feature.take_back() + Feature.drop_back())); - } - } - // TODO: We need to pass in the full target-id and handle it properly in the // linker wrapper. SmallVector Parts{ @@ -9123,7 +9119,7 @@ void OffloadPackager::ConstructJob(Compilation &C, const JobAction &JA, "kind=" + Kind.str(), }; - if (TC->getDriver().isUsingOffloadLTO() || TC->getTriple().isAMDGPU()) + if (TC->getDriver().isUsingOffloadLTO()) for (StringRef Feature : FeatureArgs) Parts.emplace_back("feature=" + Feature.str()); diff --git a/clang/lib/Driver/ToolChains/Cuda.cpp b/clang/lib/Driver/ToolChains/Cuda.cpp index 509cd87b28c37e..7a70cf1c5694fd 100644 --- a/clang/lib/Driver/ToolChains/Cuda.cpp +++ b/clang/lib/Driver/ToolChains/Cuda.cpp @@ -848,6 +848,10 @@ void CudaToolChain::addClangTargetOptions( if (CudaInstallation.version() >= CudaVersion::CUDA_90) CC1Args.push_back("-fcuda-allow-variadic-functions"); + if (DriverArgs.hasFlag(options::OPT_fcuda_short_ptr, + options::OPT_fno_cuda_short_ptr, false)) + CC1Args.append({"-mllvm", "--nvptx-short-ptr"}); + if (DriverArgs.hasArg(options::OPT_nogpulib)) return; @@ -873,10 +877,6 @@ void CudaToolChain::addClangTargetOptions( clang::CudaVersion CudaInstallationVersion = CudaInstallation.version(); - if (DriverArgs.hasFlag(options::OPT_fcuda_short_ptr, - options::OPT_fno_cuda_short_ptr, false)) - CC1Args.append({"-mllvm", "--nvptx-short-ptr"}); - if (CudaInstallationVersion >= CudaVersion::UNKNOWN) CC1Args.push_back( DriverArgs.MakeArgString(Twine("-target-sdk-version=") + diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index f6e5798057bbd2..364d7e9855e8cf 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -5454,6 +5454,10 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, } if ((Left.is(TT_TemplateOpener)) != (Right.is(TT_TemplateCloser))) return ShouldAddSpacesInAngles(); + if (Left.is(tok::r_paren) && Right.is(TT_PointerOrReference) && + Right.isOneOf(tok::amp, tok::ampamp)) { + return true; + } // Space before TT_StructuredBindingLSquare. if (Right.is(TT_StructuredBindingLSquare)) { return !Left.isOneOf(tok::amp, tok::ampamp) || diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp index e4b462b9b0fd81..64f90c493c1055 100644 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -457,8 +457,6 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback { return "BuildingDeductionGuides"; case CodeSynthesisContext::TypeAliasTemplateInstantiation: return "TypeAliasTemplateInstantiation"; - case CodeSynthesisContext::PartialOrderingTTP: - return "PartialOrderingTTP"; } return ""; } diff --git a/clang/lib/Frontend/TextDiagnostic.cpp b/clang/lib/Frontend/TextDiagnostic.cpp index a264836a54398f..4119ce6048d45d 100644 --- a/clang/lib/Frontend/TextDiagnostic.cpp +++ b/clang/lib/Frontend/TextDiagnostic.cpp @@ -1252,10 +1252,10 @@ highlightLines(StringRef FileData, unsigned StartLineNumber, for (unsigned I = 0; I <= Spelling.size(); ++I) { // This line is done. if (I == Spelling.size() || isVerticalWhitespace(Spelling[I])) { - SmallVector &LineRanges = - SnippetRanges[L - StartLineNumber]; - if (L >= StartLineNumber) { + SmallVector &LineRanges = + SnippetRanges[L - StartLineNumber]; + if (L == TokenStartLine) // First line appendStyle(LineRanges, T, StartCol, LineLength); else if (L == TokenEndLine) // Last line diff --git a/clang/lib/FrontendTool/CMakeLists.txt b/clang/lib/FrontendTool/CMakeLists.txt index 51c379ade2704c..bfc7652b4c118f 100644 --- a/clang/lib/FrontendTool/CMakeLists.txt +++ b/clang/lib/FrontendTool/CMakeLists.txt @@ -12,6 +12,15 @@ set(link_libs clangRewriteFrontend ) +set(deps) + +if(CLANG_ENABLE_CIR) + list(APPEND link_libs + clangCIRFrontendAction + MLIRIR + ) +endif() + if(CLANG_ENABLE_ARCMT) list(APPEND link_libs clangARCMigrate @@ -29,7 +38,13 @@ add_clang_library(clangFrontendTool DEPENDS ClangDriverOptions + ${deps} LINK_LIBS ${link_libs} ) + +if(CLANG_ENABLE_CIR) + target_include_directories(clangFrontendTool PRIVATE ${LLVM_MAIN_SRC_DIR}/../mlir/include) + target_include_directories(clangFrontendTool PRIVATE ${CMAKE_BINARY_DIR}/tools/mlir/include) +endif() diff --git a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp index 7476b1076d1038..60fde03289cf35 100644 --- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -31,6 +31,11 @@ #include "llvm/Support/BuryPointer.h" #include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/ErrorHandling.h" + +#if CLANG_ENABLE_CIR +#include "clang/CIR/FrontendAction/CIRGenAction.h" +#endif + using namespace clang; using namespace llvm::opt; @@ -42,6 +47,13 @@ CreateFrontendBaseAction(CompilerInstance &CI) { StringRef Action("unknown"); (void)Action; + unsigned UseCIR = CI.getFrontendOpts().UseClangIRPipeline; + frontend::ActionKind Act = CI.getFrontendOpts().ProgramAction; + bool EmitsCIR = Act == EmitCIR; + + if (!UseCIR && EmitsCIR) + llvm::report_fatal_error("-emit-cir and only valid when using -fclangir"); + switch (CI.getFrontendOpts().ProgramAction) { case ASTDeclList: return std::make_unique(); case ASTDump: return std::make_unique(); @@ -54,7 +66,11 @@ CreateFrontendBaseAction(CompilerInstance &CI) { case EmitAssembly: return std::make_unique(); case EmitBC: return std::make_unique(); case EmitCIR: +#if CLANG_ENABLE_CIR + return std::make_unique<::cir::EmitCIRAction>(); +#else llvm_unreachable("CIR suppport not built into clang"); +#endif case EmitHTML: return std::make_unique(); case EmitLLVM: return std::make_unique(); case EmitLLVMOnly: return std::make_unique(); diff --git a/clang/lib/Headers/cpuid.h b/clang/lib/Headers/cpuid.h index 82d995f1b966a3..2601aa5724f056 100644 --- a/clang/lib/Headers/cpuid.h +++ b/clang/lib/Headers/cpuid.h @@ -187,17 +187,18 @@ #define bit_ENQCMD 0x20000000 /* Features in %edx for leaf 7 sub-leaf 0 */ -#define bit_AVX5124VNNIW 0x00000004 -#define bit_AVX5124FMAPS 0x00000008 -#define bit_UINTR 0x00000020 -#define bit_SERIALIZE 0x00004000 -#define bit_TSXLDTRK 0x00010000 -#define bit_PCONFIG 0x00040000 -#define bit_IBT 0x00100000 -#define bit_AMXBF16 0x00400000 -#define bit_AVX512FP16 0x00800000 -#define bit_AMXTILE 0x01000000 -#define bit_AMXINT8 0x02000000 +#define bit_AVX5124VNNIW 0x00000004 +#define bit_AVX5124FMAPS 0x00000008 +#define bit_UINTR 0x00000020 +#define bit_AVX512VP2INTERSECT 0x00000100 +#define bit_SERIALIZE 0x00004000 +#define bit_TSXLDTRK 0x00010000 +#define bit_PCONFIG 0x00040000 +#define bit_IBT 0x00100000 +#define bit_AMXBF16 0x00400000 +#define bit_AVX512FP16 0x00800000 +#define bit_AMXTILE 0x01000000 +#define bit_AMXINT8 0x02000000 /* Features in %eax for leaf 7 sub-leaf 1 */ #define bit_SHA512 0x00000001 diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index 64dfcd47296998..108b532be41689 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -3080,6 +3080,18 @@ OMPClause *Parser::ParseOpenMPSizesClause() { OpenLoc, CloseLoc); } +OMPClause *Parser::ParseOpenMPPermutationClause() { + SourceLocation ClauseNameLoc, OpenLoc, CloseLoc; + SmallVector ArgExprs; + if (ParseOpenMPExprListClause(OMPC_permutation, ClauseNameLoc, OpenLoc, + CloseLoc, ArgExprs, + /*ReqIntConst=*/true)) + return nullptr; + + return Actions.OpenMP().ActOnOpenMPPermutationClause(ArgExprs, ClauseNameLoc, + OpenLoc, CloseLoc); +} + OMPClause *Parser::ParseOpenMPUsesAllocatorClause(OpenMPDirectiveKind DKind) { SourceLocation Loc = Tok.getLocation(); ConsumeAnyToken(); @@ -3377,6 +3389,14 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, Clause = ParseOpenMPSizesClause(); break; + case OMPC_permutation: + if (!FirstClause) { + Diag(Tok, diag::err_omp_more_one_clause) + << getOpenMPDirectiveName(DKind) << getOpenMPClauseName(CKind) << 0; + ErrorFound = true; + } + Clause = ParseOpenMPPermutationClause(); + break; case OMPC_uses_allocators: Clause = ParseOpenMPUsesAllocatorClause(DKind); break; diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp index e04cbeec165552..798cabaa31a476 100644 --- a/clang/lib/Sema/SemaAvailability.cpp +++ b/clang/lib/Sema/SemaAvailability.cpp @@ -1005,25 +1005,54 @@ bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty) { return true; } +struct ExtractedAvailabilityExpr { + const ObjCAvailabilityCheckExpr *E = nullptr; + bool isNegated = false; +}; + +ExtractedAvailabilityExpr extractAvailabilityExpr(const Expr *IfCond) { + const auto *E = IfCond; + bool IsNegated = false; + while (true) { + E = E->IgnoreParens(); + if (const auto *AE = dyn_cast(E)) { + return ExtractedAvailabilityExpr{AE, IsNegated}; + } + + const auto *UO = dyn_cast(E); + if (!UO || UO->getOpcode() != UO_LNot) { + return ExtractedAvailabilityExpr{}; + } + E = UO->getSubExpr(); + IsNegated = !IsNegated; + } +} + bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) { - VersionTuple CondVersion; - if (auto *E = dyn_cast(If->getCond())) { - CondVersion = E->getVersion(); - - // If we're using the '*' case here or if this check is redundant, then we - // use the enclosing version to check both branches. - if (CondVersion.empty() || CondVersion <= AvailabilityStack.back()) - return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse()); - } else { + ExtractedAvailabilityExpr IfCond = extractAvailabilityExpr(If->getCond()); + if (!IfCond.E) { // This isn't an availability checking 'if', we can just continue. return Base::TraverseIfStmt(If); } + VersionTuple CondVersion = IfCond.E->getVersion(); + // If we're using the '*' case here or if this check is redundant, then we + // use the enclosing version to check both branches. + if (CondVersion.empty() || CondVersion <= AvailabilityStack.back()) { + return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse()); + } + + auto *Guarded = If->getThen(); + auto *Unguarded = If->getElse(); + if (IfCond.isNegated) { + std::swap(Guarded, Unguarded); + } + AvailabilityStack.push_back(CondVersion); - bool ShouldContinue = TraverseStmt(If->getThen()); + bool ShouldContinue = TraverseStmt(Guarded); AvailabilityStack.pop_back(); - return ShouldContinue && TraverseStmt(If->getElse()); + return ShouldContinue && TraverseStmt(Unguarded); } } // end anonymous namespace diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 83d71913f8635e..072f43d360ee1c 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -7961,7 +7961,8 @@ NamedDecl *Sema::ActOnVariableDeclarator( } if (!R->isIntegralType(Context) && !R->isPointerType()) { - Diag(TInfo->getTypeLoc().getBeginLoc(), diag::err_asm_bad_register_type) + Diag(TInfo->getTypeLoc().getBeginLoc(), + diag::err_asm_unsupported_register_type) << TInfo->getTypeLoc().getSourceRange(); NewVD->setInvalidDecl(true); } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index f930a21ea870ec..7401e4cc8f0928 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -18,6 +18,7 @@ #include "clang/AST/ASTLambda.h" #include "clang/AST/ASTMutationListener.h" #include "clang/AST/CXXInheritance.h" +#include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/EvaluatedExprVisitor.h" @@ -65,6 +66,7 @@ #include "llvm/Support/Casting.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/SaveAndRestore.h" +#include "llvm/Support/TimeProfiler.h" #include "llvm/Support/TypeSize.h" #include @@ -18144,6 +18146,15 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func, Func->setInstantiationIsPending(true); PendingInstantiations.push_back( std::make_pair(Func, PointOfInstantiation)); + if (llvm::isTimeTraceVerbose()) { + llvm::timeTraceAddInstantEvent("DeferInstantiation", [&] { + std::string Name; + llvm::raw_string_ostream OS(Name); + Func->getNameForDiagnostic(OS, getPrintingPolicy(), + /*Qualified=*/true); + return Name; + }); + } // Notify the consumer that a function was implicitly instantiated. Consumer.HandleCXXImplicitFunctionInstantiation(Func); } diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index d490452e91c3bb..8e9bcb10a80b46 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -8429,7 +8429,8 @@ ExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, Expr *Base, QualType ObjectType; QualType T; TypeLocBuilder TLB; - if (CheckArrow(*this, ObjectType, Base, OpKind, OpLoc)) + if (CheckArrow(*this, ObjectType, Base, OpKind, OpLoc) || + DS.getTypeSpecType() == DeclSpec::TST_error) return ExprError(); switch (DS.getTypeSpecType()) { diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index 74ab52106e3234..d3e696a79b94fc 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -14956,7 +14956,9 @@ StmtResult SemaOpenMP::ActOnOpenMPInterchangeDirective( return StmtError(); // interchange without permutation clause swaps two loops. - constexpr size_t NumLoops = 2; + const OMPPermutationClause *PermutationClause = + OMPExecutableDirective::getSingleClause(Clauses); + size_t NumLoops = PermutationClause ? PermutationClause->getNumLoops() : 2; // Verify and diagnose loop nest. SmallVector LoopHelpers(NumLoops); @@ -14971,6 +14973,12 @@ StmtResult SemaOpenMP::ActOnOpenMPInterchangeDirective( return OMPInterchangeDirective::Create(Context, StartLoc, EndLoc, Clauses, NumLoops, AStmt, nullptr, nullptr); + // An invalid expression in the permutation clause is set to nullptr in + // ActOnOpenMPPermutationClause. + if (PermutationClause && + llvm::is_contained(PermutationClause->getArgsRefs(), nullptr)) + return StmtError(); + assert(LoopHelpers.size() == NumLoops && "Expecting loop iteration space dimensionaly to match number of " "affected loops"); @@ -14979,7 +14987,44 @@ StmtResult SemaOpenMP::ActOnOpenMPInterchangeDirective( "affected loops"); // Decode the permutation clause. - constexpr uint64_t Permutation[] = {1, 0}; + SmallVector Permutation; + if (!PermutationClause) { + Permutation = {1, 0}; + } else { + ArrayRef PermArgs = PermutationClause->getArgsRefs(); + llvm::BitVector Flags(PermArgs.size()); + for (Expr *PermArg : PermArgs) { + std::optional PermCstExpr = + PermArg->getIntegerConstantExpr(Context); + if (!PermCstExpr) + continue; + uint64_t PermInt = PermCstExpr->getZExtValue(); + assert(1 <= PermInt && PermInt <= NumLoops && + "Must be a permutation; diagnostic emitted in " + "ActOnOpenMPPermutationClause"); + if (Flags[PermInt - 1]) { + SourceRange ExprRange(PermArg->getBeginLoc(), PermArg->getEndLoc()); + Diag(PermArg->getExprLoc(), + diag::err_omp_interchange_permutation_value_repeated) + << PermInt << ExprRange; + continue; + } + Flags[PermInt - 1] = true; + + Permutation.push_back(PermInt - 1); + } + + if (Permutation.size() != NumLoops) + return StmtError(); + } + + // Nothing to transform with trivial permutation. + if (NumLoops <= 1 || llvm::all_of(llvm::enumerate(Permutation), [](auto P) { + auto [Idx, Arg] = P; + return Idx == Arg; + })) + return OMPInterchangeDirective::Create(Context, StartLoc, EndLoc, Clauses, + NumLoops, AStmt, AStmt, nullptr); // Find the affected loops. SmallVector LoopStmts(NumLoops, nullptr); @@ -16111,6 +16156,44 @@ OMPClause *SemaOpenMP::ActOnOpenMPSizesClause(ArrayRef SizeExprs, SanitizedSizeExprs); } +OMPClause *SemaOpenMP::ActOnOpenMPPermutationClause(ArrayRef PermExprs, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc) { + size_t NumLoops = PermExprs.size(); + SmallVector SanitizedPermExprs; + llvm::append_range(SanitizedPermExprs, PermExprs); + + for (Expr *&PermExpr : SanitizedPermExprs) { + // Skip if template-dependent or already sanitized, e.g. during a partial + // template instantiation. + if (!PermExpr || PermExpr->isInstantiationDependent()) + continue; + + llvm::APSInt PermVal; + ExprResult PermEvalExpr = SemaRef.VerifyIntegerConstantExpression( + PermExpr, &PermVal, Sema::AllowFold); + bool IsValid = PermEvalExpr.isUsable(); + if (IsValid) + PermExpr = PermEvalExpr.get(); + + if (IsValid && (PermVal < 1 || NumLoops < PermVal)) { + SourceRange ExprRange(PermEvalExpr.get()->getBeginLoc(), + PermEvalExpr.get()->getEndLoc()); + Diag(PermEvalExpr.get()->getExprLoc(), + diag::err_omp_interchange_permutation_value_range) + << NumLoops << ExprRange; + IsValid = false; + } + + if (!PermExpr->isInstantiationDependent() && !IsValid) + PermExpr = nullptr; + } + + return OMPPermutationClause::Create(getASTContext(), StartLoc, LParenLoc, + EndLoc, SanitizedPermExprs); +} + OMPClause *SemaOpenMP::ActOnOpenMPFullClause(SourceLocation StartLoc, SourceLocation EndLoc) { return OMPFullClause::Create(getASTContext(), StartLoc, EndLoc); diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index b1a34edba9150b..dfd56debc75e99 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -5498,7 +5498,8 @@ bool Sema::CheckTemplateArgumentList( DefaultArgs && ParamIdx >= DefaultArgs.StartPos) { // All written arguments should have been consumed by this point. assert(ArgIdx == NumArgs && "bad default argument deduction"); - if (ParamIdx == DefaultArgs.StartPos) { + // FIXME: Don't ignore parameter packs. + if (ParamIdx == DefaultArgs.StartPos && !(*Param)->isParameterPack()) { assert(Param + DefaultArgs.Args.size() <= ParamEnd); // Default arguments from a DeducedTemplateName are already converted. for (const TemplateArgument &DefArg : DefaultArgs.Args) { @@ -5575,6 +5576,9 @@ bool Sema::CheckTemplateArgumentList( return true; } + // We're now done with this argument. + ++ArgIdx; + if ((*Param)->isTemplateParameterPack()) { // The template parameter was a template parameter pack, so take the // deduced argument and place it on the argument pack. Note that we @@ -5585,19 +5589,8 @@ bool Sema::CheckTemplateArgumentList( } else { // Move to the next template parameter. ++Param; - if (PartialOrderingTTP && PackExpansionIntoNonPack) { - // Keep converting the pattern in the argument against - // subsequent parameters. The argument is converted - // in place and will be added back when we are done. - SugaredConverted.pop_back(); - CanonicalConverted.pop_back(); - continue; - } } - // We're now done with this argument. - ++ArgIdx; - // If we just saw a pack expansion into a non-pack, then directly convert // the remaining arguments, because we don't know what parameters they'll // match up with. @@ -5731,10 +5724,14 @@ bool Sema::CheckTemplateArgumentList( // pack expansions; they might be empty. This can happen even if // PartialTemplateArgs is false (the list of arguments is complete but // still dependent). - while (ArgIdx < NumArgs && NewArgs[ArgIdx].getArgument().isPackExpansion()) { - const TemplateArgument &Arg = NewArgs[ArgIdx++].getArgument(); - SugaredConverted.push_back(Arg); - CanonicalConverted.push_back(Context.getCanonicalTemplateArgument(Arg)); + if (ArgIdx < NumArgs && CurrentInstantiationScope && + CurrentInstantiationScope->getPartiallySubstitutedPack()) { + while (ArgIdx < NumArgs && + NewArgs[ArgIdx].getArgument().isPackExpansion()) { + const TemplateArgument &Arg = NewArgs[ArgIdx++].getArgument(); + SugaredConverted.push_back(Arg); + CanonicalConverted.push_back(Context.getCanonicalTemplateArgument(Arg)); + } } // If we have any leftover arguments, then there were too many arguments. @@ -7324,46 +7321,64 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, << Template; } - if (!getLangOpts().RelaxedTemplateTemplateArgs) - return !TemplateParameterListsAreEqual( - Template->getTemplateParameters(), Params, /*Complain=*/true, - TPL_TemplateTemplateArgumentMatch, Arg.getLocation()); - // C++1z [temp.arg.template]p3: (DR 150) // A template-argument matches a template template-parameter P when P // is at least as specialized as the template-argument A. - if (!isTemplateTemplateParameterAtLeastAsSpecializedAs( - Params, Param, Template, DefaultArgs, Arg.getLocation(), IsDeduced)) - return true; - // P2113 - // C++20[temp.func.order]p2 - // [...] If both deductions succeed, the partial ordering selects the - // more constrained template (if one exists) as determined below. - SmallVector ParamsAC, TemplateAC; - Params->getAssociatedConstraints(ParamsAC); - // C++20[temp.arg.template]p3 - // [...] In this comparison, if P is unconstrained, the constraints on A - // are not considered. - if (ParamsAC.empty()) - return false; + if (getLangOpts().RelaxedTemplateTemplateArgs) { + // Quick check for the common case: + // If P contains a parameter pack, then A [...] matches P if each of A's + // template parameters matches the corresponding template parameter in + // the template-parameter-list of P. + if (TemplateParameterListsAreEqual( + Template->getTemplateParameters(), Params, false, + TPL_TemplateTemplateArgumentMatch, Arg.getLocation()) && + // If the argument has no associated constraints, then the parameter is + // definitely at least as specialized as the argument. + // Otherwise - we need a more thorough check. + !Template->hasAssociatedConstraints()) + return false; - Template->getAssociatedConstraints(TemplateAC); + if (isTemplateTemplateParameterAtLeastAsSpecializedAs( + Params, Template, DefaultArgs, Arg.getLocation(), IsDeduced)) { + // P2113 + // C++20[temp.func.order]p2 + // [...] If both deductions succeed, the partial ordering selects the + // more constrained template (if one exists) as determined below. + SmallVector ParamsAC, TemplateAC; + Params->getAssociatedConstraints(ParamsAC); + // C++2a[temp.arg.template]p3 + // [...] In this comparison, if P is unconstrained, the constraints on A + // are not considered. + if (ParamsAC.empty()) + return false; - bool IsParamAtLeastAsConstrained; - if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC, - IsParamAtLeastAsConstrained)) - return true; - if (!IsParamAtLeastAsConstrained) { - Diag(Arg.getLocation(), - diag::err_template_template_parameter_not_at_least_as_constrained) - << Template << Param << Arg.getSourceRange(); - Diag(Param->getLocation(), diag::note_entity_declared_at) << Param; - Diag(Template->getLocation(), diag::note_entity_declared_at) << Template; - MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Param, ParamsAC, Template, - TemplateAC); - return true; + Template->getAssociatedConstraints(TemplateAC); + + bool IsParamAtLeastAsConstrained; + if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC, + IsParamAtLeastAsConstrained)) + return true; + if (!IsParamAtLeastAsConstrained) { + Diag(Arg.getLocation(), + diag::err_template_template_parameter_not_at_least_as_constrained) + << Template << Param << Arg.getSourceRange(); + Diag(Param->getLocation(), diag::note_entity_declared_at) << Param; + Diag(Template->getLocation(), diag::note_entity_declared_at) + << Template; + MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Param, ParamsAC, Template, + TemplateAC); + return true; + } + return false; + } + // FIXME: Produce better diagnostics for deduction failures. } - return false; + + return !TemplateParameterListsAreEqual(Template->getTemplateParameters(), + Params, + true, + TPL_TemplateTemplateArgumentMatch, + Arg.getLocation()); } static Sema::SemaDiagnosticBuilder noteLocation(Sema &S, const NamedDecl &Decl, diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 8e80ab730ac342..dfae0d6cda0d9b 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -145,9 +145,7 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( PartialOrderingKind POK, bool DeducedFromArrayBound, bool *HasDeducedAnyParam); -/// What directions packs are allowed to match non-packs. -enum class PackFold { ParameterToArgument, ArgumentToParameter, Both }; - +enum class PackFold { ParameterToArgument, ArgumentToParameter }; static TemplateDeductionResult DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, ArrayRef Ps, @@ -1713,21 +1711,7 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( DeducedTemplateArgument Result = checkDeducedTemplateArguments(S.Context, Deduced[Index], NewDeduced); if (Result.isNull()) { - // We can also get inconsistencies when matching NTTP type. - switch (NamedDecl *Param = TemplateParams->getParam(Index); - Param->getKind()) { - case Decl::TemplateTypeParm: - Info.Param = cast(Param); - break; - case Decl::NonTypeTemplateParm: - Info.Param = cast(Param); - break; - case Decl::TemplateTemplateParm: - Info.Param = cast(Param); - break; - default: - llvm_unreachable("unexpected kind"); - } + Info.Param = cast(TemplateParams->getParam(Index)); Info.FirstArg = Deduced[Index]; Info.SecondArg = NewDeduced; return TemplateDeductionResult::Inconsistent; @@ -2565,31 +2549,8 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, if (const NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr(Info, P.getAsExpr())) { switch (A.getKind()) { - case TemplateArgument::Expression: { - const Expr *E = A.getAsExpr(); - // When checking NTTP, if either the parameter or the argument is - // dependent, as there would be otherwise nothing to deduce, we force - // the argument to the parameter type using this dependent implicit - // cast, in order to maintain invariants. Now we can deduce the - // resulting type from the original type, and deduce the original type - // against the parameter we are checking. - if (const auto *ICE = dyn_cast(E); - ICE && ICE->getCastKind() == clang::CK_Dependent) { - E = ICE->getSubExpr(); - if (auto Result = DeduceTemplateArgumentsByTypeMatch( - S, TemplateParams, ICE->getType(), E->getType(), Info, - Deduced, TDF_SkipNonDependent, - PartialOrdering ? PartialOrderingKind::NonCall - : PartialOrderingKind::None, - /*DeducedFromArrayBound=*/false, HasDeducedAnyParam); - Result != TemplateDeductionResult::Success) - return Result; - } - return DeduceNonTypeTemplateArgument( - S, TemplateParams, NTTP, DeducedTemplateArgument(A), E->getType(), - Info, PartialOrdering, Deduced, HasDeducedAnyParam); - } case TemplateArgument::Integral: + case TemplateArgument::Expression: case TemplateArgument::StructuralValue: return DeduceNonTypeTemplateArgument( S, TemplateParams, NTTP, DeducedTemplateArgument(A), @@ -2678,75 +2639,50 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, SmallVectorImpl &Deduced, bool NumberOfArgumentsMustMatch, bool PartialOrdering, PackFold PackFold, bool *HasDeducedAnyParam) { - bool FoldPackParameter = PackFold == PackFold::ParameterToArgument || - PackFold == PackFold::Both, - FoldPackArgument = PackFold == PackFold::ArgumentToParameter || - PackFold == PackFold::Both; - + if (PackFold == PackFold::ArgumentToParameter) + std::swap(Ps, As); // C++0x [temp.deduct.type]p9: // If the template argument list of P contains a pack expansion that is not // the last template argument, the entire template argument list is a // non-deduced context. - if (FoldPackParameter && hasPackExpansionBeforeEnd(Ps)) - return TemplateDeductionResult::Success; - - if (FoldPackArgument && hasPackExpansionBeforeEnd(As)) + if (hasPackExpansionBeforeEnd(Ps)) return TemplateDeductionResult::Success; // C++0x [temp.deduct.type]p9: // If P has a form that contains or , then each argument Pi of the // respective template argument list P is compared with the corresponding // argument Ai of the corresponding template argument list of A. - for (unsigned ArgIdx = 0, ParamIdx = 0; /**/; /**/) { - if (!hasTemplateArgumentForDeduction(Ps, ParamIdx)) - return !FoldPackParameter && hasTemplateArgumentForDeduction(As, ArgIdx) - ? TemplateDeductionResult::MiscellaneousDeductionFailure - : TemplateDeductionResult::Success; - - if (!Ps[ParamIdx].isPackExpansion()) { + unsigned ArgIdx = 0, ParamIdx = 0; + for (; hasTemplateArgumentForDeduction(Ps, ParamIdx); ++ParamIdx) { + const TemplateArgument &P = Ps[ParamIdx]; + if (!P.isPackExpansion()) { // The simple case: deduce template arguments by matching Pi and Ai. // Check whether we have enough arguments. if (!hasTemplateArgumentForDeduction(As, ArgIdx)) - return !FoldPackArgument && NumberOfArgumentsMustMatch + return NumberOfArgumentsMustMatch ? TemplateDeductionResult::MiscellaneousDeductionFailure : TemplateDeductionResult::Success; - if (As[ArgIdx].isPackExpansion()) { - // C++1z [temp.deduct.type]p9: - // During partial ordering, if Ai was originally a pack expansion - // [and] Pi is not a pack expansion, template argument deduction - // fails. - if (!FoldPackArgument) - return TemplateDeductionResult::MiscellaneousDeductionFailure; - - TemplateArgument Pattern = As[ArgIdx].getPackExpansionPattern(); - for (;;) { - // Deduce template parameters from the pattern. - if (auto Result = DeduceTemplateArguments( - S, TemplateParams, Ps[ParamIdx], Pattern, Info, - PartialOrdering, Deduced, HasDeducedAnyParam); - Result != TemplateDeductionResult::Success) - return Result; + // C++1z [temp.deduct.type]p9: + // During partial ordering, if Ai was originally a pack expansion [and] + // Pi is not a pack expansion, template argument deduction fails. + if (As[ArgIdx].isPackExpansion()) + return TemplateDeductionResult::MiscellaneousDeductionFailure; - ++ParamIdx; - if (!hasTemplateArgumentForDeduction(Ps, ParamIdx)) - return TemplateDeductionResult::Success; - if (Ps[ParamIdx].isPackExpansion()) - break; - } - } else { - // Perform deduction for this Pi/Ai pair. - if (auto Result = DeduceTemplateArguments( - S, TemplateParams, Ps[ParamIdx], As[ArgIdx], Info, - PartialOrdering, Deduced, HasDeducedAnyParam); - Result != TemplateDeductionResult::Success) - return Result; + // Perform deduction for this Pi/Ai pair. + TemplateArgument Pi = P, Ai = As[ArgIdx]; + if (PackFold == PackFold::ArgumentToParameter) + std::swap(Pi, Ai); + if (auto Result = DeduceTemplateArguments(S, TemplateParams, Pi, Ai, Info, + PartialOrdering, Deduced, + HasDeducedAnyParam); + Result != TemplateDeductionResult::Success) + return Result; - ++ArgIdx; - ++ParamIdx; - continue; - } + // Move to the next argument. + ++ArgIdx; + continue; } // The parameter is a pack expansion. @@ -2756,7 +2692,7 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, // each remaining argument in the template argument list of A. Each // comparison deduces template arguments for subsequent positions in the // template parameter packs expanded by Pi. - TemplateArgument Pattern = Ps[ParamIdx].getPackExpansionPattern(); + TemplateArgument Pattern = P.getPackExpansionPattern(); // Prepare to deduce the packs within the pattern. PackDeductionScope PackScope(S, TemplateParams, Deduced, Info, Pattern); @@ -2767,12 +2703,13 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, for (; hasTemplateArgumentForDeduction(As, ArgIdx) && PackScope.hasNextElement(); ++ArgIdx) { - if (!FoldPackParameter && !As[ArgIdx].isPackExpansion()) - return TemplateDeductionResult::MiscellaneousDeductionFailure; + TemplateArgument Pi = Pattern, Ai = As[ArgIdx]; + if (PackFold == PackFold::ArgumentToParameter) + std::swap(Pi, Ai); // Deduce template arguments from the pattern. - if (auto Result = DeduceTemplateArguments( - S, TemplateParams, Pattern, As[ArgIdx], Info, PartialOrdering, - Deduced, HasDeducedAnyParam); + if (auto Result = DeduceTemplateArguments(S, TemplateParams, Pi, Ai, Info, + PartialOrdering, Deduced, + HasDeducedAnyParam); Result != TemplateDeductionResult::Success) return Result; @@ -2781,8 +2718,12 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, // Build argument packs for each of the parameter packs expanded by this // pack expansion. - return PackScope.finish(); + if (auto Result = PackScope.finish(); + Result != TemplateDeductionResult::Success) + return Result; } + + return TemplateDeductionResult::Success; } TemplateDeductionResult Sema::DeduceTemplateArguments( @@ -3334,6 +3275,7 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( // Unevaluated SFINAE context. EnterExpressionEvaluationContext Unevaluated( S, Sema::ExpressionEvaluationContext::Unevaluated); + Sema::SFINAETrap Trap(S); Sema::ContextRAII SavedContext(S, getAsDeclContextOrEnclosing(Template)); @@ -3350,42 +3292,21 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( return Result; // Check that we produced the correct argument list. - for (ArrayRef Ps = TemplateArgs, As = CanonicalBuilder; - !Ps.empty() && !As.empty(); - /**/) { - TemplateArgument P = Ps.front(), A = As.front(); - if (P.getKind() == TemplateArgument::Pack) { - assert(Ps.size() == 1 && "Pack not last element?"); - Ps = P.getPackAsArray(); - continue; - } - if (A.getKind() == TemplateArgument::Pack) { - assert(As.size() == 1 && "Pack not last element?"); - As = A.getPackAsArray(); - continue; + TemplateParameterList *TemplateParams = Template->getTemplateParameters(); + for (unsigned I = 0, E = TemplateParams->size(); I != E; ++I) { + TemplateArgument InstArg = CanonicalBuilder[I]; + if (!isSameTemplateArg(S.Context, TemplateArgs[I], InstArg, PartialOrdering, + /*PackExpansionMatchesPack=*/true)) { + Info.Param = makeTemplateParameter(TemplateParams->getParam(I)); + Info.FirstArg = TemplateArgs[I]; + Info.SecondArg = InstArg; + return TemplateDeductionResult::NonDeducedMismatch; } - - if (P.isPackExpansion()) - P = P.getPackExpansionPattern(); - else - Ps = Ps.drop_front(); - if (A.isPackExpansion()) - A = A.getPackExpansionPattern(); - else - As = As.drop_front(); - - if (isSameTemplateArg(S.Context, P, A, PartialOrdering)) - continue; - unsigned I = As.end() == CanonicalBuilder.end() - ? As.begin() - CanonicalBuilder.begin() - : CanonicalBuilder.size() - 1; - Info.Param = - makeTemplateParameter(Template->getTemplateParameters()->getParam(I)); - Info.FirstArg = P; - Info.SecondArg = A; - return TemplateDeductionResult::NonDeducedMismatch; } + if (Trap.hasErrorOccurred()) + return TemplateDeductionResult::SubstitutionFailure; + if (!PartialOrdering) { if (auto Result = CheckDeducedArgumentConstraints( S, Template, SugaredBuilder, CanonicalBuilder, Info); @@ -3406,6 +3327,7 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( // Unevaluated SFINAE context. EnterExpressionEvaluationContext Unevaluated( S, Sema::ExpressionEvaluationContext::Unevaluated); + Sema::SFINAETrap Trap(S); Sema::ContextRAII SavedContext(S, getAsDeclContextOrEnclosing(TD)); @@ -3414,13 +3336,20 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( // explicitly specified, template argument deduction fails. SmallVector SugaredBuilder, CanonicalBuilder; if (auto Result = ConvertDeducedTemplateArguments( - S, TD, /*IsDeduced=*/false, Deduced, Info, SugaredBuilder, + S, TD, /*IsPartialOrdering=*/false, Deduced, Info, SugaredBuilder, CanonicalBuilder); Result != TemplateDeductionResult::Success) return Result; - return ::CheckDeducedArgumentConstraints(S, TD, SugaredBuilder, - CanonicalBuilder, Info); + if (Trap.hasErrorOccurred()) + return TemplateDeductionResult::SubstitutionFailure; + + if (auto Result = CheckDeducedArgumentConstraints(S, TD, SugaredBuilder, + CanonicalBuilder, Info); + Result != TemplateDeductionResult::Success) + return Result; + + return TemplateDeductionResult::Success; } /// Perform template argument deduction to determine whether the given template @@ -3467,20 +3396,16 @@ DeduceTemplateArguments(Sema &S, T *Partial, if (Inst.isInvalid()) return TemplateDeductionResult::InstantiationDepth; + if (Trap.hasErrorOccurred()) + return TemplateDeductionResult::SubstitutionFailure; + TemplateDeductionResult Result; S.runWithSufficientStackSpace(Info.getLocation(), [&] { Result = ::FinishTemplateArgumentDeduction(S, Partial, /*IsPartialOrdering=*/false, TemplateArgs, Deduced, Info); }); - - if (Result != TemplateDeductionResult::Success) - return Result; - - if (Trap.hasErrorOccurred()) - return TemplateDeductionResult::SubstitutionFailure; - - return TemplateDeductionResult::Success; + return Result; } TemplateDeductionResult @@ -3536,18 +3461,14 @@ Sema::DeduceTemplateArgumentsFromType(TemplateDecl *TD, QualType FromType, if (Inst.isInvalid()) return TemplateDeductionResult::InstantiationDepth; + if (Trap.hasErrorOccurred()) + return TemplateDeductionResult::SubstitutionFailure; + TemplateDeductionResult Result; runWithSufficientStackSpace(Info.getLocation(), [&] { Result = ::FinishTemplateArgumentDeduction(*this, TD, Deduced, Info); }); - - if (Result != TemplateDeductionResult::Success) - return Result; - - if (Trap.hasErrorOccurred()) - return TemplateDeductionResult::SubstitutionFailure; - - return TemplateDeductionResult::Success; + return Result; } /// Determine whether the given type T is a simple-template-id type. @@ -4007,22 +3928,7 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( if (FunctionTemplate->getFriendObjectKind()) Owner = FunctionTemplate->getLexicalDeclContext(); FunctionDecl *FD = FunctionTemplate->getTemplatedDecl(); - // additional check for inline friend, - // ``` - // template int foo(F1 X); - // template struct A { - // template friend int foo(F1 X) { return A1; } - // }; - // template struct A<1>; - // int a = foo(1.0); - // ``` - const FunctionDecl *FDFriend; - if (FD->getFriendObjectKind() == Decl::FriendObjectKind::FOK_None && - FD->isDefined(FDFriend, /*CheckForPendingFriendDefinition*/ true) && - FDFriend->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None) { - FD = const_cast(FDFriend); - Owner = FD->getLexicalDeclContext(); - } + MultiLevelTemplateArgumentList SubstArgs( FunctionTemplate, CanonicalDeducedArgumentList->asArray(), /*Final=*/false); @@ -6188,23 +6094,14 @@ static bool isAtLeastAsSpecializedAs(Sema &S, QualType T1, QualType T2, return false; const auto *TST1 = cast(T1); - - Sema::SFINAETrap Trap(S); - - TemplateDeductionResult Result; + bool AtLeastAsSpecialized; S.runWithSufficientStackSpace(Info.getLocation(), [&] { - Result = ::FinishTemplateArgumentDeduction( - S, P2, /*IsPartialOrdering=*/true, TST1->template_arguments(), Deduced, - Info); + AtLeastAsSpecialized = + FinishTemplateArgumentDeduction( + S, P2, /*IsPartialOrdering=*/true, TST1->template_arguments(), + Deduced, Info) == TemplateDeductionResult::Success; }); - - if (Result != TemplateDeductionResult::Success) - return false; - - if (Trap.hasErrorOccurred()) - return false; - - return true; + return AtLeastAsSpecialized; } namespace { @@ -6442,9 +6339,8 @@ bool Sema::isMoreSpecializedThanPrimary( } bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( - TemplateParameterList *P, TemplateDecl *PArg, TemplateDecl *AArg, - const DefaultArguments &DefaultArgs, SourceLocation ArgLoc, - bool IsDeduced) { + TemplateParameterList *P, TemplateDecl *AArg, + const DefaultArguments &DefaultArgs, SourceLocation Loc, bool IsDeduced) { // C++1z [temp.arg.template]p4: (DR 150) // A template template-parameter P is at least as specialized as a // template template-argument A if, given the following rewrite to two @@ -6456,12 +6352,6 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( // TemplateParameterList *A = AArg->getTemplateParameters(); - Sema::InstantiatingTemplate Inst( - *this, ArgLoc, Sema::InstantiatingTemplate::PartialOrderingTTP(), PArg, - SourceRange(P->getTemplateLoc(), P->getRAngleLoc())); - if (Inst.isInvalid()) - return false; - // Given an invented class template X with the template parameter list of // A (including default arguments): // - Each function template has a single function parameter whose type is @@ -6476,6 +6366,8 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( // templates. SmallVector PArgs; { + SFINAETrap Trap(*this); + Context.getInjectedTemplateArgs(P, PArgs); TemplateArgumentListInfo PArgList(P->getLAngleLoc(), P->getRAngleLoc()); @@ -6495,17 +6387,18 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( // C++1z [temp.arg.template]p3: // If the rewrite produces an invalid type, then P is not at least as // specialized as A. - SmallVector CanonicalPArgs; - if (CheckTemplateArgumentList(AArg, ArgLoc, PArgList, DefaultArgs, false, - PArgs, CanonicalPArgs, + SmallVector SugaredPArgs; + if (CheckTemplateArgumentList(AArg, Loc, PArgList, DefaultArgs, false, + SugaredPArgs, PArgs, /*UpdateArgsWithConversions=*/true, /*ConstraintsNotSatisfied=*/nullptr, - /*PartialOrderingTTP=*/true)) + /*PartialOrderTTP=*/true) || + Trap.hasErrorOccurred()) return false; } // Determine whether P1 is at least as specialized as P2. - TemplateDeductionInfo Info(ArgLoc, A->getDepth()); + TemplateDeductionInfo Info(Loc, A->getDepth()); SmallVector Deduced; Deduced.resize(A->size()); @@ -6520,89 +6413,29 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( // be inverted between Ps and As. On non-deduced context, matching needs to // happen both ways, according to [temp.arg.template]p3, but this is // currently implemented as a special case elsewhere. - switch (::DeduceTemplateArguments( - *this, A, AArgs, PArgs, Info, Deduced, - /*NumberOfArgumentsMustMatch=*/false, /*PartialOrdering=*/true, - IsDeduced ? PackFold::ArgumentToParameter : PackFold::Both, - /*HasDeducedAnyParam=*/nullptr)) { - case clang::TemplateDeductionResult::Success: - break; - - case TemplateDeductionResult::MiscellaneousDeductionFailure: - Diag(AArg->getLocation(), diag::err_template_param_list_different_arity) - << (A->size() > P->size()) << /*isTemplateTemplateParameter=*/true - << SourceRange(A->getTemplateLoc(), P->getRAngleLoc()); + if (::DeduceTemplateArguments(*this, A, AArgs, PArgs, Info, Deduced, + /*NumberOfArgumentsMustMatch=*/false, + /*PartialOrdering=*/true, + IsDeduced ? PackFold::ArgumentToParameter + : PackFold::ParameterToArgument, + /*HasDeducedAnyParam=*/nullptr) != + TemplateDeductionResult::Success) return false; - case TemplateDeductionResult::NonDeducedMismatch: - Diag(AArg->getLocation(), diag::err_non_deduced_mismatch) - << Info.FirstArg << Info.SecondArg; - return false; - case TemplateDeductionResult::Inconsistent: - Diag(getAsNamedDecl(Info.Param)->getLocation(), - diag::err_inconsistent_deduction) - << Info.FirstArg << Info.SecondArg; - return false; - case TemplateDeductionResult::AlreadyDiagnosed: - return false; - - // None of these should happen for a plain deduction. - case TemplateDeductionResult::Invalid: - case TemplateDeductionResult::InstantiationDepth: - case TemplateDeductionResult::Incomplete: - case TemplateDeductionResult::IncompletePack: - case TemplateDeductionResult::Underqualified: - case TemplateDeductionResult::SubstitutionFailure: - case TemplateDeductionResult::DeducedMismatch: - case TemplateDeductionResult::DeducedMismatchNested: - case TemplateDeductionResult::TooManyArguments: - case TemplateDeductionResult::TooFewArguments: - case TemplateDeductionResult::InvalidExplicitArguments: - case TemplateDeductionResult::NonDependentConversionFailure: - case TemplateDeductionResult::ConstraintsNotSatisfied: - case TemplateDeductionResult::CUDATargetMismatch: - llvm_unreachable("Unexpected Result"); - } SmallVector DeducedArgs(Deduced.begin(), Deduced.end()); + Sema::InstantiatingTemplate Inst(*this, Info.getLocation(), AArg, DeducedArgs, + Info); + if (Inst.isInvalid()) + return false; - TemplateDeductionResult TDK; + bool AtLeastAsSpecialized; runWithSufficientStackSpace(Info.getLocation(), [&] { - TDK = ::FinishTemplateArgumentDeduction( - *this, AArg, /*IsPartialOrdering=*/true, PArgs, Deduced, Info); + AtLeastAsSpecialized = + ::FinishTemplateArgumentDeduction( + *this, AArg, /*IsPartialOrdering=*/true, PArgs, Deduced, Info) == + TemplateDeductionResult::Success; }); - switch (TDK) { - case TemplateDeductionResult::Success: - return true; - - // It doesn't seem possible to get a non-deduced mismatch when partial - // ordering TTPs. - case TemplateDeductionResult::NonDeducedMismatch: - llvm_unreachable("Unexpected NonDeducedMismatch"); - - // Substitution failures should have already been diagnosed. - case TemplateDeductionResult::AlreadyDiagnosed: - case TemplateDeductionResult::SubstitutionFailure: - case TemplateDeductionResult::InstantiationDepth: - return false; - - // None of these should happen when just converting deduced arguments. - case TemplateDeductionResult::Invalid: - case TemplateDeductionResult::Incomplete: - case TemplateDeductionResult::IncompletePack: - case TemplateDeductionResult::Inconsistent: - case TemplateDeductionResult::Underqualified: - case TemplateDeductionResult::DeducedMismatch: - case TemplateDeductionResult::DeducedMismatchNested: - case TemplateDeductionResult::TooManyArguments: - case TemplateDeductionResult::TooFewArguments: - case TemplateDeductionResult::InvalidExplicitArguments: - case TemplateDeductionResult::NonDependentConversionFailure: - case TemplateDeductionResult::ConstraintsNotSatisfied: - case TemplateDeductionResult::MiscellaneousDeductionFailure: - case TemplateDeductionResult::CUDATargetMismatch: - llvm_unreachable("Unexpected Result"); - } - llvm_unreachable("Unexpected TDK"); + return AtLeastAsSpecialized; } namespace { diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index f2007fc5d85a50..261ef4edf17593 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -512,13 +512,13 @@ struct TemplateInstantiationArgumentCollecter } // namespace -MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs( - const NamedDecl *ND, const DeclContext *DC, bool Final, +void Sema::getTemplateInstantiationArgs( + MultiLevelTemplateArgumentList &Result, const NamedDecl *ND, + const DeclContext *DC, bool Final, std::optional> Innermost, bool RelativeToPrimary, bool ForConstraintInstantiation) { assert((ND || DC) && "Can't find arguments for a decl if one isn't provided"); // Accumulate the set of template argument lists in this structure. - MultiLevelTemplateArgumentList Result; const Decl *CurDecl = ND; if (!CurDecl) @@ -529,6 +529,17 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs( do { CurDecl = Collecter.Visit(const_cast(CurDecl)); } while (CurDecl); +} + +MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs( + const NamedDecl *ND, const DeclContext *DC, bool Final, + std::optional> Innermost, bool RelativeToPrimary, + bool ForConstraintInstantiation) { + assert((ND || DC) && "Can't find arguments for a decl if one isn't provided"); + // Accumulate the set of template argument lists in this structure. + MultiLevelTemplateArgumentList Result; + getTemplateInstantiationArgs(Result, ND, DC, Final, Innermost, + RelativeToPrimary, ForConstraintInstantiation); return Result; } @@ -562,7 +573,6 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const { case LambdaExpressionSubstitution: case BuildingDeductionGuides: case TypeAliasTemplateInstantiation: - case PartialOrderingTTP: return false; // This function should never be called when Kind's value is Memoization. @@ -795,11 +805,6 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( SemaRef, CodeSynthesisContext::BuildingDeductionGuides, PointOfInstantiation, InstantiationRange, Entity) {} -Sema::InstantiatingTemplate::InstantiatingTemplate( - Sema &SemaRef, SourceLocation ArgLoc, PartialOrderingTTP, - TemplateDecl *PArg, SourceRange InstantiationRange) - : InstantiatingTemplate(SemaRef, CodeSynthesisContext::PartialOrderingTTP, - ArgLoc, InstantiationRange, PArg) {} void Sema::pushCodeSynthesisContext(CodeSynthesisContext Ctx) { Ctx.SavedInNonInstantiationSFINAEContext = InNonInstantiationSFINAEContext; @@ -1239,14 +1244,6 @@ void Sema::PrintInstantiationStack() { << cast(Active->Entity) << Active->InstantiationRange; break; - case CodeSynthesisContext::PartialOrderingTTP: - Diags.Report(Active->PointOfInstantiation, - diag::note_template_arg_template_params_mismatch); - if (SourceLocation ParamLoc = Active->Entity->getLocation(); - ParamLoc.isValid()) - Diags.Report(ParamLoc, diag::note_template_prev_declaration) - << /*isTemplateTemplateParam=*/true << Active->InstantiationRange; - break; } } } @@ -1289,7 +1286,6 @@ std::optional Sema::isSFINAEContext() const { case CodeSynthesisContext::PriorTemplateArgumentSubstitution: case CodeSynthesisContext::DefaultTemplateArgumentChecking: case CodeSynthesisContext::RewritingOperatorAsSpaceship: - case CodeSynthesisContext::PartialOrderingTTP: // A default template argument instantiation and substitution into // template parameters with arguments for prior parameters may or may // not be a SFINAE context; look further up the stack. diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index d29434486dcb06..8cdf0b17d2dd2f 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -5015,6 +5015,16 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, Function->setInstantiationIsPending(true); PendingInstantiations.push_back( std::make_pair(Function, PointOfInstantiation)); + + if (llvm::isTimeTraceVerbose()) { + llvm::timeTraceAddInstantEvent("DeferInstantiation", [&] { + std::string Name; + llvm::raw_string_ostream OS(Name); + Function->getNameForDiagnostic(OS, getPrintingPolicy(), + /*Qualified=*/true); + return Name; + }); + } } else if (TSK == TSK_ImplicitInstantiation) { if (AtEndOfTU && !getDiagnostics().hasErrorOccurred() && !getSourceManager().isInSystemHeader(PatternDecl->getBeginLoc())) { @@ -5214,8 +5224,26 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, RebuildTypeSourceInfoForDefaultSpecialMembers(); SetDeclDefaulted(Function, PatternDecl->getLocation()); } else { - MultiLevelTemplateArgumentList TemplateArgs = getTemplateInstantiationArgs( - Function, Function->getLexicalDeclContext()); + DeclContext *DC = Function; + MultiLevelTemplateArgumentList TemplateArgs; + if (auto *Primary = Function->getPrimaryTemplate(); + Primary && + !isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) { + auto It = llvm::find_if(Primary->redecls(), + [](const RedeclarableTemplateDecl *RTD) { + return cast(RTD) + ->isCompatibleWithDefinition(); + }); + assert(It != Primary->redecls().end() && + "Should't get here without a definition"); + DC = (*It)->getLexicalDeclContext(); + if (Function->getTemplateSpecializationKind() != + TSK_ExplicitSpecialization) + TemplateArgs.addOuterTemplateArguments( + Function, Function->getTemplateSpecializationArgs()->asArray(), + /*Final=*/false); + } + getTemplateInstantiationArgs(TemplateArgs, /*D=*/nullptr, DC); // Substitute into the qualifier; we can get a substitution failure here // through evil use of alias templates. diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 01c086a602dd5a..5753c9eccf6c92 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -1760,6 +1760,15 @@ class TreeTransform { EndLoc); } + /// Build a new OpenMP 'permutation' clause. + OMPClause *RebuildOMPPermutationClause(ArrayRef PermExprs, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc) { + return getSema().OpenMP().ActOnOpenMPPermutationClause(PermExprs, StartLoc, + LParenLoc, EndLoc); + } + /// Build a new OpenMP 'full' clause. OMPClause *RebuildOMPFullClause(SourceLocation StartLoc, SourceLocation EndLoc) { @@ -10279,6 +10288,32 @@ OMPClause *TreeTransform::TransformOMPSizesClause(OMPSizesClause *C) { C->getLParenLoc(), C->getEndLoc()); } +template +OMPClause * +TreeTransform::TransformOMPPermutationClause(OMPPermutationClause *C) { + SmallVector TransformedArgs; + TransformedArgs.reserve(C->getNumLoops()); + bool Changed = false; + for (Expr *E : C->getArgsRefs()) { + if (!E) { + TransformedArgs.push_back(nullptr); + continue; + } + + ExprResult T = getDerived().TransformExpr(E); + if (T.isInvalid()) + return nullptr; + if (E != T.get()) + Changed = true; + TransformedArgs.push_back(T.get()); + } + + if (!Changed && !getDerived().AlwaysRebuild()) + return C; + return RebuildOMPPermutationClause(TransformedArgs, C->getBeginLoc(), + C->getLParenLoc(), C->getEndLoc()); +} + template OMPClause *TreeTransform::TransformOMPFullClause(OMPFullClause *C) { if (!getDerived().AlwaysRebuild()) diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 4a20dfc09cd061..5c4f8d0e9c46cd 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -10605,6 +10605,11 @@ OMPClause *OMPClauseReader::readClause() { C = OMPSizesClause::CreateEmpty(Context, NumSizes); break; } + case llvm::omp::OMPC_permutation: { + unsigned NumLoops = Record.readInt(); + C = OMPPermutationClause::CreateEmpty(Context, NumLoops); + break; + } case llvm::omp::OMPC_full: C = OMPFullClause::CreateEmpty(Context); break; @@ -10993,6 +10998,12 @@ void OMPClauseReader::VisitOMPSizesClause(OMPSizesClause *C) { C->setLParenLoc(Record.readSourceLocation()); } +void OMPClauseReader::VisitOMPPermutationClause(OMPPermutationClause *C) { + for (Expr *&E : C->getArgsRefs()) + E = Record.readSubExpr(); + C->setLParenLoc(Record.readSourceLocation()); +} + void OMPClauseReader::VisitOMPFullClause(OMPFullClause *C) {} void OMPClauseReader::VisitOMPPartialClause(OMPPartialClause *C) { diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 1ccc810f415eb4..a44df84a8bcef2 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -1087,6 +1087,7 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) { FD->setHasImplicitReturnZero(FunctionDeclBits.getNextBit()); FD->setIsMultiVersion(FunctionDeclBits.getNextBit()); FD->setLateTemplateParsed(FunctionDeclBits.getNextBit()); + FD->setInstantiatedFromMemberTemplate(FunctionDeclBits.getNextBit()); FD->setFriendConstraintRefersToEnclosingTemplate( FunctionDeclBits.getNextBit()); FD->setUsesSEHTry(FunctionDeclBits.getNextBit()); diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 836532ca402ffc..4976327fc654ee 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -7188,6 +7188,13 @@ void OMPClauseWriter::VisitOMPSizesClause(OMPSizesClause *C) { Record.AddSourceLocation(C->getLParenLoc()); } +void OMPClauseWriter::VisitOMPPermutationClause(OMPPermutationClause *C) { + Record.push_back(C->getNumLoops()); + for (Expr *Size : C->getArgsRefs()) + Record.AddStmt(Size); + Record.AddSourceLocation(C->getLParenLoc()); +} + void OMPClauseWriter::VisitOMPFullClause(OMPFullClause *C) {} void OMPClauseWriter::VisitOMPPartialClause(OMPPartialClause *C) { diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index f21cbd11b6ab89..dec93317dc7b37 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -626,7 +626,7 @@ void ASTDeclWriter::VisitDeclaratorDecl(DeclaratorDecl *D) { } void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) { - static_assert(DeclContext::NumFunctionDeclBits == 44, + static_assert(DeclContext::NumFunctionDeclBits == 45, "You need to update the serializer after you change the " "FunctionDeclBits"); @@ -732,6 +732,7 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) { FunctionDeclBits.addBit(D->hasImplicitReturnZero()); FunctionDeclBits.addBit(D->isMultiVersion()); FunctionDeclBits.addBit(D->isLateTemplateParsed()); + FunctionDeclBits.addBit(D->isInstantiatedFromMemberTemplate()); FunctionDeclBits.addBit(D->FriendConstraintRefersToEnclosingTemplate()); FunctionDeclBits.addBit(D->usesSEHTry()); Record.push_back(FunctionDeclBits); diff --git a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp index cb5fcbade2cfc2..92e9d245520345 100644 --- a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -600,8 +600,9 @@ SVal SValBuilder::evalIntegralCast(ProgramStateRef state, SVal val, if (getContext().getTypeSize(castTy) >= getContext().getTypeSize(originalTy)) return evalCast(val, castTy, originalTy); - SymbolRef se = val.getAsSymbol(); - if (!se) // Let evalCast handle non symbolic expressions. + auto AsNonLoc = val.getAs(); + SymbolRef AsSymbol = val.getAsSymbol(); + if (!AsSymbol || !AsNonLoc) // Let evalCast handle non symbolic expressions. return evalCast(val, castTy, originalTy); // Find the maximum value of the target type. @@ -613,15 +614,14 @@ SVal SValBuilder::evalIntegralCast(ProgramStateRef state, SVal val, // Check the range of the symbol being casted against the maximum value of the // target type. - NonLoc FromVal = val.castAs(); QualType CmpTy = getConditionType(); - NonLoc CompVal = - evalBinOpNN(state, BO_LE, FromVal, ToTypeMaxVal, CmpTy).castAs(); + NonLoc CompVal = evalBinOpNN(state, BO_LE, *AsNonLoc, ToTypeMaxVal, CmpTy) + .castAs(); ProgramStateRef IsNotTruncated, IsTruncated; std::tie(IsNotTruncated, IsTruncated) = state->assume(CompVal); if (!IsNotTruncated && IsTruncated) { // Symbol is truncated so we evaluate it as a cast. - return makeNonLoc(se, originalTy, castTy); + return makeNonLoc(AsSymbol, originalTy, castTy); } return evalCast(val, castTy, originalTy); } diff --git a/clang/test/AST/ByteCode/builtin-functions.cpp b/clang/test/AST/ByteCode/builtin-functions.cpp index 18ccee382d44e3..450ff5671314db 100644 --- a/clang/test/AST/ByteCode/builtin-functions.cpp +++ b/clang/test/AST/ByteCode/builtin-functions.cpp @@ -956,7 +956,7 @@ namespace shufflevector { static_assert(vectorShuffle6[7] == 7, ""); constexpr vector4char vectorShuffleFail1 = __builtin_shufflevector( // both-error {{must be initialized by a constant expression}}\ - // ref-error {{index for __builtin_shufflevector not within the bounds of the input vectors; index of -1 found at position 0 is not permitted in a constexpr context}} + // both-error {{index for __builtin_shufflevector not within the bounds of the input vectors; index of -1 found at position 0 is not permitted in a constexpr context}} vector4charConst1, vector4charConst2, -1, -1, -1, -1); } diff --git a/clang/test/AST/ByteCode/codegen.cpp b/clang/test/AST/ByteCode/codegen.cpp index 12d8b5a5c548e1..ea2c812f30f6f0 100644 --- a/clang/test/AST/ByteCode/codegen.cpp +++ b/clang/test/AST/ByteCode/codegen.cpp @@ -1,6 +1,10 @@ // RUN: %clang_cc1 -triple x86_64-linux -emit-llvm -o - %s | FileCheck %s // RUN: %clang_cc1 -triple x86_64-linux -emit-llvm -o - %s -fexperimental-new-constant-interpreter | FileCheck %s +#ifdef __SIZEOF_INT128__ +// CHECK: @PR11705 = global i128 0 +__int128_t PR11705 = (__int128_t)&PR11705; +#endif int arr[2]; // CHECK: @pastEnd = constant ptr getelementptr (i8, ptr @arr, i64 8) diff --git a/clang/test/Analysis/range_casts.c b/clang/test/Analysis/range_casts.c index b1967730bf8613..8a3b610fd63dc1 100644 --- a/clang/test/Analysis/range_casts.c +++ b/clang/test/Analysis/range_casts.c @@ -154,3 +154,12 @@ void f15(long foo) else clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} } + +int *getIntPtr(void) { + extern int *intPtr; + return intPtr; +} +char call_malformed_fptr() { + int (*fptr)(void) = (int (*)(void))getIntPtr; + return fptr(); // no-crash +} diff --git a/clang/test/CIR/hello.c b/clang/test/CIR/hello.c new file mode 100644 index 00000000000000..61f38d0a5bd01a --- /dev/null +++ b/clang/test/CIR/hello.c @@ -0,0 +1,5 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s | FileCheck --allow-empty %s + +// just confirm that we don't crash +// CHECK-NOT: * +void foo() {} diff --git a/clang/test/CIR/lit.local.cfg b/clang/test/CIR/lit.local.cfg new file mode 100644 index 00000000000000..6afd60f47bff90 --- /dev/null +++ b/clang/test/CIR/lit.local.cfg @@ -0,0 +1,2 @@ +if not config.root.clang_enable_cir: + config.unsupported = True diff --git a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp index ce27e6aa83c3b9..19793fe8263726 100644 --- a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp +++ b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp @@ -2,13 +2,13 @@ template struct eval; // expected-note 3{{template is declared here}} -template