diff --git a/.mailmap b/.mailmap index 8a99d77389b24..4c787a4560399 100644 --- a/.mailmap +++ b/.mailmap @@ -36,6 +36,8 @@ Jon Roelofs Jon Roelofs LLVM GN Syncbot Martin Storsjö +Med Ismail Bennani +Med Ismail Bennani Ramkumar Ramachandra Saleem Abdulrasool Tommy Chiang diff --git a/bolt/docs/doxygen.cfg.in b/bolt/docs/doxygen.cfg.in index acdf6f66842b6..421ef4760aa37 100644 --- a/bolt/docs/doxygen.cfg.in +++ b/bolt/docs/doxygen.cfg.in @@ -2107,7 +2107,7 @@ CLASS_GRAPH = YES # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -COLLABORATION_GRAPH = YES +COLLABORATION_GRAPH = NO # If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for # groups, showing the direct groups dependencies. @@ -2152,7 +2152,7 @@ TEMPLATE_RELATIONS = NO # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -INCLUDE_GRAPH = YES +INCLUDE_GRAPH = NO # If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are # set to YES then doxygen will generate a graph for each documented file showing @@ -2161,7 +2161,7 @@ INCLUDE_GRAPH = YES # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -INCLUDED_BY_GRAPH = YES +INCLUDED_BY_GRAPH = NO # If the CALL_GRAPH tag is set to YES then doxygen will generate a call # dependency graph for every global function or class method. diff --git a/bolt/include/bolt/Core/BinaryBasicBlock.h b/bolt/include/bolt/Core/BinaryBasicBlock.h index 8c044597b5fd0..c9694ab29d440 100644 --- a/bolt/include/bolt/Core/BinaryBasicBlock.h +++ b/bolt/include/bolt/Core/BinaryBasicBlock.h @@ -144,6 +144,9 @@ class BinaryBasicBlock { /// blocks may contain out of date or incorrect information. bool IsValid{true}; + /// Last computed hash value. + mutable uint64_t Hash{0}; + private: BinaryBasicBlock() = delete; BinaryBasicBlock(const BinaryBasicBlock &) = delete; @@ -939,6 +942,9 @@ class BinaryBasicBlock { /// Check if the block has a jump table instruction. bool hasJumpTable() const { return getJumpTable() != nullptr; } + /// Returns the last computed hash value of the block. + uint64_t getHash() const { return Hash; } + private: void adjustNumPseudos(const MCInst &Inst, int Sign); @@ -963,6 +969,9 @@ class BinaryBasicBlock { /// Set the index of this basic block. void setIndex(unsigned I) { Index = I; } + /// Sets the hash value of the basic block. + void setHash(uint64_t Value) const { Hash = Value; } + template void clearList(T &List) { T TempList; TempList.swap(List); diff --git a/bolt/include/bolt/Core/BinaryFunction.h b/bolt/include/bolt/Core/BinaryFunction.h index a96243c579eb0..a54492c290f00 100644 --- a/bolt/include/bolt/Core/BinaryFunction.h +++ b/bolt/include/bolt/Core/BinaryFunction.h @@ -2205,6 +2205,9 @@ class BinaryFunction { return std::string(); }) const; + /// Compute hash values for each block of the function. + void computeBlockHashes() const; + void setDWARFUnit(DWARFUnit *Unit) { DwarfUnit = Unit; } /// Return DWARF compile unit for this function. diff --git a/bolt/include/bolt/Core/HashUtilities.h b/bolt/include/bolt/Core/HashUtilities.h new file mode 100644 index 0000000000000..8d445ff837564 --- /dev/null +++ b/bolt/include/bolt/Core/HashUtilities.h @@ -0,0 +1,41 @@ +//===- bolt/Core/HashUtilities.h - Misc hash utilities --------------------===// +// +// 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 contains functions for computing hash values over BinaryFunction +// and BinaryBasicBlock. +// +//===----------------------------------------------------------------------===// + +#ifndef BOLT_CORE_HASH_UTILITIES_H +#define BOLT_CORE_HASH_UTILITIES_H + +#include "bolt/Core/BinaryBasicBlock.h" +#include "bolt/Core/BinaryContext.h" + +namespace llvm { +namespace bolt { + +uint16_t hash_64_to_16(const uint64_t Hash); + +std::string hashInteger(uint64_t Value); + +std::string hashSymbol(BinaryContext &BC, const MCSymbol &Symbol); + +std::string hashExpr(BinaryContext &BC, const MCExpr &Expr); + +std::string hashInstOperand(BinaryContext &BC, const MCOperand &Operand); + +using OperandHashFuncTy = function_ref; + +std::string hashBlock(BinaryContext &BC, const BinaryBasicBlock &BB, + OperandHashFuncTy OperandHashFunc); + +} // namespace bolt +} // namespace llvm + +#endif diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h index e6eb1429d3a18..86756a4434ed9 100644 --- a/bolt/include/bolt/Core/MCPlusBuilder.h +++ b/bolt/include/bolt/Core/MCPlusBuilder.h @@ -466,6 +466,11 @@ class MCPlusBuilder { virtual MCPhysReg getX86R11() const { llvm_unreachable("not implemented"); } + virtual unsigned getShortBranchOpcode(unsigned Opcode) const { + llvm_unreachable("not implemented"); + return 0; + } + /// Create increment contents of target by 1 for Instrumentation virtual InstructionListType createInstrIncMemory(const MCSymbol *Target, MCContext *Ctx, diff --git a/bolt/include/bolt/Profile/ProfileYAMLMapping.h b/bolt/include/bolt/Profile/ProfileYAMLMapping.h index e20d449e0b93e..f02f2197c469a 100644 --- a/bolt/include/bolt/Profile/ProfileYAMLMapping.h +++ b/bolt/include/bolt/Profile/ProfileYAMLMapping.h @@ -126,6 +126,7 @@ template <> struct MappingTraits { static void mapping(IO &YamlIO, bolt::BinaryBasicBlockProfile &BBP) { YamlIO.mapRequired("bid", BBP.Index); YamlIO.mapRequired("insns", BBP.NumInstructions); + YamlIO.mapOptional("hash", BBP.Hash, (llvm::yaml::Hex64)0); YamlIO.mapOptional("exec", BBP.ExecCount, (uint64_t)0); YamlIO.mapOptional("events", BBP.EventCount, (uint64_t)0); YamlIO.mapOptional("calls", BBP.CallSites, diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp index 89ba3627cdd7d..47c6b7eddaf83 100644 --- a/bolt/lib/Core/BinaryFunction.cpp +++ b/bolt/lib/Core/BinaryFunction.cpp @@ -14,6 +14,7 @@ #include "bolt/Core/BinaryBasicBlock.h" #include "bolt/Core/BinaryDomTree.h" #include "bolt/Core/DynoStats.h" +#include "bolt/Core/HashUtilities.h" #include "bolt/Core/MCPlusBuilder.h" #include "bolt/Utils/NameResolver.h" #include "bolt/Utils/NameShortener.h" @@ -3604,34 +3605,18 @@ size_t BinaryFunction::computeHash(bool UseDFS, // The hash is computed by creating a string of all instruction opcodes and // possibly their operands and then hashing that string with std::hash. std::string HashString; - for (const BinaryBasicBlock *BB : Order) { - for (const MCInst &Inst : *BB) { - unsigned Opcode = Inst.getOpcode(); - - if (BC.MIB->isPseudo(Inst)) - continue; + for (const BinaryBasicBlock *BB : Order) + HashString.append(hashBlock(BC, *BB, OperandHashFunc)); - // Ignore unconditional jumps since we check CFG consistency by processing - // basic blocks in order and do not rely on branches to be in-sync with - // CFG. Note that we still use condition code of conditional jumps. - if (BC.MIB->isUnconditionalBranch(Inst)) - continue; - - if (Opcode == 0) - HashString.push_back(0); - - while (Opcode) { - uint8_t LSB = Opcode & 0xff; - HashString.push_back(LSB); - Opcode = Opcode >> 8; - } + return Hash = std::hash{}(HashString); +} - for (const MCOperand &Op : MCPlus::primeOperands(Inst)) - HashString.append(OperandHashFunc(Op)); - } +void BinaryFunction::computeBlockHashes() const { + for (const BinaryBasicBlock *BB : BasicBlocks) { + std::string Hash = + hashBlock(BC, *BB, [](const MCOperand &Op) { return std::string(); }); + BB->setHash(std::hash{}(Hash)); } - - return Hash = std::hash{}(HashString); } void BinaryFunction::insertBasicBlocks( diff --git a/bolt/lib/Core/CMakeLists.txt b/bolt/lib/Core/CMakeLists.txt index e7215b2babd65..7c633e37eeb77 100644 --- a/bolt/lib/Core/CMakeLists.txt +++ b/bolt/lib/Core/CMakeLists.txt @@ -20,6 +20,7 @@ add_llvm_library(LLVMBOLTCore DynoStats.cpp Exceptions.cpp FunctionLayout.cpp + HashUtilities.cpp JumpTable.cpp MCPlusBuilder.cpp ParallelUtilities.cpp diff --git a/bolt/lib/Core/DebugData.cpp b/bolt/lib/Core/DebugData.cpp index 2b56357df9c03..f1204925a98a4 100644 --- a/bolt/lib/Core/DebugData.cpp +++ b/bolt/lib/Core/DebugData.cpp @@ -732,8 +732,16 @@ void DebugLoclistWriter::addList(AttrInfo &AttrVal, uint32_t DebugLoclistWriter::LoclistBaseOffset = 0; void DebugLoclistWriter::finalizeDWARF5( DebugInfoBinaryPatcher &DebugInfoPatcher, DebugAbbrevWriter &AbbrevWriter) { - if (LocBodyBuffer->empty()) + if (LocBodyBuffer->empty()) { + std::optional AttrInfoVal = + findAttributeInfo(CU.getUnitDIE(), dwarf::DW_AT_loclists_base); + // Pointing to first one, because it doesn't matter. There are no uses of it + // in this CU. + if (!isSplitDwarf() && AttrInfoVal) + DebugInfoPatcher.addLE32Patch(AttrInfoVal->Offset, + getDWARF5RngListLocListHeaderSize()); return; + } std::unique_ptr LocArrayBuffer = std::make_unique(); diff --git a/bolt/lib/Core/HashUtilities.cpp b/bolt/lib/Core/HashUtilities.cpp new file mode 100644 index 0000000000000..1c980e003f884 --- /dev/null +++ b/bolt/lib/Core/HashUtilities.cpp @@ -0,0 +1,135 @@ +//===- bolt/Core/HashUtilities.cpp - Misc hash utilities ------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Computation of hash values over BinaryFunction and BinaryBasicBlock. +// +//===----------------------------------------------------------------------===// + +#include "bolt/Core/HashUtilities.h" +#include "bolt/Core/BinaryContext.h" +#include "bolt/Core/BinaryFunction.h" + +namespace llvm { +namespace bolt { + +/// Hashing a 64-bit integer to a 16-bit one. +uint16_t hash_64_to_16(const uint64_t Hash) { + uint16_t Res = (uint16_t)(Hash & 0xFFFF); + Res ^= (uint16_t)((Hash >> 16) & 0xFFFF); + Res ^= (uint16_t)((Hash >> 32) & 0xFFFF); + Res ^= (uint16_t)((Hash >> 48) & 0xFFFF); + return Res; +} + +std::string hashInteger(uint64_t Value) { + std::string HashString; + if (Value == 0) + HashString.push_back(0); + + while (Value) { + uint8_t LSB = Value & 0xff; + HashString.push_back(LSB); + Value >>= 8; + } + + return HashString; +} + +std::string hashSymbol(BinaryContext &BC, const MCSymbol &Symbol) { + std::string HashString; + + // Ignore function references. + if (BC.getFunctionForSymbol(&Symbol)) + return HashString; + + llvm::ErrorOr ErrorOrValue = BC.getSymbolValue(Symbol); + if (!ErrorOrValue) + return HashString; + + // Ignore jump table references. + if (BC.getJumpTableContainingAddress(*ErrorOrValue)) + return HashString; + + return HashString.append(hashInteger(*ErrorOrValue)); +} + +std::string hashExpr(BinaryContext &BC, const MCExpr &Expr) { + switch (Expr.getKind()) { + case MCExpr::Constant: + return hashInteger(cast(Expr).getValue()); + case MCExpr::SymbolRef: + return hashSymbol(BC, cast(Expr).getSymbol()); + case MCExpr::Unary: { + const auto &UnaryExpr = cast(Expr); + return hashInteger(UnaryExpr.getOpcode()) + .append(hashExpr(BC, *UnaryExpr.getSubExpr())); + } + case MCExpr::Binary: { + const auto &BinaryExpr = cast(Expr); + return hashExpr(BC, *BinaryExpr.getLHS()) + .append(hashInteger(BinaryExpr.getOpcode())) + .append(hashExpr(BC, *BinaryExpr.getRHS())); + } + case MCExpr::Target: + return std::string(); + } + + llvm_unreachable("invalid expression kind"); +} + +std::string hashInstOperand(BinaryContext &BC, const MCOperand &Operand) { + if (Operand.isImm()) + return hashInteger(Operand.getImm()); + if (Operand.isReg()) + return hashInteger(Operand.getReg()); + if (Operand.isExpr()) + return hashExpr(BC, *Operand.getExpr()); + + return std::string(); +} + +std::string hashBlock(BinaryContext &BC, const BinaryBasicBlock &BB, + OperandHashFuncTy OperandHashFunc) { + const bool IsX86 = BC.isX86(); + + // The hash is computed by creating a string of all instruction opcodes and + // possibly their operands and then hashing that string with std::hash. + std::string HashString; + + for (const MCInst &Inst : BB) { + if (BC.MIB->isPseudo(Inst)) + continue; + + unsigned Opcode = Inst.getOpcode(); + + // Ignore unconditional jumps since we check CFG consistency by processing + // basic blocks in order and do not rely on branches to be in-sync with + // CFG. Note that we still use condition code of conditional jumps. + if (BC.MIB->isUnconditionalBranch(Inst)) + continue; + + if (IsX86 && BC.MIB->isConditionalBranch(Inst)) + Opcode = BC.MIB->getShortBranchOpcode(Opcode); + + if (Opcode == 0) + HashString.push_back(0); + + while (Opcode) { + uint8_t LSB = Opcode & 0xff; + HashString.push_back(LSB); + Opcode = Opcode >> 8; + } + + for (const MCOperand &Op : MCPlus::primeOperands(Inst)) + HashString.append(OperandHashFunc(Op)); + } + return HashString; +} + +} // namespace bolt +} // namespace llvm diff --git a/bolt/lib/Passes/AsmDump.cpp b/bolt/lib/Passes/AsmDump.cpp index c163345fe7f2e..18d0395cbc4ad 100644 --- a/bolt/lib/Passes/AsmDump.cpp +++ b/bolt/lib/Passes/AsmDump.cpp @@ -55,34 +55,6 @@ void dumpCFI(const BinaryFunction &BF, const MCInst &Instr, AsmPrinter &MAP) { } } -void dumpJumpTableFdata(raw_ostream &OS, const BinaryFunction &BF, - const MCInst &Instr, const std::string &BranchLabel) { - StringRef FunctionName = BF.getOneName(); - const JumpTable *JT = BF.getJumpTable(Instr); - for (uint32_t i = 0; i < JT->Entries.size(); ++i) { - StringRef TargetName = JT->Entries[i]->getName(); - const uint64_t Mispreds = JT->Counts[i].Mispreds; - const uint64_t Count = JT->Counts[i].Count; - OS << "# FDATA: 1 " << FunctionName << " #" << BranchLabel << "# " - << "1 " << FunctionName << " #" << TargetName << "# " << Mispreds << " " - << Count << '\n'; - } -} - -void dumpTailCallFdata(raw_ostream &OS, const BinaryFunction &BF, - const MCInst &Instr, const std::string &BranchLabel) { - const BinaryContext &BC = BF.getBinaryContext(); - StringRef FunctionName = BF.getOneName(); - auto CallFreq = BC.MIB->getAnnotationWithDefault(Instr, "Count"); - const MCSymbol *Target = BC.MIB->getTargetSymbol(Instr); - const BinaryFunction *TargetBF = BC.getFunctionForSymbol(Target); - if (!TargetBF) - return; - OS << "# FDATA: 1 " << FunctionName << " #" << BranchLabel << "# " - << "1 " << TargetBF->getPrintName() << " 0 " - << "0 " << CallFreq << '\n'; -} - void dumpTargetFunctionStub(raw_ostream &OS, const BinaryContext &BC, const MCSymbol *CalleeSymb, const BinarySection *&LastCS) { @@ -230,12 +202,6 @@ void dumpFunction(const BinaryFunction &BF) { BC.InstPrinter->printInst(&Instr, 0, "", *BC.STI, OS); OS << '\n'; - - // Dump profile data in FDATA format (as parsed by link_fdata). - if (BC.MIB->getJumpTable(Instr)) - dumpJumpTableFdata(OS, BF, Instr, BranchLabel); - else if (BC.MIB->isTailCall(Instr)) - dumpTailCallFdata(OS, BF, Instr, BranchLabel); } // Dump profile data in FDATA format (as parsed by link_fdata). diff --git a/bolt/lib/Passes/IdenticalCodeFolding.cpp b/bolt/lib/Passes/IdenticalCodeFolding.cpp index ccf1749c47bf3..e96666b37bd93 100644 --- a/bolt/lib/Passes/IdenticalCodeFolding.cpp +++ b/bolt/lib/Passes/IdenticalCodeFolding.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "bolt/Passes/IdenticalCodeFolding.h" +#include "bolt/Core/HashUtilities.h" #include "bolt/Core/ParallelUtilities.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/CommandLine.h" @@ -337,74 +338,6 @@ typedef std::unordered_map, KeyHash, KeyEqual> IdenticalBucketsMap; -static std::string hashInteger(uint64_t Value) { - std::string HashString; - if (Value == 0) - HashString.push_back(0); - - while (Value) { - uint8_t LSB = Value & 0xff; - HashString.push_back(LSB); - Value >>= 8; - } - - return HashString; -} - -static std::string hashSymbol(BinaryContext &BC, const MCSymbol &Symbol) { - std::string HashString; - - // Ignore function references. - if (BC.getFunctionForSymbol(&Symbol)) - return HashString; - - llvm::ErrorOr ErrorOrValue = BC.getSymbolValue(Symbol); - if (!ErrorOrValue) - return HashString; - - // Ignore jump table references. - if (BC.getJumpTableContainingAddress(*ErrorOrValue)) - return HashString; - - return HashString.append(hashInteger(*ErrorOrValue)); -} - -static std::string hashExpr(BinaryContext &BC, const MCExpr &Expr) { - switch (Expr.getKind()) { - case MCExpr::Constant: - return hashInteger(cast(Expr).getValue()); - case MCExpr::SymbolRef: - return hashSymbol(BC, cast(Expr).getSymbol()); - case MCExpr::Unary: { - const auto &UnaryExpr = cast(Expr); - return hashInteger(UnaryExpr.getOpcode()) - .append(hashExpr(BC, *UnaryExpr.getSubExpr())); - } - case MCExpr::Binary: { - const auto &BinaryExpr = cast(Expr); - return hashExpr(BC, *BinaryExpr.getLHS()) - .append(hashInteger(BinaryExpr.getOpcode())) - .append(hashExpr(BC, *BinaryExpr.getRHS())); - } - case MCExpr::Target: - return std::string(); - } - - llvm_unreachable("invalid expression kind"); -} - -static std::string hashInstOperand(BinaryContext &BC, - const MCOperand &Operand) { - if (Operand.isImm()) - return hashInteger(Operand.getImm()); - if (Operand.isReg()) - return hashInteger(Operand.getReg()); - if (Operand.isExpr()) - return hashExpr(BC, *Operand.getExpr()); - - return std::string(); -} - namespace llvm { namespace bolt { diff --git a/bolt/lib/Profile/YAMLProfileWriter.cpp b/bolt/lib/Profile/YAMLProfileWriter.cpp index bee18a6c1627e..01ff9e2c26dee 100644 --- a/bolt/lib/Profile/YAMLProfileWriter.cpp +++ b/bolt/lib/Profile/YAMLProfileWriter.cpp @@ -28,9 +28,13 @@ void convert(const BinaryFunction &BF, const uint16_t LBRProfile = BF.getProfileFlags() & BinaryFunction::PF_LBR; + // Prepare function and block hashes + BF.computeHash(/*UseDFS=*/true); + BF.computeBlockHashes(); + YamlBF.Name = BF.getPrintName(); YamlBF.Id = BF.getFunctionNumber(); - YamlBF.Hash = BF.computeHash(/*UseDFS=*/true); + YamlBF.Hash = BF.getHash(); YamlBF.NumBasicBlocks = BF.size(); YamlBF.ExecCount = BF.getKnownExecutionCount(); @@ -38,6 +42,7 @@ void convert(const BinaryFunction &BF, yaml::bolt::BinaryBasicBlockProfile YamlBB; YamlBB.Index = BB->getLayoutIndex(); YamlBB.NumInstructions = BB->getNumNonPseudos(); + YamlBB.Hash = BB->getHash(); if (!LBRProfile) { YamlBB.EventCount = BB->getKnownExecutionCount(); @@ -112,11 +117,15 @@ void convert(const BinaryFunction &BF, // Include landing pads with non-zero execution count. if (YamlBB.CallSites.empty() && !BB->isEntryPoint() && !(BB->isLandingPad() && BB->getKnownExecutionCount() != 0)) { + // Include blocks having successors or predecessors with positive counts. uint64_t SuccessorExecCount = 0; for (const BinaryBasicBlock::BinaryBranchInfo &BranchInfo : BB->branch_info()) SuccessorExecCount += BranchInfo.Count; - if (!SuccessorExecCount) + uint64_t PredecessorExecCount = 0; + for (auto Pred : BB->predecessors()) + PredecessorExecCount += Pred->getBranchInfo(*BB).Count; + if (!SuccessorExecCount && !PredecessorExecCount) continue; } diff --git a/bolt/lib/Rewrite/ExecutableFileMemoryManager.cpp b/bolt/lib/Rewrite/ExecutableFileMemoryManager.cpp index 8c41ca97fd353..bc0dd2faa694c 100644 --- a/bolt/lib/Rewrite/ExecutableFileMemoryManager.cpp +++ b/bolt/lib/Rewrite/ExecutableFileMemoryManager.cpp @@ -27,6 +27,12 @@ uint8_t *ExecutableFileMemoryManager::allocateSection( uint8_t *Ret = static_cast(llvm::allocate_buffer(Size, Alignment)); AllocatedSections.push_back(AllocInfo{Ret, Size, Alignment}); + // A Size of 1 might mean an empty section for which RuntimeDyld decided to + // allocate 1 byte. In this case, the allocation will never be initialized + // causing non-deterministic output section contents. + if (Size == 1) + *Ret = 0; + // Register a debug section as a note section. if (!ObjectsLoaded && RewriteInstance::isDebugSection(SectionName)) { BinarySection &Section = diff --git a/bolt/lib/Rewrite/MachORewriteInstance.cpp b/bolt/lib/Rewrite/MachORewriteInstance.cpp index f1f20f278b112..8078dc4e3fd24 100644 --- a/bolt/lib/Rewrite/MachORewriteInstance.cpp +++ b/bolt/lib/Rewrite/MachORewriteInstance.cpp @@ -527,6 +527,7 @@ void MachORewriteInstance::emitAndLink() { mapCodeSections(); mapInstrumentationSection("__counters"); mapInstrumentationSection("__tables"); + RTDyld->finalizeWithMemoryManagerLocking(); // TODO: Refactor addRuntimeLibSections to work properly on Mach-O // and use it here. diff --git a/bolt/lib/Target/X86/X86MCPlusBuilder.cpp b/bolt/lib/Target/X86/X86MCPlusBuilder.cpp index ad80255dcf35e..5a45f14db5979 100644 --- a/bolt/lib/Target/X86/X86MCPlusBuilder.cpp +++ b/bolt/lib/Target/X86/X86MCPlusBuilder.cpp @@ -50,17 +50,6 @@ static cl::opt X86StripRedundantAddressSize( namespace { -unsigned getShortBranchOpcode(unsigned Opcode) { - switch (Opcode) { - default: - return Opcode; - case X86::JMP_2: return X86::JMP_1; - case X86::JMP_4: return X86::JMP_1; - case X86::JCC_2: return X86::JCC_1; - case X86::JCC_4: return X86::JCC_1; - } -} - unsigned getShortArithOpcode(unsigned Opcode) { return X86::getShortOpcodeArith(Opcode); } @@ -2867,6 +2856,21 @@ class X86MCPlusBuilder : public MCPlusBuilder { MCPhysReg getX86R11() const override { return X86::R11; } + unsigned getShortBranchOpcode(unsigned Opcode) const override { + switch (Opcode) { + default: + return Opcode; + case X86::JMP_2: + return X86::JMP_1; + case X86::JMP_4: + return X86::JMP_1; + case X86::JCC_2: + return X86::JCC_1; + case X86::JCC_4: + return X86::JCC_1; + } + } + MCPhysReg getIntArgRegister(unsigned ArgNo) const override { // FIXME: this should depend on the calling convention. switch (ArgNo) { diff --git a/bolt/test/X86/Inputs/dwarf5-loc-base-no-loc-accesshelper.s b/bolt/test/X86/Inputs/dwarf5-loc-base-no-loc-accesshelper.s new file mode 100644 index 0000000000000..dcf9f6a23a07b --- /dev/null +++ b/bolt/test/X86/Inputs/dwarf5-loc-base-no-loc-accesshelper.s @@ -0,0 +1,410 @@ +# clang++ helper.cpp -g -O2 -S +# int fooVar = 0; +# void useFoo(int * x) { +# *x += 4; +# } +# +# int foo(int argc) { +# int x = argc; +# useFoo(&x); +# return x; +# } +# Manually removed DW_AT_location + .text + .file "foo.cpp" + .file 0 "/testLocListMultiple" "foo.cpp" md5 0x9410f31145d031fcc1f2464b809e409a + .globl _Z6useFooPi # -- Begin function _Z6useFooPi + .p2align 4, 0x90 + .type _Z6useFooPi,@function +_Z6useFooPi: # @_Z6useFooPi +.Lfunc_begin0: + .loc 0 2 0 # foo.cpp:2:0 + .cfi_startproc +# %bb.0: # %entry + #DEBUG_VALUE: useFoo:x <- $rdi + .loc 0 3 4 prologue_end # foo.cpp:3:4 + addl $4, (%rdi) + .loc 0 4 1 # foo.cpp:4:1 + retq +.Ltmp0: +.Lfunc_end0: + .size _Z6useFooPi, .Lfunc_end0-_Z6useFooPi + .cfi_endproc + # -- End function + .globl _Z3fooi # -- Begin function _Z3fooi + .p2align 4, 0x90 + .type _Z3fooi,@function +_Z3fooi: # @_Z3fooi +.Lfunc_begin1: + .loc 0 6 0 # foo.cpp:6:0 + .cfi_startproc +# %bb.0: # %entry + #DEBUG_VALUE: foo:argc <- $edi + #DEBUG_VALUE: foo:x <- $edi + # kill: def $edi killed $edi def $rdi + #DEBUG_VALUE: useFoo:x <- undef + .loc 0 3 4 prologue_end # foo.cpp:3:4 + leal 4(%rdi), %eax +.Ltmp1: + #DEBUG_VALUE: foo:x <- $eax + .loc 0 9 4 # foo.cpp:9:4 + retq +.Ltmp2: +.Lfunc_end1: + .size _Z3fooi, .Lfunc_end1-_Z3fooi + .cfi_endproc + # -- End function + .type fooVar,@object # @fooVar + .bss + .globl fooVar + .p2align 2 +fooVar: + .long 0 # 0x0 + .size fooVar, 4 + + .section .debug_loclists,"",@progbits + .long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length +.Ldebug_list_header_start0: + .short 5 # Version + .byte 8 # Address size + .byte 0 # Segment selector size + .long 1 # Offset entry count +.Lloclists_table_base0: + .long .Ldebug_loc0-.Lloclists_table_base0 +.Ldebug_loc0: + .byte 4 # DW_LLE_offset_pair + .uleb128 .Lfunc_begin1-.Lfunc_begin0 # starting offset + .uleb128 .Ltmp1-.Lfunc_begin0 # ending offset + .byte 1 # Loc expr size + .byte 85 # super-register DW_OP_reg5 + .byte 4 # DW_LLE_offset_pair + .uleb128 .Ltmp1-.Lfunc_begin0 # starting offset + .uleb128 .Lfunc_end1-.Lfunc_begin0 # ending offset + .byte 1 # Loc expr size + .byte 80 # super-register DW_OP_reg0 + .byte 0 # DW_LLE_end_of_list +.Ldebug_list_header_end0: + .section .debug_abbrev,"",@progbits + .byte 1 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 1 # DW_CHILDREN_yes + .byte 37 # DW_AT_producer + .byte 37 # DW_FORM_strx1 + .byte 19 # DW_AT_language + .byte 5 # DW_FORM_data2 + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 114 # DW_AT_str_offsets_base + .byte 23 # DW_FORM_sec_offset + .byte 16 # DW_AT_stmt_list + .byte 23 # DW_FORM_sec_offset + .byte 27 # DW_AT_comp_dir + .byte 37 # DW_FORM_strx1 + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 115 # DW_AT_addr_base + .byte 23 # DW_FORM_sec_offset + .ascii "\214\001" # DW_AT_loclists_base + .byte 23 # DW_FORM_sec_offset + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 2 # Abbreviation Code + .byte 52 # DW_TAG_variable + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 3 # Abbreviation Code + .byte 36 # DW_TAG_base_type + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 62 # DW_AT_encoding + .byte 11 # DW_FORM_data1 + .byte 11 # DW_AT_byte_size + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 4 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 1 # DW_CHILDREN_yes + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 64 # DW_AT_frame_base + .byte 24 # DW_FORM_exprloc + .byte 122 # DW_AT_call_all_calls + .byte 25 # DW_FORM_flag_present + .byte 49 # DW_AT_abstract_origin + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 5 # Abbreviation Code + .byte 5 # DW_TAG_formal_parameter + .byte 0 # DW_CHILDREN_no + .byte 49 # DW_AT_abstract_origin + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 6 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 1 # DW_CHILDREN_yes + .byte 110 # DW_AT_linkage_name + .byte 37 # DW_FORM_strx1 + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 32 # DW_AT_inline + .byte 33 # DW_FORM_implicit_const + .byte 1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 7 # Abbreviation Code + .byte 5 # DW_TAG_formal_parameter + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 8 # Abbreviation Code + .byte 15 # DW_TAG_pointer_type + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 9 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 1 # DW_CHILDREN_yes + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 64 # DW_AT_frame_base + .byte 24 # DW_FORM_exprloc + .byte 122 # DW_AT_call_all_calls + .byte 25 # DW_FORM_flag_present + .byte 110 # DW_AT_linkage_name + .byte 37 # DW_FORM_strx1 + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 10 # Abbreviation Code + .byte 5 # DW_TAG_formal_parameter + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 11 # Abbreviation Code + .byte 52 # DW_TAG_variable + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 12 # Abbreviation Code + .byte 29 # DW_TAG_inlined_subroutine + .byte 0 # DW_CHILDREN_no + .byte 49 # DW_AT_abstract_origin + .byte 19 # DW_FORM_ref4 + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 88 # DW_AT_call_file + .byte 11 # DW_FORM_data1 + .byte 89 # DW_AT_call_line + .byte 11 # DW_FORM_data1 + .byte 87 # DW_AT_call_column + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + .section .debug_info,"",@progbits +.Lcu_begin0: + .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit +.Ldebug_info_start0: + .short 5 # DWARF version number + .byte 1 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long .debug_abbrev # Offset Into Abbrev. Section + .byte 1 # Abbrev [1] 0xc:0x83 DW_TAG_compile_unit + .byte 0 # DW_AT_producer + .short 33 # DW_AT_language + .byte 1 # DW_AT_name + .long .Lstr_offsets_base0 # DW_AT_str_offsets_base + .long .Lline_table_start0 # DW_AT_stmt_list + .byte 2 # DW_AT_comp_dir + .byte 1 # DW_AT_low_pc + .long .Lfunc_end1-.Lfunc_begin0 # DW_AT_high_pc + .long .Laddr_table_base0 # DW_AT_addr_base + .long .Lloclists_table_base0 # DW_AT_loclists_base + .byte 2 # Abbrev [2] 0x27:0xb DW_TAG_variable + .byte 3 # DW_AT_name + .long 50 # DW_AT_type + # DW_AT_external + .byte 0 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + .byte 0 + .byte 3 # Abbrev [3] 0x32:0x4 DW_TAG_base_type + .byte 4 # DW_AT_name + .byte 5 # DW_AT_encoding + .byte 4 # DW_AT_byte_size + .byte 4 # Abbrev [4] 0x36:0x14 DW_TAG_subprogram + .byte 1 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .byte 1 # DW_AT_frame_base + .byte 87 + # DW_AT_call_all_calls + .long 74 # DW_AT_abstract_origin + .byte 5 # Abbrev [5] 0x42:0x7 DW_TAG_formal_parameter + .long 79 # DW_AT_abstract_origin + .byte 0 # End Of Children Mark + .byte 6 # Abbrev [6] 0x4a:0xe DW_TAG_subprogram + .byte 5 # DW_AT_linkage_name + .byte 6 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 2 # DW_AT_decl_line + # DW_AT_external + # DW_AT_inline + .byte 7 # Abbrev [7] 0x4f:0x8 DW_TAG_formal_parameter + .byte 7 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 2 # DW_AT_decl_line + .long 88 # DW_AT_type + .byte 0 # End Of Children Mark + .byte 8 # Abbrev [8] 0x58:0x5 DW_TAG_pointer_type + .long 50 # DW_AT_type + .byte 9 # Abbrev [9] 0x5d:0x31 DW_TAG_subprogram + .byte 2 # DW_AT_low_pc + .long .Lfunc_end1-.Lfunc_begin1 # DW_AT_high_pc + .byte 1 # DW_AT_frame_base + .byte 87 + # DW_AT_call_all_calls + .byte 8 # DW_AT_linkage_name + .byte 9 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 6 # DW_AT_decl_line + .long 50 # DW_AT_type + # DW_AT_external + .byte 10 # Abbrev [10] 0x6d:0xa DW_TAG_formal_parameter + .byte 10 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 6 # DW_AT_decl_line + .long 50 # DW_AT_type + .byte 11 # Abbrev [11] 0x77:0x9 DW_TAG_variable + .byte 7 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 7 # DW_AT_decl_line + .long 50 # DW_AT_type + .byte 12 # Abbrev [12] 0x80:0xd DW_TAG_inlined_subroutine + .long 74 # DW_AT_abstract_origin + .byte 2 # DW_AT_low_pc + .long .Ltmp1-.Lfunc_begin1 # DW_AT_high_pc + .byte 0 # DW_AT_call_file + .byte 8 # DW_AT_call_line + .byte 4 # DW_AT_call_column + .byte 0 # End Of Children Mark + .byte 0 # End Of Children Mark +.Ldebug_info_end0: + .section .debug_str_offsets,"",@progbits + .long 48 # Length of String Offsets Set + .short 5 + .short 0 +.Lstr_offsets_base0: + .section .debug_str,"MS",@progbits,1 +.Linfo_string0: + .asciz "clang version 15.0.0)" # string offset=0 +.Linfo_string1: + .asciz "foo.cpp" # string offset=134 +.Linfo_string2: + .asciz "/testLocListMultiple" # string offset=142 +.Linfo_string3: + .asciz "fooVar" # string offset=199 +.Linfo_string4: + .asciz "int" # string offset=206 +.Linfo_string5: + .asciz "_Z6useFooPi" # string offset=210 +.Linfo_string6: + .asciz "useFoo" # string offset=222 +.Linfo_string7: + .asciz "x" # string offset=229 +.Linfo_string8: + .asciz "_Z3fooi" # string offset=231 +.Linfo_string9: + .asciz "foo" # string offset=239 +.Linfo_string10: + .asciz "argc" # string offset=243 + .section .debug_str_offsets,"",@progbits + .long .Linfo_string0 + .long .Linfo_string1 + .long .Linfo_string2 + .long .Linfo_string3 + .long .Linfo_string4 + .long .Linfo_string5 + .long .Linfo_string6 + .long .Linfo_string7 + .long .Linfo_string8 + .long .Linfo_string9 + .long .Linfo_string10 + .section .debug_addr,"",@progbits + .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution +.Ldebug_addr_start0: + .short 5 # DWARF version number + .byte 8 # Address size + .byte 0 # Segment selector size +.Laddr_table_base0: + .quad fooVar + .quad .Lfunc_begin0 + .quad .Lfunc_begin1 +.Ldebug_addr_end0: + .ident "clang version 15.0.0" + .section ".note.GNU-stack","",@progbits + .addrsig + .section .debug_line,"",@progbits +.Lline_table_start0: diff --git a/bolt/test/X86/MachO/Inputs/external_symbol.yaml b/bolt/test/X86/MachO/Inputs/external_symbol.yaml new file mode 100644 index 0000000000000..049c39d0f7ba2 --- /dev/null +++ b/bolt/test/X86/MachO/Inputs/external_symbol.yaml @@ -0,0 +1,357 @@ +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x1000007 + cpusubtype: 0x80000003 + filetype: 0x2 + ncmds: 15 + sizeofcmds: 1216 + flags: 0x200085 + reserved: 0x0 +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 72 + segname: __PAGEZERO + vmaddr: 0 + vmsize: 4294967296 + fileoff: 0 + filesize: 0 + maxprot: 0 + initprot: 0 + nsects: 0 + flags: 0 + - cmd: LC_SEGMENT_64 + cmdsize: 392 + segname: __TEXT + vmaddr: 4294967296 + vmsize: 4096 + fileoff: 0 + filesize: 4096 + maxprot: 5 + initprot: 5 + nsects: 4 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x100000F50 + size: 70 + offset: 0xF50 + align: 4 + reloff: 0x0 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + content: 554889E54883EC20C745FC00000000897DF8488975F0837DF8010F8E0D000000E8210000008945FCE910000000C745EC010000008B45EC83C0028945FC8B45FC4883C4205DC3 + - sectname: __stubs + segname: __TEXT + addr: 0x100000F96 + size: 6 + offset: 0xF96 + align: 1 + reloff: 0x0 + nreloc: 0 + flags: 0x80000408 + reserved1: 0x0 + reserved2: 0x6 + reserved3: 0x0 + content: FF2574000000 + - sectname: __stub_helper + segname: __TEXT + addr: 0x100000F9C + size: 28 + offset: 0xF9C + align: 2 + reloff: 0x0 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + content: 6800000000E90200000000004C8D1D510000004153FF255100000090 + - sectname: __unwind_info + segname: __TEXT + addr: 0x100000FB8 + size: 72 + offset: 0xFB8 + align: 2 + reloff: 0x0 + nreloc: 0 + flags: 0x0 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + content: 010000001C000000000000001C000000000000001C00000002000000500F00003400000034000000970F00000000000034000000030000000C000100100001000000000000000001 + - cmd: LC_SEGMENT_64 + cmdsize: 312 + segname: __DATA + vmaddr: 4294971392 + vmsize: 4096 + fileoff: 4096 + filesize: 4096 + maxprot: 3 + initprot: 3 + nsects: 3 + flags: 0 + Sections: + - sectname: __nl_symbol_ptr + segname: __DATA + addr: 0x100001000 + size: 8 + offset: 0x1000 + align: 3 + reloff: 0x0 + nreloc: 0 + flags: 0x6 + reserved1: 0x1 + reserved2: 0x0 + reserved3: 0x0 + content: '0000000000000000' + - sectname: __got + segname: __DATA + addr: 0x100001008 + size: 8 + offset: 0x1008 + align: 3 + reloff: 0x0 + nreloc: 0 + flags: 0x6 + reserved1: 0x2 + reserved2: 0x0 + reserved3: 0x0 + content: '0000000000000000' + - sectname: __la_symbol_ptr + segname: __DATA + addr: 0x100001010 + size: 8 + offset: 0x1010 + align: 3 + reloff: 0x0 + nreloc: 0 + flags: 0x7 + reserved1: 0x3 + reserved2: 0x0 + reserved3: 0x0 + content: 9C0F000001000000 + - cmd: LC_SEGMENT_64 + cmdsize: 72 + segname: __LINKEDIT + vmaddr: 4294975488 + vmsize: 4096 + fileoff: 8192 + filesize: 232 + maxprot: 1 + initprot: 1 + nsects: 0 + flags: 0 + - cmd: LC_DYLD_INFO_ONLY + cmdsize: 48 + rebase_off: 8192 + rebase_size: 8 + bind_off: 8200 + bind_size: 24 + weak_bind_off: 0 + weak_bind_size: 0 + lazy_bind_off: 8224 + lazy_bind_size: 16 + export_off: 8240 + export_size: 48 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 8296 + nsyms: 4 + stroff: 8376 + strsize: 48 + - cmd: LC_DYSYMTAB + cmdsize: 80 + ilocalsym: 0 + nlocalsym: 0 + iextdefsym: 0 + nextdefsym: 2 + iundefsym: 2 + nundefsym: 2 + tocoff: 0 + ntoc: 0 + modtaboff: 0 + nmodtab: 0 + extrefsymoff: 0 + nextrefsyms: 0 + indirectsymoff: 8360 + nindirectsyms: 4 + extreloff: 0 + nextrel: 0 + locreloff: 0 + nlocrel: 0 + - cmd: LC_LOAD_DYLINKER + cmdsize: 32 + name: 12 + Content: '/usr/lib/dyld' + ZeroPadBytes: 7 + - cmd: LC_UUID + cmdsize: 24 + uuid: 4A03D302-35D2-3E3B-93AC-22375185ACFB + - cmd: LC_BUILD_VERSION + cmdsize: 32 + platform: 1 + minos: 658944 + sdk: 658944 + ntools: 1 + Tools: + - tool: 3 + version: 34734080 + - cmd: LC_SOURCE_VERSION + cmdsize: 16 + version: 0 + - cmd: LC_MAIN + cmdsize: 24 + entryoff: 3920 + stacksize: 0 + - cmd: LC_LOAD_DYLIB + cmdsize: 56 + dylib: + name: 24 + timestamp: 2 + current_version: 82115073 + compatibility_version: 65536 + Content: '/usr/lib/libSystem.B.dylib' + ZeroPadBytes: 6 + - cmd: LC_FUNCTION_STARTS + cmdsize: 16 + dataoff: 8288 + datasize: 8 + - cmd: LC_DATA_IN_CODE + cmdsize: 16 + dataoff: 8296 + datasize: 0 +LinkEditData: + RebaseOpcodes: + - Opcode: REBASE_OPCODE_SET_TYPE_IMM + Imm: 1 + - Opcode: REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB + Imm: 2 + ExtraData: [ 0x10 ] + - Opcode: REBASE_OPCODE_DO_REBASE_IMM_TIMES + Imm: 1 + - Opcode: REBASE_OPCODE_DONE + Imm: 0 + BindOpcodes: + - Opcode: BIND_OPCODE_SET_DYLIB_ORDINAL_IMM + Imm: 1 + Symbol: '' + - Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM + Imm: 0 + Symbol: dyld_stub_binder + - Opcode: BIND_OPCODE_SET_TYPE_IMM + Imm: 1 + Symbol: '' + - Opcode: BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB + Imm: 2 + ULEBExtraData: [ 0x8 ] + Symbol: '' + - Opcode: BIND_OPCODE_DO_BIND + Imm: 0 + Symbol: '' + - Opcode: BIND_OPCODE_DONE + Imm: 0 + Symbol: '' + LazyBindOpcodes: + - Opcode: BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB + Imm: 2 + ULEBExtraData: [ 0x10 ] + Symbol: '' + - Opcode: BIND_OPCODE_SET_DYLIB_SPECIAL_IMM + Imm: 14 + Symbol: '' + - Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM + Imm: 0 + Symbol: _f + - Opcode: BIND_OPCODE_DO_BIND + Imm: 0 + Symbol: '' + - Opcode: BIND_OPCODE_DONE + Imm: 0 + Symbol: '' + - Opcode: BIND_OPCODE_DONE + Imm: 0 + Symbol: '' + - Opcode: BIND_OPCODE_DONE + Imm: 0 + Symbol: '' + - Opcode: BIND_OPCODE_DONE + Imm: 0 + Symbol: '' + - Opcode: BIND_OPCODE_DONE + Imm: 0 + Symbol: '' + - Opcode: BIND_OPCODE_DONE + Imm: 0 + Symbol: '' + - Opcode: BIND_OPCODE_DONE + Imm: 0 + Symbol: '' + - Opcode: BIND_OPCODE_DONE + Imm: 0 + Symbol: '' + ExportTrie: + TerminalSize: 0 + NodeOffset: 0 + Name: '' + Flags: 0x0 + Address: 0x0 + Other: 0x0 + ImportName: '' + Children: + - TerminalSize: 0 + NodeOffset: 5 + Name: _ + Flags: 0x0 + Address: 0x0 + Other: 0x0 + ImportName: '' + Children: + - TerminalSize: 2 + NodeOffset: 33 + Name: _mh_execute_header + Flags: 0x0 + Address: 0x0 + Other: 0x0 + ImportName: '' + - TerminalSize: 3 + NodeOffset: 37 + Name: main + Flags: 0x0 + Address: 0xF50 + Other: 0x0 + ImportName: '' + NameList: + - n_strx: 2 + n_type: 0xF + n_sect: 1 + n_desc: 16 + n_value: 4294967296 + - n_strx: 22 + n_type: 0xF + n_sect: 1 + n_desc: 0 + n_value: 4294971216 + - n_strx: 28 + n_type: 0x1 + n_sect: 0 + n_desc: 65024 + n_value: 0 + - n_strx: 31 + n_type: 0x1 + n_sect: 0 + n_desc: 256 + n_value: 0 + StringTable: + - ' ' + - __mh_execute_header + - _main + - _f + - dyld_stub_binder + IndirectSymbols: [ 0x2, 0x40000000, 0x3, 0x2 ] + FunctionStarts: [ 0xF50 ] +... diff --git a/bolt/test/X86/MachO/emit_new_binary_with_external_symbol.test b/bolt/test/X86/MachO/emit_new_binary_with_external_symbol.test new file mode 100644 index 0000000000000..8d9c545b3cfa3 --- /dev/null +++ b/bolt/test/X86/MachO/emit_new_binary_with_external_symbol.test @@ -0,0 +1,55 @@ +# RUN: yaml2obj -o %t.exe %p/Inputs/external_symbol.yaml +# RUN: llvm-bolt -o %t.original %t.exe +# RUN: llvm-objdump --macho --disassemble-all --no-print-imm-hex %t.original | FileCheck --check-prefix=ORIGINAL %s + +# ORIGINAL: _main: +# ORIGINAL-NEXT: 100000f50: 55 pushq %rbp +# ORIGINAL-NEXT: 100000f51: 48 89 e5 movq %rsp, %rbp +# ORIGINAL-NEXT: 100000f54: 48 83 ec 20 subq $32, %rsp +# ORIGINAL-NEXT: 100000f58: c7 45 fc 00 00 00 00 movl $0, -4(%rbp) +# ORIGINAL-NEXT: 100000f5f: 89 7d f8 movl %edi, -8(%rbp) +# ORIGINAL-NEXT: 100000f62: 48 89 75 f0 movq %rsi, -16(%rbp) +# ORIGINAL-NEXT: 100000f66: 83 7d f8 01 cmpl $1, -8(%rbp) +# ORIGINAL-NEXT: 100000f6a: 7e 0a jle 0x100000f76 +# ORIGINAL-NEXT: 100000f6c: e8 25 00 00 00 callq 0x100000f96 +# ORIGINAL-NEXT: 100000f71: 89 45 fc movl %eax, -4(%rbp) +# ORIGINAL-NEXT: 100000f74: eb 10 jmp 0x100000f86 +# ORIGINAL-NEXT: 100000f76: c7 45 ec 01 00 00 00 movl $1, -20(%rbp) +# ORIGINAL-NEXT: 100000f7d: 8b 45 ec movl -20(%rbp), %eax +# ORIGINAL-NEXT: 100000f80: 83 c0 02 addl $2, %eax +# ORIGINAL-NEXT: 100000f83: 89 45 fc movl %eax, -4(%rbp) +# ORIGINAL-NEXT: 100000f86: 8b 45 fc movl -4(%rbp), %eax +# ORIGINAL-NEXT: 100000f89: 48 83 c4 20 addq $32, %rsp +# ORIGINAL-NEXT: 100000f8d: 5d popq %rbp +# ORIGINAL-NEXT: 100000f8e: c3 retq +# ORIGINAL-NEXT: 100000f8f: fc cld +# ORIGINAL-NEXT: 100000f90: 48 83 c4 20 addq $32, %rsp +# ORIGINAL-NEXT: 100000f94: 5d popq %rbp +# ORIGINAL-NEXT: 100000f95: c3 retq + +# RUN: llvm-bolt -o %t.reversed -reorder-blocks=reverse %t.exe +# RUN: llvm-objdump --macho --disassemble-all --no-print-imm-hex %t.reversed | FileCheck --check-prefix=REVERSED %s + +# REVERSED: _main: +# REVERSED-NEXT: 100000f50: 55 pushq %rbp +# REVERSED-NEXT: 100000f51: 48 89 e5 movq %rsp, %rbp +# REVERSED-NEXT: 100000f54: 48 83 ec 20 subq $32, %rsp +# REVERSED-NEXT: 100000f58: c7 45 fc 00 00 00 00 movl $0, -4(%rbp) +# REVERSED-NEXT: 100000f5f: 89 7d f8 movl %edi, -8(%rbp) +# REVERSED-NEXT: 100000f62: 48 89 75 f0 movq %rsi, -16(%rbp) +# REVERSED-NEXT: 100000f66: 83 7d f8 01 cmpl $1, -8(%rbp) +# REVERSED-NEXT: 100000f6a: 7e 0b jle 0x100000f77 +# REVERSED-NEXT: 100000f6c: eb 1b jmp 0x100000f89 +# REVERSED-NEXT: 100000f6e: 8b 45 fc movl -4(%rbp), %eax +# REVERSED-NEXT: 100000f71: 48 83 c4 20 addq $32, %rsp +# REVERSED-NEXT: 100000f75: 5d popq %rbp +# REVERSED-NEXT: 100000f76: c3 retq +# REVERSED-NEXT: 100000f77: c7 45 ec 01 00 00 00 movl $1, -20(%rbp) +# REVERSED-NEXT: 100000f7e: 8b 45 ec movl -20(%rbp), %eax +# REVERSED-NEXT: 100000f81: 83 c0 02 addl $2, %eax +# REVERSED-NEXT: 100000f84: 89 45 fc movl %eax, -4(%rbp) +# REVERSED-NEXT: 100000f87: eb e5 jmp 0x100000f6e +# REVERSED-NEXT: 100000f89: e8 08 00 00 00 callq 0x100000f96 +# REVERSED-NEXT: 100000f8e: 89 45 fc movl %eax, -4(%rbp) +# REVERSED-NEXT: 100000f91: eb db jmp 0x100000f6e +# REVERSED-NEXT: 100000f93: 20 5d c3 andb %bl, -61(%rbp) diff --git a/bolt/test/X86/dwarf5-one-loclists-two-bases.test b/bolt/test/X86/dwarf5-one-loclists-two-bases.test new file mode 100644 index 0000000000000..d85eca18b08ac --- /dev/null +++ b/bolt/test/X86/dwarf5-one-loclists-two-bases.test @@ -0,0 +1,57 @@ +# REQUIRES: system-linux + +# RUN: llvm-mc -dwarf-version=5 -filetype=obj -triple x86_64-unknown-linux %p/Inputs/dwarf5_main.s -o %tmain.o +# RUN: llvm-mc -dwarf-version=5 -filetype=obj -triple x86_64-unknown-linux %p/Inputs/dwarf5-loc-base-no-loc-accesshelper.s -o %thelper.o +# RUN: %clang %cflags -dwarf-5 %tmain.o %thelper.o -o %t.exe -Wl,-q +# RUN: llvm-bolt %t.exe -o %t.bolt --update-debug-sections +# RUN: llvm-dwarfdump --show-form --verbose --debug-info %t.exe | FileCheck --check-prefix=PRECHECK %s +# RUN: llvm-dwarfdump --show-form --verbose --debug-addr %t.bolt > %t.txt +# RUN: llvm-dwarfdump --show-form --verbose --debug-info %t.bolt >> %t.txt +# RUN: cat %t.txt | FileCheck --check-prefix=POSTCHECK %s + +# This tests checks that re-writing of .debug_loclists is handled correctly when one of the CUs +# doesn't have any DW_AT_location accesses. + +# PRECHECK: version = 0x0005 +# PRECHECK: DW_AT_loclists_base [DW_FORM_sec_offset] (0x0000000c) +# PRECHECK-EMPTY: +# PRECHECK: DW_TAG_variable +# PRECHECK: DW_AT_location [DW_FORM_loclistx] +# PRECHECK-SAME: indexed (0x0) +# PRECHECK-SAME: loclist = 0x00000014 +# PRECHECK: DW_AT_location [DW_FORM_loclistx] +# PRECHECK-SAME: indexed (0x1) +# PRECHECK-SAME: loclist = 0x00000028 +# PRECHECK: DW_AT_loclists_base [DW_FORM_sec_offset] (0x00000043) +# PRECHECK-NOT: DW_AT_location + +# POSTCHECK: Addrs: [ +# POSTCHECK-NEXT: 0x +# POSTCHECK-NEXT: 0x +# POSTCHECK-NEXT: 0x[[#%.16x,ADDR:]] +# POSTCHECK-NEXT: 0x[[#%.16x,ADDR2:]] + +# POSTCHECK: version = 0x0005 +# POSTCHECK: DW_AT_loclists_base [DW_FORM_sec_offset] (0x0000000c) +# POSTCHECK: DW_AT_rnglists_base [DW_FORM_sec_offset] (0x0000000c) +# POSTCHECK-EMPTY +# POSTCHECK: DW_TAG_variable +# POSTCHECK: DW_AT_location [DW_FORM_loclistx] +# POSTCHECK-SAME: indexed (0x0) +# POSTCHECK-SAME: loclist = 0x00000014 +# POSTCHECK-NEXT: [0x[[#ADDR]] +# POSTCHECK-SAME: 0x[[#ADDR + 0x6]] +# POSTCHECK-NEXT: [0x[[#ADDR + 0x6]] +# POSTCHECK-SAME: 0x[[#ADDR + 0xc]] +# POSTCHECK: DW_TAG_variable +# POSTCHECK: DW_AT_location [DW_FORM_loclistx] +# POSTCHECK-SAME: indexed (0x1) +# POSTCHECK-SAME: loclist = 0x0000002a +# POSTCHECK-NEXT: [0x[[#ADDR2]] +# POSTCHECK-SAME: 0x[[#ADDR2 + 0x2]] + +# Checking second CU +# POSTCHECK: version = 0x0005 +# POSTCHECK: DW_AT_loclists_base [DW_FORM_sec_offset] (0x0000000c) +# POSTCHECK: DW_AT_rnglists_base [DW_FORM_sec_offset] (0x00000035) +# POSTCHECK-NOT: DW_AT_location diff --git a/bolt/test/lit.cfg.py b/bolt/test/lit.cfg.py index b5e15e20b1fe0..5838699157db7 100644 --- a/bolt/test/lit.cfg.py +++ b/bolt/test/lit.cfg.py @@ -110,4 +110,4 @@ def calculate_arch_features(arch_string): ('--targets-built', calculate_arch_features) ]) -config.targets = frozenset(config.targets_to_build.split()) +config.targets = frozenset(config.targets_to_build.split(';')) diff --git a/bolt/utils/llvm-bolt-wrapper.py b/bolt/utils/llvm-bolt-wrapper.py index 652cc6074462e..a6d863ce7caa6 100755 --- a/bolt/utils/llvm-bolt-wrapper.py +++ b/bolt/utils/llvm-bolt-wrapper.py @@ -206,7 +206,8 @@ def parse_cmp_offset(cmp_out): Extracts byte number from cmp output: file1 file2 differ: byte X, line Y ''' - return int(re.search(r'byte (\d+),', cmp_out).groups()[0]) + # NOTE: cmp counts bytes starting from 1! + return int(re.search(r'byte (\d+),', cmp_out).groups()[0]) - 1 def report_real_time(binary, main_err, cmp_err, cfg): ''' diff --git a/clang-tools-extra/clang-tidy/ClangTidyOptions.h b/clang-tools-extra/clang-tidy/ClangTidyOptions.h index d0df474bc38e0..b3b714353642b 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyOptions.h +++ b/clang-tools-extra/clang-tidy/ClangTidyOptions.h @@ -108,15 +108,15 @@ struct ClangTidyOptions { /// Helper structure for storing option value with priority of the value. struct ClangTidyValue { - ClangTidyValue() : Value(), Priority(0) {} - ClangTidyValue(const char *Value) : Value(Value), Priority(0) {} + ClangTidyValue() = default; + ClangTidyValue(const char *Value) : Value(Value) {} ClangTidyValue(llvm::StringRef Value, unsigned Priority = 0) : Value(Value), Priority(Priority) {} std::string Value; /// Priority stores relative precedence of the value loaded from config /// files to disambiguate local vs global value from different levels. - unsigned Priority; + unsigned Priority = 0; }; typedef std::pair StringPair; typedef llvm::StringMap OptionMap; diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp index 60666287cf307..5e9c7d0add4f8 100644 --- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp @@ -36,6 +36,7 @@ #include "MisplacedPointerArithmeticInAllocCheck.h" #include "MisplacedWideningCastCheck.h" #include "MoveForwardingReferenceCheck.h" +#include "MultipleNewInOneExpressionCheck.h" #include "MultipleStatementMacroCheck.h" #include "NoEscapeCheck.h" #include "NonZeroEnumToBoolConversionCheck.h" @@ -133,6 +134,8 @@ class BugproneModule : public ClangTidyModule { "bugprone-misplaced-widening-cast"); CheckFactories.registerCheck( "bugprone-move-forwarding-reference"); + CheckFactories.registerCheck( + "bugprone-multiple-new-in-one-expression"); CheckFactories.registerCheck( "bugprone-multiple-statement-macro"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt index 5af5a59e5340f..e70d1b426a1c6 100644 --- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt @@ -31,6 +31,7 @@ add_clang_library(clangTidyBugproneModule MisplacedPointerArithmeticInAllocCheck.cpp MisplacedWideningCastCheck.cpp MoveForwardingReferenceCheck.cpp + MultipleNewInOneExpressionCheck.cpp MultipleStatementMacroCheck.cpp NoEscapeCheck.cpp NonZeroEnumToBoolConversionCheck.cpp diff --git a/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp index 276c46ca9fe90..635cc2ca05d12 100644 --- a/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp @@ -52,7 +52,8 @@ void ExceptionEscapeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { void ExceptionEscapeCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( - functionDecl(anyOf(isNoThrow(), cxxDestructorDecl(), + functionDecl(isDefinition(), + anyOf(isNoThrow(), cxxDestructorDecl(), cxxConstructorDecl(isMoveConstructor()), cxxMethodDecl(isMoveAssignmentOperator()), hasName("main"), hasName("swap"), diff --git a/clang-tools-extra/clang-tidy/bugprone/MultipleNewInOneExpressionCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/MultipleNewInOneExpressionCheck.cpp new file mode 100644 index 0000000000000..47b9667859903 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/MultipleNewInOneExpressionCheck.cpp @@ -0,0 +1,162 @@ +//===--- MultipleNewInOneExpressionCheck.cpp - clang-tidy------------------===// +// +// 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 "MultipleNewInOneExpressionCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" + +using namespace clang::ast_matchers; +using namespace clang; + +namespace { + +// Determine if the result of an expression is "stored" in some way. +// It is true if the value is stored into a variable or used as initialization +// or passed to a function or constructor. +// For this use case compound assignments are not counted as a "store" (the 'E' +// expression should have pointer type). +bool isExprValueStored(const Expr *E, ASTContext &C) { + E = E->IgnoreParenCasts(); + // Get first non-paren, non-cast parent. + ParentMapContext &PMap = C.getParentMapContext(); + DynTypedNodeList P = PMap.getParents(*E); + if (P.size() != 1) + return false; + const Expr *ParentE; + while ((ParentE = P[0].get()) && ParentE->IgnoreParenCasts() == E) { + P = PMap.getParents(P[0]); + if (P.size() != 1) + return false; + } + + if (const auto *ParentVarD = P[0].get()) + return ParentVarD->getInit()->IgnoreParenCasts() == E; + + if (!ParentE) + return false; + + if (const auto *BinOp = dyn_cast(ParentE)) + return BinOp->getOpcode() == BO_Assign && + BinOp->getRHS()->IgnoreParenCasts() == E; + + return isa(ParentE); +} + +} // namespace + +namespace clang { +namespace tidy { +namespace bugprone { + +AST_MATCHER_P(CXXTryStmt, hasHandlerFor, + ast_matchers::internal::Matcher, InnerMatcher) { + for (unsigned NH = Node.getNumHandlers(), I = 0; I < NH; ++I) { + const CXXCatchStmt *CatchS = Node.getHandler(I); + // Check for generic catch handler (match anything). + if (CatchS->getCaughtType().isNull()) + return true; + ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder); + if (InnerMatcher.matches(CatchS->getCaughtType(), Finder, &Result)) { + *Builder = std::move(Result); + return true; + } + } + return false; +} + +AST_MATCHER(CXXNewExpr, mayThrow) { + FunctionDecl *OperatorNew = Node.getOperatorNew(); + if (!OperatorNew) + return false; + return !OperatorNew->getType()->castAs()->isNothrow(); +} + +void MultipleNewInOneExpressionCheck::registerMatchers(MatchFinder *Finder) { + auto BadAllocType = + recordType(hasDeclaration(cxxRecordDecl(hasName("::std::bad_alloc")))); + auto ExceptionType = + recordType(hasDeclaration(cxxRecordDecl(hasName("::std::exception")))); + auto BadAllocReferenceType = referenceType(pointee(BadAllocType)); + auto ExceptionReferenceType = referenceType(pointee(ExceptionType)); + + auto CatchBadAllocType = + qualType(hasCanonicalType(anyOf(BadAllocType, BadAllocReferenceType, + ExceptionType, ExceptionReferenceType))); + auto BadAllocCatchingTryBlock = cxxTryStmt(hasHandlerFor(CatchBadAllocType)); + + auto NewExprMayThrow = cxxNewExpr(mayThrow()); + auto HasNewExpr1 = expr(anyOf(NewExprMayThrow.bind("new1"), + hasDescendant(NewExprMayThrow.bind("new1")))); + auto HasNewExpr2 = expr(anyOf(NewExprMayThrow.bind("new2"), + hasDescendant(NewExprMayThrow.bind("new2")))); + + Finder->addMatcher( + callExpr( + hasAnyArgument( + expr(HasNewExpr1).bind("arg1")), + hasAnyArgument( + expr(HasNewExpr2, unless(equalsBoundNode("arg1"))).bind("arg2")), + hasAncestor(BadAllocCatchingTryBlock)), + this); + Finder->addMatcher( + cxxConstructExpr( + hasAnyArgument( + expr(HasNewExpr1).bind("arg1")), + hasAnyArgument( + expr(HasNewExpr2, unless(equalsBoundNode("arg1"))).bind("arg2")), + unless(isListInitialization()), + hasAncestor(BadAllocCatchingTryBlock)), + this); + Finder->addMatcher(binaryOperator(hasLHS(HasNewExpr1), hasRHS(HasNewExpr2), + unless(hasAnyOperatorName("&&", "||", ",")), + hasAncestor(BadAllocCatchingTryBlock)), + this); + Finder->addMatcher( + cxxNewExpr(mayThrow(), + hasDescendant(NewExprMayThrow.bind("new2_in_new1")), + hasAncestor(BadAllocCatchingTryBlock)) + .bind("new1"), + this); +} + +void MultipleNewInOneExpressionCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *NewExpr1 = Result.Nodes.getNodeAs("new1"); + const auto *NewExpr2 = Result.Nodes.getNodeAs("new2"); + const auto *NewExpr2InNewExpr1 = + Result.Nodes.getNodeAs("new2_in_new1"); + if (!NewExpr2) + NewExpr2 = NewExpr2InNewExpr1; + assert(NewExpr1 && NewExpr2 && "Bound nodes not found."); + + // No warning if both allocations are not stored. + // The value may be intentionally not stored (no deallocations needed or + // self-destructing object). + if (!isExprValueStored(NewExpr1, *Result.Context) && + !isExprValueStored(NewExpr2, *Result.Context)) + return; + + // In C++17 sequencing of a 'new' inside constructor arguments of another + // 'new' is fixed. Still a leak can happen if the returned value from the + // first 'new' is not saved (yet) and the second fails. + if (getLangOpts().CPlusPlus17 && NewExpr2InNewExpr1) + diag(NewExpr1->getBeginLoc(), + "memory allocation may leak if an other allocation is sequenced after " + "it and throws an exception") + << NewExpr1->getSourceRange() << NewExpr2->getSourceRange(); + else + diag(NewExpr1->getBeginLoc(), + "memory allocation may leak if an other allocation is sequenced after " + "it and throws an exception; order of these allocations is undefined") + << NewExpr1->getSourceRange() << NewExpr2->getSourceRange(); +} + +} // namespace bugprone +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/bugprone/MultipleNewInOneExpressionCheck.h b/clang-tools-extra/clang-tidy/bugprone/MultipleNewInOneExpressionCheck.h new file mode 100644 index 0000000000000..eba6c84d818ca --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/MultipleNewInOneExpressionCheck.h @@ -0,0 +1,35 @@ +//===--- MultipleNewInOneExpressionCheck.h - clang-tidy----------*- 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_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MULTIPLENEWINONEEXPRESSIONCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MULTIPLENEWINONEEXPRESSIONCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace bugprone { + +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/multiple-new-in-one-expression.html +class MultipleNewInOneExpressionCheck : public ClangTidyCheck { +public: + MultipleNewInOneExpressionCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace bugprone +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MULTIPLENEWINONEEXPRESSIONCHECK_H diff --git a/clang-tools-extra/clang-tidy/readability/RedundantStringCStrCheck.cpp b/clang-tools-extra/clang-tidy/readability/RedundantStringCStrCheck.cpp index dfcc82c270540..27fa07ed3a385 100644 --- a/clang-tools-extra/clang-tidy/readability/RedundantStringCStrCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/RedundantStringCStrCheck.cpp @@ -78,7 +78,7 @@ RedundantStringCStrCheck::RedundantStringCStrCheck(StringRef Name, Options.get("StringParameterFunctions", ""))) { if (getLangOpts().CPlusPlus20) StringParameterFunctions.push_back("::std::format"); - if (getLangOpts().CPlusPlus2b) + if (getLangOpts().CPlusPlus23) StringParameterFunctions.push_back("::std::print"); } diff --git a/clang-tools-extra/clang-tidy/utils/ExprSequence.cpp b/clang-tools-extra/clang-tidy/utils/ExprSequence.cpp index f9555be57e738..50df451ecfa26 100644 --- a/clang-tools-extra/clang-tidy/utils/ExprSequence.cpp +++ b/clang-tools-extra/clang-tidy/utils/ExprSequence.cpp @@ -131,6 +131,16 @@ const Stmt *ExprSequence::getSequenceSuccessor(const Stmt *S) const { } } } + } else if (const auto *ConstructExpr = dyn_cast(Parent)) { + // Constructor arguments are sequenced if the constructor call is written + // as list-initialization. + if (ConstructExpr->isListInitialization()) { + for (unsigned I = 1; I < ConstructExpr->getNumArgs(); ++I) { + if (ConstructExpr->getArg(I - 1) == S) { + return ConstructExpr->getArg(I); + } + } + } } else if (const auto *Compound = dyn_cast(Parent)) { // Compound statement: Each sub-statement is sequenced after the // statements that precede it. diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp index 6ead59a7ec90e..26e239fa7bed6 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include namespace clang { @@ -95,6 +96,36 @@ CodeAction toCodeAction(const ClangdServer::TweakRef &T, const URIForFile &File, return CA; } +/// Convert from Fix to LSP CodeAction. +CodeAction toCodeAction(const Fix &F, const URIForFile &File, + const std::optional &Version, + bool SupportsDocumentChanges, + bool SupportChangeAnnotation) { + CodeAction Action; + Action.title = F.Message; + Action.kind = std::string(CodeAction::QUICKFIX_KIND); + Action.edit.emplace(); + if (!SupportsDocumentChanges) { + Action.edit->changes.emplace(); + auto &Changes = (*Action.edit->changes)[File.uri()]; + for (const auto &E : F.Edits) + Changes.push_back({E.range, E.newText, /*annotationId=*/""}); + } else { + Action.edit->documentChanges.emplace(); + TextDocumentEdit &Edit = Action.edit->documentChanges->emplace_back(); + Edit.textDocument = VersionedTextDocumentIdentifier{{File}, Version}; + for (const auto &E : F.Edits) + Edit.edits.push_back( + {E.range, E.newText, + SupportChangeAnnotation ? E.annotationId : ""}); + if (SupportChangeAnnotation) { + for (const auto &[AID, Annotation]: F.Annotations) + Action.edit->changeAnnotations[AID] = Annotation; + } + } + return Action; +} + void adjustSymbolKinds(llvm::MutableArrayRef Syms, SymbolKindBitset Kinds) { for (auto &S : Syms) { @@ -487,6 +518,8 @@ void ClangdLSPServer::onInitialize(const InitializeParams &Params, Params.capabilities.HierarchicalDocumentSymbol; SupportsReferenceContainer = Params.capabilities.ReferenceContainer; SupportFileStatus = Params.initializationOptions.FileStatus; + SupportsDocumentChanges = Params.capabilities.DocumentChanges; + SupportsChangeAnnotation = Params.capabilities.ChangeAnnotation; HoverContentFormat = Params.capabilities.HoverContentFormat; Opts.LineFoldingOnly = Params.capabilities.LineFoldingOnly; SupportsOffsetsInSignatureHelp = Params.capabilities.OffsetsInSignatureHelp; @@ -761,8 +794,10 @@ void ClangdLSPServer::onCommandApplyTweak(const TweakArgs &Args, return Reply(std::move(Err)); WorkspaceEdit WE; + // FIXME: use documentChanges when SupportDocumentChanges is true. + WE.changes.emplace(); for (const auto &It : R->ApplyEdits) { - WE.changes[URI::createFile(It.first()).toString()] = + (*WE.changes)[URI::createFile(It.first()).toString()] = It.second.asTextEdits(); } // ApplyEdit will take care of calling Reply(). @@ -833,8 +868,12 @@ void ClangdLSPServer::onRename(const RenameParams &Params, if (auto Err = validateEdits(*Server, R->GlobalChanges)) return Reply(std::move(Err)); WorkspaceEdit Result; + // FIXME: use documentChanges if SupportDocumentChanges is + // true. + Result.changes.emplace(); for (const auto &Rep : R->GlobalChanges) { - Result.changes[URI::createFile(Rep.first()).toString()] = + (*Result + .changes)[URI::createFile(Rep.first()).toString()] = Rep.second.asTextEdits(); } Reply(Result); @@ -984,8 +1023,8 @@ void ClangdLSPServer::onCodeAction(const CodeActionParams &Params, std::vector FixIts; if (KindAllowed(CodeAction::QUICKFIX_KIND)) { for (const Diagnostic &D : Params.context.diagnostics) { - for (auto &F : getFixes(File.file(), D)) { - FixIts.push_back(toCodeAction(F, Params.textDocument.uri)); + for (auto &CA : getFixes(File.file(), D)) { + FixIts.push_back(CA); FixIts.back().diagnostics = {D}; } } @@ -1663,8 +1702,8 @@ void ClangdLSPServer::profile(MemoryTree &MT) const { Server->profile(MT.child("clangd_server")); } -std::vector ClangdLSPServer::getFixes(llvm::StringRef File, - const clangd::Diagnostic &D) { +std::vector ClangdLSPServer::getFixes(llvm::StringRef File, + const clangd::Diagnostic &D) { std::lock_guard Lock(FixItsMutex); auto DiagToFixItsIter = FixItsMap.find(File); if (DiagToFixItsIter == FixItsMap.end()) @@ -1710,8 +1749,19 @@ void ClangdLSPServer::onDiagnosticsReady(PathRef File, llvm::StringRef Version, for (auto &Diag : Diagnostics) { toLSPDiags(Diag, Notification.uri, DiagOpts, [&](clangd::Diagnostic Diag, llvm::ArrayRef Fixes) { - auto &FixItsForDiagnostic = LocalFixIts[Diag]; - llvm::copy(Fixes, std::back_inserter(FixItsForDiagnostic)); + std::vector CodeActions; + for (const auto &Fix : Fixes) + CodeActions.push_back(toCodeAction(Fix, Notification.uri, + Notification.version, + SupportsDocumentChanges, + SupportsChangeAnnotation)); + + if (DiagOpts.EmbedFixesInDiagnostics) { + Diag.codeActions.emplace(CodeActions); + if (Diag.codeActions->size() == 1) + Diag.codeActions->front().isPreferred = true; + } + LocalFixIts[Diag] = std::move(CodeActions); Notification.diagnostics.push_back(std::move(Diag)); }); } diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h index cd5bb662c3931..33c8d221f9bd4 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.h +++ b/clang-tools-extra/clangd/ClangdLSPServer.h @@ -21,6 +21,7 @@ #include "llvm/Support/JSON.h" #include #include +#include #include #include #include @@ -197,7 +198,8 @@ class ClangdLSPServer : private ClangdServer::Callbacks, Callback Reply); void bindMethods(LSPBinder &, const ClientCapabilities &Caps); - std::vector getFixes(StringRef File, const clangd::Diagnostic &D); + std::vector getFixes(StringRef File, const clangd::Diagnostic &D); + /// Checks if completion request should be ignored. We need this due to the /// limitation of the LSP. Per LSP, a client sends requests for all "trigger @@ -231,10 +233,12 @@ class ClangdLSPServer : private ClangdServer::Callbacks, std::atomic IsBeingDestroyed = {false}; std::mutex FixItsMutex; - typedef std::map, LSPDiagnosticCompare> + typedef std::map, + LSPDiagnosticCompare> DiagnosticToReplacementMap; /// Caches FixIts per file and diagnostics - llvm::StringMap FixItsMap; + llvm::StringMap + FixItsMap; // Last semantic-tokens response, for incremental requests. std::mutex SemanticTokensMutex; llvm::StringMap LastSemanticTokens; @@ -271,6 +275,11 @@ class ClangdLSPServer : private ClangdServer::Callbacks, MarkupKind HoverContentFormat = MarkupKind::PlainText; /// Whether the client supports offsets for parameter info labels. bool SupportsOffsetsInSignatureHelp = false; + /// Whether the client supports the versioned document changes. + bool SupportsDocumentChanges = false; + /// Whether the client supports change annotations on text edits. + bool SupportsChangeAnnotation = false; + std::mutex BackgroundIndexProgressMutex; enum class BackgroundIndexProgress { // Client doesn't support reporting progress. No transitions possible. diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp index 260c44b2064b5..bb01fa421b498 100644 --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -2242,7 +2242,7 @@ CompletionItem CodeCompletion::render(const CodeCompleteOptions &Opts) const { } LSP.sortText = sortText(Score.Total, FilterText); LSP.filterText = FilterText; - LSP.textEdit = {CompletionTokenRange, RequiredQualifier + Name}; + LSP.textEdit = {CompletionTokenRange, RequiredQualifier + Name, ""}; // Merge continuous additionalTextEdits into main edit. The main motivation // behind this is to help LSP clients, it seems most of them are confused when // they are provided with additionalTextEdits that are consecutive to main diff --git a/clang-tools-extra/clangd/Compiler.cpp b/clang-tools-extra/clangd/Compiler.cpp index f242db44b4317..7b93353cab1f3 100644 --- a/clang-tools-extra/clangd/Compiler.cpp +++ b/clang-tools-extra/clangd/Compiler.cpp @@ -73,7 +73,7 @@ void disableUnsupportedOptions(CompilerInvocation &CI) { // Always default to raw container format as clangd doesn't registry any other // and clang dies when faced with unknown formats. CI.getHeaderSearchOpts().ModuleFormat = - PCHContainerOperations().getRawReader().getFormat().str(); + PCHContainerOperations().getRawReader().getFormats().front().str(); CI.getFrontendOpts().Plugins.clear(); CI.getFrontendOpts().AddPluginActions.clear(); diff --git a/clang-tools-extra/clangd/Diagnostics.cpp b/clang-tools-extra/clangd/Diagnostics.cpp index 963bf02e21848..d1726142c5ded 100644 --- a/clang-tools-extra/clangd/Diagnostics.cpp +++ b/clang-tools-extra/clangd/Diagnostics.cpp @@ -423,15 +423,6 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Diag &D) { return OS; } -CodeAction toCodeAction(const Fix &F, const URIForFile &File) { - CodeAction Action; - Action.title = F.Message; - Action.kind = std::string(CodeAction::QUICKFIX_KIND); - Action.edit.emplace(); - Action.edit->changes[File.uri()] = {F.Edits.begin(), F.Edits.end()}; - return Action; -} - Diag toDiag(const llvm::SMDiagnostic &D, Diag::DiagSource Source) { Diag Result; Result.Message = D.getMessage().str(); @@ -499,13 +490,6 @@ void toLSPDiags( case Diag::Unknown: break; } - if (Opts.EmbedFixesInDiagnostics) { - Main.codeActions.emplace(); - for (const auto &Fix : D.Fixes) - Main.codeActions->push_back(toCodeAction(Fix, File)); - if (Main.codeActions->size() == 1) - Main.codeActions->front().isPreferred = true; - } if (Opts.SendDiagnosticCategory && !D.Category.empty()) Main.category = D.Category; @@ -786,7 +770,7 @@ void StoreDiags::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, if (Message.empty()) // either !SyntheticMessage, or we failed to make one. Info.FormatDiagnostic(Message); LastDiag->Fixes.push_back( - Fix{std::string(Message.str()), std::move(Edits)}); + Fix{std::string(Message.str()), std::move(Edits), {}}); return true; }; diff --git a/clang-tools-extra/clangd/Diagnostics.h b/clang-tools-extra/clangd/Diagnostics.h index b20b82fcb3298..d4c0478c63a5c 100644 --- a/clang-tools-extra/clangd/Diagnostics.h +++ b/clang-tools-extra/clangd/Diagnostics.h @@ -83,6 +83,10 @@ struct Fix { std::string Message; /// TextEdits from clang's fix-its. Must be non-empty. llvm::SmallVector Edits; + + /// Annotations for the Edits. + llvm::SmallVector> + Annotations; }; llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Fix &F); @@ -120,9 +124,6 @@ void toLSPDiags( const Diag &D, const URIForFile &File, const ClangdDiagnosticOptions &Opts, llvm::function_ref)> OutFn); -/// Convert from Fix to LSP CodeAction. -CodeAction toCodeAction(const Fix &D, const URIForFile &File); - /// Convert from clang diagnostic level to LSP severity. int getSeverity(DiagnosticsEngine::Level L); diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp index 3eb0900715744..558769f209860 100644 --- a/clang-tools-extra/clangd/Hover.cpp +++ b/clang-tools-extra/clangd/Hover.cpp @@ -981,12 +981,23 @@ void maybeAddCalleeArgInfo(const SelectionTree::Node *N, HoverInfo &HI, const auto &OuterNode = N->outerImplicit(); if (!OuterNode.Parent) return; - const auto *CE = OuterNode.Parent->ASTNode.get(); - if (!CE) + + const FunctionDecl *FD = nullptr; + llvm::ArrayRef Args; + + if (const auto *CE = OuterNode.Parent->ASTNode.get()) { + FD = CE->getDirectCallee(); + Args = {CE->getArgs(), CE->getNumArgs()}; + } else if (const auto *CE = + OuterNode.Parent->ASTNode.get()) { + FD = CE->getConstructor(); + Args = {CE->getArgs(), CE->getNumArgs()}; + } + if (!FD) return; - const FunctionDecl *FD = CE->getDirectCallee(); - // For non-function-call-like operatators (e.g. operator+, operator<<) it's - // not immediattely obvious what the "passed as" would refer to and, given + + // For non-function-call-like operators (e.g. operator+, operator<<) it's + // not immediately obvious what the "passed as" would refer to and, given // fixed function signature, the value would be very low anyway, so we choose // to not support that. // Both variadic functions and operator() (especially relevant for lambdas) @@ -996,13 +1007,15 @@ void maybeAddCalleeArgInfo(const SelectionTree::Node *N, HoverInfo &HI, HoverInfo::PassType PassType; + auto Parameters = resolveForwardingParameters(FD); + // Find argument index for N. - for (unsigned I = 0; I < CE->getNumArgs() && I < FD->getNumParams(); ++I) { - if (CE->getArg(I) != OuterNode.ASTNode.get()) + for (unsigned I = 0; I < Args.size() && I < Parameters.size(); ++I) { + if (Args[I] != OuterNode.ASTNode.get()) continue; // Extract matching argument from function declaration. - if (const ParmVarDecl *PVD = FD->getParamDecl(I)) { + if (const ParmVarDecl *PVD = Parameters[I]) { HI.CalleeArgInfo.emplace(toHoverInfoParam(PVD, PP)); if (N == &OuterNode) PassType.PassBy = getPassMode(PVD->getType()); diff --git a/clang-tools-extra/clangd/IncludeCleaner.cpp b/clang-tools-extra/clangd/IncludeCleaner.cpp index d15dd70efdb70..3b913d851abe2 100644 --- a/clang-tools-extra/clangd/IncludeCleaner.cpp +++ b/clang-tools-extra/clangd/IncludeCleaner.cpp @@ -40,10 +40,12 @@ #include "clang/Tooling/Syntax/Tokens.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/GenericUniformityImpl.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/STLFunctionalExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/Casting.h" @@ -415,11 +417,138 @@ IncludeCleanerFindings computeIncludeCleanerFindings(ParsedAST &AST) { Ref.Target, TouchingTokens.back().range(SM), Providers}; MissingIncludes.push_back(std::move(DiagInfo)); }); + // Put possibly equal diagnostics together for deduplication. + // The duplicates might be from macro arguments that get expanded multiple + // times. + llvm::stable_sort(MissingIncludes, [](const MissingIncludeDiagInfo &LHS, + const MissingIncludeDiagInfo &RHS) { + // First sort by reference location. + if (LHS.SymRefRange != RHS.SymRefRange) { + // We can get away just by comparing the offsets as all the ranges are in + // main file. + return LHS.SymRefRange.beginOffset() < RHS.SymRefRange.beginOffset(); + } + // For the same location, break ties using the symbol. Note that this won't + // be stable across runs. + using MapInfo = llvm::DenseMapInfo; + return MapInfo::getHashValue(LHS.Symbol) < + MapInfo::getHashValue(RHS.Symbol); + }); + MissingIncludes.erase(llvm::unique(MissingIncludes), MissingIncludes.end()); std::vector UnusedIncludes = getUnused(AST, Used, /*ReferencedPublicHeaders*/ {}); return {std::move(UnusedIncludes), std::move(MissingIncludes)}; } +Fix removeAllUnusedIncludes(llvm::ArrayRef UnusedIncludes) { + assert(!UnusedIncludes.empty()); + + Fix RemoveAll; + RemoveAll.Message = "remove all unused includes"; + for (const auto &Diag : UnusedIncludes) { + assert(Diag.Fixes.size() == 1 && "Expected exactly one fix."); + RemoveAll.Edits.insert(RemoveAll.Edits.end(), + Diag.Fixes.front().Edits.begin(), + Diag.Fixes.front().Edits.end()); + } + + // TODO(hokein): emit a suitable text for the label. + ChangeAnnotation Annotation = {/*label=*/"", + /*needsConfirmation=*/true, + /*description=*/""}; + static const ChangeAnnotationIdentifier RemoveAllUnusedID = + "RemoveAllUnusedIncludes"; + for (unsigned I = 0; I < RemoveAll.Edits.size(); ++I) { + ChangeAnnotationIdentifier ID = RemoveAllUnusedID + std::to_string(I); + RemoveAll.Edits[I].annotationId = ID; + RemoveAll.Annotations.push_back({ID, Annotation}); + } + return RemoveAll; +} +Fix addAllMissingIncludes(llvm::ArrayRef MissingIncludeDiags) { + assert(!MissingIncludeDiags.empty()); + + Fix AddAllMissing; + AddAllMissing.Message = "add all missing includes"; + // A map to deduplicate the edits with the same new text. + // newText (#include "my_missing_header.h") -> TextEdit. + llvm::StringMap Edits; + for (const auto &Diag : MissingIncludeDiags) { + assert(Diag.Fixes.size() == 1 && "Expected exactly one fix."); + for (const auto& Edit : Diag.Fixes.front().Edits) { + Edits.try_emplace(Edit.newText, Edit); + } + } + // FIXME(hokein): emit used symbol reference in the annotation. + ChangeAnnotation Annotation = {/*label=*/"", + /*needsConfirmation=*/true, + /*description=*/""}; + static const ChangeAnnotationIdentifier AddAllMissingID = + "AddAllMissingIncludes"; + unsigned I = 0; + for (auto &It : Edits) { + ChangeAnnotationIdentifier ID = AddAllMissingID + std::to_string(I++); + AddAllMissing.Edits.push_back(std::move(It.getValue())); + AddAllMissing.Edits.back().annotationId = ID; + + AddAllMissing.Annotations.push_back({ID, Annotation}); + } + return AddAllMissing; +} +Fix fixAll(const Fix& RemoveAllUnused, const Fix& AddAllMissing) { + Fix FixAll; + FixAll.Message = "fix all includes"; + + for (const auto &F : RemoveAllUnused.Edits) + FixAll.Edits.push_back(F); + for (const auto &F : AddAllMissing.Edits) + FixAll.Edits.push_back(F); + + for (const auto& A : RemoveAllUnused.Annotations) + FixAll.Annotations.push_back(A); + for (const auto& A : AddAllMissing.Annotations) + FixAll.Annotations.push_back(A); + return FixAll; +} + +std::vector generateIncludeCleanerDiagnostic( + ParsedAST &AST, const IncludeCleanerFindings &Findings, + llvm::StringRef Code) { + std::vector UnusedIncludes = generateUnusedIncludeDiagnostics( + AST.tuPath(), Findings.UnusedIncludes, Code); + std::optional RemoveAllUnused;; + if (UnusedIncludes.size() > 1) + RemoveAllUnused = removeAllUnusedIncludes(UnusedIncludes); + + std::vector MissingIncludeDiags = generateMissingIncludeDiagnostics( + AST, Findings.MissingIncludes, Code); + std::optional AddAllMissing; + if (MissingIncludeDiags.size() > 1) + AddAllMissing = addAllMissingIncludes(MissingIncludeDiags); + + std::optional FixAll; + if (RemoveAllUnused && AddAllMissing) + FixAll = fixAll(*RemoveAllUnused, *AddAllMissing); + + auto AddBatchFix = [](const std::optional &F, clang::clangd::Diag *Out) { + if (!F) return; + Out->Fixes.push_back(*F); + }; + for (auto &Diag : MissingIncludeDiags) { + AddBatchFix(AddAllMissing, &Diag); + AddBatchFix(FixAll, &Diag); + } + for (auto &Diag : UnusedIncludes) { + AddBatchFix(RemoveAllUnused, &Diag); + AddBatchFix(FixAll, &Diag); + } + + auto Result = std::move(MissingIncludeDiags); + llvm::move(UnusedIncludes, + std::back_inserter(Result)); + return Result; +} + std::vector issueIncludeCleanerDiagnostics(ParsedAST &AST, llvm::StringRef Code) { // Interaction is only polished for C/CPP. @@ -435,13 +564,7 @@ std::vector issueIncludeCleanerDiagnostics(ParsedAST &AST, // will need include-cleaner results, call it once Findings = computeIncludeCleanerFindings(AST); } - - std::vector Result = generateUnusedIncludeDiagnostics( - AST.tuPath(), Findings.UnusedIncludes, Code); - llvm::move( - generateMissingIncludeDiagnostics(AST, Findings.MissingIncludes, Code), - std::back_inserter(Result)); - return Result; + return generateIncludeCleanerDiagnostic(AST, Findings, Code); } std::optional diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp index 4b2472ad4be04..f613624cd25d8 100644 --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -187,14 +187,45 @@ bool fromJSON(const llvm::json::Value &Params, TextDocumentItem &R, bool fromJSON(const llvm::json::Value &Params, TextEdit &R, llvm::json::Path P) { llvm::json::ObjectMapper O(Params, P); - return O && O.map("range", R.range) && O.map("newText", R.newText); + return O && O.map("range", R.range) && O.map("newText", R.newText) && + O.mapOptional("annotationId", R.annotationId); } llvm::json::Value toJSON(const TextEdit &P) { - return llvm::json::Object{ + llvm::json::Object Result{ {"range", P.range}, {"newText", P.newText}, }; + if (!P.annotationId.empty()) + Result["annotationId"] = P.annotationId; + return Result; +} + +bool fromJSON(const llvm::json::Value &Params, ChangeAnnotation &R, + llvm::json::Path P) { + llvm::json::ObjectMapper O(Params, P); + return O && O.map("label", R.label) && + O.map("needsConfirmation", R.needsConfirmation) && + O.mapOptional("description", R.description); +} +llvm::json::Value toJSON(const ChangeAnnotation & CA) { + llvm::json::Object Result{{"label", CA.label}}; + if (CA.needsConfirmation) + Result["needsConfirmation"] = *CA.needsConfirmation; + if (!CA.description.empty()) + Result["description"] = CA.description; + return Result; +} + +bool fromJSON(const llvm::json::Value &Params, TextDocumentEdit &R, + llvm::json::Path P) { + llvm::json::ObjectMapper O(Params, P); + return O && O.map("textDocument", R.textDocument) && O.map("edits", R.edits); +} +llvm::json::Value toJSON(const TextDocumentEdit &P) { + llvm::json::Object Result{{"textDocument", P.textDocument}, + {"edits", P.edits}}; + return Result; } llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const TextEdit &TE) { @@ -444,6 +475,13 @@ bool fromJSON(const llvm::json::Value &Params, ClientCapabilities &R, if (auto RefreshSupport = SemanticTokens->getBoolean("refreshSupport")) R.SemanticTokenRefreshSupport = *RefreshSupport; } + if (auto *WorkspaceEdit = Workspace->getObject("workspaceEdit")) { + if (auto DocumentChanges = WorkspaceEdit->getBoolean("documentChanges")) + R.DocumentChanges = *DocumentChanges; + if (WorkspaceEdit->getObject("changeAnnotationSupport")) { + R.ChangeAnnotation = true; + } + } } if (auto *Window = O->getObject("window")) { if (auto WorkDoneProgress = Window->getBoolean("workDoneProgress")) @@ -717,7 +755,9 @@ bool fromJSON(const llvm::json::Value &Params, CodeActionParams &R, bool fromJSON(const llvm::json::Value &Params, WorkspaceEdit &R, llvm::json::Path P) { llvm::json::ObjectMapper O(Params, P); - return O && O.map("changes", R.changes); + return O && O.map("changes", R.changes) && + O.map("documentChanges", R.documentChanges) && + O.mapOptional("changeAnnotations", R.changeAnnotations); } bool fromJSON(const llvm::json::Value &Params, ExecuteCommandParams &R, @@ -863,10 +903,22 @@ llvm::json::Value toJSON(const DocumentSymbol &S) { } llvm::json::Value toJSON(const WorkspaceEdit &WE) { - llvm::json::Object FileChanges; - for (auto &Change : WE.changes) - FileChanges[Change.first] = llvm::json::Array(Change.second); - return llvm::json::Object{{"changes", std::move(FileChanges)}}; + llvm::json::Object Result; + if (WE.changes) { + llvm::json::Object FileChanges; + for (auto &Change : *WE.changes) + FileChanges[Change.first] = llvm::json::Array(Change.second); + Result["changes"] = std::move(FileChanges); + } + if (WE.documentChanges) + Result["documentChanges"] = *WE.documentChanges; + if (!WE.changeAnnotations.empty()) { + llvm::json::Object ChangeAnnotations; + for (auto &Annotation : WE.changeAnnotations) + ChangeAnnotations[Annotation.first] = Annotation.second; + Result["changeAnnotations"] = std::move(ChangeAnnotations); + } + return Result; } bool fromJSON(const llvm::json::Value &Params, TweakArgs &A, diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h index eb271676e651f..1d6fccff6ea4d 100644 --- a/clang-tools-extra/clangd/Protocol.h +++ b/clang-tools-extra/clangd/Protocol.h @@ -238,6 +238,8 @@ struct ReferenceLocation : Location { llvm::json::Value toJSON(const ReferenceLocation &); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const ReferenceLocation &); +using ChangeAnnotationIdentifier = std::string; +// A combination of a LSP standard TextEdit and AnnotatedTextEdit. struct TextEdit { /// The range of the text document to be manipulated. To insert /// text into a document create a range where start === end. @@ -246,14 +248,46 @@ struct TextEdit { /// The string to be inserted. For delete operations use an /// empty string. std::string newText; + + /// The actual annotation identifier (optional) + /// If empty, then this field is nullopt. + ChangeAnnotationIdentifier annotationId = ""; }; inline bool operator==(const TextEdit &L, const TextEdit &R) { - return std::tie(L.newText, L.range) == std::tie(R.newText, R.range); + return std::tie(L.newText, L.range, L.annotationId) == + std::tie(R.newText, R.range, L.annotationId); } bool fromJSON(const llvm::json::Value &, TextEdit &, llvm::json::Path); llvm::json::Value toJSON(const TextEdit &); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const TextEdit &); +struct ChangeAnnotation { + /// A human-readable string describing the actual change. The string + /// is rendered prominent in the user interface. + std::string label; + + /// A flag which indicates that user confirmation is needed + /// before applying the change. + std::optional needsConfirmation; + + /// A human-readable string which is rendered less prominent in + /// the user interface. + std::string description; +}; +bool fromJSON(const llvm::json::Value &, ChangeAnnotation &, llvm::json::Path); +llvm::json::Value toJSON(const ChangeAnnotation &); + +struct TextDocumentEdit { + /// The text document to change. + VersionedTextDocumentIdentifier textDocument; + + /// The edits to be applied. + /// FIXME: support the AnnotatedTextEdit variant. + std::vector edits; +}; +bool fromJSON(const llvm::json::Value &, TextDocumentEdit &, llvm::json::Path); +llvm::json::Value toJSON(const TextDocumentEdit &); + struct TextDocumentItem { /// The text document's URI. URIForFile uri; @@ -517,6 +551,12 @@ struct ClientCapabilities { /// server to the client. bool SemanticTokenRefreshSupport = false; + /// The client supports versioned document changes for WorkspaceEdit. + bool DocumentChanges = false; + + /// The client supports change annotations on text edits, + bool ChangeAnnotation = false; + /// Whether the client supports the textDocument/inactiveRegions /// notification. This is a clangd extension. bool InactiveRegions = false; @@ -970,12 +1010,22 @@ struct CodeActionParams { }; bool fromJSON(const llvm::json::Value &, CodeActionParams &, llvm::json::Path); +/// The edit should either provide changes or documentChanges. If the client +/// can handle versioned document edits and if documentChanges are present, +/// the latter are preferred over changes. struct WorkspaceEdit { /// Holds changes to existing resources. - std::map> changes; - - /// Note: "documentChanges" is not currently used because currently there is - /// no support for versioned edits. + std::optional>> changes; + /// Versioned document edits. + /// + /// If a client neither supports `documentChanges` nor + /// `workspace.workspaceEdit.resourceOperations` then only plain `TextEdit`s + /// using the `changes` property are supported. + std::optional> documentChanges; + + /// A map of change annotations that can be referenced in + /// AnnotatedTextEdit. + std::map changeAnnotations; }; bool fromJSON(const llvm::json::Value &, WorkspaceEdit &, llvm::json::Path); llvm::json::Value toJSON(const WorkspaceEdit &WE); diff --git a/clang-tools-extra/clangd/index/StdLib.cpp b/clang-tools-extra/clangd/index/StdLib.cpp index 1133d984efbae..381d599e3d2aa 100644 --- a/clang-tools-extra/clangd/index/StdLib.cpp +++ b/clang-tools-extra/clangd/index/StdLib.cpp @@ -47,8 +47,8 @@ llvm::StringLiteral mandatoryHeader(Lang L) { LangStandard::Kind standardFromOpts(const LangOptions &LO) { if (LO.CPlusPlus) { - if (LO.CPlusPlus2b) - return LangStandard::lang_cxx2b; + if (LO.CPlusPlus23) + return LangStandard::lang_cxx23; if (LO.CPlusPlus20) return LangStandard::lang_cxx20; if (LO.CPlusPlus17) diff --git a/clang-tools-extra/clangd/test/Inputs/include-cleaner/all1.h b/clang-tools-extra/clangd/test/Inputs/include-cleaner/all1.h new file mode 100644 index 0000000000000..c1ec51b8f24b9 --- /dev/null +++ b/clang-tools-extra/clangd/test/Inputs/include-cleaner/all1.h @@ -0,0 +1,4 @@ +#pragma once + +#include "bar.h" +#include "foo.h" diff --git a/clang-tools-extra/clangd/test/Inputs/include-cleaner/all2.h b/clang-tools-extra/clangd/test/Inputs/include-cleaner/all2.h new file mode 100644 index 0000000000000..c1ec51b8f24b9 --- /dev/null +++ b/clang-tools-extra/clangd/test/Inputs/include-cleaner/all2.h @@ -0,0 +1,4 @@ +#pragma once + +#include "bar.h" +#include "foo.h" diff --git a/clang-tools-extra/clangd/test/Inputs/include-cleaner/bar.h b/clang-tools-extra/clangd/test/Inputs/include-cleaner/bar.h new file mode 100644 index 0000000000000..f70dd9565fd48 --- /dev/null +++ b/clang-tools-extra/clangd/test/Inputs/include-cleaner/bar.h @@ -0,0 +1,2 @@ +#pragma once +class Bar {}; diff --git a/clang-tools-extra/clangd/test/Inputs/include-cleaner/foo.h b/clang-tools-extra/clangd/test/Inputs/include-cleaner/foo.h new file mode 100644 index 0000000000000..f35930c9fe80d --- /dev/null +++ b/clang-tools-extra/clangd/test/Inputs/include-cleaner/foo.h @@ -0,0 +1,2 @@ +#pragma once +class Foo {}; diff --git a/clang-tools-extra/clangd/test/fixits-codeaction-documentchanges.test b/clang-tools-extra/clangd/test/fixits-codeaction-documentchanges.test new file mode 100644 index 0000000000000..7a591319a7405 --- /dev/null +++ b/clang-tools-extra/clangd/test/fixits-codeaction-documentchanges.test @@ -0,0 +1,145 @@ +# RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{"textDocument":{"codeAction":{"codeActionLiteralSupport":{}}},"workspace":{"workspaceEdit":{"documentChanges":true}}},"trace":"off"}} +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"int main(int i, char **a) { if (i = 2) {}}"}}} +# CHECK: "method": "textDocument/publishDiagnostics", +# CHECK-NEXT: "params": { +# CHECK-NEXT: "diagnostics": [ +# CHECK-NEXT: { +# CHECK-NEXT: "code": "-Wparentheses", +# CHECK-NEXT: "message": "Using the result of an assignment as a condition without parentheses (fixes available)", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 37, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 32, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "severity": 2, +# CHECK-NEXT: "source": "clang" +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "uri": "file://{{.*}}/foo.c", +# CHECK-NEXT: "version": 1 +# CHECK-NEXT: } +--- +{"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"test:///foo.c"},"range":{"start":{"line":0,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 37}},"severity":2,"message":"Using the result of an assignment as a condition without parentheses (fixes available)", "code": "-Wparentheses", "source": "clang"}]}}} +# CHECK: "id": 2, +# CHECK-NEXT: "jsonrpc": "2.0", +# CHECK-NEXT: "result": [ +# CHECK-NEXT: { +# CHECK-NEXT: "diagnostics": [ +# CHECK-NEXT: { +# CHECK-NEXT: "code": "-Wparentheses", +# CHECK-NEXT: "message": "Using the result of an assignment as a condition without parentheses (fixes available)", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 37, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 32, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "severity": 2, +# CHECK-NEXT: "source": "clang" +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "edit": { +# CHECK-NEXT: "documentChanges": [ +# CHECK-NEXT: { +# CHECK-NEXT: "edits": [ +# CHECK-NEXT: { +# CHECK-NEXT: "newText": "(", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 32, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 32, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "newText": ")", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 37, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 37, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "textDocument": { +# CHECK-NEXT: "uri": "file://{{.*}}/foo.c", +# CHECK-NEXT: "version": 1 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: }, +# CHECK-NEXT: "kind": "quickfix", +# CHECK-NEXT: "title": "place parentheses around the assignment to silence this warning" +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "diagnostics": [ +# CHECK-NEXT: { +# CHECK-NEXT: "code": "-Wparentheses", +# CHECK-NEXT: "message": "Using the result of an assignment as a condition without parentheses (fixes available)", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 37, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 32, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "severity": 2, +# CHECK-NEXT: "source": "clang" +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "edit": { +# CHECK-NEXT: "documentChanges": [ +# CHECK-NEXT: { +# CHECK-NEXT: "edits": [ +# CHECK-NEXT: { +# CHECK-NEXT: "newText": "==", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 35, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 34, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "textDocument": { +# CHECK-NEXT: "uri": "file://{{.*}}/foo.c", +# CHECK-NEXT: "version": 1 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: }, +# CHECK-NEXT: "kind": "quickfix", +# CHECK-NEXT: "title": "use '==' to turn this assignment into an equality comparison" +# CHECK-NEXT: } +# CHECK-NEXT: ] +--- +{"jsonrpc":"2.0","id":4,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} + diff --git a/clang-tools-extra/clangd/test/fixits-command-documentchanges.test b/clang-tools-extra/clangd/test/fixits-command-documentchanges.test new file mode 100644 index 0000000000000..cd636c4df387a --- /dev/null +++ b/clang-tools-extra/clangd/test/fixits-command-documentchanges.test @@ -0,0 +1,194 @@ +# RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{"workspace":{"workspaceEdit":{"documentChanges":true}}},"trace":"off"}} +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","text":"int main(int i, char **a) { if (i = 2) {}}"}}} +# CHECK: "method": "textDocument/publishDiagnostics", +# CHECK-NEXT: "params": { +# CHECK-NEXT: "diagnostics": [ +# CHECK-NEXT: { +# CHECK-NEXT: "code": "-Wparentheses", +# CHECK-NEXT: "message": "Using the result of an assignment as a condition without parentheses (fixes available)", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 37, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 32, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "severity": 2, +# CHECK-NEXT: "source": "clang" +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "uri": "file://{{.*}}/foo.c", +# CHECK-NEXT: "version": 0 +# CHECK-NEXT: } +--- +{"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"test:///foo.c"},"range":{"start":{"line":0,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 37}},"severity":2,"message":"Using the result of an assignment as a condition without parentheses (fixes available)"}]}}} +# CHECK: "id": 2, +# CHECK-NEXT: "jsonrpc": "2.0", +# CHECK-NEXT: "result": [ +# CHECK-NEXT: { +# CHECK-NEXT: "arguments": [ +# CHECK-NEXT: { +# CHECK-NEXT: "documentChanges": [ +# CHECK-NEXT: { +# CHECK-NEXT: "edits": [ +# CHECK-NEXT: { +# CHECK-NEXT: "newText": "(", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 32, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 32, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "newText": ")", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 37, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 37, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "textDocument": { +# CHECK-NEXT: "uri": "file://{{.*}}/foo.c", +# CHECK-NEXT: "version": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "command": "clangd.applyFix", +# CHECK-NEXT: "title": "Apply fix: place parentheses around the assignment to silence this warning" +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "arguments": [ +# CHECK-NEXT: { +# CHECK-NEXT: "documentChanges": [ +# CHECK-NEXT: { +# CHECK-NEXT: "edits": [ +# CHECK-NEXT: { +# CHECK-NEXT: "newText": "==", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 35, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 34, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "textDocument": { +# CHECK-NEXT: "uri": "file://{{.*}}/foo.c", +# CHECK-NEXT: "version": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "command": "clangd.applyFix", +# CHECK-NEXT: "title": "Apply fix: use '==' to turn this assignment into an equality comparison" +# CHECK-NEXT: } +# CHECK-NEXT: ] +--- +{"jsonrpc":"2.0","id":3,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"test:///foo.c"},"range":{"start":{"line":0,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 37}},"severity":2,"message":"Using the result of an assignment as a condition without parentheses (fixes available)"}]}}} +# Make sure unused "code" and "source" fields ignored gracefully +# CHECK: "id": 3, +# CHECK-NEXT: "jsonrpc": "2.0", +# CHECK-NEXT: "result": [ +# CHECK-NEXT: { +# CHECK-NEXT: "arguments": [ +# CHECK-NEXT: { +# CHECK-NEXT: "documentChanges": [ +# CHECK-NEXT: { +# CHECK-NEXT: "edits": [ +# CHECK-NEXT: { +# CHECK-NEXT: "newText": "(", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 32, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 32, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "newText": ")", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 37, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 37, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "textDocument": { +# CHECK-NEXT: "uri": "file://{{.*}}/foo.c", +# CHECK-NEXT: "version": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "command": "clangd.applyFix", +# CHECK-NEXT: "title": "Apply fix: place parentheses around the assignment to silence this warning" +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "arguments": [ +# CHECK-NEXT: { +# CHECK-NEXT: "documentChanges": [ +# CHECK-NEXT: { +# CHECK-NEXT: "edits": [ +# CHECK-NEXT: { +# CHECK-NEXT: "newText": "==", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 35, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 34, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "textDocument": { +# CHECK-NEXT: "uri": "file://{{.*}}/foo.c", +# CHECK-NEXT: "version": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "command": "clangd.applyFix", +# CHECK-NEXT: "title": "Apply fix: use '==' to turn this assignment into an equality comparison" +# CHECK-NEXT: } +# CHECK-NEXT: ] +--- +{"jsonrpc":"2.0","id":4,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} diff --git a/clang-tools-extra/clangd/test/fixits-embed-in-diagnostic.test b/clang-tools-extra/clangd/test/fixits-embed-in-diagnostic.test index debe4dfa5e789..26d3820d04322 100644 --- a/clang-tools-extra/clangd/test/fixits-embed-in-diagnostic.test +++ b/clang-tools-extra/clangd/test/fixits-embed-in-diagnostic.test @@ -48,6 +48,7 @@ # CHECK-NEXT: "source": "clang" # CHECK-NEXT: }, # CHECK-NEXT: { +# CHECK-NEXT: "codeActions": [], # CHECK-NEXT: "message": "Previous use is here\n\nfoo.c:1:18: error: use of 'Point' with tag type that does not match previous declaration", # CHECK-NEXT: "range": { # CHECK-NEXT: "end": { diff --git a/clang-tools-extra/clangd/test/include-cleaner-batch-fix.test b/clang-tools-extra/clangd/test/include-cleaner-batch-fix.test new file mode 100644 index 0000000000000..b7e9661b79b08 --- /dev/null +++ b/clang-tools-extra/clangd/test/include-cleaner-batch-fix.test @@ -0,0 +1,487 @@ +# We specify a custom path in XDG_CONFIG_HOME, which only works on some systems. +# UNSUPPORTED: system-windows +# UNSUPPORTED: system-darwin + +# RUN: rm -rf %t +# RUN: mkdir -p %t/clangd +# RUN: cp -r %S/Inputs/include-cleaner %t/include +# Create a config file enabling include-cleaner features. +# RUN: echo $'Diagnostics:\n UnusedIncludes: Strict\n MissingIncludes: Strict' >> %t/clangd/config.yaml + +# RUN: env XDG_CONFIG_HOME=%t clangd -lit-test -enable-config --resource-dir=%t < %s | FileCheck -strict-whitespace %s +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{"workspace":{"workspaceEdit":{"documentChanges":true, "changeAnnotationSupport":{"groupsOnLabel":true}}}},"trace":"off"}} +--- +{ + "jsonrpc": "2.0", + "method": "textDocument/didOpen", + "params": { + "textDocument": { + "uri": "test:///simple.cpp", + "languageId": "cpp", + "text": "#include \"all1.h\"\n#include \"all2.h\"\n Foo* foo; Bar* bar;" + } + } +} +# First, the diagnostic from the config file. +# CHECK: "method": "textDocument/publishDiagnostics", +# CHECK-NEXT: "params": { +# CHECK-NEXT: "diagnostics": [], + +# Then, diagnostic from the main cpp file. +# CHECK: "method": "textDocument/publishDiagnostics", +# CHECK-NEXT: "params": { +# CHECK-NEXT: "diagnostics": [ +# CHECK-NEXT: { +# CHECK-NEXT: "code": "missing-includes", +# CHECK-NEXT: "message": "No header providing \"Foo\" is directly included (fixes available)", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 4, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 1, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "severity": 2, +# CHECK-NEXT: "source": "clangd" +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "code": "missing-includes", +# CHECK-NEXT: "message": "No header providing \"Bar\" is directly included (fixes available)", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 14, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 11, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "severity": 2, +# CHECK-NEXT: "source": "clangd" +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "code": "unused-includes", +# CHECK-NEXT: "codeDescription": { +# CHECK-NEXT: "href": "https://clangd.llvm.org/guides/include-cleaner" +# CHECK-NEXT: }, +# CHECK-NEXT: "message": "Included header all1.h is not used directly (fixes available)", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 17, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "severity": 2, +# CHECK-NEXT: "source": "clangd", +# CHECK-NEXT: "tags": [ +# CHECK-NEXT: 1 +# CHECK-NEXT: ] +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "code": "unused-includes", +# CHECK-NEXT: "codeDescription": { +# CHECK-NEXT: "href": "https://clangd.llvm.org/guides/include-cleaner" +# CHECK-NEXT: }, +# CHECK-NEXT: "message": "Included header all2.h is not used directly (fixes available)", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 17, +# CHECK-NEXT: "line": 1 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 1 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "severity": 2, +# CHECK-NEXT: "source": "clangd", +# CHECK-NEXT: "tags": [ +# CHECK-NEXT: 1 +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "uri": "file://{{.*}}/simple.cpp", +# CHECK-NEXT: "version": 0 +# CHECK-NEXT: } +--- +{"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"test:///simple.cpp"},"range":{"start":{"line":2,"character":1},"end":{"line":2,"character":4}},"context":{"diagnostics":[{"range":{"start": {"line": 2, "character": 1}, "end": {"line": 2, "character": 4}},"severity":2,"message":"No header providing \"Foo\" is directly included (fixes available)", "code": "missing-includes", "source": "clangd"}]}}} +# CHECK: "id": 2, +# CHECK-NEXT: "jsonrpc": "2.0", +# CHECK-NEXT: "result": [ +# CHECK-NEXT: { +# CHECK-NEXT: "arguments": [ +# CHECK-NEXT: { +# CHECK-NEXT: "documentChanges": [ +# CHECK-NEXT: { +# CHECK-NEXT: "edits": [ +# CHECK-NEXT: { +# CHECK-NEXT: "newText": "#include {{.*}}foo.h{{.*}}", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "textDocument": { +# CHECK-NEXT: "uri": "file://{{.*}}/simple.cpp", +# CHECK-NEXT: "version": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "command": "clangd.applyFix", +# CHECK-NEXT: "title": "Apply fix: #include {{.*}}foo.h{{.*}}" +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "arguments": [ +# CHECK-NEXT: { +# CHECK-NEXT: "changeAnnotations": { +# CHECK-NEXT: "AddAllMissingIncludes0": { +# CHECK-NEXT: "label": "", +# CHECK-NEXT: "needsConfirmation": true +# CHECK-NEXT: }, +# CHECK-NEXT: "AddAllMissingIncludes1": { +# CHECK-NEXT: "label": "", +# CHECK-NEXT: "needsConfirmation": true +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "documentChanges": [ +# CHECK-NEXT: { +# CHECK-NEXT: "edits": [ +# CHECK-NEXT: { +# CHECK-NEXT: "annotationId": "AddAllMissingIncludes0", +# CHECK-NEXT: "newText": "#include {{.*}}bar.h{{.*}}", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "annotationId": "AddAllMissingIncludes1", +# CHECK-NEXT: "newText": "#include {{.*}}foo.h{{.*}}", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "textDocument": { +# CHECK-NEXT: "uri": "file://{{.*}}/simple.cpp", +# CHECK-NEXT: "version": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "command": "clangd.applyFix", +# CHECK-NEXT: "title": "Apply fix: add all missing includes" +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "arguments": [ +# CHECK-NEXT: { +# CHECK-NEXT: "changeAnnotations": { +# CHECK-NEXT: "AddAllMissingIncludes0": { +# CHECK-NEXT: "label": "", +# CHECK-NEXT: "needsConfirmation": true +# CHECK-NEXT: }, +# CHECK-NEXT: "AddAllMissingIncludes1": { +# CHECK-NEXT: "label": "", +# CHECK-NEXT: "needsConfirmation": true +# CHECK-NEXT: }, +# CHECK-NEXT: "RemoveAllUnusedIncludes0": { +# CHECK-NEXT: "label": "", +# CHECK-NEXT: "needsConfirmation": true +# CHECK-NEXT: }, +# CHECK-NEXT: "RemoveAllUnusedIncludes1": { +# CHECK-NEXT: "label": "", +# CHECK-NEXT: "needsConfirmation": true +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "documentChanges": [ +# CHECK-NEXT: { +# CHECK-NEXT: "edits": [ +# CHECK-NEXT: { +# CHECK-NEXT: "annotationId": "RemoveAllUnusedIncludes0", +# CHECK-NEXT: "newText": "", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 1 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "annotationId": "RemoveAllUnusedIncludes1", +# CHECK-NEXT: "newText": "", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 1 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "annotationId": "AddAllMissingIncludes0", +# CHECK-NEXT: "newText": "#include {{.*}}bar.h{{.*}}", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "annotationId": "AddAllMissingIncludes1", +# CHECK-NEXT: "newText": "#include {{.*}}foo.h{{.*}}", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "textDocument": { +# CHECK-NEXT: "uri": "file://{{.*}}/simple.cpp", +# CHECK-NEXT: "version": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "command": "clangd.applyFix", +# CHECK-NEXT: "title": "Apply fix: fix all includes" +# CHECK-NEXT: } +# CHECK-NEXT: ] +--- +{"jsonrpc":"2.0","id":3,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"test:///simple.cpp"},"range":{"start":{"line":0,"character":0},"end":{"line":0,"character":17}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 17}},"severity":2,"message":"Included header all1.h is not used directly (fixes available)", "code": "unused-includes", "source": "clangd"}]}}} +# CHECK: "id": 3, +# CHECK-NEXT: "jsonrpc": "2.0", +# CHECK-NEXT: "result": [ +# CHECK-NEXT: { +# CHECK-NEXT: "arguments": [ +# CHECK-NEXT: { +# CHECK-NEXT: "documentChanges": [ +# CHECK-NEXT: { +# CHECK-NEXT: "edits": [ +# CHECK-NEXT: { +# CHECK-NEXT: "newText": "", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 1 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "textDocument": { +# CHECK-NEXT: "uri": "file://{{.*}}/simple.cpp", +# CHECK-NEXT: "version": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "command": "clangd.applyFix", +# CHECK-NEXT: "title": "Apply fix: remove #include directive" +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "arguments": [ +# CHECK-NEXT: { +# CHECK-NEXT: "changeAnnotations": { +# CHECK-NEXT: "RemoveAllUnusedIncludes0": { +# CHECK-NEXT: "label": "", +# CHECK-NEXT: "needsConfirmation": true +# CHECK-NEXT: }, +# CHECK-NEXT: "RemoveAllUnusedIncludes1": { +# CHECK-NEXT: "label": "", +# CHECK-NEXT: "needsConfirmation": true +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "documentChanges": [ +# CHECK-NEXT: { +# CHECK-NEXT: "edits": [ +# CHECK-NEXT: { +# CHECK-NEXT: "annotationId": "RemoveAllUnusedIncludes0", +# CHECK-NEXT: "newText": "", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 1 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "annotationId": "RemoveAllUnusedIncludes1", +# CHECK-NEXT: "newText": "", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 1 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "textDocument": { +# CHECK-NEXT: "uri": "file://{{.*}}/simple.cpp", +# CHECK-NEXT: "version": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "command": "clangd.applyFix", +# CHECK-NEXT: "title": "Apply fix: remove all unused includes" +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "arguments": [ +# CHECK-NEXT: { +# CHECK-NEXT: "changeAnnotations": { +# CHECK-NEXT: "AddAllMissingIncludes0": { +# CHECK-NEXT: "label": "", +# CHECK-NEXT: "needsConfirmation": true +# CHECK-NEXT: }, +# CHECK-NEXT: "AddAllMissingIncludes1": { +# CHECK-NEXT: "label": "", +# CHECK-NEXT: "needsConfirmation": true +# CHECK-NEXT: }, +# CHECK-NEXT: "RemoveAllUnusedIncludes0": { +# CHECK-NEXT: "label": "", +# CHECK-NEXT: "needsConfirmation": true +# CHECK-NEXT: }, +# CHECK-NEXT: "RemoveAllUnusedIncludes1": { +# CHECK-NEXT: "label": "", +# CHECK-NEXT: "needsConfirmation": true +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "documentChanges": [ +# CHECK-NEXT: { +# CHECK-NEXT: "edits": [ +# CHECK-NEXT: { +# CHECK-NEXT: "annotationId": "RemoveAllUnusedIncludes0", +# CHECK-NEXT: "newText": "", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 1 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "annotationId": "RemoveAllUnusedIncludes1", +# CHECK-NEXT: "newText": "", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 1 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "annotationId": "AddAllMissingIncludes0", +# CHECK-NEXT: "newText": "#include {{.*}}bar.h{{.*}}", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "annotationId": "AddAllMissingIncludes1", +# CHECK-NEXT: "newText": "#include {{.*}}foo.h{{.*}}", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "textDocument": { +# CHECK-NEXT: "uri": "file://{{.*}}/simple.cpp", +# CHECK-NEXT: "version": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "command": "clangd.applyFix", +# CHECK-NEXT: "title": "Apply fix: fix all includes" +# CHECK-NEXT: } +# CHECK-NEXT: ] +--- +{"jsonrpc":"2.0","id":4,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} diff --git a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp index 289767b0dec0f..552f4f4ecf886 100644 --- a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp +++ b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp @@ -1908,11 +1908,11 @@ TEST(DiagnosticsTest, IncludeCleaner) { auto AST = TU.build(); EXPECT_THAT( *AST.getDiagnostics(), - UnorderedElementsAre(AllOf( - Diag(Test.range("diag"), - "included header unused.h is not used directly"), - withTag(DiagnosticTag::Unnecessary), diagSource(Diag::Clangd), - withFix(Fix(Test.range("fix"), "", "remove #include directive"))))); + UnorderedElementsAre( + AllOf(Diag(Test.range("diag"), + "included header unused.h is not used directly"), + withTag(DiagnosticTag::Unnecessary), diagSource(Diag::Clangd), + withFix(Fix(Test.range("fix"), "", "remove #include directive"))))); auto &Diag = AST.getDiagnostics()->front(); EXPECT_EQ(getDiagnosticDocURI(Diag.Source, Diag.ID, Diag.Name), std::string("https://clangd.llvm.org/guides/include-cleaner")); diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp index 5ad9d69cb0dc2..ce546f54dd75f 100644 --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -906,6 +906,35 @@ class Foo final {})cpp"; HI.CalleeArgInfo->Type = "int &"; HI.CallPassType = HoverInfo::PassType{PassMode::Ref, false}; }}, + {// make_unique-like function call + R"cpp( + struct Foo { + explicit Foo(int arg_a) {} + }; + template + T make(Args&&... args) + { + return T(args...); + } + + void code() { + int a = 1; + auto foo = make([[^a]]); + } + )cpp", + [](HoverInfo &HI) { + HI.Name = "a"; + HI.Kind = index::SymbolKind::Variable; + HI.NamespaceScope = ""; + HI.Definition = "int a = 1"; + HI.LocalScope = "code::"; + HI.Value = "1"; + HI.Type = "int"; + HI.CalleeArgInfo.emplace(); + HI.CalleeArgInfo->Name = "arg_a"; + HI.CalleeArgInfo->Type = "int"; + HI.CallPassType = HoverInfo::PassType{PassMode::Value, false}; + }}, { R"cpp( void foobar(const float &arg); @@ -927,6 +956,29 @@ class Foo final {})cpp"; HI.CalleeArgInfo->Type = "const float &"; HI.CallPassType = HoverInfo::PassType{PassMode::Value, true}; }}, + { + R"cpp( + struct Foo { + explicit Foo(const float& arg) {} + }; + int main() { + int a = 0; + Foo foo([[^a]]); + } + )cpp", + [](HoverInfo &HI) { + HI.Name = "a"; + HI.Kind = index::SymbolKind::Variable; + HI.NamespaceScope = ""; + HI.Definition = "int a = 0"; + HI.LocalScope = "main::"; + HI.Value = "0"; + HI.Type = "int"; + HI.CalleeArgInfo.emplace(); + HI.CalleeArgInfo->Name = "arg"; + HI.CalleeArgInfo->Type = "const float &"; + HI.CallPassType = HoverInfo::PassType{PassMode::Value, true}; + }}, {// Literal passed to function call R"cpp( void fun(int arg_a, const int &arg_b) {}; @@ -1313,6 +1365,7 @@ class CustomClass { CustomClass(const Base &x) {} CustomClass(int &x) {} CustomClass(float x) {} + CustomClass(int x, int y) {} }; void int_by_ref(int &x) {} @@ -1359,6 +1412,11 @@ void fun() { {"base_by_ref([[^derived]]);", PassMode::Ref, false}, {"base_by_const_ref([[^derived]]);", PassMode::ConstRef, false}, {"base_by_value([[^derived]]);", PassMode::Value, false}, + // Custom class constructor tests + {"CustomClass c1([[^base]]);", PassMode::ConstRef, false}, + {"auto c2 = new CustomClass([[^base]]);", PassMode::ConstRef, false}, + {"CustomClass c3([[^int_x]]);", PassMode::Ref, false}, + {"CustomClass c3(int_x, [[^int_x]]);", PassMode::Value, false}, // Converted tests {"float_by_value([[^int_x]]);", PassMode::Value, true}, {"float_by_value([[^int_ref]]);", PassMode::Value, true}, diff --git a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp index 39901fda31656..a38c01b43270f 100644 --- a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp +++ b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp @@ -45,8 +45,8 @@ using ::testing::Matcher; using ::testing::Pointee; using ::testing::UnorderedElementsAre; -Matcher withFix(::testing::Matcher FixMatcher) { - return Field(&Diag::Fixes, ElementsAre(FixMatcher)); +Matcher withFix(std::vector<::testing::Matcher> FixMatcheres) { + return Field(&Diag::Fixes, testing::UnorderedElementsAreArray(FixMatcheres)); } MATCHER_P2(Diag, Range, Message, @@ -60,6 +60,8 @@ MATCHER_P3(Fix, Range, Replacement, Message, return arg.Message == Message && arg.Edits.size() == 1 && arg.Edits[0].range == Range && arg.Edits[0].newText == Replacement; } +MATCHER_P(FixMessage, Message, "") { return arg.Message == Message; } + std::string guard(llvm::StringRef Code) { return "#pragma once\n" + Code.str(); @@ -255,42 +257,51 @@ TEST(IncludeCleaner, GenerateMissingHeaderDiags) { UnorderedElementsAre( AllOf(Diag(MainFile.range("b"), "No header providing \"b\" is directly included"), - withFix(Fix(MainFile.range("insert_b"), "#include \"b.h\"\n", - "#include \"b.h\""))), + withFix({Fix(MainFile.range("insert_b"), "#include \"b.h\"\n", + "#include \"b.h\""), + FixMessage("add all missing includes")})), AllOf(Diag(MainFile.range("bar"), "No header providing \"ns::Bar\" is directly included"), - withFix(Fix(MainFile.range("insert_d"), - "#include \"dir/d.h\"\n", "#include \"dir/d.h\""))), + withFix({Fix(MainFile.range("insert_d"), + "#include \"dir/d.h\"\n", "#include \"dir/d.h\""), + FixMessage("add all missing includes")})), AllOf(Diag(MainFile.range("f"), "No header providing \"f\" is directly included"), - withFix(Fix(MainFile.range("insert_f"), "#include \n", - "#include "))), + withFix({Fix(MainFile.range("insert_f"), "#include \n", + "#include "), + FixMessage("add all missing includes")})), AllOf( Diag(MainFile.range("foobar"), "No header providing \"foobar\" is directly included"), - withFix(Fix(MainFile.range("insert_foobar"), - "#include \"public.h\"\n", "#include \"public.h\""))), + withFix({Fix(MainFile.range("insert_foobar"), + "#include \"public.h\"\n", "#include \"public.h\""), + FixMessage("add all missing includes")})), AllOf( Diag(MainFile.range("vector"), "No header providing \"std::vector\" is directly included"), - withFix(Fix(MainFile.range("insert_vector"), - "#include \n", "#include "))), + withFix({Fix(MainFile.range("insert_vector"), + "#include \n", "#include "), + FixMessage("add all missing includes"),})), AllOf(Diag(MainFile.range("FOO"), "No header providing \"FOO\" is directly included"), - withFix(Fix(MainFile.range("insert_foo"), - "#include \"foo.h\"\n", "#include \"foo.h\""))), + withFix({Fix(MainFile.range("insert_foo"), + "#include \"foo.h\"\n", "#include \"foo.h\""), + FixMessage("add all missing includes")})), AllOf(Diag(MainFile.range("DEF"), "No header providing \"Foo\" is directly included"), - withFix(Fix(MainFile.range("insert_foo"), - "#include \"foo.h\"\n", "#include \"foo.h\""))), + withFix({Fix(MainFile.range("insert_foo"), + "#include \"foo.h\"\n", "#include \"foo.h\""), + FixMessage("add all missing includes")})), AllOf(Diag(MainFile.range("BAR"), "No header providing \"BAR\" is directly included"), - withFix(Fix(MainFile.range("insert_foo"), - "#include \"foo.h\"\n", "#include \"foo.h\""))), + withFix({Fix(MainFile.range("insert_foo"), + "#include \"foo.h\"\n", "#include \"foo.h\""), + FixMessage("add all missing includes")})), AllOf(Diag(MainFile.range("Foo"), "No header providing \"Foo\" is directly included"), - withFix(Fix(MainFile.range("insert_foo"), - "#include \"foo.h\"\n", "#include \"foo.h\""))))); + withFix({Fix(MainFile.range("insert_foo"), + "#include \"foo.h\"\n", "#include \"foo.h\""), + FixMessage("add all missing includes")})))); } TEST(IncludeCleaner, IWYUPragmas) { @@ -403,15 +414,36 @@ TEST(IncludeCleaner, MacroExpandedThroughIncludes) { ParsedAST AST = TU.build(); auto Findings = computeIncludeCleanerFindings(AST).MissingIncludes; - // FIXME: Deduplicate references resulting from expansion of the same macro in - // multiple places. - EXPECT_THAT(Findings, testing::SizeIs(2)); + EXPECT_THAT(Findings, testing::SizeIs(1)); auto RefRange = Findings.front().SymRefRange; auto &SM = AST.getSourceManager(); EXPECT_EQ(RefRange.file(), SM.getMainFileID()); // FIXME: Point at the spelling location, rather than the include. EXPECT_EQ(halfOpenToRange(SM, RefRange.toCharRange(SM)), MainFile.range()); - EXPECT_EQ(RefRange, Findings[1].SymRefRange); +} + +TEST(IncludeCleaner, MissingIncludesAreUnique) { + Annotations MainFile(R"cpp( + #include "all.h" + FOO([[Foo]]); + )cpp"); + + TestTU TU; + TU.AdditionalFiles["foo.h"] = guard("struct Foo {};"); + TU.AdditionalFiles["all.h"] = guard(R"cpp( + #include "foo.h" + #define FOO(X) X y; X z + )cpp"); + + TU.Code = MainFile.code(); + ParsedAST AST = TU.build(); + + auto Findings = computeIncludeCleanerFindings(AST).MissingIncludes; + EXPECT_THAT(Findings, testing::SizeIs(1)); + auto RefRange = Findings.front().SymRefRange; + auto &SM = AST.getSourceManager(); + EXPECT_EQ(RefRange.file(), SM.getMainFileID()); + EXPECT_EQ(halfOpenToRange(SM, RefRange.toCharRange(SM)), MainFile.range()); } TEST(IncludeCleaner, NoCrash) { diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 49f880c971a34..98f18cc539faf 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -106,6 +106,12 @@ Improvements to clang-tidy New checks ^^^^^^^^^^ +- New :doc:`bugprone-multiple-new-in-one-expression + ` check. + + Finds multiple ``new`` operator calls in a single expression, where the allocated + memory by the first ``new`` may leak if the second allocation fails and throws exception. + - New :doc:`bugprone-non-zero-enum-to-bool-conversion ` check. @@ -201,6 +207,10 @@ Changes in existing checks ` check. Global options of the same name should be used instead. +- Improved :doc:`bugprone-exception-escape + ` to not emit warnings for + forward declarations of functions. + - Improved :doc:`bugprone-fold-init-type ` to handle iterators that do not define `value_type` type aliases. @@ -219,8 +229,9 @@ Changes in existing checks to ``std::forward``. - Improved :doc:`bugprone-use-after-move - ` check to also cover constructor - initializers. + ` check. Detect uses and moves in + constructor initializers. Correctly handle constructor arguments as being + sequenced when constructor call is written as list-initialization. - Deprecated :doc:`cert-dcl21-cpp ` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/multiple-new-in-one-expression.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/multiple-new-in-one-expression.rst new file mode 100644 index 0000000000000..b9b8984ef6584 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/multiple-new-in-one-expression.rst @@ -0,0 +1,99 @@ +.. title:: clang-tidy - bugprone-multiple-new-in-one-expression + +bugprone-multiple-new-in-one-expression +======================================= + +Finds multiple ``new`` operator calls in a single expression, where the +allocated memory by the first ``new`` may leak if the second allocation fails +and throws exception. + +C++ does often not specify the exact order of evaluation of the operands of an +operator or arguments of a function. Therefore if a first allocation succeeds +and a second fails, in an exception handler it is not possible to tell which +allocation has failed and free the memory. Even if the order is fixed the result +of a first ``new`` may be stored in a temporary location that is not reachable +at the time when a second allocation fails. It is best to avoid any expression +that contains more than one ``operator new`` call, if exception handling is +used to check for allocation errors. + +Different rules apply for are the short-circuit operators ``||`` and ``&&`` and +the ``,`` operator, where evaluation of one side must be completed before the +other starts. Expressions of a list-initialization (initialization or +construction using ``{`` and ``}`` characters) are evaluated in fixed order. +Similarly, condition of a ``?`` operator is evaluated before the branches are +evaluated. + +The check reports warning if two ``new`` calls appear in one expression at +different sides of an operator, or if ``new`` calls appear in different +arguments of a function call (that can be an object construction with ``()`` +syntax). These ``new`` calls can be nested at any level. +For any warning to be emitted the ``new`` calls should be in a code block where +exception handling is used with catch for ``std::bad_alloc`` or +``std::exception``. At ``||``, ``&&``, ``,``, ``?`` (condition and one branch) +operators no warning is emitted. No warning is emitted if both of the memory +allocations are not assigned to a variable or not passed directly to a function. +The reason is that in this case the memory may be intentionally not freed or the +allocated objects can be self-destructing objects. + +Examples: + +.. code-block:: c++ + + struct A { + int Var; + }; + struct B { + B(); + B(A *); + int Var; + }; + struct C { + int *X1; + int *X2; + }; + + void f(A *, B *); + int f1(A *); + int f1(B *); + bool f2(A *); + + void foo() { + A *PtrA; + B *PtrB; + try { + // Allocation of 'B'/'A' may fail after memory for 'A'/'B' was allocated. + f(new A, new B); // warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; order of these allocations is undefined + + // List (aggregate) initialization is used. + C C1{new int, new int}; // no warning + + // Allocation of 'B'/'A' may fail after memory for 'A'/'B' was allocated but not yet passed to function 'f1'. + int X = f1(new A) + f1(new B); // warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; order of these allocations is undefined + + // Allocation of 'B' may fail after memory for 'A' was allocated. + // From C++17 on memory for 'B' is allocated first but still may leak if allocation of 'A' fails. + PtrB = new B(new A); // warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception + + // 'new A' and 'new B' may be performed in any order. + // 'new B'/'new A' may fail after memory for 'A'/'B' was allocated but not assigned to 'PtrA'/'PtrB'. + (PtrA = new A)->Var = (PtrB = new B)->Var; // warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; order of these allocations is undefined + + // Evaluation of 'f2(new A)' must be finished before 'f1(new B)' starts. + // If 'new B' fails the allocated memory for 'A' is supposedly handled correctly because function 'f2' could take the ownership. + bool Z = f2(new A) || f1(new B); // no warning + + X = (f2(new A) ? f1(new A) : f1(new B)); // no warning + + // No warning if the result of both allocations is not passed to a function + // or stored in a variable. + (new A)->Var = (new B)->Var; // no warning + + // No warning if at least one non-throwing allocation is used. + f(new(std::nothrow) A, new B); // no warning + } catch(std::bad_alloc) { + } + + // No warning if the allocation is outside a try block (or no catch handler exists for std::bad_alloc). + // (The fact if exceptions can escape from 'foo' is not taken into account.) + f(new A, new B); // no warning + } diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 3ddd257e70de6..6a58bb932e9ec 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -102,6 +102,7 @@ Clang-Tidy Checks `bugprone-misplaced-pointer-arithmetic-in-alloc `_, "Yes" `bugprone-misplaced-widening-cast `_, `bugprone-move-forwarding-reference `_, "Yes" + `bugprone-multiple-new-in-one-expression `_, `bugprone-multiple-statement-macro `_, `bugprone-no-escape `_, `bugprone-non-zero-enum-to-bool-conversion `_, diff --git a/clang-tools-extra/docs/doxygen.cfg.in b/clang-tools-extra/docs/doxygen.cfg.in index 7e1d47a7a95a5..df33a63906389 100644 --- a/clang-tools-extra/docs/doxygen.cfg.in +++ b/clang-tools-extra/docs/doxygen.cfg.in @@ -2102,7 +2102,7 @@ CLASS_GRAPH = YES # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -COLLABORATION_GRAPH = YES +COLLABORATION_GRAPH = NO # If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for # groups, showing the direct groups dependencies. @@ -2147,7 +2147,7 @@ TEMPLATE_RELATIONS = YES # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -INCLUDE_GRAPH = YES +INCLUDE_GRAPH = NO # If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are # set to YES then doxygen will generate a graph for each documented file showing @@ -2156,7 +2156,7 @@ INCLUDE_GRAPH = YES # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -INCLUDED_BY_GRAPH = YES +INCLUDED_BY_GRAPH = NO # If the CALL_GRAPH tag is set to YES then doxygen will generate a call # dependency graph for every global function or class method. diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp index 142c8b114b7c8..78d434854b095 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp @@ -655,7 +655,6 @@ int directly_recursive(int n) noexcept { } int indirectly_recursive(int n) noexcept; - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'indirectly_recursive' which should not throw exceptions int recursion_helper(int n) { indirectly_recursive(n); diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/multiple-new-in-one-expression.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/multiple-new-in-one-expression.cpp new file mode 100644 index 0000000000000..3bf8544d599f3 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/multiple-new-in-one-expression.cpp @@ -0,0 +1,197 @@ +// RUN: %check_clang_tidy -std=c++11 -check-suffixes=ALL,CPP11 %s bugprone-multiple-new-in-one-expression %t -- -- -target x86_64-unknown-unknown +// RUN: %check_clang_tidy -std=c++17 -check-suffixes=ALL,CPP17 %s bugprone-multiple-new-in-one-expression %t -- -- -target x86_64-unknown-unknown + +namespace std { +typedef __typeof__(sizeof(0)) size_t; +enum class align_val_t : std::size_t {}; +class exception {}; +class bad_alloc : public exception {}; +struct nothrow_t {}; +extern const nothrow_t nothrow; +} // namespace std + +void *operator new(std::size_t, const std::nothrow_t &) noexcept; +void *operator new(std::size_t, std::align_val_t, const std::nothrow_t &) noexcept; +void *operator new(std::size_t, void *) noexcept; +void *operator new(std::size_t, char); + +struct B; + +struct A { int VarI; int *PtrI; B *PtrB; }; + +struct B { int VarI; }; + +struct G { + G(A*, B*) {} + int operator+=(A *) { return 3; }; +}; + +struct H { + int *a; + int *b; +}; + +int f(int); +int f(A*); +int f(A*, B*); +int f(int, B*); +int f(G, G); +int f(B*); +int f(const H &); +void f1(void *, void *); +A *g(A *); + +G operator+(const G&, const G&); + +void test_function_parameter(A *XA, B *XB) { + (void)f(new A, new B); + try { + (void)f(new A, new B); + } + catch (A) {}; + try { + (void)f(new A, new B); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:13: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; order of these allocations is undefined [ + (void)f(f(new A, new B)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:15: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + int X = f(new A, new B); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:15: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + X = f(new A, new B); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:11: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + X = 1 + f(new A, new B); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:15: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + + (void)f(g(new A), new B); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:15: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + + (void)f(1 + f(new A), new B); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:19: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + (void)f(XA = new A, new B); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:18: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + (void)f(1 + f(new A), XB = new B); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:19: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + } + catch (std::exception) {} +} + +void test_operator(G *G1) { + (void)(f(new A) + f(new B)); + try { + (void)(f(new A) + f(new B)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:14: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + (void)f(f(new A) + f(new B)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:15: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + int X = f(new A) + f(new B); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:15: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + X = f(new A) + f(new B); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:11: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + X = 1 + f(new A) + 1 + f(new B); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:15: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + + (void)(f(g(new A)) + f(new B)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:16: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + + (void)(f(1 + f(new A)) + f(new B)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:20: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + (void)(f(1 + f(new A)) + f(1 + f(new B))); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:20: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + + (void)((new A)->VarI + (new A)->VarI); + + (void)(f(new A) + ((*G1) += new A)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:14: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + } + catch (std::bad_alloc) {} +} + +void test_construct() { + (void)(G(new A, new B)); + try { + (void)(G(new A, new B)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:14: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + (void)(G(new A, nullptr) + G(nullptr, new B)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:14: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + f(G(new A, nullptr), G(new A, nullptr)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:9: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + + (void)new G(new A, nullptr); + // CHECK-MESSAGES-CPP11: :[[@LINE-1]]:11: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + // CHECK-MESSAGES-CPP17: :[[@LINE-2]]:11: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception [ + (void)new G(nullptr, (new A)->PtrB); + G *Z = new G(new A, nullptr); + // CHECK-MESSAGES-CPP11: :[[@LINE-1]]:12: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + // CHECK-MESSAGES-CPP17: :[[@LINE-2]]:12: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception [ + Z = new G(g(new A), nullptr); + // CHECK-MESSAGES-CPP11: :[[@LINE-1]]:9: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + // CHECK-MESSAGES-CPP17: :[[@LINE-2]]:9: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception [ + G *Z1, *Z2 = new G(nullptr, (new A)->PtrB), *Z3; + // CHECK-MESSAGES-CPP11: :[[@LINE-1]]:18: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + // CHECK-MESSAGES-CPP17: :[[@LINE-2]]:18: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception [ + } + catch (const std::bad_alloc &) {} +} + +void test_new_assign() { + A *X, *Y; + (X = new A)->VarI = (Y = new A)->VarI; + try { + (X = new A)->VarI = (Y = new A)->VarI; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + (new A)->VarI = (Y = new A)->VarI; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:6: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + (X = new A)->VarI = (new A)->VarI; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + (new A)->VarI = (new A)->VarI; + (new A)->PtrI = new int; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:6: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + (X = new A)->VarI += (new A)->VarI; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + } + catch (...) {} +} + +void test_operator_fixed_order(unsigned int L) { + (void)(f((f(new A) || f(0)) + f(new B[L]))); + try { + (void)(f(new A) || f(new B)); + (void)(f(new A) && f(new B)); + (void)(f(new A) || f(new B) || f(new A)); + + (void)(f(new A), f(new B)); + + int Y = f(0, new B) ? f(new A) : f(new B); + Y = f(new A) ? 1 : f(new B); + Y = f(new A) ? f(new B) : 1; + + G g{new A, new B}; + H h{new int, new int}; + f({new int, new int}); + (void)f({new A, new B}, {nullptr, nullptr}); + (void)f({new A, new B}, {new A, nullptr}); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:14: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + + (void)(f((f(new A) || f(0)) + f(new B[L]))); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:17: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + } + catch (std::bad_alloc) {} +} + +void test_cast() { + try { + f1(static_cast(new A), new B); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:28: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + } + catch (std::bad_alloc &) {} +} + +void test_nothrow(void *P) { + try { + (void)f(new(std::nothrow) A, new B); + (void)f(new A, new(std::nothrow) B); + (void)f(new(static_cast(8), std::nothrow) A, new B); + (void)f(new(P) A, new B); + (void)f(new('a') A, new B); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:13: warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; + } + catch (std::exception) {} +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp index 1e0831048dbd4..00b1da1e727e4 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp @@ -1160,42 +1160,63 @@ void commaOperatorSequences() { } } +namespace InitializerListSequences { + +struct S1 { + int i; + A a; +}; + +struct S2 { + A a; + int i; +}; + +struct S3 { + S3() {} + S3(int, A) {} + S3(A, int) {} +}; + // An initializer list sequences its initialization clauses. void initializerListSequences() { { - struct S1 { - int i; - A a; - }; - { - A a; - S1 s1{a.getInt(), std::move(a)}; - } - { - A a; - S1 s1{.i = a.getInt(), .a = std::move(a)}; - } + A a; + S1 s1{a.getInt(), std::move(a)}; } { - struct S2 { - A a; - int i; - }; - { - A a; - S2 s2{std::move(a), a.getInt()}; - // CHECK-NOTES: [[@LINE-1]]:27: warning: 'a' used after it was moved - // CHECK-NOTES: [[@LINE-2]]:13: note: move occurred here - } - { - A a; - S2 s2{.a = std::move(a), .i = a.getInt()}; - // CHECK-NOTES: [[@LINE-1]]:37: warning: 'a' used after it was moved - // CHECK-NOTES: [[@LINE-2]]:13: note: move occurred here - } + A a; + S1 s1{.i = a.getInt(), .a = std::move(a)}; + } + { + A a; + S2 s2{std::move(a), a.getInt()}; + // CHECK-NOTES: [[@LINE-1]]:25: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-2]]:11: note: move occurred here + } + { + A a; + S2 s2{.a = std::move(a), .i = a.getInt()}; + // CHECK-NOTES: [[@LINE-1]]:35: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-2]]:11: note: move occurred here + } + { + // Check the case where the constructed type has a constructor and the + // initializer list therefore manifests as a `CXXConstructExpr` instead of + // an `InitListExpr`. + A a; + S3 s3{a.getInt(), std::move(a)}; + } + { + A a; + S3 s3{std::move(a), a.getInt()}; + // CHECK-NOTES: [[@LINE-1]]:25: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-2]]:11: note: move occurred here } } +} // namespace InitializerListSequences + // A declaration statement containing multiple declarations sequences the // initializer expressions. void declarationSequences() { diff --git a/clang/CMakeLists.txt b/clang/CMakeLists.txt index 4508ea4c77aa5..fd8558a16f81d 100644 --- a/clang/CMakeLists.txt +++ b/clang/CMakeLists.txt @@ -660,8 +660,6 @@ if (CLANG_ENABLE_BOOTSTRAP) LLVM_VERSION_SUFFIX LLVM_BINUTILS_INCDIR CLANG_REPOSITORY_STRING - CMAKE_C_COMPILER_LAUNCHER - CMAKE_CXX_COMPILER_LAUNCHER CMAKE_MAKE_PROGRAM CMAKE_OSX_ARCHITECTURES CMAKE_BUILD_TYPE diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 2e9038933c814..bdcd1e88d2404 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -1829,6 +1829,41 @@ the configuration (without a prefix: ``Auto``). } +.. _BracedInitializerIndentWidth: + +**BracedInitializerIndentWidth** (``Unsigned``) :versionbadge:`clang-format 17` :ref:`¶ ` + The number of columns to use to indent the contents of braced init lists. + If unset, ``ContinuationIndentWidth`` is used. + + .. code-block:: c++ + + AlignAfterOpenBracket: AlwaysBreak + BracedInitializerIndentWidth: 2 + + void f() { + SomeClass c{ + "foo", + "bar", + "baz", + }; + auto s = SomeStruct{ + .foo = "foo", + .bar = "bar", + .baz = "baz", + }; + SomeArrayT a[3] = { + { + foo, + bar, + }, + { + foo, + bar, + }, + SomeArrayT{}, + }; + } + .. _BreakAfterAttributes: **BreakAfterAttributes** (``AttributeBreakingStyle``) :versionbadge:`clang-format 16` :ref:`¶ ` diff --git a/clang/docs/CommandGuide/clang.rst b/clang/docs/CommandGuide/clang.rst index 0722979885afb..6c59ffd2ab120 100644 --- a/clang/docs/CommandGuide/clang.rst +++ b/clang/docs/CommandGuide/clang.rst @@ -193,13 +193,13 @@ Language Selection and Mode Options ISO C++ 2020 with amendments and GNU extensions - | ``c++2b`` + | ``c++23`` - Working draft for ISO C++ 2023 + ISO C++ 2023 with amendments - | ``gnu++2b`` + | ``gnu++23`` - Working draft for ISO C++ 2023 with GNU extensions + ISO C++ 2023 with amendments and GNU extensions The default C++ language standard is ``gnu++17``. diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index a1bb925e8ae24..e0ad5f5e3ae53 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -93,13 +93,19 @@ C++20 Feature Support error again in the future once there is a less fragile way to mark a module as being part of the implementation rather than a user module. -C++2b Feature Support +C++23 Feature Support ^^^^^^^^^^^^^^^^^^^^^ - Implemented `P2036R3: Change scope of lambda trailing-return-type `_ and `P2579R0 Mitigation strategies for P2036 `_. These proposals modify how variables captured in lambdas can appear in trailing return type expressions and how their types are deduced therein, in all C++ language versions. +- Implemented partial support for `P2448R2: Relaxing some constexpr restrictions `_ + Explicitly defaulted functions no longer have to be constexpr-compatible but merely constexpr suitable. + We do not support outside of defaulted special memeber functions the change that constexpr functions no + longer have to be constexpr compatible but rather support a less restricted requirements for constexpr + functions. Which include allowing non-literal types as return values and paremeters, allow calling of + non-constexpr functions and constructors. Resolutions to C++ Defect Reports ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -139,6 +145,25 @@ C2x Feature Support removed, as this is no longer a GNU extension but a C2x extension. You can use ``-Wno-c2x-extensions`` to silence the extension warning instead. +- Updated the implementation of + `WG14 N3042 `_ + based on decisions reached during the WG14 CD Ballot Resolution meetings held + in Jan and Feb 2023. This should complete the implementation of ``nullptr`` + and ``nullptr_t`` in C. The specific changes are: + + .. code-block:: c + + void func(nullptr_t); + func(0); // Previously required to be rejected, is now accepted. + func((void *)0); // Previously required to be rejected, is now accepted. + + nullptr_t val; + val = 0; // Previously required to be rejected, is now accepted. + val = (void *)0; // Previously required to be rejected, is now accepted. + + bool b = nullptr; // Was incorrectly rejected by Clang, is now accepted. + + Non-comprehensive list of changes in this release ------------------------------------------------- - Clang now saves the address of ABI-indirect function parameters on the stack, @@ -157,9 +182,13 @@ Non-comprehensive list of changes in this release - A new builtin type trait ``__is_trivially_equaltiy_comparable`` has been added, which checks whether comparing two instances of a type is equivalent to ``memcmp(&lhs, &rhs, sizeof(T)) == 0``. +- Clang now ignores null directives outside of the include guard when deciding + whether a file can be enabled for the multiple-include optimization. New Compiler Flags ------------------ +- The flag ``-std=c++23`` has been added. This behaves the same as the existing + flag ``-std=c++2b``. Deprecated Compiler Flags ------------------------- @@ -231,10 +260,14 @@ Improvements to Clang's diagnostics - ``-Wformat`` now recognizes ``%lb`` for the ``printf``/``scanf`` family of functions. (`#62247: `_). +- Clang now diagnoses shadowing of lambda's template parameter by a capture. + (`#61105: `_). Bug Fixes in This Version ------------------------- +- Fix segfault while running clang-rename on a non existing file. + (`#36471 `_) - Fix crash when diagnosing incorrect usage of ``_Nullable`` involving alias templates. (`#60344 `_) @@ -307,8 +340,8 @@ Bug Fixes in This Version not a type concept. - Fix crash when a doc comment contains a line splicing. (`#62054 `_) -- Work around with a clang coverage crash which happens when visiting - expressions/statements with invalid source locations in non-assert builds. +- Work around with a clang coverage crash which happens when visiting + expressions/statements with invalid source locations in non-assert builds. Assert builds may still see assertions triggered from this. - Fix a failed assertion due to an invalid source location when trying to form a coverage report for an unresolved constructor expression. @@ -321,12 +354,31 @@ Bug Fixes in This Version (`#61885 `_) - Clang constexpr evaluator now treats comparison of [[gnu::weak]]-attributed member pointer as an invalid expression. +- Fix crash when member function contains invalid default argument. + (`#62122 `_) +- Fix crash when handling undefined template partial specialization + (`#61356 `_) +- Fix premature substitution into the constraints of an inherited constructor. +- Fix crash when attempting to perform parenthesized initialization of an + aggregate with a base class with only non-public constructors. + (`#62296 `_) +- Fix a stack overflow issue when evaluating ``consteval`` default arguments. + (`#60082` `_) +- Fix the assertion hit when generating code for global variable initializer of + _BitInt(1) type. + (`#62207 `_) +- Fix lambdas and other anonymous function names not respecting ``-fdebug-prefix-map`` + (`#62192 `_) Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Bug Fixes to Attribute Support ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +- Fixed a bug where attribute annotations on type specifiers (enums, classes, + structs, unions, and scoped enums) were not properly ignored, resulting in + misleading warning messages. Now, such attribute annotations are correctly + ignored. (`#61660 `_) Bug Fixes to C++ Support ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -342,7 +394,7 @@ Bug Fixes to C++ Support (`#58674 `_) - Fix incorrect deletion of the default constructor of unions in some cases. (`#48416 `_) -- No longer issue a pre-C++2b compatibility warning in ``-pedantic`` mode +- No longer issue a pre-C++23 compatibility warning in ``-pedantic`` mode regading overloaded `operator[]` with more than one parmeter or for static lambdas. (`#61582 `_) - Stop stripping CV qualifiers from the type of ``this`` when capturing it by value in @@ -355,6 +407,13 @@ Bug Fixes to C++ Support - Fix bug in the computation of the ``__has_unique_object_representations`` builtin for types with unnamed bitfields. (`#61336 `_) +- Fix default member initializers sometimes being ignored when performing + parenthesized aggregate initialization of templated types. + (`#62266 `_) +- Fix overly aggressive lifetime checks for parenthesized aggregate + initialization. + (`#61567 `_) +- Fix a crash when expanding a pack as the index of a subscript expression. Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -402,6 +461,18 @@ Arm and AArch64 Support - Clang builtin ``__arithmetic_fence`` and the command line option ``-fprotect-parens`` are now enabled for AArch64. +- Clang supports flag output operands by which conditions in the NZCV could be outputs + of inline assembly for AArch64. This change is more consistent with the behavior of + GCC. + + .. code-block:: c + + // int a = foo(); int* b = bar(); + asm("ands %w[a], %w[a], #3" : [a] "+r"(a), "=@cceq"(*b)); + +- Fix a crash when ``preserve_all`` calling convention is used on AArch64. + `Issue 58145 `_ + Windows Support ^^^^^^^^^^^^^^^ @@ -420,6 +491,11 @@ RISC-V Support - Fixed incorrect ABI lowering of ``_Float16`` in the case of structs containing ``_Float16`` that are eligible for passing via GPR+FPR or FPR+FPR. +- Removed support for ``__attribute__((interrupt("user")))``. User-level + interrupts are not in version 1.12 of the privileged specification. +- Added ``attribute(riscv_rvv_vector_bits(__riscv_v_fixed_vlen))`` to allow + the size of a RVV (RISC-V Vector) scalable type to be specified. This allows + RVV scalable vector types to be used in structs or in global variables. CUDA/HIP Language Changes ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -475,6 +551,8 @@ clang-format - Add additional Qualifier Ordering support for special cases such as templates, requires clauses, long qualified names. - Fix all known issues associated with ``LambdaBodyIndentation: OuterScope``. +- Add ``BracedInitializerIndentWidth`` which can be used to configure + the indentation level of the contents of braced init lists. libclang -------- diff --git a/clang/docs/StandardCPlusPlusModules.rst b/clang/docs/StandardCPlusPlusModules.rst index 0b44265374a8b..a59a3edbbbac2 100644 --- a/clang/docs/StandardCPlusPlusModules.rst +++ b/clang/docs/StandardCPlusPlusModules.rst @@ -434,7 +434,7 @@ The following example is not allowed: .. code-block:: console $ clang++ -std=c++20 M.cppm --precompile -o M.pcm - $ clang++ -std=c++2b Use.cpp -fprebuilt-module-path=. + $ clang++ -std=c++23 Use.cpp -fprebuilt-module-path=. The compiler would reject the example due to the inconsistent language options. Not all options are language options. @@ -611,7 +611,7 @@ The following describes issues in the current implementation of modules. Please see https://github.com/llvm/llvm-project/labels/clang%3Amodules for more issues or file a new issue if you don't find an existing one. If you're going to create a new issue for standard C++ modules, -please start the title with ``[C++20] [Modules]`` (or ``[C++2b] [Modules]``, etc) +please start the title with ``[C++20] [Modules]`` (or ``[C++23] [Modules]``, etc) and add the label ``clang:modules`` (if you have permissions for that). For higher level support for proposals, you could visit https://clang.llvm.org/cxx_status.html. diff --git a/clang/docs/doxygen.cfg.in b/clang/docs/doxygen.cfg.in index 39a346409b935..251afb179b205 100644 --- a/clang/docs/doxygen.cfg.in +++ b/clang/docs/doxygen.cfg.in @@ -2090,7 +2090,7 @@ CLASS_GRAPH = YES # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -COLLABORATION_GRAPH = YES +COLLABORATION_GRAPH = NO # If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for # groups, showing the direct groups dependencies. @@ -2135,7 +2135,7 @@ TEMPLATE_RELATIONS = YES # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -INCLUDE_GRAPH = YES +INCLUDE_GRAPH = NO # If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are # set to YES then doxygen will generate a graph for each documented file showing @@ -2144,7 +2144,7 @@ INCLUDE_GRAPH = YES # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -INCLUDED_BY_GRAPH = YES +INCLUDED_BY_GRAPH = NO # If the CALL_GRAPH tag is set to YES then doxygen will generate a call # dependency graph for every global function or class method. diff --git a/clang/docs/tools/dump_format_style.py b/clang/docs/tools/dump_format_style.py index fe6e9147ee94f..7b032894ce62c 100755 --- a/clang/docs/tools/dump_format_style.py +++ b/clang/docs/tools/dump_format_style.py @@ -69,9 +69,13 @@ def to_yaml_type(typestr: str): elif typestr == 'std::string': return 'String' - subtype, napplied = re.subn(r'^std::vector<(.*)>$', r'\1', typestr) - if napplied == 1: - return 'List of ' + pluralize(to_yaml_type(subtype)) + match = re.match(r'std::vector<(.*)>$', typestr) + if match: + return 'List of ' + pluralize(to_yaml_type(match.group(1))) + + match = re.match(r'std::optional<(.*)>$', typestr) + if match: + return to_yaml_type(match.group(1)) return typestr @@ -331,7 +335,8 @@ class State: if option.type not in ['bool', 'unsigned', 'int', 'std::string', 'std::vector', 'std::vector', - 'std::vector']: + 'std::vector', + 'std::optional']: if option.type in enums: option.enum = enums[option.type] elif option.type in nested_structs: diff --git a/clang/include/clang-c/module.modulemap b/clang/include/clang-c/module.modulemap deleted file mode 100644 index 95a59d62344cc..0000000000000 --- a/clang/include/clang-c/module.modulemap +++ /dev/null @@ -1,4 +0,0 @@ -module Clang_C { - umbrella "." - module * { export * } -} diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 54b41d248a341..b08eb525602b6 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -2253,6 +2253,17 @@ class ASTContext : public RefCountedBase { /// false otherwise. bool areLaxCompatibleSveTypes(QualType FirstType, QualType SecondType); + /// Return true if the given types are an RISC-V vector builtin type and a + /// VectorType that is a fixed-length representation of the RISC-V vector + /// builtin type for a specific vector-length. + bool areCompatibleRVVTypes(QualType FirstType, QualType SecondType); + + /// Return true if the given vector types are lax-compatible RISC-V vector + /// types as defined by -flax-vector-conversions=, which permits implicit + /// conversions between vectors with different number of elements and/or + /// incompatible element types, false otherwise. + bool areLaxCompatibleRVVTypes(QualType FirstType, QualType SecondType); + /// Return true if the type has been explicitly qualified with ObjC ownership. /// A type may be implicitly qualified with ownership under ObjC ARC, and in /// some cases the compiler treats these differently. diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index d9b17f8eaf936..0ab778e5d8cd3 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -2233,14 +2233,14 @@ class UnaryOperator final bool canOverflow() const { return UnaryOperatorBits.CanOverflow; } void setCanOverflow(bool C) { UnaryOperatorBits.CanOverflow = C; } - // Get the FP contractability status of this operator. Only meaningful for - // operations on floating point types. + /// Get the FP contractability status of this operator. Only meaningful for + /// operations on floating point types. bool isFPContractableWithinStatement(const LangOptions &LO) const { return getFPFeaturesInEffect(LO).allowFPContractWithinStatement(); } - // Get the FENV_ACCESS status of this operator. Only meaningful for - // operations on floating point types. + /// Get the FENV_ACCESS status of this operator. Only meaningful for + /// operations on floating point types. bool isFEnvAccessOn(const LangOptions &LO) const { return getFPFeaturesInEffect(LO).getAllowFEnvAccess(); } @@ -2325,8 +2325,8 @@ class UnaryOperator final void setStoredFPFeatures(FPOptionsOverride F) { getTrailingFPFeatures() = F; } public: - // Get the FP features status of this operator. Only meaningful for - // operations on floating point types. + /// Get the FP features status of this operator. Only meaningful for + /// operations on floating point types. FPOptions getFPFeaturesInEffect(const LangOptions &LO) const { if (UnaryOperatorBits.HasFPFeatures) return getStoredFPFeatures().applyOverrides(LO); @@ -3082,8 +3082,8 @@ class CallExpr : public Expr { *getTrailingFPFeatures() = F; } - // Get the FP features status of this operator. Only meaningful for - // operations on floating point types. + /// Get the FP features status of this operator. Only meaningful for + /// operations on floating point types. FPOptions getFPFeaturesInEffect(const LangOptions &LO) const { if (hasStoredFPFeatures()) return getStoredFPFeatures().applyOverrides(LO); @@ -3573,8 +3573,8 @@ class CastExpr : public Expr { return *getTrailingFPFeatures(); } - // Get the FP features status of this operation. Only meaningful for - // operations on floating point types. + /// Get the FP features status of this operation. Only meaningful for + /// operations on floating point types. FPOptions getFPFeaturesInEffect(const LangOptions &LO) const { if (hasStoredFPFeatures()) return getStoredFPFeatures().applyOverrides(LO); @@ -3971,9 +3971,9 @@ class BinaryOperator : public Expr { return isShiftAssignOp(getOpcode()); } - // Return true if a binary operator using the specified opcode and operands - // would match the 'p = (i8*)nullptr + n' idiom for casting a pointer-sized - // integer to a pointer. + /// Return true if a binary operator using the specified opcode and operands + /// would match the 'p = (i8*)nullptr + n' idiom for casting a pointer-sized + /// integer to a pointer. static bool isNullPointerArithmeticExtension(ASTContext &Ctx, Opcode Opc, const Expr *LHS, const Expr *RHS); @@ -4007,8 +4007,8 @@ class BinaryOperator : public Expr { *getTrailingFPFeatures() = F; } - // Get the FP features status of this operator. Only meaningful for - // operations on floating point types. + /// Get the FP features status of this operator. Only meaningful for + /// operations on floating point types. FPOptions getFPFeaturesInEffect(const LangOptions &LO) const { if (BinaryOperatorBits.HasFPFeatures) return getStoredFPFeatures().applyOverrides(LO); @@ -4022,14 +4022,14 @@ class BinaryOperator : public Expr { return FPOptionsOverride(); } - // Get the FP contractability status of this operator. Only meaningful for - // operations on floating point types. + /// Get the FP contractability status of this operator. Only meaningful for + /// operations on floating point types. bool isFPContractableWithinStatement(const LangOptions &LO) const { return getFPFeaturesInEffect(LO).allowFPContractWithinStatement(); } - // Get the FENV_ACCESS status of this operator. Only meaningful for - // operations on floating point types. + /// Get the FENV_ACCESS status of this operator. Only meaningful for + /// operations on floating point types. bool isFEnvAccessOn(const LangOptions &LO) const { return getFPFeaturesInEffect(LO).getAllowFEnvAccess(); } @@ -4125,17 +4125,17 @@ class AbstractConditionalOperator : public Expr { : Expr(SC, Empty) { } public: - // getCond - Return the expression representing the condition for - // the ?: operator. + /// getCond - Return the expression representing the condition for + /// the ?: operator. Expr *getCond() const; - // getTrueExpr - Return the subexpression representing the value of - // the expression if the condition evaluates to true. + /// getTrueExpr - Return the subexpression representing the value of + /// the expression if the condition evaluates to true. Expr *getTrueExpr() const; - // getFalseExpr - Return the subexpression representing the value of - // the expression if the condition evaluates to false. This is - // the same as getRHS. + /// getFalseExpr - Return the subexpression representing the value of + /// the expression if the condition evaluates to false. This is + /// the same as getRHS. Expr *getFalseExpr() const; SourceLocation getQuestionLoc() const { return QuestionLoc; } @@ -4170,17 +4170,17 @@ class ConditionalOperator : public AbstractConditionalOperator { explicit ConditionalOperator(EmptyShell Empty) : AbstractConditionalOperator(ConditionalOperatorClass, Empty) { } - // getCond - Return the expression representing the condition for - // the ?: operator. + /// getCond - Return the expression representing the condition for + /// the ?: operator. Expr *getCond() const { return cast(SubExprs[COND]); } - // getTrueExpr - Return the subexpression representing the value of - // the expression if the condition evaluates to true. + /// getTrueExpr - Return the subexpression representing the value of + /// the expression if the condition evaluates to true. Expr *getTrueExpr() const { return cast(SubExprs[LHS]); } - // getFalseExpr - Return the subexpression representing the value of - // the expression if the condition evaluates to false. This is - // the same as getRHS. + /// getFalseExpr - Return the subexpression representing the value of + /// the expression if the condition evaluates to false. This is + /// the same as getRHS. Expr *getFalseExpr() const { return cast(SubExprs[RHS]); } Expr *getLHS() const { return cast(SubExprs[LHS]); } @@ -4903,7 +4903,7 @@ class InitListExpr : public Expr { /// has been set. bool hasArrayFiller() const { return getArrayFiller(); } - // Determine whether this initializer list contains a designated initializer. + /// Determine whether this initializer list contains a designated initializer. bool hasDesignatedInit() const { return std::any_of(begin(), end(), [](const Stmt *S) { return isa(S); @@ -4938,8 +4938,8 @@ class InitListExpr : public Expr { return LBraceLoc.isValid() && RBraceLoc.isValid(); } - // Is this an initializer for an array of characters, initialized by a string - // literal or an @encode? + /// Is this an initializer for an array of characters, initialized by a string + /// literal or an @encode? bool isStringLiteralInit() const; /// Is this a transparent initializer list (that is, an InitListExpr that is diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 724904b4d2041..16f54d753d42f 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -2321,7 +2321,7 @@ class CXXNewExpr final /// This might return std::nullopt even if isArray() returns true, /// since there might not be an array size expression. - /// If the result is not-None, it will never wrap a nullptr. + /// If the result is not std::nullopt, it will never wrap a nullptr. std::optional getArraySize() { if (!isArray()) return std::nullopt; @@ -2335,7 +2335,7 @@ class CXXNewExpr final /// This might return std::nullopt even if isArray() returns true, /// since there might not be an array size expression. - /// If the result is not-None, it will never wrap a nullptr. + /// If the result is not std::nullopt, it will never wrap a nullptr. std::optional getArraySize() const { if (!isArray()) return std::nullopt; diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index af0e724311017..7ce1ad9b90998 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -1770,7 +1770,7 @@ class alignas(8) Type : public ExtQualsTypeCommonBase { /// The kind of vector, either a generic vector type or some /// target-specific vector type such as for AltiVec or Neon. - unsigned VecKind : 3; + unsigned VecKind : 4; /// The number of elements in the vector. uint32_t NumElements; }; @@ -2049,6 +2049,16 @@ class alignas(8) Type : public ExtQualsTypeCommonBase { /// 'arm_sve_vector_bits' type attribute as VectorType. QualType getSveEltType(const ASTContext &Ctx) const; + /// Determines if this is a sizeless type supported by the + /// 'riscv_rvv_vector_bits' type attribute, which can be applied to a single + /// RVV vector or mask. + bool isRVVVLSBuiltinType() const; + + /// Returns the representative type for the element of an RVV builtin type. + /// This is used to represent fixed-length RVV vectors created with the + /// 'riscv_rvv_vector_bits' type attribute as VectorType. + QualType getRVVEltType(const ASTContext &Ctx) const; + /// Types are partitioned into 3 broad categories (C99 6.2.5p1): /// object types, function types, and incomplete types. @@ -3402,7 +3412,10 @@ class VectorType : public Type, public llvm::FoldingSetNode { SveFixedLengthDataVector, /// is AArch64 SVE fixed-length predicate vector - SveFixedLengthPredicateVector + SveFixedLengthPredicateVector, + + /// is RISC-V RVV fixed-length data vector + RVVFixedLengthDataVector, }; protected: diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h index 8690616411db9..cd1703be0507b 100644 --- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h @@ -27,6 +27,7 @@ #include "clang/Analysis/FlowSensitive/Value.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" #include #include @@ -178,12 +179,16 @@ class Environment { /// with a symbolic representation of the `this` pointee. Environment(DataflowAnalysisContext &DACtx, const DeclContext &DeclCtx); + LLVM_DEPRECATED("Use getDataflowAnalysisContext().getOptions() instead.", "") const DataflowAnalysisContext::Options &getAnalysisOptions() const { return DACtx->getOptions(); } + LLVM_DEPRECATED("Use getDataflowAnalysisContext().arena() instead.", "") Arena &arena() const { return DACtx->arena(); } + LLVM_DEPRECATED("Use getDataflowAnalysisContext().getOptions().Log instead.", + "") Logger &logger() const { return *DACtx->getOptions().Log; } /// Creates and returns an environment to use for an inline analysis of the @@ -264,13 +269,24 @@ class Environment { /// /// Requirements: /// - /// `D` must not be assigned a storage location in the environment. + /// `D` must not already have a storage location in the environment. + /// + /// If `D` has reference type, `Loc` must refer directly to the referenced + /// object (if any), not to a `ReferenceValue`, and it is not permitted to + /// later change `Loc` to refer to a `ReferenceValue.` void setStorageLocation(const ValueDecl &D, StorageLocation &Loc); - /// Returns the storage location assigned to `D` in the environment, applying - /// the `SP` policy for skipping past indirections, or null if `D` isn't - /// assigned a storage location in the environment. - StorageLocation *getStorageLocation(const ValueDecl &D, SkipPast SP) const; + /// Returns the storage location assigned to `D` in the environment, or null + /// if `D` isn't assigned a storage location in the environment. + /// + /// Note that if `D` has reference type, the storage location that is returned + /// refers directly to the referenced object, not a `ReferenceValue`. + /// + /// The `SP` parameter is deprecated and has no effect. In addition, it is + /// not permitted to pass `SkipPast::ReferenceThenPointer` for this parameter. + /// New uses of this function should use the default argument for `SP`. + StorageLocation *getStorageLocation(const ValueDecl &D, + SkipPast SP = SkipPast::None) const; /// Assigns `Loc` as the storage location of `E` in the environment. /// @@ -315,7 +331,11 @@ class Environment { /// Equivalent to `getValue(getStorageLocation(D, SP), SkipPast::None)` if `D` /// is assigned a storage location in the environment, otherwise returns null. - Value *getValue(const ValueDecl &D, SkipPast SP) const; + /// + /// The `SP` parameter is deprecated and has no effect. In addition, it is + /// not permitted to pass `SkipPast::ReferenceThenPointer` for this parameter. + /// New uses of this function should use the default argument for `SP`. + Value *getValue(const ValueDecl &D, SkipPast SP = SkipPast::None) const; /// Equivalent to `getValue(getStorageLocation(E, SP), SkipPast::None)` if `E` /// is assigned a storage location in the environment, otherwise returns null. @@ -331,23 +351,23 @@ class Environment { template std::enable_if_t::value, T &> create(Args &&...args) { - return arena().create(std::forward(args)...); + return DACtx->arena().create(std::forward(args)...); } /// Returns a symbolic boolean value that models a boolean literal equal to /// `Value` AtomicBoolValue &getBoolLiteralValue(bool Value) const { - return arena().makeLiteral(Value); + return DACtx->arena().makeLiteral(Value); } /// Returns an atomic boolean value. BoolValue &makeAtomicBoolValue() const { - return arena().create(); + return DACtx->arena().create(); } /// Returns a unique instance of boolean Top. BoolValue &makeTopBoolValue() const { - return arena().create(); + return DACtx->arena().create(); } /// Returns a boolean value that represents the conjunction of `LHS` and @@ -355,7 +375,7 @@ class Environment { /// order, will return the same result. If the given boolean values represent /// the same value, the result will be the value itself. BoolValue &makeAnd(BoolValue &LHS, BoolValue &RHS) const { - return arena().makeAnd(LHS, RHS); + return DACtx->arena().makeAnd(LHS, RHS); } /// Returns a boolean value that represents the disjunction of `LHS` and @@ -363,13 +383,13 @@ class Environment { /// order, will return the same result. If the given boolean values represent /// the same value, the result will be the value itself. BoolValue &makeOr(BoolValue &LHS, BoolValue &RHS) const { - return arena().makeOr(LHS, RHS); + return DACtx->arena().makeOr(LHS, RHS); } /// Returns a boolean value that represents the negation of `Val`. Subsequent /// calls with the same argument will return the same result. BoolValue &makeNot(BoolValue &Val) const { - return arena().makeNot(Val); + return DACtx->arena().makeNot(Val); } /// Returns a boolean value represents `LHS` => `RHS`. Subsequent calls with @@ -377,7 +397,7 @@ class Environment { /// values represent the same value, the result will be a value that /// represents the true boolean literal. BoolValue &makeImplication(BoolValue &LHS, BoolValue &RHS) const { - return arena().makeImplies(LHS, RHS); + return DACtx->arena().makeImplies(LHS, RHS); } /// Returns a boolean value represents `LHS` <=> `RHS`. Subsequent calls with @@ -385,7 +405,7 @@ class Environment { /// result. If the given boolean values represent the same value, the result /// will be a value that represents the true boolean literal. BoolValue &makeIff(BoolValue &LHS, BoolValue &RHS) const { - return arena().makeEquals(LHS, RHS); + return DACtx->arena().makeEquals(LHS, RHS); } /// Returns the token that identifies the flow condition of the environment. @@ -409,10 +429,15 @@ class Environment { /// Returns the `ControlFlowContext` registered for `F`, if any. Otherwise, /// returns null. + LLVM_DEPRECATED( + "Use getDataflowAnalysisContext().getControlFlowContext(F) instead.", "") const ControlFlowContext *getControlFlowContext(const FunctionDecl *F) { return DACtx->getControlFlowContext(F); } + /// Returns the `DataflowAnalysisContext` used by the environment. + DataflowAnalysisContext &getDataflowAnalysisContext() const { return *DACtx; } + LLVM_DUMP_METHOD void dump() const; LLVM_DUMP_METHOD void dump(raw_ostream &OS) const; diff --git a/clang/include/clang/Analysis/FlowSensitive/Value.h b/clang/include/clang/Analysis/FlowSensitive/Value.h index 32d10a3489483..861a9963e6689 100644 --- a/clang/include/clang/Analysis/FlowSensitive/Value.h +++ b/clang/include/clang/Analysis/FlowSensitive/Value.h @@ -73,6 +73,11 @@ class Value { Properties.insert_or_assign(Name, &Val); } + llvm::iterator_range::const_iterator> + properties() const { + return {Properties.begin(), Properties.end()}; + } + private: Kind ValKind; llvm::StringMap Properties; @@ -307,6 +312,12 @@ class StructValue final : public Value { /// Assigns `Val` as the child value for `D`. void setChild(const ValueDecl &D, Value &Val) { Children[&D] = &Val; } + llvm::iterator_range< + llvm::DenseMap::const_iterator> + children() const { + return {Children.begin(), Children.end()}; + } + private: llvm::DenseMap Children; }; diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index a23dd5ce3634b..8507e65137697 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1864,13 +1864,23 @@ def RISCVInterrupt : InheritableAttr, TargetSpecificAttr { let Spellings = [GCC<"interrupt">]; let Subjects = SubjectList<[Function]>; let Args = [EnumArgument<"Interrupt", "InterruptType", - ["user", "supervisor", "machine"], - ["user", "supervisor", "machine"], + ["supervisor", "machine"], + ["supervisor", "machine"], 1>]; let ParseKind = "Interrupt"; let Documentation = [RISCVInterruptDocs]; } +def RISCVRVVVectorBits : TypeAttr { + let Spellings = [GNU<"riscv_rvv_vector_bits">]; + let Subjects = SubjectList<[TypedefName], ErrorDiag>; + let Args = [UnsignedArgument<"NumBits">]; + let Documentation = [RISCVRVVVectorBitsDocs]; + let PragmaAttributeSupport = 0; + // Represented as VectorType instead. + let ASTNode = 0; +} + // This is not a TargetSpecificAttr so that is silently accepted and // ignored on other targets as encouraged by the OpenCL spec. // diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index dae12624a822b..f62350f33013c 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -2317,6 +2317,37 @@ Version 1.10. }]; } +def RISCVRVVVectorBitsDocs : Documentation { + let Category = DocCatType; + let Content = [{ +On RISC-V targets, the ``riscv_rvv_vector_bits(N)`` attribute is used to define +fixed-length variants of sizeless types. + +For example: + +.. code-block:: c + + #include + + #if defined(__riscv_v_fixed_vlen) + typedef vint8m1_t fixed_vint8m1_t __attribute__((riscv_rvv_vector_bits(__riscv_v_fixed_vlen))); + #endif + +Creates a type ``fixed_vint8m1_t_t`` that is a fixed-length variant of +``vint8m1_t`` that contains exactly 512 bits. Unlike ``vint8m1_t``, this type +can be used in globals, structs, unions, and arrays, all of which are +unsupported for sizeless types. + +The attribute can be attached to a single RVV vector (such as ``vint8m1_t``). +The attribute will be rejected unless +``N==__riscv_v_fixed_vlen``, the implementation defined feature macro that +is enabled under the ``-mrvv-vector-bits`` flag. ``__riscv_v_fixed_vlen`` can +only be a power of 2 between 64 and 65536. + +Only ``*m1_t`` (LMUL=1) types are supported at this time. +}]; +} + def AVRInterruptDocs : Documentation { let Category = DocCatFunction; let Heading = "interrupt (AVR)"; @@ -5182,6 +5213,9 @@ apply for values returned in callee-saved registers. R11. R11 can be used as a scratch register. Floating-point registers (XMMs/YMMs) are not preserved and need to be saved by the caller. +- On AArch64 the callee preserve all general purpose registers, except X0-X8 and + X16-X18. + The idea behind this convention is to support calls to runtime functions that have a hot path and a cold path. The hot path is usually a small piece of code that doesn't use many registers. The cold path might need to call out to @@ -5222,6 +5256,10 @@ returned in callee-saved registers. R11. R11 can be used as a scratch register. Furthermore it also preserves all floating-point registers (XMMs/YMMs). +- On AArch64 the callee preserve all general purpose registers, except X0-X8 and + X16-X18. Furthermore it also preserves lower 128 bits of V8-V31 SIMD - floating + point registers. + The idea behind this convention is to support calls to runtime functions that don't need to call out to any other functions. diff --git a/clang/include/clang/Basic/BuiltinsRISCV.def b/clang/include/clang/Basic/BuiltinsRISCV.def index 3ca7654a32adc..370ef0af8f9a5 100644 --- a/clang/include/clang/Basic/BuiltinsRISCV.def +++ b/clang/include/clang/Basic/BuiltinsRISCV.def @@ -79,5 +79,9 @@ TARGET_BUILTIN(__builtin_riscv_sm4ks, "LiLiLiIUc", "nc", "zksed") TARGET_BUILTIN(__builtin_riscv_sm3p0, "LiLi", "nc", "zksh") TARGET_BUILTIN(__builtin_riscv_sm3p1, "LiLi", "nc", "zksh") +// Zihintntl extension +TARGET_BUILTIN(__builtin_riscv_ntl_load, "v.", "t", "experimental-zihintntl") +TARGET_BUILTIN(__builtin_riscv_ntl_store, "v.", "t", "experimental-zihintntl") + #undef BUILTIN #undef TARGET_BUILTIN diff --git a/clang/include/clang/Basic/BuiltinsRISCVVector.def b/clang/include/clang/Basic/BuiltinsRISCVVector.def index 008cb939a30bc..6dfa87a1a1d31 100644 --- a/clang/include/clang/Basic/BuiltinsRISCVVector.def +++ b/clang/include/clang/Basic/BuiltinsRISCVVector.def @@ -16,6 +16,7 @@ #endif #include "clang/Basic/riscv_vector_builtins.inc" +#include "clang/Basic/riscv_sifive_vector_builtins.inc" #undef BUILTIN #undef TARGET_BUILTIN diff --git a/clang/include/clang/Basic/CMakeLists.txt b/clang/include/clang/Basic/CMakeLists.txt index b930842ae8cfd..53a713b13ea39 100644 --- a/clang/include/clang/Basic/CMakeLists.txt +++ b/clang/include/clang/Basic/CMakeLists.txt @@ -93,3 +93,12 @@ clang_tablegen(riscv_vector_builtin_cg.inc -gen-riscv-vector-builtin-codegen clang_tablegen(riscv_vector_builtin_sema.inc -gen-riscv-vector-builtin-sema SOURCE riscv_vector.td TARGET ClangRISCVVectorBuiltinSema) +clang_tablegen(riscv_sifive_vector_builtins.inc -gen-riscv-sifive-vector-builtins + SOURCE riscv_sifive_vector.td + TARGET ClangRISCVSiFiveVectorBuiltins) +clang_tablegen(riscv_sifive_vector_builtin_cg.inc -gen-riscv-sifive-vector-builtin-codegen + SOURCE riscv_sifive_vector.td + TARGET ClangRISCVSiFiveVectorBuiltinCG) +clang_tablegen(riscv_sifive_vector_builtin_sema.inc -gen-riscv-sifive-vector-builtin-sema + SOURCE riscv_sifive_vector.td + TARGET ClangRISCVSiFiveVectorBuiltinSema) diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h index e1817b990cdf9..b0f22411e1ad2 100644 --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -206,8 +206,11 @@ class CodeGenOptions : public CodeGenOptionsBase { /// if non-empty. std::string RecordCommandLine; - std::map DebugPrefixMap; - std::map CoveragePrefixMap; + llvm::SmallVector, 0> DebugPrefixMap; + + /// Prefix replacement map for source-based code coverage to remap source + /// file paths in coverage mapping. + llvm::SmallVector, 0> CoveragePrefixMap; /// The ABI to use for passing floating point arguments. std::string FloatABI; @@ -368,9 +371,6 @@ class CodeGenOptions : public CodeGenOptionsBase { /// transformation. OptRemark OptimizationRemarkAnalysis; - /// Set of files defining the rules for the symbol rewriting. - std::vector RewriteMapFiles; - /// Set of sanitizer checks that are non-fatal (i.e. execution should be /// continued when possible). SanitizerSet SanitizeRecover; diff --git a/clang/include/clang/Basic/Diagnostic.td b/clang/include/clang/Basic/Diagnostic.td index 503b833e63bbf..8d66e265fbaef 100644 --- a/clang/include/clang/Basic/Diagnostic.td +++ b/clang/include/clang/Basic/Diagnostic.td @@ -157,7 +157,6 @@ class DefaultRemark { Severity DefaultSeverity = SEV_Remark; } // Definitions for Diagnostics. include "DiagnosticASTKinds.td" -include "DiagnosticAnalysisKinds.td" include "DiagnosticCommentKinds.td" include "DiagnosticCommonKinds.td" include "DiagnosticCrossTUKinds.td" diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td index bac77299671c5..42d8a0abfbceb 100644 --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -210,14 +210,14 @@ def ext_cxx11_longlong : Extension< def warn_cxx98_compat_longlong : Warning< "'long long' is incompatible with C++98">, InGroup, DefaultIgnore; -def ext_cxx2b_size_t_suffix : ExtWarn< - "'size_t' suffix for literals is a C++2b extension">, - InGroup; +def ext_cxx23_size_t_suffix : ExtWarn< + "'size_t' suffix for literals is a C++23 extension">, + InGroup; def warn_cxx20_compat_size_t_suffix : Warning< "'size_t' suffix for literals is incompatible with C++ standards before " - "C++2b">, InGroup, DefaultIgnore; -def err_cxx2b_size_t_suffix: Error< - "'size_t' suffix for literals is a C++2b feature">; + "C++23">, InGroup, DefaultIgnore; +def err_cxx23_size_t_suffix: Error< + "'size_t' suffix for literals is a C++23 feature">; def err_size_t_literal_too_large: Error< "%select{signed |}0'size_t' literal is out of range of possible " "%select{signed |}0'size_t' values">; @@ -308,7 +308,7 @@ def warn_dup_category_def : Warning< // Targets def err_target_unknown_triple : Error< - "unknown target triple '%0', please use -triple or -arch">; + "unknown target triple '%0'">; def err_target_unknown_cpu : Error<"unknown target CPU '%0'">; def note_valid_options : Note<"valid target CPU values are: %0">; def err_target_unsupported_cpu_for_micromips : Error< diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index 1efe7573028ef..4c7d9e80338ec 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -255,6 +255,7 @@ def warn_drv_unknown_argument_clang_cl : Warning< def warn_drv_unknown_argument_clang_cl_with_suggestion : Warning< "unknown argument ignored in clang-cl '%0'; did you mean '%1'?">, InGroup; +def err_drv_unknown_target_triple : Error<"unknown target triple '%0'">; def warn_drv_ycyu_different_arg_clang_cl : Warning< "support for '/Yc' and '/Yu' with different filenames not implemented yet; flags ignored">, diff --git a/clang/include/clang/Basic/DiagnosticError.h b/clang/include/clang/Basic/DiagnosticError.h index 3660bd1b3b3d8..744f7fe19db79 100644 --- a/clang/include/clang/Basic/DiagnosticError.h +++ b/clang/include/clang/Basic/DiagnosticError.h @@ -35,8 +35,8 @@ class DiagnosticError : public llvm::ErrorInfo { } /// Extracts and returns the diagnostic payload from the given \c Error if - /// the error is a \c DiagnosticError. Returns none if the given error is not - /// a \c DiagnosticError. + /// the error is a \c DiagnosticError. Returns std::nullopt if the given error + /// is not a \c DiagnosticError. static std::optional take(llvm::Error &Err) { std::optional Result; Err = llvm::handleErrors(std::move(Err), [&](DiagnosticError &E) { diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 0ee43fb8837a1..87e72f000d494 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -304,9 +304,9 @@ def CXXPre20CompatPedantic : DiagGroup<"pre-c++20-compat-pedantic", [CXXPre20Compat]>; def : DiagGroup<"c++98-c++11-c++14-c++17-compat-pedantic", [CXXPre20CompatPedantic]>; -def CXXPre2bCompat : DiagGroup<"pre-c++2b-compat">; -def CXXPre2bCompatPedantic : - DiagGroup<"pre-c++2b-compat-pedantic", [CXXPre2bCompat]>; +def CXXPre23Compat : DiagGroup<"pre-c++23-compat">; +def CXXPre23CompatPedantic : + DiagGroup<"pre-c++23-compat-pedantic", [CXXPre23Compat]>; def CXX98CompatBindToTemporaryCopy : DiagGroup<"c++98-compat-bind-to-temporary-copy">; @@ -321,7 +321,7 @@ def CXX98Compat : DiagGroup<"c++98-compat", CXXPre14Compat, CXXPre17Compat, CXXPre20Compat, - CXXPre2bCompat]>; + CXXPre23Compat]>; // Warnings for C++11 features which are Extensions in C++98 mode. def CXX98CompatPedantic : DiagGroup<"c++98-compat-pedantic", [CXX98Compat, @@ -330,7 +330,7 @@ def CXX98CompatPedantic : DiagGroup<"c++98-compat-pedantic", CXXPre14CompatPedantic, CXXPre17CompatPedantic, CXXPre20CompatPedantic, - CXXPre2bCompatPedantic]>; + CXXPre23CompatPedantic]>; def CXX11Narrowing : DiagGroup<"c++11-narrowing">; @@ -360,39 +360,39 @@ def CXX11Compat : DiagGroup<"c++11-compat", CXXPre14Compat, CXXPre17Compat, CXXPre20Compat, - CXXPre2bCompat]>; + CXXPre23Compat]>; def : DiagGroup<"c++0x-compat", [CXX11Compat]>; def CXX11CompatPedantic : DiagGroup<"c++11-compat-pedantic", [CXX11Compat, CXXPre14CompatPedantic, CXXPre17CompatPedantic, CXXPre20CompatPedantic, - CXXPre2bCompatPedantic]>; + CXXPre23CompatPedantic]>; def CXX14Compat : DiagGroup<"c++14-compat", [CXXPre17Compat, CXXPre20Compat, - CXXPre2bCompat]>; + CXXPre23Compat]>; def CXX14CompatPedantic : DiagGroup<"c++14-compat-pedantic", [CXX14Compat, CXXPre17CompatPedantic, CXXPre20CompatPedantic, - CXXPre2bCompatPedantic]>; + CXXPre23CompatPedantic]>; def CXX17Compat : DiagGroup<"c++17-compat", [DeprecatedRegister, DeprecatedIncrementBool, CXX17CompatMangling, CXXPre20Compat, - CXXPre2bCompat]>; + CXXPre23Compat]>; def CXX17CompatPedantic : DiagGroup<"c++17-compat-pedantic", [CXX17Compat, CXXPre20CompatPedantic, - CXXPre2bCompatPedantic]>; + CXXPre23CompatPedantic]>; def : DiagGroup<"c++1z-compat", [CXX17Compat]>; -def CXX20Compat : DiagGroup<"c++20-compat", [CXXPre2bCompat]>; +def CXX20Compat : DiagGroup<"c++20-compat", [CXXPre23Compat]>; def CXX20CompatPedantic : DiagGroup<"c++20-compat-pedantic", [CXX20Compat, - CXXPre2bCompatPedantic]>; + CXXPre23CompatPedantic]>; def : DiagGroup<"c++2a-compat", [CXX20Compat]>; def : DiagGroup<"c++2a-compat-pedantic", [CXX20CompatPedantic]>; @@ -1108,9 +1108,9 @@ def CXX17 : DiagGroup<"c++17-extensions", [CXX17Attrs]>; // earlier C++ versions. def CXX20 : DiagGroup<"c++20-extensions", [CXX20Designator, CXX20Attrs]>; -// A warning group for warnings about using C++2b features as extensions in +// A warning group for warnings about using C++23 features as extensions in // earlier C++ versions. -def CXX2b : DiagGroup<"c++2b-extensions">; +def CXX23 : DiagGroup<"c++23-extensions">; def : DiagGroup<"c++0x-extensions", [CXX11]>; def : DiagGroup<"c++1y-extensions", [CXX14]>; diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td index b77c19b816d86..ef5b1d8be0fd5 100644 --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -138,13 +138,13 @@ def ext_mathematical_notation : ExtWarn< def ext_delimited_escape_sequence : Extension< "%select{delimited|named}0 escape sequences are a " - "%select{Clang|C++2b}1 extension">, + "%select{Clang|C++23}1 extension">, InGroup>; -def warn_cxx2b_delimited_escape_sequence : Warning< +def warn_cxx23_delimited_escape_sequence : Warning< "%select{delimited|named}0 escape sequences are " - "incompatible with C++ standards before C++2b">, - InGroup, DefaultIgnore; + "incompatible with C++ standards before C++23">, + InGroup, DefaultIgnore; def err_delimited_escape_empty : Error< "delimited escape sequence cannot be empty">; @@ -396,10 +396,10 @@ def ext_pp_include_next_directive : Extension< "#include_next is a language extension">, InGroup; def ext_pp_warning_directive : Extension< - "#warning is a %select{C2x|C++2b}0 extension">; -def warn_cxx2b_compat_warning_directive : Warning< - "#warning is incompatible with C++ standards before C++2b">, - InGroup, DefaultIgnore; + "#warning is a %select{C2x|C++23}0 extension">; +def warn_cxx23_compat_warning_directive : Warning< + "#warning is incompatible with C++ standards before C++23">, + InGroup, DefaultIgnore; def warn_c2x_compat_warning_directive : Warning< "#warning is incompatible with C standards before C2x">, InGroup, DefaultIgnore; @@ -739,14 +739,14 @@ def ext_c2x_pp_directive : ExtWarn< "use of a '#%select{|elifdef|elifndef}0' directive " "is a C2x extension">, InGroup; -def warn_cxx2b_compat_pp_directive : Warning< +def warn_cxx23_compat_pp_directive : Warning< "use of a '#%select{|elifdef|elifndef}0' directive " - "is incompatible with C++ standards before C++2b">, - InGroup, DefaultIgnore; -def ext_cxx2b_pp_directive : ExtWarn< + "is incompatible with C++ standards before C++23">, + InGroup, DefaultIgnore; +def ext_cxx23_pp_directive : ExtWarn< "use of a '#%select{|elifdef|elifndef}0' directive " - "is a C++2b extension">, - InGroup; + "is a C++23 extension">, + InGroup; def err_pp_visibility_non_macro : Error<"no macro named %0">; diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index d8ad04728b32d..5d5048a7d2c04 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -300,14 +300,14 @@ def ext_c_label_end_of_compound_statement : ExtWarn< "label at end of compound statement is a C2x extension">, InGroup; def ext_cxx_label_end_of_compound_statement : ExtWarn< - "label at end of compound statement is a C++2b extension">, - InGroup; + "label at end of compound statement is a C++23 extension">, + InGroup; def warn_c2x_compat_label_end_of_compound_statement : Warning< "label at end of compound statement is incompatible with C standards before C2x">, InGroup, DefaultIgnore; def warn_cxx20_compat_label_end_of_compound_statement : Warning< - "label at end of compound statement is incompatible with C++ standards before C++2b">, - InGroup, DefaultIgnore; + "label at end of compound statement is incompatible with C++ standards before C++23">, + InGroup, DefaultIgnore; def err_address_of_label_outside_fn : Error< "use of address-of-label extension outside of a function body">; def err_asm_operand_wide_string_literal : Error< @@ -566,11 +566,11 @@ def err_expected_init_in_condition_lparen : Error< def err_extraneous_rparen_in_condition : Error< "extraneous ')' after condition, expected a statement">; def ext_alias_in_init_statement : ExtWarn< - "alias declaration in this context is a C++2b extension">, - InGroup; + "alias declaration in this context is a C++23 extension">, + InGroup; def warn_cxx20_alias_in_init_statement : Warning< - "alias declaration in this context is incompatible with C++ standards before C++2b">, - DefaultIgnore, InGroup; + "alias declaration in this context is incompatible with C++ standards before C++23">, + DefaultIgnore, InGroup; def warn_dangling_else : Warning< "add explicit braces to avoid dangling else">, InGroup; @@ -652,11 +652,11 @@ def warn_cxx14_compat_constexpr_if : Warning< "constexpr if is incompatible with C++ standards before C++17">, DefaultIgnore, InGroup; def ext_consteval_if : ExtWarn< - "consteval if is a C++2b extension">, - InGroup; + "consteval if is a C++23 extension">, + InGroup; def warn_cxx20_compat_consteval_if : Warning< - "consteval if is incompatible with C++ standards before C++2b">, - InGroup, DefaultIgnore; + "consteval if is incompatible with C++ standards before C++23">, + InGroup, DefaultIgnore; def ext_init_statement : ExtWarn< "'%select{if|switch}0' initialization statements are a C++17 extension">, @@ -1014,14 +1014,14 @@ def err_lambda_capture_multiple_ellipses : Error< def err_capture_default_first : Error< "capture default must be first">; def ext_decl_attrs_on_lambda : ExtWarn< - "an attribute specifier sequence in this position is a C++2b extension">, - InGroup; + "an attribute specifier sequence in this position is a C++23 extension">, + InGroup; def ext_lambda_missing_parens : ExtWarn< - "lambda without a parameter clause is a C++2b extension">, - InGroup; + "lambda without a parameter clause is a C++23 extension">, + InGroup; def warn_cxx20_compat_decl_attrs_on_lambda : Warning< "an attribute specifier sequence in this position is incompatible with C++ " - "standards before C++2b">, InGroup, DefaultIgnore; + "standards before C++23">, InGroup, DefaultIgnore; // C++17 lambda expressions def err_expected_star_this_capture : Error< @@ -1044,12 +1044,12 @@ def warn_cxx17_compat_lambda_template_parameter_list: Warning< def err_lambda_template_parameter_list_empty : Error< "lambda template parameter list cannot be empty">; -// C++2b static lambdas +// C++23 static lambdas def err_static_lambda: ExtWarn< - "static lambdas are a C++2b extension">, InGroup; + "static lambdas are a C++23 extension">, InGroup; def warn_cxx20_compat_static_lambda : Warning< - "static lambdas are incompatible with C++ standards before C++2b">, - InGroup, DefaultIgnore; + "static lambdas are incompatible with C++ standards before C++23">, + InGroup, DefaultIgnore; def err_static_mutable_lambda : Error< "lambda cannot be both mutable and static">; def err_static_lambda_captures : Error< diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 18a0154b00411..b496900fac06e 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2126,7 +2126,8 @@ def err_init_conversion_failed : Error< "exception object|a member subobject|an array element|a new value|a value|a " "base class|a constructor delegation|a vector element|a block element|a " "block element|a complex element|a lambda capture|a compound literal " - "initializer|a related result|a parameter of CF audited function}0 " + "initializer|a related result|a parameter of CF audited function|a " + "structured binding|a member subobject}0 " "%diff{of type $ with an %select{rvalue|lvalue}2 of type $|" "with an %select{rvalue|lvalue}2 of incompatible type}1,3" "%select{|: different classes%diff{ ($ vs $)|}5,6" @@ -2369,7 +2370,7 @@ def err_auto_expr_init_paren_braces : Error< "%select{parenthesized|nested}0 initializer list">; def warn_cxx20_compat_auto_expr : Warning< "'auto' as a functional-style cast is incompatible with C++ standards " - "before C++2b">, InGroup, DefaultIgnore; + "before C++23">, InGroup, DefaultIgnore; def err_auto_missing_trailing_return : Error< "'auto' return without trailing return type; deduced return types are a " "C++14 extension">; @@ -2666,7 +2667,7 @@ def err_invalid_constexpr : Error< def err_invalid_constexpr_member : Error<"non-static data member cannot be " "constexpr%select{; did you intend to make it %select{const|static}0?|}1">; def err_constexpr_tag : Error< - "%select{class|struct|interface|union|enum}0 " + "%select{class|struct|interface|union|enum|enum class|enum struct}0 " "cannot be marked %sub{select_constexpr_spec_kind}1">; def err_constexpr_dtor : Error< "destructor cannot be declared %sub{select_constexpr_spec_kind}0">; @@ -2726,13 +2727,13 @@ def warn_cxx17_compat_constexpr_body_invalid_stmt : Warning< "use of this statement in a constexpr %select{function|constructor}0 " "is incompatible with C++ standards before C++20">, InGroup, DefaultIgnore; -def ext_constexpr_body_invalid_stmt_cxx2b : ExtWarn< +def ext_constexpr_body_invalid_stmt_cxx23 : ExtWarn< "use of this statement in a constexpr %select{function|constructor}0 " - "is a C++2b extension">, InGroup; + "is a C++23 extension">, InGroup; def warn_cxx20_compat_constexpr_body_invalid_stmt : Warning< "use of this statement in a constexpr %select{function|constructor}0 " - "is incompatible with C++ standards before C++2b">, - InGroup, DefaultIgnore; + "is incompatible with C++ standards before C++23">, + InGroup, DefaultIgnore; def ext_constexpr_type_definition : ExtWarn< "type definition in a constexpr %select{function|constructor}0 " "is a C++14 extension">, InGroup; @@ -2753,15 +2754,15 @@ def warn_cxx11_compat_constexpr_local_var : Warning< def ext_constexpr_static_var : ExtWarn< "definition of a %select{static|thread_local}1 variable " "in a constexpr %select{function|constructor}0 " - "is a C++2b extension">, InGroup; + "is a C++23 extension">, InGroup; def warn_cxx20_compat_constexpr_var : Warning< "definition of a %select{static variable|thread_local variable|variable " "of non-literal type}1 in a constexpr %select{function|constructor}0 " - "is incompatible with C++ standards before C++2b">, - InGroup, DefaultIgnore; + "is incompatible with C++ standards before C++23">, + InGroup, DefaultIgnore; def err_constexpr_local_var_non_literal_type : Error< "variable of non-literal type %1 cannot be defined in a constexpr " - "%select{function|constructor}0 before C++2b">; + "%select{function|constructor}0 before C++23">; def ext_constexpr_local_var_no_init : ExtWarn< "uninitialized variable in a constexpr %select{function|constructor}0 " "is a C++20 extension">, InGroup; @@ -3055,6 +3056,14 @@ def err_attribute_arm_feature_sve_bits_unsupported : Error< "value of 128, 256, 512, 1024 or 2048.">; def err_sve_vector_in_non_sve_target : Error< "SVE vector type %0 cannot be used in a target without sve">; +def err_attribute_riscv_rvv_bits_unsupported : Error< + "%0 is only supported when '-mrvv-vector-bits=' is specified with a " + "value of \"zvl\" or a power 2 in the range [64,65536]">; +def err_attribute_bad_rvv_vector_size : Error< + "invalid RVV vector size '%0', must match value set by " + "'-mrvv-vector-bits' ('%1')">; +def err_attribute_invalid_rvv_type : Error< + "%0 attribute applied to non-RVV type %1">; def err_attribute_requires_positive_integer : Error< "%0 attribute requires a %select{positive|non-negative}1 " "integral compile time constant expression">; @@ -3165,8 +3174,9 @@ def err_attribute_invalid_size : Error< "vector size not an integral multiple of component size">; def err_attribute_zero_size : Error<"zero %0 size">; def err_attribute_size_too_large : Error<"%0 size too large">; -def err_typecheck_sve_ambiguous : Error< - "cannot combine fixed-length and sizeless SVE vectors in expression, result is ambiguous (%0 and %1)">; +def err_typecheck_sve_rvv_ambiguous : Error< + "cannot combine fixed-length and sizeless %select{SVE|RVV}0 vectors " + "in expression, result is ambiguous (%1 and %2)">; def err_typecheck_sve_rvv_gnu_ambiguous : Error< "cannot combine GNU and %select{SVE|RVV}0 vectors in expression, result is ambiguous (%1 and %2)">; def err_typecheck_vector_not_convertable_implict_truncation : Error< @@ -3423,7 +3433,7 @@ def warn_type_attribute_deprecated_on_decl : Warning< InGroup; def warn_declspec_attribute_ignored : Warning< "attribute %0 is ignored, place it after " - "\"%select{class|struct|interface|union|enum}1\" to apply attribute to " + "\"%select{class|struct|interface|union|enum|enum class|enum struct}1\" to apply attribute to " "type declaration">, InGroup; def warn_attribute_precede_definition : Warning< "attribute declaration must precede definition">, @@ -6734,7 +6744,7 @@ def err_arithmetic_nonfragile_interface : Error< def warn_deprecated_comma_subscript : Warning< "top-level comma expression in array subscript is deprecated " - "in C++20 and unsupported in C++2b">, + "in C++20 and unsupported in C++23">, InGroup; def ext_subscript_non_lvalue : Extension< @@ -6928,7 +6938,7 @@ def warn_standalone_specifier : Warning<"'%0' ignored on this declaration">, def ext_standalone_specifier : ExtWarn<"'%0' is not permitted on a declaration " "of a type">, InGroup; def err_standalone_class_nested_name_specifier : Error< - "forward declaration of %select{class|struct|interface|union|enum}0 cannot " + "forward declaration of %select{class|struct|interface|union|enum|enum class|enum struct}0 cannot " "have a nested name specifier">; def err_typecheck_sclass_func : Error<"illegal storage class on function">; def err_static_block_func : Error< @@ -9138,9 +9148,9 @@ def err_operator_overload_needs_class_or_enum : Error< def err_operator_overload_variadic : Error<"overloaded %0 cannot be variadic">; def warn_cxx20_compat_operator_overload_static : Warning< "declaring overloaded %0 as 'static' is incompatible with C++ standards " - "before C++2b">, InGroup, DefaultIgnore; + "before C++23">, InGroup, DefaultIgnore; def ext_operator_overload_static : ExtWarn< - "declaring overloaded %0 as 'static' is a C++2b extension">, InGroup; + "declaring overloaded %0 as 'static' is a C++23 extension">, InGroup; def err_operator_overload_static : Error< "overloaded %0 cannot be a static member function">; def err_operator_overload_default_arg : Error< @@ -9148,9 +9158,9 @@ def err_operator_overload_default_arg : Error< def ext_subscript_overload : Warning< "overloaded %0 with %select{no|a defaulted|more than one}1 parameter is a " - "C++2b extension">, InGroup, DefaultIgnore; + "C++23 extension">, InGroup, DefaultIgnore; def error_subscript_overload : Error< - "overloaded %0 cannot have %select{no|a defaulted|more than one}1 parameter before C++2b">; + "overloaded %0 cannot have %select{no|a defaulted|more than one}1 parameter before C++23">; def err_operator_overload_must_be : Error< "overloaded %0 must be a %select{unary|binary|unary or binary}2 operator " @@ -9416,12 +9426,21 @@ def note_defaulted_comparison_cannot_deduce_undeduced_auto : Note< "%select{|member|base class}0 %1 declared here">; def note_defaulted_comparison_cannot_deduce_callee : Note< "selected 'operator<=>' for %select{|member|base class}0 %1 declared here">; -def err_incorrect_defaulted_comparison_constexpr : Error< +def ext_defaulted_comparison_constexpr_mismatch : Extension< "defaulted definition of %select{%sub{select_defaulted_comparison_kind}1|" - "three-way comparison operator}0 " - "cannot be declared %select{constexpr|consteval}2 because " - "%select{it|the corresponding implicit 'operator=='}0 " - "invokes a non-constexpr comparison function">; + "three-way comparison operator}0 that is " + "declared %select{constexpr|consteval}2 but" + "%select{|for which the corresponding implicit 'operator==' }0 " + "invokes a non-constexpr comparison function is a C++23 extension">, + InGroup>; +def warn_cxx23_compat_defaulted_comparison_constexpr_mismatch : Warning< + "defaulted definition of %select{%sub{select_defaulted_comparison_kind}1|" + "three-way comparison operator}0 that is " + "declared %select{constexpr|consteval}2 but" + "%select{|for which the corresponding implicit 'operator==' }0 " + "invokes a non-constexpr comparison function is incompatible with C++ " + "standards before C++23">, + InGroup, DefaultIgnore; def note_defaulted_comparison_not_constexpr : Note< "non-constexpr comparison function would be used to compare " "%select{|member %1|base class %1}0">; diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 20bddca48cba1..495b17e769e5d 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -97,7 +97,7 @@ LANGOPT(CPlusPlus11 , 1, 0, "C++11") LANGOPT(CPlusPlus14 , 1, 0, "C++14") LANGOPT(CPlusPlus17 , 1, 0, "C++17") LANGOPT(CPlusPlus20 , 1, 0, "C++20") -LANGOPT(CPlusPlus2b , 1, 0, "C++2b") +LANGOPT(CPlusPlus23 , 1, 0, "C++23") LANGOPT(ObjC , 1, 0, "Objective-C") BENIGN_LANGOPT(ObjCDefaultSynthProperties , 1, 0, "Objective-C auto-synthesized properties") diff --git a/clang/include/clang/Basic/LangStandard.h b/clang/include/clang/Basic/LangStandard.h index 4e78570ff733b..ebe14c8a3f8fd 100644 --- a/clang/include/clang/Basic/LangStandard.h +++ b/clang/include/clang/Basic/LangStandard.h @@ -55,7 +55,7 @@ enum LangFeatures { CPlusPlus14 = (1 << 7), CPlusPlus17 = (1 << 8), CPlusPlus20 = (1 << 9), - CPlusPlus2b = (1 << 10), + CPlusPlus23 = (1 << 10), Digraphs = (1 << 11), GNUMode = (1 << 12), HexFloat = (1 << 13), @@ -118,8 +118,8 @@ struct LangStandard { /// isCPlusPlus20 - Language is a C++20 variant (or later). bool isCPlusPlus20() const { return Flags & CPlusPlus20; } - /// isCPlusPlus2b - Language is a post-C++20 variant (or later). - bool isCPlusPlus2b() const { return Flags & CPlusPlus2b; } + /// isCPlusPlus23 - Language is a post-C++23 variant (or later). + bool isCPlusPlus23() const { return Flags & CPlusPlus23; } /// hasDigraphs - Language supports digraphs. bool hasDigraphs() const { return Flags & Digraphs; } diff --git a/clang/include/clang/Basic/LangStandards.def b/clang/include/clang/Basic/LangStandards.def index c5d4da1cb2f90..8e4d3759f33b7 100644 --- a/clang/include/clang/Basic/LangStandards.def +++ b/clang/include/clang/Basic/LangStandards.def @@ -151,15 +151,17 @@ LANGSTANDARD(gnucxx20, "gnu++20", CPlusPlus20 | Digraphs | HexFloat | GNUMode) LANGSTANDARD_ALIAS_DEPR(gnucxx20, "gnu++2a") -LANGSTANDARD(cxx2b, "c++2b", - CXX, "Working draft for ISO C++ 2023 DIS", +LANGSTANDARD(cxx23, "c++23", + CXX, "ISO C++ 2023 DIS", LineComment | CPlusPlus | CPlusPlus11 | CPlusPlus14 | CPlusPlus17 | - CPlusPlus20 | CPlusPlus2b | Digraphs | HexFloat) + CPlusPlus20 | CPlusPlus23 | Digraphs | HexFloat) +LANGSTANDARD_ALIAS_DEPR(cxx23, "c++2b") -LANGSTANDARD(gnucxx2b, "gnu++2b", - CXX, "Working draft for ISO C++ 2023 DIS with GNU extensions", +LANGSTANDARD(gnucxx23, "gnu++23", + CXX, "ISO C++ 2023 DIS with GNU extensions", LineComment | CPlusPlus | CPlusPlus11 | CPlusPlus14 | CPlusPlus17 | - CPlusPlus20 | CPlusPlus2b | Digraphs | HexFloat | GNUMode) + CPlusPlus20 | CPlusPlus23 | Digraphs | HexFloat | GNUMode) +LANGSTANDARD_ALIAS_DEPR(gnucxx23, "gnu++2b") // OpenCL LANGSTANDARD(opencl10, "cl1.0", diff --git a/clang/include/clang/Basic/RISCVVTypes.def b/clang/include/clang/Basic/RISCVVTypes.def index 1d4024dfb20d3..b21a3bb4d5a6b 100644 --- a/clang/include/clang/Basic/RISCVVTypes.def +++ b/clang/include/clang/Basic/RISCVVTypes.def @@ -40,6 +40,10 @@ // //===----------------------------------------------------------------------===// +#ifndef RVV_TYPE +#define RVV_TYPE(Name, Id, SingletonId) +#endif + #ifndef RVV_VECTOR_TYPE #define RVV_VECTOR_TYPE(Name, Id, SingletonId, NumEls, ElBits, NF, IsSigned, IsFP)\ RVV_TYPE(Name, Id, SingletonId) diff --git a/clang/include/clang/Basic/Specifiers.h b/clang/include/clang/Basic/Specifiers.h index a8c35fed9997e..06279a016a507 100644 --- a/clang/include/clang/Basic/Specifiers.h +++ b/clang/include/clang/Basic/Specifiers.h @@ -19,6 +19,9 @@ #include "llvm/Support/DataTypes.h" #include "llvm/Support/ErrorHandling.h" +namespace llvm { +class raw_ostream; +} // namespace llvm namespace clang { /// Define the meaning of possible values of the kind in ExplicitSpecifier. @@ -333,6 +336,8 @@ namespace clang { // parameters are assumed to only get null on error. NullableResult, }; + /// Prints human-readable debug representation. + llvm::raw_ostream &operator<<(llvm::raw_ostream&, NullabilityKind); /// Return true if \p L has a weaker nullability annotation than \p R. The /// ordering is: Unspecified < Nullable < NonNull. diff --git a/clang/include/clang/Basic/riscv_sifive_vector.td b/clang/include/clang/Basic/riscv_sifive_vector.td new file mode 100644 index 0000000000000..0d390be711c83 --- /dev/null +++ b/clang/include/clang/Basic/riscv_sifive_vector.td @@ -0,0 +1,105 @@ +//==--- riscv_sifive_vector.td - RISC-V SiFive VCIX function list ---------===// +// +// 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 defines the builtins for RISC-V SiFive VCIX. See: +// +// https://sifive.cdn.prismic.io/sifive/c3829e36-8552-41f0-a841-79945784241b_vcix-spec-software.pdf +// +//===----------------------------------------------------------------------===// + +include "riscv_vector_common.td" + +//===----------------------------------------------------------------------===// +// Instruction definitions +//===----------------------------------------------------------------------===// + +class VCIXSuffix { + list suffix = !cond(!eq(range, "c"): ["8mf8", "8mf4", "8mf2", "8m1", "8m2", "8m4", "8m8"], + !eq(range, "s"): ["16mf4", "16mf2", "16m1", "16m2", "16m4", "16m8"], + !eq(range, "i"): ["32mf2", "32m1", "32m2", "32m4", "32m8"], + !eq(range, "l"): ["64m1", "64m2", "64m4", "64m8"]); +} + +class VCIXBuiltinSet intrinsic_types> + : RVVBuiltin { + let Name = name; + let OverloadedName = name; + let IRName = IR_name; + let HasMasked = false; + let IntrinsicTypes = intrinsic_types; +} + +multiclass VCIXBuiltinSet intrinsic_types> { + if !find(prototype, "0") then { + def : VCIXBuiltinSet; + } + def : VCIXBuiltinSet; +} + +multiclass RVVVCIXBuiltinSet range, string prototype, + list intrinsic_types, bit UseGPR> { + foreach r = range in + let RequiredFeatures = !if(!and(UseGPR, !eq(r, "l")), + ["Xsfvcp", "RV64"], ["Xsfvcp"]) in + defm : VCIXBuiltinSet; +} + +multiclass RVVVCIXBuiltinSetWVType range, string prototype, + list intrinsic_types, bit UseGPR> { + foreach r = range in + let RequiredFeatures = !if(!and(UseGPR, !eq(r, "l")), + ["Xsfvcp", "RV64"], ["Xsfvcp"]) in + // These intrinsics don't have any vector types in the output and inputs, + // but we still need to add vetvli for them. So we encode different + // VTYPE into the intrinsic names, and then will know which vsetvli is + // correct. + foreach s = VCIXSuffix.suffix in + // Since we already encode the Vtype into the name, so just set + // Log2LMUL to zero. Otherwise the RISCVVEmitter will expand + // lots of redundant intrinsic but have same names. + let Log2LMUL = [0] in + def : VCIXBuiltinSet; +} + +let SupportOverloading = false in { + defm sf_vc_x_se : RVVVCIXBuiltinSetWVType<["c", "s", "i", "l"], "0KzKzKzUe", [0, 3], /*UseGPR*/1>; + defm sf_vc_i_se : RVVVCIXBuiltinSetWVType<["c", "s", "i", "l"], "0KzKzKzKz", [2, 3], /*UseGPR*/0>; + defm sf_vc_xv : RVVVCIXBuiltinSet<["csi", "l"], "0KzKzUvUe", [0, 2, 3], /*UseGPR*/1>; + defm sf_vc_iv : RVVVCIXBuiltinSet<["csi", "l"], "0KzKzUvKz", [0, 2, 3], /*UseGPR*/0>; + defm sf_vc_vv : RVVVCIXBuiltinSet<["csi", "l"], "0KzKzUvUv", [0, 2, 3], /*UseGPR*/0>; + defm sf_vc_fv : RVVVCIXBuiltinSet<["si", "l"], "0KzKzUvFe", [0, 2, 3], /*UseGPR*/0>; + defm sf_vc_xvv : RVVVCIXBuiltinSet<["csi", "l"], "0KzUvUvUe", [0, 1, 3], /*UseGPR*/1>; + defm sf_vc_ivv : RVVVCIXBuiltinSet<["csi", "l"], "0KzUvUvKz", [0, 1, 3], /*UseGPR*/0>; + defm sf_vc_vvv : RVVVCIXBuiltinSet<["csi", "l"], "0KzUvUvUv", [0, 1, 3], /*UseGPR*/0>; + defm sf_vc_fvv : RVVVCIXBuiltinSet<["si", "l"], "0KzUvUvFe", [0, 1, 3], /*UseGPR*/0>; + defm sf_vc_v_x : RVVVCIXBuiltinSet<["csi", "l"], "UvKzKzUe", [-1, 1, 2], /*UseGPR*/1>; + defm sf_vc_v_i : RVVVCIXBuiltinSet<["csi", "l"], "UvKzKzKz", [-1, 1, 2], /*UseGPR*/0>; + defm sf_vc_v_xv : RVVVCIXBuiltinSet<["csi", "l"], "UvKzUvUe", [-1, 0, 2], /*UseGPR*/1>; + defm sf_vc_v_iv : RVVVCIXBuiltinSet<["csi", "l"], "UvKzUvKz", [-1, 0, 2], /*UseGPR*/0>; + defm sf_vc_v_vv : RVVVCIXBuiltinSet<["csi", "l"], "UvKzUvUv", [-1, 0, 2], /*UseGPR*/0>; + defm sf_vc_v_fv : RVVVCIXBuiltinSet<["si", "l"], "UvKzUvFe", [-1, 0, 2], /*UseGPR*/0>; + defm sf_vc_v_xvv : RVVVCIXBuiltinSet<["csi", "l"], "UvKzUvUvUe", [-1, 0, 3], /*UseGPR*/1>; + defm sf_vc_v_ivv : RVVVCIXBuiltinSet<["csi", "l"], "UvKzUvUvKz", [-1, 0, 3], /*UseGPR*/0>; + defm sf_vc_v_vvv : RVVVCIXBuiltinSet<["csi", "l"], "UvKzUvUvUv", [-1, 0, 3], /*UseGPR*/0>; + defm sf_vc_v_fvv : RVVVCIXBuiltinSet<["si", "l"], "UvKzUvUvFe", [-1, 0, 3], /*UseGPR*/0>; + let Log2LMUL = [-3, -2, -1, 0, 1, 2] in { + defm sf_vc_xvw : RVVVCIXBuiltinSet<["csi"], "0KzUwUvUe", [0, 1, 2, 3], /*UseGPR*/1>; + defm sf_vc_ivw : RVVVCIXBuiltinSet<["csi"], "0KzUwUvKz", [0, 1, 2, 3], /*UseGPR*/0>; + defm sf_vc_vvw : RVVVCIXBuiltinSet<["csi"], "0KzUwUvUv", [0, 1, 2, 3], /*UseGPR*/0>; + defm sf_vc_fvw : RVVVCIXBuiltinSet<["si"], "0KzUwUvFe", [0, 1, 2, 3], /*UseGPR*/0>; + defm sf_vc_v_xvw : RVVVCIXBuiltinSet<["csi"], "UwKzUwUvUe", [-1, 0, 2, 3], /*UseGPR*/1>; + defm sf_vc_v_ivw : RVVVCIXBuiltinSet<["csi"], "UwKzUwUvKz", [-1, 0, 2, 3], /*UseGPR*/0>; + defm sf_vc_v_vvw : RVVVCIXBuiltinSet<["csi"], "UwKzUwUvUv", [-1, 0, 2, 3], /*UseGPR*/0>; + defm sf_vc_v_fvw : RVVVCIXBuiltinSet<["si"], "UwKzUwUvFe", [-1, 0, 2, 3], /*UseGPR*/0>; + } +} diff --git a/clang/include/clang/Basic/riscv_vector.td b/clang/include/clang/Basic/riscv_vector.td index b23e26ecaa579..8eb873dfafc79 100644 --- a/clang/include/clang/Basic/riscv_vector.td +++ b/clang/include/clang/Basic/riscv_vector.td @@ -12,233 +12,7 @@ // //===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// -// Instruction definitions -//===----------------------------------------------------------------------===// -// Each record of the class RVVBuiltin defines a collection of builtins (i.e. -// "def vadd : RVVBuiltin" will be used to define things like "vadd_vv_i32m1", -// "vadd_vv_i32m2", etc). -// -// The elements of this collection are defined by an instantiation process the -// range of which is specified by the cross product of the LMUL attribute and -// every element in the attribute TypeRange. By default builtins have LMUL = [1, -// 2, 4, 8, 1/2, 1/4, 1/8] so the process is repeated 7 times. In tablegen we -// use the Log2LMUL [0, 1, 2, 3, -1, -2, -3] to represent the LMUL. -// -// LMUL represents the fact that the types of values used by that builtin are -// values generated by instructions that are executed under that LMUL. However, -// this does not mean the builtin is necessarily lowered into an instruction -// that executes under the specified LMUL. An example where this happens are -// loads and stores of masks. A mask like `vbool8_t` can be generated, for -// instance, by comparing two `__rvv_int8m1_t` (this is LMUL=1) or comparing two -// `__rvv_int16m2_t` (this is LMUL=2). The actual load or store, however, will -// be performed under LMUL=1 because mask registers are not grouped. -// -// TypeRange is a non-empty sequence of basic types: -// -// c: int8_t (i8) -// s: int16_t (i16) -// i: int32_t (i32) -// l: int64_t (i64) -// x: float16_t (half) -// f: float32_t (float) -// d: float64_t (double) -// -// This way, given an LMUL, a record with a TypeRange "sil" will cause the -// definition of 3 builtins. Each type "t" in the TypeRange (in this example -// they are int16_t, int32_t, int64_t) is used as a parameter that drives the -// definition of that particular builtin (for the given LMUL). -// -// During the instantiation, types can be transformed or modified using type -// transformers. Given a type "t" the following primitive type transformers can -// be applied to it to yield another type. -// -// e: type of "t" as is (identity) -// v: computes a vector type whose element type is "t" for the current LMUL -// w: computes a vector type identical to what 'v' computes except for the -// element type which is twice as wide as the element type of 'v' -// q: computes a vector type identical to what 'v' computes except for the -// element type which is four times as wide as the element type of 'v' -// o: computes a vector type identical to what 'v' computes except for the -// element type which is eight times as wide as the element type of 'v' -// m: computes a vector type identical to what 'v' computes except for the -// element type which is bool -// 0: void type, ignores "t" -// z: size_t, ignores "t" -// t: ptrdiff_t, ignores "t" -// u: unsigned long, ignores "t" -// l: long, ignores "t" -// -// So for instance if t is "i", i.e. int, then "e" will yield int again. "v" -// will yield an RVV vector type (assume LMUL=1), so __rvv_int32m1_t. -// Accordingly "w" would yield __rvv_int64m2_t. -// -// A type transformer can be prefixed by other non-primitive type transformers. -// -// P: constructs a pointer to the current type -// C: adds const to the type -// K: requires the integer type to be a constant expression -// U: given an integer type or vector type, computes its unsigned variant -// I: given a vector type, compute the vector type with integer type -// elements of the same width -// F: given a vector type, compute the vector type with floating-point type -// elements of the same width -// S: given a vector type, computes its equivalent one for LMUL=1. This is a -// no-op if the vector was already LMUL=1 -// (Log2EEW:Value): Log2EEW value could be 3/4/5/6 (8/16/32/64), given a -// vector type (SEW and LMUL) and EEW (8/16/32/64), computes its -// equivalent integer vector type with EEW and corresponding ELMUL (elmul = -// (eew/sew) * lmul). For example, vector type is __rvv_float16m4 -// (SEW=16, LMUL=4) and Log2EEW is 3 (EEW=8), and then equivalent vector -// type is __rvv_uint8m2_t (elmul=(8/16)*4 = 2). Ignore to define a new -// builtins if its equivalent type has illegal lmul. -// (FixedSEW:Value): Given a vector type (SEW and LMUL), and computes another -// vector type which only changed SEW as given value. Ignore to define a new -// builtin if its equivalent type has illegal lmul or the SEW does not changed. -// (SFixedLog2LMUL:Value): Smaller Fixed Log2LMUL. Given a vector type (SEW -// and LMUL), and computes another vector type which only changed LMUL as -// given value. The new LMUL should be smaller than the old one. Ignore to -// define a new builtin if its equivalent type has illegal lmul. -// (LFixedLog2LMUL:Value): Larger Fixed Log2LMUL. Given a vector type (SEW -// and LMUL), and computes another vector type which only changed LMUL as -// given value. The new LMUL should be larger than the old one. Ignore to -// define a new builtin if its equivalent type has illegal lmul. -// -// Following with the example above, if t is "i", then "Ue" will yield unsigned -// int and "Fv" will yield __rvv_float32m1_t (again assuming LMUL=1), Fw would -// yield __rvv_float64m2_t, etc. -// -// Each builtin is then defined by applying each type in TypeRange against the -// sequence of type transformers described in Suffix and Prototype. -// -// The name of the builtin is defined by the Name attribute (which defaults to -// the name of the class) appended (separated with an underscore) the Suffix -// attribute. For instance with Name="foo", Suffix = "v" and TypeRange = "il", -// the builtin generated will be __builtin_rvv_foo_i32m1 and -// __builtin_rvv_foo_i64m1 (under LMUL=1). If Suffix contains more than one -// type transformer (say "vv") each of the types is separated with an -// underscore as in "__builtin_rvv_foo_i32m1_i32m1". -// -// The C/C++ prototype of the builtin is defined by the Prototype attribute. -// Prototype is a non-empty sequence of type transformers, the first of which -// is the return type of the builtin and the rest are the parameters of the -// builtin, in order. For instance if Prototype is "wvv" and TypeRange is "si" -// a first builtin will have type -// __rvv_int32m2_t (__rvv_int16m1_t, __rvv_int16m1_t) and the second builtin -// will have type __rvv_int64m2_t (__rvv_int32m1_t, __rvv_int32m1_t) (again -// under LMUL=1). -// -// There are a number of attributes that are used to constraint the number and -// shape of the builtins generated. Refer to the comments below for them. - -class PolicyScheme{ - int Value = val; -} -def NonePolicy : PolicyScheme<0>; -def HasPassthruOperand : PolicyScheme<1>; -def HasPolicyOperand : PolicyScheme<2>; - -class RVVBuiltin { - // Base name that will be prepended in __builtin_rvv_ and appended the - // computed Suffix. - string Name = NAME; - - // If not empty, each instantiated builtin will have this appended after an - // underscore (_). It is instantiated like Prototype. - string Suffix = suffix; - - // If empty, default OverloadedName is sub string of `Name` which end of first - // '_'. For example, the default overloaded name is `vadd` for Name `vadd_vv`. - // It's used for describe some special naming cases. - string OverloadedName = ""; - - // If not empty, each OverloadedName will have this appended after an - // underscore (_). It is instantiated like Prototype. - string OverloadedSuffix = overloaded_suffix; - - // The different variants of the builtin, parameterised with a type. - string TypeRange = type_range; - - // We use each type described in TypeRange and LMUL with prototype to - // instantiate a specific element of the set of builtins being defined. - // Prototype attribute defines the C/C++ prototype of the builtin. It is a - // non-empty sequence of type transformers, the first of which is the return - // type of the builtin and the rest are the parameters of the builtin, in - // order. For instance if Prototype is "wvv", TypeRange is "si" and LMUL=1, a - // first builtin will have type - // __rvv_int32m2_t (__rvv_int16m1_t, __rvv_int16m1_t), and the second builtin - // will have type __rvv_int64m2_t (__rvv_int32m1_t, __rvv_int32m1_t). - string Prototype = prototype; - - // This builtin has a masked form. - bit HasMasked = true; - - // If HasMasked, this flag states that this builtin has a maskedoff operand. It - // is always the first operand in builtin and IR intrinsic. - bit HasMaskedOffOperand = true; - - // This builtin has a granted vector length parameter. - bit HasVL = true; - - // The policy scheme for masked intrinsic IR. - // It could be NonePolicy or HasPolicyOperand. - // HasPolicyOperand: Has a policy operand. 0 is tail and mask undisturbed, 1 is - // tail agnostic, 2 is mask undisturbed, and 3 is tail and mask agnostic. The - // policy operand is located at the last position. - PolicyScheme MaskedPolicyScheme = HasPolicyOperand; - - // The policy scheme for unmasked intrinsic IR. - // It could be NonePolicy, HasPassthruOperand or HasPolicyOperand. - // HasPassthruOperand: Has a passthru operand to decide tail policy. If it is - // poison, tail policy is tail agnostic, otherwise policy is tail undisturbed. - // HasPolicyOperand: Has a policy operand. 1 is tail agnostic and 0 is tail - // undisturbed. - PolicyScheme UnMaskedPolicyScheme = NonePolicy; - - // This builtin support tail agnostic and undisturbed policy. - bit HasTailPolicy = true; - // This builtin support mask agnostic and undisturbed policy. - bit HasMaskPolicy = true; - - // This builtin prototype with TA or TAMA policy could not support overloading - // API. Other policy intrinsic functions would support overloading API with - // suffix `_tu`, `tumu`, `tuma`, `tamu` and `tama`. - bit SupportOverloading = true; - - // This builtin is valid for the given Log2LMULs. - list Log2LMUL = [0, 1, 2, 3, -1, -2, -3]; - - // Manual code in clang codegen riscv_vector_builtin_cg.inc - code ManualCodegen = [{}]; - - // When emit the automatic clang codegen, it describes what types we have to use - // to obtain the specific LLVM intrinsic. -1 means the return type, otherwise, - // k >= 0 meaning the k-th operand (counting from zero) of the codegen'd - // parameter of the unmasked version. k can't be the mask operand's position. - list IntrinsicTypes = []; - - // If these names are not empty, this is the ID of the LLVM intrinsic - // we want to lower to. - string IRName = NAME; - - // If HasMasked, this is the ID of the LLVM intrinsic we want to lower to. - string MaskedIRName = NAME #"_mask"; - - // Use clang_builtin_alias to save the number of builtins. - bit HasBuiltinAlias = true; - - // Features required to enable for this builtin. - list RequiredFeatures = []; - - // Number of fields for Load/Store Segment instructions. - int NF = 1; -} - -// This is the code emitted in the header. -class RVVHeader { - code HeaderCode; -} +include "riscv_vector_common.td" //===----------------------------------------------------------------------===// // Basic classes with automatic codegen. diff --git a/clang/include/clang/Basic/riscv_vector_common.td b/clang/include/clang/Basic/riscv_vector_common.td new file mode 100644 index 0000000000000..6fd1dbde52be4 --- /dev/null +++ b/clang/include/clang/Basic/riscv_vector_common.td @@ -0,0 +1,239 @@ +//==------ riscv_vector_common.td - RISC-V V-ext builtin class ------------===// +// +// 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 defines RVV builtin base class for RISC-V V-extension. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Instruction definitions +//===----------------------------------------------------------------------===// +// Each record of the class RVVBuiltin defines a collection of builtins (i.e. +// "def vadd : RVVBuiltin" will be used to define things like "vadd_vv_i32m1", +// "vadd_vv_i32m2", etc). +// +// The elements of this collection are defined by an instantiation process the +// range of which is specified by the cross product of the LMUL attribute and +// every element in the attribute TypeRange. By default builtins have LMUL = [1, +// 2, 4, 8, 1/2, 1/4, 1/8] so the process is repeated 7 times. In tablegen we +// use the Log2LMUL [0, 1, 2, 3, -1, -2, -3] to represent the LMUL. +// +// LMUL represents the fact that the types of values used by that builtin are +// values generated by instructions that are executed under that LMUL. However, +// this does not mean the builtin is necessarily lowered into an instruction +// that executes under the specified LMUL. An example where this happens are +// loads and stores of masks. A mask like `vbool8_t` can be generated, for +// instance, by comparing two `__rvv_int8m1_t` (this is LMUL=1) or comparing two +// `__rvv_int16m2_t` (this is LMUL=2). The actual load or store, however, will +// be performed under LMUL=1 because mask registers are not grouped. +// +// TypeRange is a non-empty sequence of basic types: +// +// c: int8_t (i8) +// s: int16_t (i16) +// i: int32_t (i32) +// l: int64_t (i64) +// x: float16_t (half) +// f: float32_t (float) +// d: float64_t (double) +// +// This way, given an LMUL, a record with a TypeRange "sil" will cause the +// definition of 3 builtins. Each type "t" in the TypeRange (in this example +// they are int16_t, int32_t, int64_t) is used as a parameter that drives the +// definition of that particular builtin (for the given LMUL). +// +// During the instantiation, types can be transformed or modified using type +// transformers. Given a type "t" the following primitive type transformers can +// be applied to it to yield another type. +// +// e: type of "t" as is (identity) +// v: computes a vector type whose element type is "t" for the current LMUL +// w: computes a vector type identical to what 'v' computes except for the +// element type which is twice as wide as the element type of 'v' +// q: computes a vector type identical to what 'v' computes except for the +// element type which is four times as wide as the element type of 'v' +// o: computes a vector type identical to what 'v' computes except for the +// element type which is eight times as wide as the element type of 'v' +// m: computes a vector type identical to what 'v' computes except for the +// element type which is bool +// 0: void type, ignores "t" +// z: size_t, ignores "t" +// t: ptrdiff_t, ignores "t" +// u: unsigned long, ignores "t" +// l: long, ignores "t" +// +// So for instance if t is "i", i.e. int, then "e" will yield int again. "v" +// will yield an RVV vector type (assume LMUL=1), so __rvv_int32m1_t. +// Accordingly "w" would yield __rvv_int64m2_t. +// +// A type transformer can be prefixed by other non-primitive type transformers. +// +// P: constructs a pointer to the current type +// C: adds const to the type +// K: requires the integer type to be a constant expression +// U: given an integer type or vector type, computes its unsigned variant +// I: given a vector type, compute the vector type with integer type +// elements of the same width +// F: given a vector type, compute the vector type with floating-point type +// elements of the same width +// S: given a vector type, computes its equivalent one for LMUL=1. This is a +// no-op if the vector was already LMUL=1 +// (Log2EEW:Value): Log2EEW value could be 3/4/5/6 (8/16/32/64), given a +// vector type (SEW and LMUL) and EEW (8/16/32/64), computes its +// equivalent integer vector type with EEW and corresponding ELMUL (elmul = +// (eew/sew) * lmul). For example, vector type is __rvv_float16m4 +// (SEW=16, LMUL=4) and Log2EEW is 3 (EEW=8), and then equivalent vector +// type is __rvv_uint8m2_t (elmul=(8/16)*4 = 2). Ignore to define a new +// builtins if its equivalent type has illegal lmul. +// (FixedSEW:Value): Given a vector type (SEW and LMUL), and computes another +// vector type which only changed SEW as given value. Ignore to define a new +// builtin if its equivalent type has illegal lmul or the SEW does not changed. +// (SFixedLog2LMUL:Value): Smaller Fixed Log2LMUL. Given a vector type (SEW +// and LMUL), and computes another vector type which only changed LMUL as +// given value. The new LMUL should be smaller than the old one. Ignore to +// define a new builtin if its equivalent type has illegal lmul. +// (LFixedLog2LMUL:Value): Larger Fixed Log2LMUL. Given a vector type (SEW +// and LMUL), and computes another vector type which only changed LMUL as +// given value. The new LMUL should be larger than the old one. Ignore to +// define a new builtin if its equivalent type has illegal lmul. +// +// Following with the example above, if t is "i", then "Ue" will yield unsigned +// int and "Fv" will yield __rvv_float32m1_t (again assuming LMUL=1), Fw would +// yield __rvv_float64m2_t, etc. +// +// Each builtin is then defined by applying each type in TypeRange against the +// sequence of type transformers described in Suffix and Prototype. +// +// The name of the builtin is defined by the Name attribute (which defaults to +// the name of the class) appended (separated with an underscore) the Suffix +// attribute. For instance with Name="foo", Suffix = "v" and TypeRange = "il", +// the builtin generated will be __builtin_rvv_foo_i32m1 and +// __builtin_rvv_foo_i64m1 (under LMUL=1). If Suffix contains more than one +// type transformer (say "vv") each of the types is separated with an +// underscore as in "__builtin_rvv_foo_i32m1_i32m1". +// +// The C/C++ prototype of the builtin is defined by the Prototype attribute. +// Prototype is a non-empty sequence of type transformers, the first of which +// is the return type of the builtin and the rest are the parameters of the +// builtin, in order. For instance if Prototype is "wvv" and TypeRange is "si" +// a first builtin will have type +// __rvv_int32m2_t (__rvv_int16m1_t, __rvv_int16m1_t) and the second builtin +// will have type __rvv_int64m2_t (__rvv_int32m1_t, __rvv_int32m1_t) (again +// under LMUL=1). +// +// There are a number of attributes that are used to constraint the number and +// shape of the builtins generated. Refer to the comments below for them. + +class PolicyScheme{ + int Value = val; +} +def NonePolicy : PolicyScheme<0>; +def HasPassthruOperand : PolicyScheme<1>; +def HasPolicyOperand : PolicyScheme<2>; + +class RVVBuiltin { + // Base name that will be prepended in __builtin_rvv_ and appended the + // computed Suffix. + string Name = NAME; + + // If not empty, each instantiated builtin will have this appended after an + // underscore (_). It is instantiated like Prototype. + string Suffix = suffix; + + // If empty, default OverloadedName is sub string of `Name` which end of first + // '_'. For example, the default overloaded name is `vadd` for Name `vadd_vv`. + // It's used for describe some special naming cases. + string OverloadedName = ""; + + // If not empty, each OverloadedName will have this appended after an + // underscore (_). It is instantiated like Prototype. + string OverloadedSuffix = overloaded_suffix; + + // The different variants of the builtin, parameterised with a type. + string TypeRange = type_range; + + // We use each type described in TypeRange and LMUL with prototype to + // instantiate a specific element of the set of builtins being defined. + // Prototype attribute defines the C/C++ prototype of the builtin. It is a + // non-empty sequence of type transformers, the first of which is the return + // type of the builtin and the rest are the parameters of the builtin, in + // order. For instance if Prototype is "wvv", TypeRange is "si" and LMUL=1, a + // first builtin will have type + // __rvv_int32m2_t (__rvv_int16m1_t, __rvv_int16m1_t), and the second builtin + // will have type __rvv_int64m2_t (__rvv_int32m1_t, __rvv_int32m1_t). + string Prototype = prototype; + + // This builtin has a masked form. + bit HasMasked = true; + + // If HasMasked, this flag states that this builtin has a maskedoff operand. It + // is always the first operand in builtin and IR intrinsic. + bit HasMaskedOffOperand = true; + + // This builtin has a granted vector length parameter. + bit HasVL = true; + + // The policy scheme for masked intrinsic IR. + // It could be NonePolicy or HasPolicyOperand. + // HasPolicyOperand: Has a policy operand. 0 is tail and mask undisturbed, 1 is + // tail agnostic, 2 is mask undisturbed, and 3 is tail and mask agnostic. The + // policy operand is located at the last position. + PolicyScheme MaskedPolicyScheme = HasPolicyOperand; + + // The policy scheme for unmasked intrinsic IR. + // It could be NonePolicy, HasPassthruOperand or HasPolicyOperand. + // HasPassthruOperand: Has a passthru operand to decide tail policy. If it is + // poison, tail policy is tail agnostic, otherwise policy is tail undisturbed. + // HasPolicyOperand: Has a policy operand. 1 is tail agnostic and 0 is tail + // undisturbed. + PolicyScheme UnMaskedPolicyScheme = NonePolicy; + + // This builtin support tail agnostic and undisturbed policy. + bit HasTailPolicy = true; + // This builtin support mask agnostic and undisturbed policy. + bit HasMaskPolicy = true; + + // This builtin prototype with TA or TAMA policy could not support overloading + // API. Other policy intrinsic functions would support overloading API with + // suffix `_tu`, `tumu`, `tuma`, `tamu` and `tama`. + bit SupportOverloading = true; + + // This builtin is valid for the given Log2LMULs. + list Log2LMUL = [0, 1, 2, 3, -1, -2, -3]; + + // Manual code in clang codegen riscv_vector_builtin_cg.inc + code ManualCodegen = [{}]; + + // When emit the automatic clang codegen, it describes what types we have to use + // to obtain the specific LLVM intrinsic. -1 means the return type, otherwise, + // k >= 0 meaning the k-th operand (counting from zero) of the codegen'd + // parameter of the unmasked version. k can't be the mask operand's position. + list IntrinsicTypes = []; + + // If these names are not empty, this is the ID of the LLVM intrinsic + // we want to lower to. + string IRName = NAME; + + // If HasMasked, this is the ID of the LLVM intrinsic we want to lower to. + string MaskedIRName = NAME #"_mask"; + + // Use clang_builtin_alias to save the number of builtins. + bit HasBuiltinAlias = true; + + // Features required to enable for this builtin. + list RequiredFeatures = []; + + // Number of fields for Load/Store Segment instructions. + int NF = 1; +} + +// This is the code emitted in the header. +class RVVHeader { + code HeaderCode; +} diff --git a/clang/include/clang/CodeGen/ObjectFilePCHContainerOperations.h b/clang/include/clang/CodeGen/ObjectFilePCHContainerOperations.h index c13e052149d95..7a02d8725885a 100644 --- a/clang/include/clang/CodeGen/ObjectFilePCHContainerOperations.h +++ b/clang/include/clang/CodeGen/ObjectFilePCHContainerOperations.h @@ -32,7 +32,7 @@ class ObjectFilePCHContainerWriter : public PCHContainerWriter { /// A PCHContainerReader implementation that uses LLVM to /// wraps Clang modules inside a COFF, ELF, or Mach-O container. class ObjectFilePCHContainerReader : public PCHContainerReader { - StringRef getFormat() const override { return "obj"; } + ArrayRef getFormats() const override; /// Returns the serialized AST inside the PCH container Buffer. StringRef ExtractPCH(llvm::MemoryBufferRef Buffer) const override; diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 2abec84271356..182f0290736d8 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -967,8 +967,10 @@ def cxx_isystem : JoinedOrSeparate<["-"], "cxx-isystem">, Group, MetaVarName<"">; def c : Flag<["-"], "c">, Flags<[NoXarchOption, FlangOption]>, Group, HelpText<"Only run preprocess, compile, and assemble steps">; -def fconvergent_functions : Flag<["-"], "fconvergent-functions">, Group, Flags<[CC1Option]>, - HelpText<"Assume functions may be convergent">; +defm convergent_functions : BoolFOption<"convergent-functions", + LangOpts<"ConvergentFunctions">, DefaultFalse, + NegFlag, + PosFlag>; def gpu_use_aux_triple_only : Flag<["--"], "gpu-use-aux-triple-only">, InternalDriverOpt, HelpText<"Prepare '-aux-triple' only without populating " @@ -2034,11 +2036,6 @@ defm delete_null_pointer_checks : BoolFOption<"delete-null-pointer-checks", PosFlag, BothFlags<[CoreOption]>>; -def frewrite_map_file_EQ : Joined<["-"], "frewrite-map-file=">, - Group, - Flags<[NoXarchOption, CC1Option]>, - MarshallingInfoStringVector>; - defm use_line_directives : BoolFOption<"use-line-directives", PreprocessorOutputOpts<"UseLineDirectives">, DefaultFalse, PosFlag, NegFlag>; @@ -3221,11 +3218,13 @@ def fdebug_default_version: Joined<["-"], "fdebug-default-version=">, Group; + MetaVarName<"=">, + HelpText<"For paths in debug info, remap directory to . If multiple options match a path, the last option wins">; def fcoverage_prefix_map_EQ : Joined<["-"], "fcoverage-prefix-map=">, Group, Flags<[CC1Option]>, - HelpText<"remap file source paths in coverage mapping">; + MetaVarName<"=">, + HelpText<"remap file source paths to in coverage mapping. If there are multiple options, prefix replacement is applied in reverse order starting from the last one">; def ffile_prefix_map_EQ : Joined<["-"], "ffile-prefix-map=">, Group, HelpText<"remap file source paths in debug info, predefined preprocessor " @@ -3647,9 +3646,11 @@ def mcmodel_EQ_medany : Flag<["-"], "mcmodel=medany">, Group, def menable_experimental_extensions : Flag<["-"], "menable-experimental-extensions">, Group, HelpText<"Enable use of experimental RISC-V extensions.">; def mrvv_vector_bits_EQ : Joined<["-"], "mrvv-vector-bits=">, Group, - HelpText<"Specify the size in bits of an RVV vector register. Defaults to the" - " vector length agnostic value of \"scalable\". Also accepts \"zvl\"" - " to use the value implied by -march/-mcpu (RISC-V only)">; + HelpText<"Specify the size in bits of an RVV vector register. Defaults to " + "the vector length agnostic value of \"scalable\". Accepts power of " + "2 values between 64 and 65536. Also accepts \"zvl\" " + "to use the value implied by -march/-mcpu. Value will be reflected " + "in __riscv_v_fixed_vlen preprocessor define (RISC-V only)">; def munaligned_access : Flag<["-"], "munaligned-access">, Group, HelpText<"Allow memory accesses to be unaligned (AArch32/AArch64 only)">; @@ -3830,11 +3831,6 @@ def mno_prefixed: Flag<["-"], "mno-prefixed">, Group; def mspe : Flag<["-"], "mspe">, Group; def mno_spe : Flag<["-"], "mno-spe">, Group; def mefpu2 : Flag<["-"], "mefpu2">, Group; -def mabi_EQ_vec_extabi : Flag<["-"], "mabi=vec-extabi">, Group, Flags<[CC1Option]>, - HelpText<"Enable the extended Altivec ABI on AIX (AIX only). Uses volatile and nonvolatile vector registers">, - MarshallingInfoFlag>; -def mabi_EQ_vec_default : Flag<["-"], "mabi=vec-default">, Group, Flags<[CC1Option]>, - HelpText<"Enable the default Altivec ABI on AIX (AIX only). Uses only volatile vector registers.">; def mabi_EQ_quadword_atomics : Flag<["-"], "mabi=quadword-atomics">, Group, Flags<[CC1Option]>, HelpText<"Enable quadword atomics ABI on AIX (AIX PPC64 only). Uses lqarx/stqcx. instructions.">, @@ -3963,8 +3959,12 @@ def mno_outline_atomics : Flag<["-"], "mno-outline-atomics">, Group, Group, HelpText<"Don't generate implicit floating point or vector instructions">; def mimplicit_float : Flag<["-"], "mimplicit-float">, Group; -def mrecip : Flag<["-"], "mrecip">, Group; +def mrecip : Flag<["-"], "mrecip">, Group, + HelpText<"Equivalent to '-mrecip=all'">; def mrecip_EQ : CommaJoined<["-"], "mrecip=">, Group, Flags<[CC1Option]>, + HelpText<"Control use of approximate reciprocal and reciprocal square root instructions followed by iterations of " + "Newton-Raphson refinement. " + " = ( ['!'] ['vec-'] ('rcp'|'sqrt') [('h'|'s'|'d')] [':'] ) | 'all' | 'default' | 'none'">, MarshallingInfoStringVector>; def mprefer_vector_width_EQ : Joined<["-"], "mprefer-vector-width=">, Group, Flags<[CC1Option]>, HelpText<"Specifies preferred vector width for auto-vectorization. Defaults to 'none' which allows target specific decisions.">, @@ -4648,6 +4648,8 @@ def m68030 : Flag<["-"], "m68030">, Group; def m68040 : Flag<["-"], "m68040">, Group; def m68060 : Flag<["-"], "m68060">, Group; +def m68881 : Flag<["-"], "m68881">, Group; + foreach i = {0-6} in def ffixed_a#i : Flag<["-"], "ffixed-a"#i>, Group, HelpText<"Reserve the a"#i#" register (M68k only)">; @@ -5629,6 +5631,9 @@ def mframe_pointer_EQ : Joined<["-"], "mframe-pointer=">, def mabi_EQ_ieeelongdouble : Flag<["-"], "mabi=ieeelongdouble">, HelpText<"Use IEEE 754 quadruple-precision for long double">, MarshallingInfoFlag>; +def mabi_EQ_vec_extabi : Flag<["-"], "mabi=vec-extabi">, + HelpText<"Enable the extended Altivec ABI on AIX. Use volatile and nonvolatile vector registers">, + MarshallingInfoFlag>; def mfloat_abi : Separate<["-"], "mfloat-abi">, HelpText<"The float ABI to use">, MarshallingInfoString>; @@ -5817,7 +5822,7 @@ def fexperimental_assignment_tracking_EQ : Joined<["-"], "fexperimental-assignme Group, CodeGenOpts<"EnableAssignmentTracking">, NormalizedValuesScope<"CodeGenOptions::AssignmentTrackingOpts">, Values<"disabled,enabled,forced">, NormalizedValues<["Disabled","Enabled","Forced"]>, - MarshallingInfoEnum, "Disabled">; + MarshallingInfoEnum, "Enabled">; } // let Flags = [CC1Option, NoDriverOption] @@ -5830,6 +5835,9 @@ let Flags = [CC1Option, NoDriverOption] in { def sys_header_deps : Flag<["-"], "sys-header-deps">, HelpText<"Include system headers in dependency output">, MarshallingInfoFlag>; +def canonical_system_headers : Flag<["-"], "canonical-system-headers">, + HelpText<"Canonicalize system headers in dependency output">, + MarshallingInfoFlag>; def module_file_deps : Flag<["-"], "module-file-deps">, HelpText<"Include module files in dependency output">, MarshallingInfoFlag>; @@ -6552,12 +6560,14 @@ def fno_cuda_host_device_constexpr : Flag<["-"], "fno-cuda-host-device-constexpr // OpenMP Options //===----------------------------------------------------------------------===// +let Flags = [CC1Option, FC1Option, NoDriverOption] in { + def fopenmp_is_device : Flag<["-"], "fopenmp-is-device">, - HelpText<"Generate code only for an OpenMP target device.">, - Flags<[CC1Option, FC1Option, NoDriverOption]>; + HelpText<"Generate code only for an OpenMP target device.">; def fopenmp_host_ir_file_path : Separate<["-"], "fopenmp-host-ir-file-path">, - HelpText<"Path to the IR file produced by the frontend for the host.">, - Flags<[CC1Option, NoDriverOption]>; + HelpText<"Path to the IR file produced by the frontend for the host.">; + +} // let Flags = [CC1Option, FC1Option, NoDriverOption] //===----------------------------------------------------------------------===// // SYCL Options diff --git a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h index 57f2c413545c0..8b3721a4d7adb 100644 --- a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h +++ b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h @@ -179,7 +179,7 @@ bool ExtractAPIVisitorBase::VisitFunctionDecl( return true; // Skip methods in records. - for (auto P : Context.getParents(*Method)) { + for (const auto &P : Context.getParents(*Method)) { if (P.template get()) return true; } diff --git a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h index 55c7bb32054bc..6639082bbf332 100644 --- a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h +++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h @@ -67,7 +67,7 @@ class SymbolGraphSerializer : public APISerializer { /// /// \returns an optional JSON Object representing the payload that libclang /// expects for providing symbol information for a single symbol. If this is - /// not a known symbol returns \c None. + /// not a known symbol returns \c std::nullopt. static std::optional serializeSingleSymbolSGF(StringRef USR, const APISet &API); diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 980e6297fd6ac..40f1ae0b49d46 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -944,6 +944,39 @@ struct FormatStyle { /// \version 12 BitFieldColonSpacingStyle BitFieldColonSpacing; + /// The number of columns to use to indent the contents of braced init lists. + /// If unset, ``ContinuationIndentWidth`` is used. + /// \code + /// AlignAfterOpenBracket: AlwaysBreak + /// BracedInitializerIndentWidth: 2 + /// + /// void f() { + /// SomeClass c{ + /// "foo", + /// "bar", + /// "baz", + /// }; + /// auto s = SomeStruct{ + /// .foo = "foo", + /// .bar = "bar", + /// .baz = "baz", + /// }; + /// SomeArrayT a[3] = { + /// { + /// foo, + /// bar, + /// }, + /// { + /// foo, + /// bar, + /// }, + /// SomeArrayT{}, + /// }; + /// } + /// \endcode + /// \version 17 + std::optional BracedInitializerIndentWidth; + /// Different ways to wrap braces after control statements. enum BraceWrappingAfterControlStatementStyle : int8_t { /// Never wrap braces after a control statement. @@ -4286,6 +4319,7 @@ struct FormatStyle { BinPackArguments == R.BinPackArguments && BinPackParameters == R.BinPackParameters && BitFieldColonSpacing == R.BitFieldColonSpacing && + BracedInitializerIndentWidth == R.BracedInitializerIndentWidth && BreakAfterAttributes == R.BreakAfterAttributes && BreakAfterJavaFieldAnnotations == R.BreakAfterJavaFieldAnnotations && BreakArrays == R.BreakArrays && diff --git a/clang/include/clang/Frontend/DependencyOutputOptions.h b/clang/include/clang/Frontend/DependencyOutputOptions.h index e0f445bb5970c..e140ff9baa117 100644 --- a/clang/include/clang/Frontend/DependencyOutputOptions.h +++ b/clang/include/clang/Frontend/DependencyOutputOptions.h @@ -34,6 +34,8 @@ enum ExtraDepKind { class DependencyOutputOptions { public: unsigned IncludeSystemHeaders : 1; ///< Include system header dependencies. + unsigned + CanonicalSystemHeaders : 1; ///< canonicalize system header dependencies. unsigned ShowHeaderIncludes : 1; ///< Show header inclusions (-H). unsigned UsePhonyTargets : 1; ///< Include phony targets for each /// dependency, which can avoid some 'make' @@ -85,10 +87,11 @@ class DependencyOutputOptions { public: DependencyOutputOptions() - : IncludeSystemHeaders(0), ShowHeaderIncludes(0), UsePhonyTargets(0), - AddMissingHeaderDeps(0), IncludeModuleFiles(0), - ShowSkippedHeaderIncludes(0), HeaderIncludeFormat(HIFMT_Textual), - HeaderIncludeFiltering(HIFIL_None) {} + : IncludeSystemHeaders(0), CanonicalSystemHeaders(0), + ShowHeaderIncludes(0), UsePhonyTargets(0), AddMissingHeaderDeps(0), + IncludeModuleFiles(0), ShowSkippedHeaderIncludes(0), + HeaderIncludeFormat(HIFMT_Textual), HeaderIncludeFiltering(HIFIL_None) { + } }; } // end namespace clang diff --git a/clang/include/clang/Frontend/Utils.h b/clang/include/clang/Frontend/Utils.h index 143cf4359f00b..8300e45d15fe5 100644 --- a/clang/include/clang/Frontend/Utils.h +++ b/clang/include/clang/Frontend/Utils.h @@ -41,6 +41,7 @@ class ExternalSemaSource; class FrontendOptions; class PCHContainerReader; class Preprocessor; +class FileManager; class PreprocessorOptions; class PreprocessorOutputOptions; @@ -79,11 +80,14 @@ class DependencyCollector { /// Return true if system files should be passed to sawDependency(). virtual bool needSystemDependencies() { return false; } + /// Return true if system files should be canonicalized. + virtual bool shouldCanonicalizeSystemDependencies() { return false; } + /// Add a dependency \p Filename if it has not been seen before and /// sawDependency() returns true. virtual void maybeAddDependency(StringRef Filename, bool FromModule, bool IsSystem, bool IsModuleFile, - bool IsMissing); + FileManager *FileMgr, bool IsMissing); protected: /// Return true if the filename was added to the list of dependencies, false @@ -112,6 +116,10 @@ class DependencyFileGenerator : public DependencyCollector { bool sawDependency(StringRef Filename, bool FromModule, bool IsSystem, bool IsModuleFile, bool IsMissing) final; + bool shouldCanonicalizeSystemDependencies() override { + return CanonicalSystemHeaders; + } + protected: void outputDependencyFile(llvm::raw_ostream &OS); @@ -121,6 +129,7 @@ class DependencyFileGenerator : public DependencyCollector { std::string OutputFile; std::vector Targets; bool IncludeSystemHeaders; + bool CanonicalSystemHeaders; bool PhonyTarget; bool AddMissingHeaderDeps; bool SeenMissingHeader; diff --git a/clang/include/clang/Lex/DependencyDirectivesScanner.h b/clang/include/clang/Lex/DependencyDirectivesScanner.h index 529b93aa0ffbf..0e115906fbfe5 100644 --- a/clang/include/clang/Lex/DependencyDirectivesScanner.h +++ b/clang/include/clang/Lex/DependencyDirectivesScanner.h @@ -68,6 +68,7 @@ enum DirectiveKind : uint8_t { pp_pragma_push_macro, pp_pragma_pop_macro, pp_pragma_include_alias, + pp_pragma_system_header, pp_include_next, pp_if, pp_ifdef, diff --git a/clang/include/clang/Lex/Lexer.h b/clang/include/clang/Lex/Lexer.h index 8c2923b0150a0..98d34b783f084 100644 --- a/clang/include/clang/Lex/Lexer.h +++ b/clang/include/clang/Lex/Lexer.h @@ -551,7 +551,7 @@ class Lexer : public PreprocessorLexer { /// Finds the token that comes right after the given location. /// - /// Returns the next token, or none if the location is inside a macro. + /// Returns the next token, or std::nullopt if the location is inside a macro. static std::optional findNextToken(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts); diff --git a/clang/include/clang/Lex/LiteralSupport.h b/clang/include/clang/Lex/LiteralSupport.h index fd237c2c9cd87..313af292af372 100644 --- a/clang/include/clang/Lex/LiteralSupport.h +++ b/clang/include/clang/Lex/LiteralSupport.h @@ -63,7 +63,7 @@ class NumericLiteralParser { bool isUnsigned : 1; bool isLong : 1; // This is *not* set for long long. bool isLongLong : 1; - bool isSizeT : 1; // 1z, 1uz (C++2b) + bool isSizeT : 1; // 1z, 1uz (C++23) bool isHalf : 1; // 1.0h bool isFloat : 1; // 1.0f bool isImaginary : 1; // 1.0i diff --git a/clang/include/clang/Lex/MultipleIncludeOpt.h b/clang/include/clang/Lex/MultipleIncludeOpt.h index 7ceb7e53c75d7..8e570226c4b27 100644 --- a/clang/include/clang/Lex/MultipleIncludeOpt.h +++ b/clang/include/clang/Lex/MultipleIncludeOpt.h @@ -108,6 +108,12 @@ class MultipleIncludeOpt { ImmediatelyAfterTopLevelIfndef = false; } + /// SetReadToken - Set whether the value of 'ReadAnyTokens'. Called to + /// override when encountering tokens outside of the include guard that have + /// no effect if the file in question is is included multiple times (e.g. the + /// null directive). + void SetReadToken(bool Value) { ReadAnyTokens = Value; } + /// ExpandedMacro - When a macro is expanded with this lexer as the current /// buffer, this method is called to disable the MIOpt if needed. void ExpandedMacro() { DidMacroExpansion = true; } diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index a34a451f4bad1..8bdaf25e9b870 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -729,7 +729,7 @@ class Preprocessor { /// Only one of CurLexer, or CurTokenLexer will be non-null. std::unique_ptr CurLexer; - /// The current top of the stack what we're lexing from + /// The current top of the stack that we're lexing from /// if not expanding a macro. /// /// This is an alias for CurLexer. diff --git a/clang/include/clang/Sema/EnterExpressionEvaluationContext.h b/clang/include/clang/Sema/EnterExpressionEvaluationContext.h new file mode 100644 index 0000000000000..5eca797b8842b --- /dev/null +++ b/clang/include/clang/Sema/EnterExpressionEvaluationContext.h @@ -0,0 +1,69 @@ +//===--- EnterExpressionEvaluationContext.h ---------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SEMA_ENTEREXPRESSIONEVALUATIONCONTEXT_H +#define LLVM_CLANG_SEMA_ENTEREXPRESSIONEVALUATIONCONTEXT_H + +#include "clang/Sema/Sema.h" + +namespace clang { + +class Decl; + +/// RAII object that enters a new expression evaluation context. +class EnterExpressionEvaluationContext { + Sema &Actions; + bool Entered = true; + +public: + EnterExpressionEvaluationContext( + Sema &Actions, Sema::ExpressionEvaluationContext NewContext, + Decl *LambdaContextDecl = nullptr, + Sema::ExpressionEvaluationContextRecord::ExpressionKind ExprContext = + Sema::ExpressionEvaluationContextRecord::EK_Other, + bool ShouldEnter = true) + : Actions(Actions), Entered(ShouldEnter) { + if (Entered) + Actions.PushExpressionEvaluationContext(NewContext, LambdaContextDecl, + ExprContext); + } + EnterExpressionEvaluationContext( + Sema &Actions, Sema::ExpressionEvaluationContext NewContext, + Sema::ReuseLambdaContextDecl_t, + Sema::ExpressionEvaluationContextRecord::ExpressionKind ExprContext = + Sema::ExpressionEvaluationContextRecord::EK_Other) + : Actions(Actions) { + Actions.PushExpressionEvaluationContext( + NewContext, Sema::ReuseLambdaContextDecl, ExprContext); + } + + enum InitListTag { InitList }; + EnterExpressionEvaluationContext(Sema &Actions, InitListTag, + bool ShouldEnter = true) + : Actions(Actions), Entered(false) { + // In C++11 onwards, narrowing checks are performed on the contents of + // braced-init-lists, even when they occur within unevaluated operands. + // Therefore we still need to instantiate constexpr functions used in such + // a context. + if (ShouldEnter && Actions.isUnevaluatedContext() && + Actions.getLangOpts().CPlusPlus11) { + Actions.PushExpressionEvaluationContext( + Sema::ExpressionEvaluationContext::UnevaluatedList); + Entered = true; + } + } + + ~EnterExpressionEvaluationContext() { + if (Entered) + Actions.PopExpressionEvaluationContext(); + } +}; + +} // namespace clang + +#endif diff --git a/clang/include/clang/Sema/Initialization.h b/clang/include/clang/Sema/Initialization.h index fcfb56f9731ef..2072cd8d1c3ef 100644 --- a/clang/include/clang/Sema/Initialization.h +++ b/clang/include/clang/Sema/Initialization.h @@ -123,6 +123,10 @@ class alignas(8) InitializedEntity { /// decomposition declaration. EK_Binding, + /// The entity being initialized is a non-static data member subobject of an + /// object initialized via parenthesized aggregate initialization. + EK_ParenAggInitMember, + // Note: err_init_conversion_failed in DiagnosticSemaKinds.td uses this // enum as an index for its first %select. When modifying this list, // that diagnostic text needs to be updated as well. @@ -227,8 +231,10 @@ class alignas(8) InitializedEntity { /// Create the initialization entity for a member subobject. InitializedEntity(FieldDecl *Member, const InitializedEntity *Parent, - bool Implicit, bool DefaultMemberInit) - : Kind(EK_Member), Parent(Parent), Type(Member->getType()), + bool Implicit, bool DefaultMemberInit, + bool IsParenAggInit = false) + : Kind(IsParenAggInit ? EK_ParenAggInitMember : EK_Member), + Parent(Parent), Type(Member->getType()), Variable{Member, Implicit, DefaultMemberInit} {} /// Create the initialization entity for an array element. @@ -388,6 +394,14 @@ class alignas(8) InitializedEntity { return InitializedEntity(Member->getAnonField(), Parent, Implicit, false); } + /// Create the initialization entity for a member subobject initialized via + /// parenthesized aggregate init. + static InitializedEntity InitializeMemberFromParenAggInit(FieldDecl *Member) { + return InitializedEntity(Member, /*Parent=*/nullptr, /*Implicit=*/false, + /*DefaultMemberInit=*/false, + /*IsParenAggInit=*/true); + } + /// Create the initialization entity for a default member initializer. static InitializedEntity InitializeMemberFromDefaultMemberInitializer(FieldDecl *Member) { diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h index 1827ea5d1e546..a97968dc7b209 100644 --- a/clang/include/clang/Sema/Overload.h +++ b/clang/include/clang/Sema/Overload.h @@ -162,6 +162,9 @@ class Sema; /// Arm SVE Vector conversions ICK_SVE_Vector_Conversion, + /// RISC-V RVV Vector conversions + ICK_RVV_Vector_Conversion, + /// A vector splat from an arithmetic type ICK_Vector_Splat, diff --git a/clang/include/clang/Sema/RISCVIntrinsicManager.h b/clang/include/clang/Sema/RISCVIntrinsicManager.h index 128858bb43019..731bc3ae1c9d2 100644 --- a/clang/include/clang/Sema/RISCVIntrinsicManager.h +++ b/clang/include/clang/Sema/RISCVIntrinsicManager.h @@ -14,6 +14,8 @@ #ifndef LLVM_CLANG_SEMA_RISCVINTRINSICMANAGER_H #define LLVM_CLANG_SEMA_RISCVINTRINSICMANAGER_H +#include + namespace clang { class LookupResult; class IdentifierInfo; @@ -22,6 +24,8 @@ class Preprocessor; namespace sema { class RISCVIntrinsicManager { public: + enum class IntrinsicKind : uint8_t { RVV, SIFIVE_VECTOR }; + virtual ~RISCVIntrinsicManager() = default; // Create RISC-V intrinsic and insert into symbol table and return true if diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 0ab5dd9e8bb64..92f3e6f0ff848 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -946,10 +946,10 @@ class Sema final { class DelayedDiagnostics { /// The current pool of diagnostics into which delayed /// diagnostics should go. - sema::DelayedDiagnosticPool *CurPool; + sema::DelayedDiagnosticPool *CurPool = nullptr; public: - DelayedDiagnostics() : CurPool(nullptr) {} + DelayedDiagnostics() = default; /// Adds a delayed diagnostic. void add(const sema::DelayedDiagnostic &diag); // in DelayedDiagnostic.h @@ -1370,7 +1370,7 @@ class Sema final { return Context == ExpressionEvaluationContext::ImmediateFunctionContext || (Context == ExpressionEvaluationContext::DiscardedStatement && InImmediateFunctionContext) || - // C++2b [expr.const]p14: + // C++23 [expr.const]p14: // An expression or conversion is in an immediate function // context if it is potentially evaluated and either: // * its innermost enclosing non-block scope is a function @@ -1621,6 +1621,9 @@ class Sema final { /// Indicate RISC-V vector builtin functions enabled or not. bool DeclareRISCVVBuiltins = false; + /// Indicate RISC-V Sifive vector builtin functions enabled or not. + bool DeclareRISCVVectorBuiltins = false; + private: std::unique_ptr RVIntrinsicManager; @@ -2588,13 +2591,11 @@ class Sema final { // struct SkipBodyInfo { - SkipBodyInfo() - : ShouldSkip(false), CheckSameAsPrevious(false), Previous(nullptr), - New(nullptr) {} - bool ShouldSkip; - bool CheckSameAsPrevious; - NamedDecl *Previous; - NamedDecl *New; + SkipBodyInfo() = default; + bool ShouldSkip = false; + bool CheckSameAsPrevious = false; + NamedDecl *Previous = nullptr; + NamedDecl *New = nullptr; }; DeclGroupPtrTy ConvertDeclToDeclGroup(Decl *Ptr, Decl *OwnedType = nullptr); @@ -12685,6 +12686,7 @@ class Sema final { SourceLocation Loc, bool IsCompAssign); bool isValidSveBitcast(QualType srcType, QualType destType); + bool isValidRVVBitcast(QualType srcType, QualType destType); bool areMatrixTypesOfTheSameDimension(QualType srcTy, QualType destTy); @@ -13971,55 +13973,6 @@ class Sema final { ValueDecl *DeclToCheck); }; -/// RAII object that enters a new expression evaluation context. -class EnterExpressionEvaluationContext { - Sema &Actions; - bool Entered = true; - -public: - EnterExpressionEvaluationContext( - Sema &Actions, Sema::ExpressionEvaluationContext NewContext, - Decl *LambdaContextDecl = nullptr, - Sema::ExpressionEvaluationContextRecord::ExpressionKind ExprContext = - Sema::ExpressionEvaluationContextRecord::EK_Other, - bool ShouldEnter = true) - : Actions(Actions), Entered(ShouldEnter) { - if (Entered) - Actions.PushExpressionEvaluationContext(NewContext, LambdaContextDecl, - ExprContext); - } - EnterExpressionEvaluationContext( - Sema &Actions, Sema::ExpressionEvaluationContext NewContext, - Sema::ReuseLambdaContextDecl_t, - Sema::ExpressionEvaluationContextRecord::ExpressionKind ExprContext = - Sema::ExpressionEvaluationContextRecord::EK_Other) - : Actions(Actions) { - Actions.PushExpressionEvaluationContext( - NewContext, Sema::ReuseLambdaContextDecl, ExprContext); - } - - enum InitListTag { InitList }; - EnterExpressionEvaluationContext(Sema &Actions, InitListTag, - bool ShouldEnter = true) - : Actions(Actions), Entered(false) { - // In C++11 onwards, narrowing checks are performed on the contents of - // braced-init-lists, even when they occur within unevaluated operands. - // Therefore we still need to instantiate constexpr functions used in such - // a context. - if (ShouldEnter && Actions.isUnevaluatedContext() && - Actions.getLangOpts().CPlusPlus11) { - Actions.PushExpressionEvaluationContext( - Sema::ExpressionEvaluationContext::UnevaluatedList); - Entered = true; - } - } - - ~EnterExpressionEvaluationContext() { - if (Entered) - Actions.PopExpressionEvaluationContext(); - } -}; - DeductionFailureInfo MakeDeductionFailureInfo(ASTContext &Context, Sema::TemplateDeductionResult TDK, sema::TemplateDeductionInfo &Info); diff --git a/clang/include/clang/Serialization/PCHContainerOperations.h b/clang/include/clang/Serialization/PCHContainerOperations.h index 9f9700a418a9c..be10feb5e351c 100644 --- a/clang/include/clang/Serialization/PCHContainerOperations.h +++ b/clang/include/clang/Serialization/PCHContainerOperations.h @@ -56,7 +56,7 @@ class PCHContainerReader { public: virtual ~PCHContainerReader() = 0; /// Equivalent to the format passed to -fmodule-format= - virtual llvm::StringRef getFormat() const = 0; + virtual llvm::ArrayRef getFormats() const = 0; /// Returns the serialized AST inside the PCH container Buffer. virtual llvm::StringRef ExtractPCH(llvm::MemoryBufferRef Buffer) const = 0; @@ -78,8 +78,7 @@ class RawPCHContainerWriter : public PCHContainerWriter { /// Implements read operations for a raw pass-through PCH container. class RawPCHContainerReader : public PCHContainerReader { - llvm::StringRef getFormat() const override { return "raw"; } - + llvm::ArrayRef getFormats() const override; /// Simply returns the buffer contained in Buffer. llvm::StringRef ExtractPCH(llvm::MemoryBufferRef Buffer) const override; }; @@ -87,7 +86,9 @@ class RawPCHContainerReader : public PCHContainerReader { /// A registry of PCHContainerWriter and -Reader objects for different formats. class PCHContainerOperations { llvm::StringMap> Writers; - llvm::StringMap> Readers; + llvm::StringMap Readers; + llvm::SmallVector> OwnedReaders; + public: /// Automatically registers a RawPCHContainerWriter and /// RawPCHContainerReader. @@ -96,13 +97,17 @@ class PCHContainerOperations { Writers[Writer->getFormat()] = std::move(Writer); } void registerReader(std::unique_ptr Reader) { - Readers[Reader->getFormat()] = std::move(Reader); + assert(!Reader->getFormats().empty() && + "PCHContainerReader must handle >=1 format"); + for (llvm::StringRef Fmt : Reader->getFormats()) + Readers[Fmt] = Reader.get(); + OwnedReaders.push_back(std::move(Reader)); } const PCHContainerWriter *getWriterOrNull(llvm::StringRef Format) { return Writers[Format].get(); } const PCHContainerReader *getReaderOrNull(llvm::StringRef Format) { - return Readers[Format].get(); + return Readers[Format]; } const PCHContainerReader &getRawReader() { return *getReaderOrNull("raw"); diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Taint.h b/clang/include/clang/StaticAnalyzer/Checkers/Taint.h index df863a2495413..3ec8dbfb09ee3 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Taint.h +++ b/clang/include/clang/StaticAnalyzer/Checkers/Taint.h @@ -79,26 +79,48 @@ bool isTainted(ProgramStateRef State, SymbolRef Sym, bool isTainted(ProgramStateRef State, const MemRegion *Reg, TaintTagType Kind = TaintTagGeneric); +/// Returns the tainted Symbols for a given Statement and state. +std::vector getTaintedSymbols(ProgramStateRef State, const Stmt *S, + const LocationContext *LCtx, + TaintTagType Kind = TaintTagGeneric); + +/// Returns the tainted Symbols for a given SVal and state. +std::vector getTaintedSymbols(ProgramStateRef State, SVal V, + TaintTagType Kind = TaintTagGeneric); + +/// Returns the tainted Symbols for a SymbolRef and state. +std::vector getTaintedSymbols(ProgramStateRef State, SymbolRef Sym, + TaintTagType Kind = TaintTagGeneric); + +/// Returns the tainted (index, super/sub region, symbolic region) symbols +/// for a given memory region. +std::vector getTaintedSymbols(ProgramStateRef State, + const MemRegion *Reg, + TaintTagType Kind = TaintTagGeneric); + +std::vector getTaintedSymbolsImpl(ProgramStateRef State, + const Stmt *S, + const LocationContext *LCtx, + TaintTagType Kind, + bool returnFirstOnly); + +std::vector getTaintedSymbolsImpl(ProgramStateRef State, SVal V, + TaintTagType Kind, + bool returnFirstOnly); + +std::vector getTaintedSymbolsImpl(ProgramStateRef State, + SymbolRef Sym, TaintTagType Kind, + bool returnFirstOnly); + +std::vector getTaintedSymbolsImpl(ProgramStateRef State, + const MemRegion *Reg, + TaintTagType Kind, + bool returnFirstOnly); + void printTaint(ProgramStateRef State, raw_ostream &Out, const char *nl = "\n", const char *sep = ""); LLVM_DUMP_METHOD void dumpTaint(ProgramStateRef State); - -/// The bug visitor prints a diagnostic message at the location where a given -/// variable was tainted. -class TaintBugVisitor final : public BugReporterVisitor { -private: - const SVal V; - -public: - TaintBugVisitor(const SVal V) : V(V) {} - void Profile(llvm::FoldingSetNodeID &ID) const override { ID.Add(V); } - - PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - PathSensitiveBugReport &BR) override; -}; - } // namespace taint } // namespace ento } // namespace clang diff --git a/clang/include/clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h b/clang/include/clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h index 392bc484bf62c..5d2c96e5bc9de 100644 --- a/clang/include/clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h +++ b/clang/include/clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h @@ -22,6 +22,7 @@ extern const char *const CXXObjectLifecycle; extern const char *const CXXMoveSemantics; extern const char *const SecurityError; extern const char *const UnusedCode; +extern const char *const TaintedData; } // namespace categories } // namespace ento } // namespace clang diff --git a/clang/include/clang/module.modulemap b/clang/include/clang/module.modulemap deleted file mode 100644 index 624045dc4c57b..0000000000000 --- a/clang/include/clang/module.modulemap +++ /dev/null @@ -1,199 +0,0 @@ -module Clang_Analysis { - requires cplusplus - umbrella "Analysis" - - textual header "Analysis/Analyses/ThreadSafetyOps.def" - - module * { export * } - - // FIXME: Exclude these headers to avoid pulling all of the AST matchers - // library into clang. Due to inline key functions in the headers, - // importing the AST matchers library gives a link dependency on the AST - // matchers (and thus the AST), which clang-format should not have. - exclude header "Analysis/Analyses/ExprMutationAnalyzer.h" -} - -module Clang_AST { - requires cplusplus - umbrella "AST" - - textual header "AST/BuiltinTypes.def" - textual header "AST/CXXRecordDeclDefinitionBits.def" - textual header "AST/OperationKinds.def" - textual header "AST/TypeLocNodes.def" - - module * { export * } -} - -module Clang_ASTMatchers { requires cplusplus umbrella "ASTMatchers" module * { export * } } - -module Clang_Basic { - requires cplusplus - umbrella "Basic" - - textual header "Basic/AArch64SVEACLETypes.def" - textual header "Basic/BuiltinsAArch64.def" - textual header "Basic/BuiltinsAMDGPU.def" - textual header "Basic/BuiltinsAArch64NeonSVEBridge.def" - textual header "Basic/BuiltinsAArch64NeonSVEBridge_cg.def" - textual header "Basic/BuiltinsARM.def" - textual header "Basic/BuiltinsBPF.def" - textual header "Basic/Builtins.def" - textual header "Basic/BuiltinHeaders.def" - textual header "Basic/BuiltinsHexagon.def" - textual header "Basic/BuiltinsHexagonDep.def" - textual header "Basic/BuiltinsHexagonMapCustomDep.def" - textual header "Basic/BuiltinsLoongArch.def" - textual header "Basic/BuiltinsMips.def" - textual header "Basic/BuiltinsNEON.def" - textual header "Basic/BuiltinsNVPTX.def" - textual header "Basic/BuiltinsPPC.def" - textual header "Basic/BuiltinsRISCV.def" - textual header "Basic/BuiltinsRISCVVector.def" - textual header "Basic/BuiltinsSVE.def" - textual header "Basic/BuiltinsSystemZ.def" - textual header "Basic/BuiltinsVE.def" - textual header "Basic/BuiltinsVEVL.gen.def" - textual header "Basic/BuiltinsWebAssembly.def" - textual header "Basic/BuiltinsX86.def" - textual header "Basic/BuiltinsX86_64.def" - textual header "Basic/BuiltinsXCore.def" - textual header "Basic/CodeGenOptions.def" - textual header "Basic/DiagnosticOptions.def" - textual header "Basic/Features.def" - textual header "Basic/FPOptions.def" - textual header "Basic/MSP430Target.def" - textual header "Basic/LangOptions.def" - textual header "Basic/OpenCLExtensions.def" - textual header "Basic/OpenCLImageTypes.def" - textual header "Basic/OpenCLExtensionTypes.def" - textual header "Basic/OpenMPKinds.def" - textual header "Basic/OperatorKinds.def" - textual header "Basic/PPCTypes.def" - textual header "Basic/RISCVVTypes.def" - textual header "Basic/Sanitizers.def" - textual header "Basic/TargetCXXABI.def" - textual header "Basic/TransformTypeTraits.def" - textual header "Basic/TokenKinds.def" - textual header "Basic/WebAssemblyReferenceTypes.def" - - module * { export * } -} -module Clang_Basic_TokenKinds { - requires cplusplus - - header "Basic/TokenKinds.h" - textual header "Basic/TokenKinds.def" - - export * -} - -module Clang_CodeGen { requires cplusplus umbrella "CodeGen" module * { export * } } -module Clang_Config { requires cplusplus umbrella "Config" module * { export * } } - -// Files for diagnostic groups are spread all over the include/clang/ tree, but -// logically form a single module. -module Clang_Diagnostics { - requires cplusplus - - module All { header "Basic/AllDiagnostics.h" export * } - module Analysis { textual header "Analysis/Analyses/UnsafeBufferUsageGadgets.def" } - module AST { header "AST/ASTDiagnostic.h" export * } - module Comment { header "AST/CommentDiagnostic.h" export * } - module Driver { header "Driver/DriverDiagnostic.h" export * } - module Frontend { header "Frontend/FrontendDiagnostic.h" export * } - module Lex { header "Lex/LexDiagnostic.h" export * } - module Parse { header "Parse/ParseDiagnostic.h" export * } - module Sema { header "Sema/SemaDiagnostic.h" export * } - module Serialization { header "Serialization/SerializationDiagnostic.h" export * } - module Refactoring { header "Tooling/Refactoring/RefactoringDiagnostic.h" export * } -} - -module Clang_Driver { - requires cplusplus - umbrella "Driver" - - textual header "Driver/Types.def" - - module * { export * } -} - -module Clang_Edit { requires cplusplus umbrella "Edit" module * { export * } } -module Clang_Format { requires cplusplus umbrella "Format" module * { export * } } - -module Clang_Frontend { - requires cplusplus - umbrella "Frontend" - - textual header "Basic/LangStandards.def" - - module * { export * } -} - -module Clang_FrontendTool { requires cplusplus umbrella "FrontendTool" module * { export * } } -module Clang_Index { requires cplusplus umbrella "Index" module * { export * } } -module Clang_Lex { requires cplusplus umbrella "Lex" module * { export * } } -module Clang_Parse { requires cplusplus umbrella "Parse" module * { export * } } -module Clang_Rewrite { requires cplusplus umbrella "Rewrite/Core" module * { export * } } -module Clang_RewriteFrontend { requires cplusplus umbrella "Rewrite/Frontend" module * { export * } } -module Clang_Sema { requires cplusplus umbrella "Sema" module * { export * } } - -module Clang_Serialization { - requires cplusplus - umbrella "Serialization" - - textual header "Serialization/TypeBitCodes.def" - - module * { export * } -} - -module Clang_StaticAnalyzer_Core { - requires cplusplus - umbrella "StaticAnalyzer/Core" - - textual header "StaticAnalyzer/Core/Analyses.def" - textual header "StaticAnalyzer/Core/AnalyzerOptions.def" - textual header "StaticAnalyzer/Core/PathSensitive/SVals.def" - textual header "StaticAnalyzer/Core/PathSensitive/Symbols.def" - textual header "StaticAnalyzer/Core/PathSensitive/Regions.def" - - module * { export * } -} - -module Clang_StaticAnalyzer_Checkers { - requires cplusplus - umbrella "StaticAnalyzer/Checkers" - module * { export * } -} - -module Clang_StaticAnalyzer_Frontend { - requires cplusplus - umbrella "StaticAnalyzer/Frontend" - module * { export * } -} - -module Clang_Testing { - requires cplusplus - umbrella "Testing" - module * { export * } -} - -module Clang_Tooling { - requires cplusplus umbrella "Tooling" module * { export * } - // FIXME: Exclude these headers to avoid pulling all of the AST matchers - // library into clang-format. Due to inline key functions in the headers, - // importing the AST matchers library gives a link dependency on the AST - // matchers (and thus the AST), which clang-format should not have. - exclude header "Tooling/RefactoringCallbacks.h" -} - -module Clang_ToolingCore { - requires cplusplus - umbrella "Tooling/Core" module * { export * } -} - -module Clang_ToolingInclusions { - requires cplusplus - umbrella "Tooling/Inclusions" - module * { export * } -} diff --git a/clang/include/module.modulemap b/clang/include/module.modulemap new file mode 100644 index 0000000000000..9fea3620100f4 --- /dev/null +++ b/clang/include/module.modulemap @@ -0,0 +1,204 @@ +module Clang_C { + umbrella "clang-c" + module * { export * } +} + +module Clang_Analysis { + requires cplusplus + umbrella "clang/Analysis" + + textual header "clang/Analysis/Analyses/ThreadSafetyOps.def" + + module * { export * } + + // FIXME: Exclude these headers to avoid pulling all of the AST matchers + // library into clang. Due to inline key functions in the headers, + // importing the AST matchers library gives a link dependency on the AST + // matchers (and thus the AST), which clang-format should not have. + exclude header "clang/Analysis/Analyses/ExprMutationAnalyzer.h" +} + +module Clang_AST { + requires cplusplus + umbrella "clang/AST" + + textual header "clang/AST/BuiltinTypes.def" + textual header "clang/AST/CXXRecordDeclDefinitionBits.def" + textual header "clang/AST/OperationKinds.def" + textual header "clang/AST/TypeLocNodes.def" + + module * { export * } +} + +module Clang_ASTMatchers { requires cplusplus umbrella "clang/ASTMatchers" module * { export * } } + +module Clang_Basic { + requires cplusplus + umbrella "clang/Basic" + + textual header "clang/Basic/AArch64SVEACLETypes.def" + textual header "clang/Basic/BuiltinsAArch64.def" + textual header "clang/Basic/BuiltinsAMDGPU.def" + textual header "clang/Basic/BuiltinsAArch64NeonSVEBridge.def" + textual header "clang/Basic/BuiltinsAArch64NeonSVEBridge_cg.def" + textual header "clang/Basic/BuiltinsARM.def" + textual header "clang/Basic/BuiltinsBPF.def" + textual header "clang/Basic/Builtins.def" + textual header "clang/Basic/BuiltinHeaders.def" + textual header "clang/Basic/BuiltinsHexagon.def" + textual header "clang/Basic/BuiltinsHexagonDep.def" + textual header "clang/Basic/BuiltinsHexagonMapCustomDep.def" + textual header "clang/Basic/BuiltinsLoongArch.def" + textual header "clang/Basic/BuiltinsMips.def" + textual header "clang/Basic/BuiltinsNEON.def" + textual header "clang/Basic/BuiltinsNVPTX.def" + textual header "clang/Basic/BuiltinsPPC.def" + textual header "clang/Basic/BuiltinsRISCV.def" + textual header "clang/Basic/BuiltinsRISCVVector.def" + textual header "clang/Basic/BuiltinsSVE.def" + textual header "clang/Basic/BuiltinsSystemZ.def" + textual header "clang/Basic/BuiltinsVE.def" + textual header "clang/Basic/BuiltinsVEVL.gen.def" + textual header "clang/Basic/BuiltinsWebAssembly.def" + textual header "clang/Basic/BuiltinsX86.def" + textual header "clang/Basic/BuiltinsX86_64.def" + textual header "clang/Basic/BuiltinsXCore.def" + textual header "clang/Basic/CodeGenOptions.def" + textual header "clang/Basic/DiagnosticOptions.def" + textual header "clang/Basic/Features.def" + textual header "clang/Basic/FPOptions.def" + textual header "clang/Basic/MSP430Target.def" + textual header "clang/Basic/LangOptions.def" + textual header "clang/Basic/OpenCLExtensions.def" + textual header "clang/Basic/OpenCLImageTypes.def" + textual header "clang/Basic/OpenCLExtensionTypes.def" + textual header "clang/Basic/OpenMPKinds.def" + textual header "clang/Basic/OperatorKinds.def" + textual header "clang/Basic/PPCTypes.def" + textual header "clang/Basic/RISCVVTypes.def" + textual header "clang/Basic/Sanitizers.def" + textual header "clang/Basic/TargetCXXABI.def" + textual header "clang/Basic/TransformTypeTraits.def" + textual header "clang/Basic/TokenKinds.def" + textual header "clang/Basic/WebAssemblyReferenceTypes.def" + + module * { export * } +} +module Clang_Basic_TokenKinds { + requires cplusplus + + header "clang/Basic/TokenKinds.h" + textual header "clang/Basic/TokenKinds.def" + + export * +} + +module Clang_CodeGen { requires cplusplus umbrella "clang/CodeGen" module * { export * } } +module Clang_Config { requires cplusplus umbrella "clang/Config" module * { export * } } + +// Files for diagnostic groups are spread all over the include/clang/ tree, but +// logically form a single module. +module Clang_Diagnostics { + requires cplusplus + + module All { header "clang/Basic/AllDiagnostics.h" export * } + module Analysis { textual header "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" } + module AST { header "clang/AST/ASTDiagnostic.h" export * } + module Comment { header "clang/AST/CommentDiagnostic.h" export * } + module Driver { header "clang/Driver/DriverDiagnostic.h" export * } + module Frontend { header "clang/Frontend/FrontendDiagnostic.h" export * } + module Lex { header "clang/Lex/LexDiagnostic.h" export * } + module Parse { header "clang/Parse/ParseDiagnostic.h" export * } + module Sema { header "clang/Sema/SemaDiagnostic.h" export * } + module Serialization { header "clang/Serialization/SerializationDiagnostic.h" export * } + module Refactoring { header "clang/Tooling/Refactoring/RefactoringDiagnostic.h" export * } +} + +module Clang_Driver { + requires cplusplus + umbrella "clang/Driver" + + textual header "clang/Driver/Types.def" + + module * { export * } +} + +module Clang_Edit { requires cplusplus umbrella "clang/Edit" module * { export * } } +module Clang_Format { requires cplusplus umbrella "clang/Format" module * { export * } } + +module Clang_Frontend { + requires cplusplus + umbrella "clang/Frontend" + + textual header "clang/Basic/LangStandards.def" + + module * { export * } +} + +module Clang_FrontendTool { requires cplusplus umbrella "clang/FrontendTool" module * { export * } } +module Clang_Index { requires cplusplus umbrella "clang/Index" module * { export * } } +module Clang_Lex { requires cplusplus umbrella "clang/Lex" module * { export * } } +module Clang_Parse { requires cplusplus umbrella "clang/Parse" module * { export * } } +module Clang_Rewrite { requires cplusplus umbrella "clang/Rewrite/Core" module * { export * } } +module Clang_RewriteFrontend { requires cplusplus umbrella "clang/Rewrite/Frontend" module * { export * } } +module Clang_Sema { requires cplusplus umbrella "clang/Sema" module * { export * } } + +module Clang_Serialization { + requires cplusplus + umbrella "clang/Serialization" + + textual header "clang/Serialization/TypeBitCodes.def" + + module * { export * } +} + +module Clang_StaticAnalyzer_Core { + requires cplusplus + umbrella "clang/StaticAnalyzer/Core" + + textual header "clang/StaticAnalyzer/Core/Analyses.def" + textual header "clang/StaticAnalyzer/Core/AnalyzerOptions.def" + textual header "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" + textual header "clang/StaticAnalyzer/Core/PathSensitive/Symbols.def" + textual header "clang/StaticAnalyzer/Core/PathSensitive/Regions.def" + + module * { export * } +} + +module Clang_StaticAnalyzer_Checkers { + requires cplusplus + umbrella "clang/StaticAnalyzer/Checkers" + module * { export * } +} + +module Clang_StaticAnalyzer_Frontend { + requires cplusplus + umbrella "clang/StaticAnalyzer/Frontend" + module * { export * } +} + +module Clang_Testing { + requires cplusplus + umbrella "clang/Testing" + module * { export * } +} + +module Clang_Tooling { + requires cplusplus umbrella "clang/Tooling" module * { export * } + // FIXME: Exclude these headers to avoid pulling all of the AST matchers + // library into clang-format. Due to inline key functions in the headers, + // importing the AST matchers library gives a link dependency on the AST + // matchers (and thus the AST), which clang-format should not have. + exclude header "clang/Tooling/RefactoringCallbacks.h" +} + +module Clang_ToolingCore { + requires cplusplus + umbrella "clang/Tooling/Core" module * { export * } +} + +module Clang_ToolingInclusions { + requires cplusplus + umbrella "clang/Tooling/Inclusions" + module * { export * } +} diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index e331df86235b2..3a72c3d257944 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -85,6 +85,7 @@ #include "llvm/Support/MD5.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/TargetParser/RISCVTargetParser.h" #include "llvm/TargetParser/Triple.h" #include #include @@ -2010,6 +2011,9 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { else if (VT->getVectorKind() == VectorType::SveFixedLengthPredicateVector) // Adjust the alignment for fixed-length SVE predicates. Align = 16; + else if (VT->getVectorKind() == VectorType::RVVFixedLengthDataVector) + // Adjust the alignment for fixed-length RVV vectors. + Align = 64; break; } @@ -9468,7 +9472,9 @@ bool ASTContext::areCompatibleVectorTypes(QualType FirstVec, First->getVectorKind() != VectorType::SveFixedLengthDataVector && First->getVectorKind() != VectorType::SveFixedLengthPredicateVector && Second->getVectorKind() != VectorType::SveFixedLengthDataVector && - Second->getVectorKind() != VectorType::SveFixedLengthPredicateVector) + Second->getVectorKind() != VectorType::SveFixedLengthPredicateVector && + First->getVectorKind() != VectorType::RVVFixedLengthDataVector && + Second->getVectorKind() != VectorType::RVVFixedLengthDataVector) return true; return false; @@ -9566,6 +9572,85 @@ bool ASTContext::areLaxCompatibleSveTypes(QualType FirstType, IsLaxCompatible(SecondType, FirstType); } +/// getRVVTypeSize - Return RVV vector register size. +static uint64_t getRVVTypeSize(ASTContext &Context, const BuiltinType *Ty) { + assert(Ty->isRVVVLSBuiltinType() && "Invalid RVV Type"); + auto VScale = Context.getTargetInfo().getVScaleRange(Context.getLangOpts()); + return VScale ? VScale->first * llvm::RISCV::RVVBitsPerBlock : 0; +} + +bool ASTContext::areCompatibleRVVTypes(QualType FirstType, + QualType SecondType) { + assert( + ((FirstType->isRVVSizelessBuiltinType() && SecondType->isVectorType()) || + (FirstType->isVectorType() && SecondType->isRVVSizelessBuiltinType())) && + "Expected RVV builtin type and vector type!"); + + auto IsValidCast = [this](QualType FirstType, QualType SecondType) { + if (const auto *BT = FirstType->getAs()) { + if (const auto *VT = SecondType->getAs()) { + // Predicates have the same representation as uint8 so we also have to + // check the kind to make these types incompatible. + if (VT->getVectorKind() == VectorType::RVVFixedLengthDataVector) + return FirstType->isRVVVLSBuiltinType() && + VT->getElementType().getCanonicalType() == + FirstType->getRVVEltType(*this); + if (VT->getVectorKind() == VectorType::GenericVector) + return getTypeSize(SecondType) == getRVVTypeSize(*this, BT) && + hasSameType(VT->getElementType(), + getBuiltinVectorTypeInfo(BT).ElementType); + } + } + return false; + }; + + return IsValidCast(FirstType, SecondType) || + IsValidCast(SecondType, FirstType); +} + +bool ASTContext::areLaxCompatibleRVVTypes(QualType FirstType, + QualType SecondType) { + assert( + ((FirstType->isRVVSizelessBuiltinType() && SecondType->isVectorType()) || + (FirstType->isVectorType() && SecondType->isRVVSizelessBuiltinType())) && + "Expected RVV builtin type and vector type!"); + + auto IsLaxCompatible = [this](QualType FirstType, QualType SecondType) { + const auto *BT = FirstType->getAs(); + if (!BT) + return false; + + const auto *VecTy = SecondType->getAs(); + if (VecTy && + (VecTy->getVectorKind() == VectorType::RVVFixedLengthDataVector || + VecTy->getVectorKind() == VectorType::GenericVector)) { + const LangOptions::LaxVectorConversionKind LVCKind = + getLangOpts().getLaxVectorConversions(); + + // If __riscv_v_fixed_vlen != N do not allow GNU vector lax conversion. + if (VecTy->getVectorKind() == VectorType::GenericVector && + getTypeSize(SecondType) != getRVVTypeSize(*this, BT)) + return false; + + // If -flax-vector-conversions=all is specified, the types are + // certainly compatible. + if (LVCKind == LangOptions::LaxVectorConversionKind::All) + return true; + + // If -flax-vector-conversions=integer is specified, the types are + // compatible if the elements are integer types. + if (LVCKind == LangOptions::LaxVectorConversionKind::Integer) + return VecTy->getElementType().getCanonicalType()->isIntegerType() && + FirstType->getRVVEltType(*this)->isIntegerType(); + } + + return false; + }; + + return IsLaxCompatible(FirstType, SecondType) || + IsLaxCompatible(SecondType, FirstType); +} + bool ASTContext::hasDirectOwnershipQualifier(QualType Ty) const { while (true) { // __strong id @@ -12982,8 +13067,10 @@ static QualType getCommonSugarTypeNode(ASTContext &Ctx, const Type *X, SmallVector As; if (CD && getCommonTemplateArguments(Ctx, As, AX->getTypeConstraintArguments(), - AY->getTypeConstraintArguments())) + AY->getTypeConstraintArguments())) { CD = nullptr; // The arguments differ, so make it unconstrained. + As.clear(); + } // Both auto types can't be dependent, otherwise they wouldn't have been // sugar. This implies they can't contain unexpanded packs either. diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp index c561f0462ed05..5a301c10aca6d 100644 --- a/clang/lib/AST/ComputeDependence.cpp +++ b/clang/lib/AST/ComputeDependence.cpp @@ -663,7 +663,7 @@ ExprDependence clang::computeDependence(GenericSelectionExpr *E, ExprDependence clang::computeDependence(DesignatedInitExpr *E) { auto Deps = E->getInit()->getDependence(); - for (auto D : E->designators()) { + for (const auto &D : E->designators()) { auto DesignatorDeps = ExprDependence::None; if (D.isArrayDesignator()) DesignatorDeps |= E->getArrayIndex(D)->getDependence(); diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index b728917fdda6d..cf00db9c948bd 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -1635,8 +1635,8 @@ Module *Decl::getOwningModuleForLinkage(bool IgnoreLinkage) const { llvm_unreachable("unknown module kind"); } -void NamedDecl::printName(raw_ostream &OS, const PrintingPolicy&) const { - OS << Name; +void NamedDecl::printName(raw_ostream &OS, const PrintingPolicy &Policy) const { + Name.print(OS, Policy); } void NamedDecl::printName(raw_ostream &OS) const { @@ -3245,7 +3245,7 @@ bool FunctionDecl::isReplaceableGlobalAllocationFunction( return false; const auto *FPT = getType()->castAs(); - if (FPT->getNumParams() == 0 || FPT->getNumParams() > 3 || FPT->isVariadic()) + if (FPT->getNumParams() == 0 || FPT->getNumParams() > 4 || FPT->isVariadic()) return false; // If this is a single-parameter function, it must be a replaceable global @@ -3280,8 +3280,8 @@ bool FunctionDecl::isReplaceableGlobalAllocationFunction( *AlignmentParam = Params; } - // Finally, if this is not a sized delete, the final parameter can - // be a 'const std::nothrow_t&'. + // If this is not a sized delete, the next parameter can be a + // 'const std::nothrow_t&'. if (!IsSizedDelete && !Ty.isNull() && Ty->isReferenceType()) { Ty = Ty->getPointeeType(); if (Ty.getCVRQualifiers() != Qualifiers::Const) @@ -3293,6 +3293,19 @@ bool FunctionDecl::isReplaceableGlobalAllocationFunction( } } + // Finally, recognize the not yet standard versions of new that take a + // hot/cold allocation hint (__hot_cold_t). These are currently supported by + // tcmalloc (see + // https://github.com/google/tcmalloc/blob/220043886d4e2efff7a5702d5172cb8065253664/tcmalloc/malloc_extension.h#L53). + if (!IsSizedDelete && !Ty.isNull() && Ty->isEnumeralType()) { + QualType T = Ty; + while (const auto *TD = T->getAs()) + T = TD->getDecl()->getUnderlyingType(); + IdentifierInfo *II = T->getAs()->getDecl()->getIdentifier(); + if (II && II->isStr("__hot_cold_t")) + Consume(); + } + return Params == FPT->getNumParams(); } diff --git a/clang/lib/AST/DeclarationName.cpp b/clang/lib/AST/DeclarationName.cpp index 73c56e6610ee0..a3ac5551e0cc2 100644 --- a/clang/lib/AST/DeclarationName.cpp +++ b/clang/lib/AST/DeclarationName.cpp @@ -117,12 +117,12 @@ static void printCXXConstructorDestructorName(QualType ClassType, Policy.adjustForCPlusPlus(); if (const RecordType *ClassRec = ClassType->getAs()) { - OS << *ClassRec->getDecl(); + ClassRec->getDecl()->printName(OS, Policy); return; } if (Policy.SuppressTemplateArgsInCXXConstructors) { if (auto *InjTy = ClassType->getAs()) { - OS << *InjTy->getDecl(); + InjTy->getDecl()->printName(OS, Policy); return; } } diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 6bfb3a37b2433..03eb5dfe6ff60 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -5043,7 +5043,7 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info, static bool CheckLocalVariableDeclaration(EvalInfo &Info, const VarDecl *VD) { // An expression E is a core constant expression unless the evaluation of E - // would evaluate one of the following: [C++2b] - a control flow that passes + // would evaluate one of the following: [C++23] - a control flow that passes // through a declaration of a variable with static or thread storage duration // unless that variable is usable in constant expressions. if (VD->isLocalVarDecl() && VD->isStaticLocal() && @@ -14839,6 +14839,7 @@ class AtomicExprEvaluator : switch (E->getCastKind()) { default: return ExprEvaluatorBaseTy::VisitCastExpr(E); + case CK_NullToPointer: case CK_NonAtomicToAtomic: return This ? EvaluateInPlace(Result, Info, *This, E->getSubExpr()) : Evaluate(Result, Info, E->getSubExpr()); diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index a8e8b2997ddef..df7c4a72f21a7 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -40,7 +40,7 @@ template class DeclScope final : public VariableScope { }; /// Scope used to handle initialization methods. -template class OptionScope { +template class OptionScope final { public: /// Root constructor, compiling or discarding primitives. OptionScope(ByteCodeExprGen *Ctx, bool NewDiscardResult) @@ -146,10 +146,23 @@ bool ByteCodeExprGen::VisitCastExpr(const CastExpr *CE) { if (!this->visit(SubExpr)) return false; - // TODO: Emit this only if FromT != ToT. + if (FromT == ToT) + return true; + return this->emitCast(*FromT, *ToT, CE); } + case CK_PointerToBoolean: { + // Just emit p != nullptr for this. + if (!this->visit(SubExpr)) + return false; + + if (!this->emitNullPtr(CE)) + return false; + + return this->emitNEPtr(CE); + } + case CK_ToVoid: return discard(SubExpr); @@ -177,7 +190,12 @@ bool ByteCodeExprGen::VisitFloatingLiteral(const FloatingLiteral *E) { template bool ByteCodeExprGen::VisitParenExpr(const ParenExpr *PE) { - return this->visit(PE->getSubExpr()); + const Expr *SubExpr = PE->getSubExpr(); + + if (DiscardResult) + return this->discard(SubExpr); + + return this->visit(SubExpr); } template @@ -390,7 +408,7 @@ bool ByteCodeExprGen::VisitImplicitValueInitExpr(const ImplicitValueIni if (!T) return false; - return this->visitZeroInitializer(*T, E); + return this->visitZeroInitializer(E->getType(), E); } template @@ -782,12 +800,10 @@ bool ByteCodeExprGen::VisitExprWithCleanups( const Expr *SubExpr = E->getSubExpr(); assert(E->getNumObjects() == 0 && "TODO: Implement cleanups"); - if (!this->visit(SubExpr)) - return false; - if (DiscardResult) - return this->emitPopPtr(E); - return true; + return this->discard(SubExpr); + + return this->visit(SubExpr); } template @@ -815,7 +831,7 @@ bool ByteCodeExprGen::VisitMaterializeTemporaryExpr( // For everyhing else, use local variables. if (SubExprT) { if (std::optional LocalIndex = allocateLocalPrimitive( - SubExpr, *SubExprT, /*IsMutable=*/true, /*IsExtended=*/true)) { + SubExpr, *SubExprT, /*IsConst=*/true, /*IsExtended=*/true)) { if (!this->visitInitializer(SubExpr)) return false; this->emitSetLocal(*SubExprT, *LocalIndex, E); @@ -929,7 +945,12 @@ bool ByteCodeExprGen::visitConditional( } template -bool ByteCodeExprGen::visitZeroInitializer(PrimType T, const Expr *E) { +bool ByteCodeExprGen::visitZeroInitializer(QualType QT, + const Expr *E) { + // FIXME: We need the QualType to get the float semantics, but that means we + // classify it over and over again in array situations. + PrimType T = classifyPrim(QT); + switch (T) { case PT_Bool: return this->emitZeroBool(E); @@ -954,8 +975,7 @@ bool ByteCodeExprGen::visitZeroInitializer(PrimType T, const Expr *E) { case PT_FnPtr: return this->emitNullFnPtr(E); case PT_Float: { - return this->emitConstFloat( - APFloat::getZero(Ctx.getFloatSemantics(E->getType())), E); + return this->emitConstFloat(APFloat::getZero(Ctx.getFloatSemantics(QT)), E); } } llvm_unreachable("unknown primitive type"); @@ -1276,7 +1296,7 @@ bool ByteCodeExprGen::visitArrayInitializer(const Expr *Initializer) { // since we memset our Block*s to 0 and so we have the desired value // without this. for (size_t I = 0; I != NumElems; ++I) { - if (!this->emitZero(*ElemT, Initializer)) + if (!this->visitZeroInitializer(CAT->getElementType(), Initializer)) return false; if (!this->emitInitElem(*ElemT, I, Initializer)) return false; @@ -1758,6 +1778,11 @@ bool ByteCodeExprGen::VisitUnaryOperator(const UnaryOperator *E) { return DiscardResult ? this->emitPopPtr(E) : true; } + if (T == PT_Float) { + return DiscardResult ? this->emitIncfPop(getRoundingMode(E), E) + : this->emitIncf(getRoundingMode(E), E); + } + return DiscardResult ? this->emitIncPop(*T, E) : this->emitInc(*T, E); } case UO_PostDec: { // x-- @@ -1771,6 +1796,11 @@ bool ByteCodeExprGen::VisitUnaryOperator(const UnaryOperator *E) { return DiscardResult ? this->emitPopPtr(E) : true; } + if (T == PT_Float) { + return DiscardResult ? this->emitDecfPop(getRoundingMode(E), E) + : this->emitDecf(getRoundingMode(E), E); + } + return DiscardResult ? this->emitDecPop(*T, E) : this->emitDec(*T, E); } case UO_PreInc: { // ++x @@ -1785,9 +1815,19 @@ bool ByteCodeExprGen::VisitUnaryOperator(const UnaryOperator *E) { } // Post-inc and pre-inc are the same if the value is to be discarded. - if (DiscardResult) + if (DiscardResult) { + if (T == PT_Float) + return this->emitIncfPop(getRoundingMode(E), E); return this->emitIncPop(*T, E); + } + if (T == PT_Float) { + const auto &TargetSemantics = Ctx.getFloatSemantics(E->getType()); + this->emitLoadFloat(E); + this->emitConstFloat(llvm::APFloat(TargetSemantics, 1), E); + this->emitAddf(getRoundingMode(E), E); + return this->emitStoreFloat(E); + } this->emitLoad(*T, E); this->emitConst(1, E); this->emitAdd(*T, E); @@ -1805,9 +1845,19 @@ bool ByteCodeExprGen::VisitUnaryOperator(const UnaryOperator *E) { } // Post-dec and pre-dec are the same if the value is to be discarded. - if (DiscardResult) + if (DiscardResult) { + if (T == PT_Float) + return this->emitDecfPop(getRoundingMode(E), E); return this->emitDecPop(*T, E); + } + if (T == PT_Float) { + const auto &TargetSemantics = Ctx.getFloatSemantics(E->getType()); + this->emitLoadFloat(E); + this->emitConstFloat(llvm::APFloat(TargetSemantics, 1), E); + this->emitSubf(getRoundingMode(E), E); + return this->emitStoreFloat(E); + } this->emitLoad(*T, E); this->emitConst(1, E); this->emitSub(*T, E); @@ -1857,6 +1907,9 @@ bool ByteCodeExprGen::VisitUnaryOperator(const UnaryOperator *E) { template bool ByteCodeExprGen::VisitDeclRefExpr(const DeclRefExpr *E) { + if (DiscardResult) + return true; + const auto *D = E->getDecl(); if (const auto *ECD = dyn_cast(D)) { diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h index 85588c6ecd3c1..52a8eca593c2b 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.h +++ b/clang/lib/AST/Interp/ByteCodeExprGen.h @@ -185,7 +185,7 @@ class ByteCodeExprGen : public ConstStmtVisitor, bool>, llvm::function_ref V); /// Creates a local primitive value. - unsigned allocateLocalPrimitive(DeclTy &&Decl, PrimType Ty, bool IsMutable, + unsigned allocateLocalPrimitive(DeclTy &&Decl, PrimType Ty, bool IsConst, bool IsExtended = false); /// Allocates a space storing a local given its type. @@ -201,7 +201,7 @@ class ByteCodeExprGen : public ConstStmtVisitor, bool>, friend class ArrayIndexScope; /// Emits a zero initializer. - bool visitZeroInitializer(PrimType T, const Expr *E); + bool visitZeroInitializer(QualType QT, const Expr *E); enum class DerefKind { /// Value is read and pushed to stack. diff --git a/clang/lib/AST/Interp/EvalEmitter.h b/clang/lib/AST/Interp/EvalEmitter.h index 22d7b7c68d104..99933900f2921 100644 --- a/clang/lib/AST/Interp/EvalEmitter.h +++ b/clang/lib/AST/Interp/EvalEmitter.h @@ -71,7 +71,7 @@ class EvalEmitter : public SourceMapper { /// Returns the source location of the current opcode. SourceInfo getSource(const Function *F, CodePtr PC) const override { - return F ? F->getSource(PC) : CurrentSource; + return (F && F->hasBody()) ? F->getSource(PC) : CurrentSource; } /// Parameter indices. diff --git a/clang/lib/AST/Interp/Floating.h b/clang/lib/AST/Interp/Floating.h index fb0884bb3b605..85876236a9998 100644 --- a/clang/lib/AST/Interp/Floating.h +++ b/clang/lib/AST/Interp/Floating.h @@ -105,31 +105,45 @@ class Floating final { // ------- - static APFloat::opStatus add(Floating A, Floating B, llvm::RoundingMode RM, - Floating *R) { + static APFloat::opStatus add(const Floating &A, const Floating &B, + llvm::RoundingMode RM, Floating *R) { *R = Floating(A.F); return R->F.add(B.F, RM); } - static APFloat::opStatus sub(Floating A, Floating B, llvm::RoundingMode RM, - Floating *R) { + static APFloat::opStatus increment(const Floating &A, llvm::RoundingMode RM, + Floating *R) { + APFloat One(A.F.getSemantics(), 1); + *R = Floating(A.F); + return R->F.add(One, RM); + } + + static APFloat::opStatus sub(const Floating &A, const Floating &B, + llvm::RoundingMode RM, Floating *R) { *R = Floating(A.F); return R->F.subtract(B.F, RM); } - static APFloat::opStatus mul(Floating A, Floating B, llvm::RoundingMode RM, - Floating *R) { + static APFloat::opStatus decrement(const Floating &A, llvm::RoundingMode RM, + Floating *R) { + APFloat One(A.F.getSemantics(), 1); + *R = Floating(A.F); + return R->F.subtract(One, RM); + } + + static APFloat::opStatus mul(const Floating &A, const Floating &B, + llvm::RoundingMode RM, Floating *R) { *R = Floating(A.F); return R->F.multiply(B.F, RM); } - static APFloat::opStatus div(Floating A, Floating B, llvm::RoundingMode RM, - Floating *R) { + static APFloat::opStatus div(const Floating &A, const Floating &B, + llvm::RoundingMode RM, Floating *R) { *R = Floating(A.F); return R->F.divide(B.F, RM); } - static bool neg(Floating A, Floating *R) { + static bool neg(const Floating &A, Floating *R) { *R = -A; return false; } diff --git a/clang/lib/AST/Interp/Function.cpp b/clang/lib/AST/Interp/Function.cpp index 40001faad4116..4e6d175c41b26 100644 --- a/clang/lib/AST/Interp/Function.cpp +++ b/clang/lib/AST/Interp/Function.cpp @@ -32,6 +32,7 @@ Function::ParamDescriptor Function::getParamDescriptor(unsigned Offset) const { SourceInfo Function::getSource(CodePtr PC) const { assert(PC >= getCodeBegin() && "PC does not belong to this function"); assert(PC <= getCodeEnd() && "PC Does not belong to this function"); + assert(hasBody() && "Function has no body"); unsigned Offset = PC - getCodeBegin(); using Elem = std::pair; auto It = llvm::lower_bound(SrcMap, Elem{Offset, {}}, llvm::less_first()); diff --git a/clang/lib/AST/Interp/FunctionPointer.h b/clang/lib/AST/Interp/FunctionPointer.h index 2d449bdb031d8..20d4d7793185c 100644 --- a/clang/lib/AST/Interp/FunctionPointer.h +++ b/clang/lib/AST/Interp/FunctionPointer.h @@ -8,6 +8,7 @@ #include "clang/AST/APValue.h" namespace clang { +class ASTContext; namespace interp { class FunctionPointer final { @@ -38,6 +39,13 @@ class FunctionPointer final { OS << ")"; } + std::string toDiagnosticString(const ASTContext &Ctx) const { + if (!Func) + return "nullptr"; + + return toAPValue().getAsString(Ctx, Func->getDecl()->getType()); + } + ComparisonCategoryResult compare(const FunctionPointer &RHS) const { if (Func == RHS.Func) return ComparisonCategoryResult::Equal; diff --git a/clang/lib/AST/Interp/Integral.h b/clang/lib/AST/Interp/Integral.h index 932caca29b9f0..cc7c7a9d2430e 100644 --- a/clang/lib/AST/Interp/Integral.h +++ b/clang/lib/AST/Interp/Integral.h @@ -223,6 +223,9 @@ template class Integral final { } static bool neg(Integral A, Integral *R) { + if (Signed && A.isMin()) + return true; + *R = -A; return false; } diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index 1eda38e9fa5b1..64bdd872221ac 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -413,12 +413,32 @@ bool Inv(InterpState &S, CodePtr OpPC) { template ::T> bool Neg(InterpState &S, CodePtr OpPC) { - const T &Val = S.Stk.pop(); + const T &Value = S.Stk.pop(); T Result; - T::neg(Val, &Result); + if (!T::neg(Value, &Result)) { + S.Stk.push(Result); + return true; + } + + assert(isIntegralType(Name) && + "don't expect other types to fail at constexpr negation"); S.Stk.push(Result); - return true; + + APSInt NegatedValue = -Value.toAPSInt(Value.bitWidth() + 1); + const Expr *E = S.Current->getExpr(OpPC); + QualType Type = E->getType(); + + if (S.checkingForUndefinedBehavior()) { + SmallString<32> Trunc; + NegatedValue.trunc(Result.bitWidth()).toString(Trunc, 10); + auto Loc = E->getExprLoc(); + S.report(Loc, diag::warn_integer_constant_overflow) << Trunc << Type; + return true; + } + + S.CCEDiag(E, diag::note_constexpr_overflow) << NegatedValue << Type; + return S.noteUndefinedBehavior(); } enum class PushVal : bool { @@ -436,7 +456,7 @@ bool IncDecHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { T Result; if constexpr (DoPush == PushVal::Yes) - S.Stk.push(Result); + S.Stk.push(Value); if constexpr (Op == IncDecOp::Inc) { if (!T::increment(Value, &Result)) { @@ -520,6 +540,50 @@ bool DecPop(InterpState &S, CodePtr OpPC) { return IncDecHelper(S, OpPC, Ptr); } +template +bool IncDecFloatHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + llvm::RoundingMode RM) { + Floating Value = Ptr.deref(); + Floating Result; + + if constexpr (DoPush == PushVal::Yes) + S.Stk.push(Value); + + llvm::APFloat::opStatus Status; + if constexpr (Op == IncDecOp::Inc) + Status = Floating::increment(Value, RM, &Result); + else + Status = Floating::decrement(Value, RM, &Result); + + Ptr.deref() = Result; + + return CheckFloatResult(S, OpPC, Status); +} + +inline bool Incf(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) { + // FIXME: Check initialization of Ptr + const Pointer &Ptr = S.Stk.pop(); + return IncDecFloatHelper(S, OpPC, Ptr, RM); +} + +inline bool IncfPop(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) { + // FIXME: Check initialization of Ptr + const Pointer &Ptr = S.Stk.pop(); + return IncDecFloatHelper(S, OpPC, Ptr, RM); +} + +inline bool Decf(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) { + // FIXME: Check initialization of Ptr + const Pointer &Ptr = S.Stk.pop(); + return IncDecFloatHelper(S, OpPC, Ptr, RM); +} + +inline bool DecfPop(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) { + // FIXME: Check initialization of Ptr + const Pointer &Ptr = S.Stk.pop(); + return IncDecFloatHelper(S, OpPC, Ptr, RM); +} + /// 1) Pops the value from the stack. /// 2) Pushes the bitwise complemented value on the stack (~V). template ::T> @@ -554,6 +618,29 @@ bool CmpHelperEQ(InterpState &S, CodePtr OpPC, CompareFn Fn) { return CmpHelper(S, OpPC, Fn); } +/// Function pointers cannot be compared in an ordered way. +template <> +inline bool CmpHelper(InterpState &S, CodePtr OpPC, + CompareFn Fn) { + const auto &RHS = S.Stk.pop(); + const auto &LHS = S.Stk.pop(); + + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_unspecified) + << LHS.toDiagnosticString(S.getCtx()) + << RHS.toDiagnosticString(S.getCtx()); + return false; +} + +template <> +inline bool CmpHelperEQ(InterpState &S, CodePtr OpPC, + CompareFn Fn) { + const auto &RHS = S.Stk.pop(); + const auto &LHS = S.Stk.pop(); + S.Stk.push(Boolean::from(Fn(LHS.compare(RHS)))); + return true; +} + template <> inline bool CmpHelper(InterpState &S, CodePtr OpPC, CompareFn Fn) { using BoolT = PrimConv::T; @@ -701,6 +788,9 @@ bool GetLocal(InterpState &S, CodePtr OpPC, uint32_t I) { return true; } +/// 1) Pops the value from the stack. +/// 2) Writes the value to the local variable with the +/// given offset. template ::T> bool SetLocal(InterpState &S, CodePtr OpPC, uint32_t I) { S.Current->setLocal(I, S.Stk.pop()); diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td index ed0774a788333..15f7312ad00e6 100644 --- a/clang/lib/AST/Interp/Opcodes.td +++ b/clang/lib/AST/Interp/Opcodes.td @@ -94,7 +94,7 @@ def AllTypeClass : TypeClass { } def ComparableTypeClass : TypeClass { - let Types = !listconcat(AluTypeClass.Types, [Ptr], [Float]); + let Types = !listconcat(AluTypeClass.Types, [Ptr], [Float], [FnPtr]); } class SingletonTypeClass : TypeClass { @@ -499,11 +499,18 @@ def Inv: Opcode { let HasGroup = 1; } +// Increment and decrement. def Inc: IntegerOpcode; def IncPop : IntegerOpcode; def Dec: IntegerOpcode; def DecPop: IntegerOpcode; +// Float increment and decrement. +def Incf: FloatOpcode; +def IncfPop : FloatOpcode; +def Decf: FloatOpcode; +def DecfPop : FloatOpcode; + // [Real] -> [Real] def Neg: Opcode { let Types = [NonPtrTypeClass]; diff --git a/clang/lib/AST/Interp/Pointer.cpp b/clang/lib/AST/Interp/Pointer.cpp index 8f1dfa346c632..7f127143a99d8 100644 --- a/clang/lib/AST/Interp/Pointer.cpp +++ b/clang/lib/AST/Interp/Pointer.cpp @@ -147,7 +147,7 @@ APValue Pointer::toAPValue() const { bool Pointer::isInitialized() const { assert(Pointee && "Cannot check if null pointer was initialized"); - Descriptor *Desc = getFieldDesc(); + const Descriptor *Desc = getFieldDesc(); assert(Desc); if (Desc->isPrimitiveArray()) { if (isStatic() && Base == 0) @@ -167,7 +167,7 @@ bool Pointer::isInitialized() const { void Pointer::initialize() const { assert(Pointee && "Cannot initialize null pointer"); - Descriptor *Desc = getFieldDesc(); + const Descriptor *Desc = getFieldDesc(); assert(Desc); if (Desc->isPrimitiveArray()) { diff --git a/clang/lib/AST/Interp/Pointer.h b/clang/lib/AST/Interp/Pointer.h index 5895c61295e02..10d21a27167f0 100644 --- a/clang/lib/AST/Interp/Pointer.h +++ b/clang/lib/AST/Interp/Pointer.h @@ -290,7 +290,7 @@ class Pointer { /// Returns the number of elements. unsigned getNumElems() const { return getSize() / elemSize(); } - Block *block() const { return Pointee; } + const Block *block() const { return Pointee; } /// Returns the index into an array. int64_t getIndex() const { diff --git a/clang/lib/AST/Interp/PrimType.h b/clang/lib/AST/Interp/PrimType.h index 30bec3f2a17cf..693e57210608d 100644 --- a/clang/lib/AST/Interp/PrimType.h +++ b/clang/lib/AST/Interp/PrimType.h @@ -42,6 +42,8 @@ enum PrimType : unsigned { PT_FnPtr, }; +constexpr bool isIntegralType(PrimType T) { return T <= PT_Uint64; } + /// Mapping from primitive types to their representation. template struct PrimConv; template <> struct PrimConv { using T = Integral<8, true>; }; diff --git a/clang/lib/AST/Interp/State.cpp b/clang/lib/AST/Interp/State.cpp index 2512979257605..f67bde1082fa0 100644 --- a/clang/lib/AST/Interp/State.cpp +++ b/clang/lib/AST/Interp/State.cpp @@ -11,6 +11,7 @@ #include "Program.h" #include "clang/AST/ASTContext.h" #include "clang/AST/CXXInheritance.h" +#include "clang/AST/OptionalDiagnostic.h" using namespace clang; using namespace clang::interp; diff --git a/clang/lib/AST/Interp/State.h b/clang/lib/AST/Interp/State.h index 131fbcf3cffc4..d897b7c202756 100644 --- a/clang/lib/AST/Interp/State.h +++ b/clang/lib/AST/Interp/State.h @@ -15,9 +15,9 @@ #include "clang/AST/ASTDiagnostic.h" #include "clang/AST/Expr.h" -#include "clang/AST/OptionalDiagnostic.h" namespace clang { +class OptionalDiagnostic; /// Kinds of access we can perform on an object, for diagnostics. Note that /// we consider a member function call to be a kind of access, even though @@ -36,7 +36,7 @@ enum AccessKinds { AK_Destroy, }; -// The order of this enum is important for diagnostics. +/// The order of this enum is important for diagnostics. enum CheckSubobjectKind { CSK_Base, CSK_Derived, @@ -72,7 +72,7 @@ class State { public: State() : InConstantContext(false) {} - // Diagnose that the evaluation could not be folded (FF => FoldFailure) + /// Diagnose that the evaluation could not be folded (FF => FoldFailure) OptionalDiagnostic FFDiag(SourceLocation Loc, diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr, diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 53371394730a2..806bd30972312 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -563,6 +563,8 @@ class CXXNameMangler { void mangleAArch64NeonVectorType(const DependentVectorType *T); void mangleAArch64FixedSveVectorType(const VectorType *T); void mangleAArch64FixedSveVectorType(const DependentVectorType *T); + void mangleRISCVFixedRVVVectorType(const VectorType *T); + void mangleRISCVFixedRVVVectorType(const DependentVectorType *T); void mangleIntegerLiteral(QualType T, const llvm::APSInt &Value); void mangleFloatLiteral(QualType T, const llvm::APFloat &V); @@ -3812,6 +3814,68 @@ void CXXNameMangler::mangleAArch64FixedSveVectorType( Diags.Report(T->getAttributeLoc(), DiagID); } +void CXXNameMangler::mangleRISCVFixedRVVVectorType(const VectorType *T) { + assert(T->getVectorKind() == VectorType::RVVFixedLengthDataVector && + "expected fixed-length RVV vector!"); + + QualType EltType = T->getElementType(); + assert(EltType->isBuiltinType() && + "expected builtin type for fixed-length RVV vector!"); + + StringRef TypeName; + switch (cast(EltType)->getKind()) { + case BuiltinType::SChar: + TypeName = "__rvv_int8m1_t"; + break; + case BuiltinType::UChar: + TypeName = "__rvv_uint8m1_t"; + break; + case BuiltinType::Short: + TypeName = "__rvv_int16m1_t"; + break; + case BuiltinType::UShort: + TypeName = "__rvv_uint16m1_t"; + break; + case BuiltinType::Int: + TypeName = "__rvv_int32m1_t"; + break; + case BuiltinType::UInt: + TypeName = "__rvv_uint32m1_t"; + break; + case BuiltinType::Long: + TypeName = "__rvv_int64m1_t"; + break; + case BuiltinType::ULong: + TypeName = "__rvv_uint64m1_t"; + break; + case BuiltinType::Half: + TypeName = "__rvv_float16m1_t"; + break; + case BuiltinType::Float: + TypeName = "__rvv_float32m1_t"; + break; + case BuiltinType::Double: + TypeName = "__rvv_float64m1_t"; + break; + default: + llvm_unreachable("unexpected element type for fixed-length RVV vector!"); + } + + unsigned VecSizeInBits = getASTContext().getTypeInfo(T).Width; + + Out << "9__RVV_VLSI" << 'u' << TypeName.size() << TypeName << "Lj" + << VecSizeInBits << "EE"; +} + +void CXXNameMangler::mangleRISCVFixedRVVVectorType( + const DependentVectorType *T) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID( + DiagnosticsEngine::Error, + "cannot mangle this dependent fixed-length RVV vector type yet"); + Diags.Report(T->getAttributeLoc(), DiagID); +} + // GNU extension: vector types // ::= // ::= Dv _ @@ -3836,6 +3900,9 @@ void CXXNameMangler::mangleType(const VectorType *T) { T->getVectorKind() == VectorType::SveFixedLengthPredicateVector) { mangleAArch64FixedSveVectorType(T); return; + } else if (T->getVectorKind() == VectorType::RVVFixedLengthDataVector) { + mangleRISCVFixedRVVVectorType(T); + return; } Out << "Dv" << T->getNumElements() << '_'; if (T->getVectorKind() == VectorType::AltiVecPixel) @@ -3862,6 +3929,9 @@ void CXXNameMangler::mangleType(const DependentVectorType *T) { T->getVectorKind() == VectorType::SveFixedLengthPredicateVector) { mangleAArch64FixedSveVectorType(T); return; + } else if (T->getVectorKind() == VectorType::RVVFixedLengthDataVector) { + mangleRISCVFixedRVVVectorType(T); + return; } Out << "Dv"; diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp index 95692e1e7f6ec..43b7b710d7f45 100644 --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/clang/lib/AST/JSONNodeDumper.cpp @@ -662,6 +662,9 @@ void JSONNodeDumper::VisitVectorType(const VectorType *VT) { case VectorType::SveFixedLengthPredicateVector: JOS.attribute("vectorKind", "fixed-length sve predicate vector"); break; + case VectorType::RVVFixedLengthDataVector: + JOS.attribute("vectorKind", "fixed-length rvv data vector"); + break; } } diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index e0fd8abe5e3b8..d70f1a19acbbd 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -29,12 +29,14 @@ #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/CRC.h" #include "llvm/Support/MD5.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/StringSaver.h" #include "llvm/Support/xxhash.h" +#include #include using namespace clang; @@ -368,9 +370,13 @@ class MicrosoftCXXNameMangler { void mangleVariableEncoding(const VarDecl *VD); void mangleMemberDataPointer(const CXXRecordDecl *RD, const ValueDecl *VD, StringRef Prefix = "$"); + void mangleMemberDataPointerInClassNTTP(const CXXRecordDecl *, + const ValueDecl *); void mangleMemberFunctionPointer(const CXXRecordDecl *RD, const CXXMethodDecl *MD, StringRef Prefix = "$"); + void mangleMemberFunctionPointerInClassNTTP(const CXXRecordDecl *RD, + const CXXMethodDecl *MD); void mangleVirtualMemPtrThunk(const CXXMethodDecl *MD, const MethodVFTableLocation &ML); void mangleNumber(int64_t Number); @@ -711,6 +717,28 @@ void MicrosoftCXXNameMangler::mangleMemberDataPointer(const CXXRecordDecl *RD, mangleNumber(VBTableOffset); } +void MicrosoftCXXNameMangler::mangleMemberDataPointerInClassNTTP( + const CXXRecordDecl *RD, const ValueDecl *VD) { + MSInheritanceModel IM = RD->getMSInheritanceModel(); + // ::= + // ::= N + // ::= 8 @ @ + + if (IM != MSInheritanceModel::Single && IM != MSInheritanceModel::Multiple) + return mangleMemberDataPointer(RD, VD, ""); + + if (!VD) { + Out << 'N'; + return; + } + + Out << '8'; + mangleNestedName(VD); + Out << '@'; + mangleUnqualifiedName(VD); + Out << '@'; +} + void MicrosoftCXXNameMangler::mangleMemberFunctionPointer(const CXXRecordDecl *RD, const CXXMethodDecl *MD, @@ -775,6 +803,34 @@ MicrosoftCXXNameMangler::mangleMemberFunctionPointer(const CXXRecordDecl *RD, mangleNumber(VBTableOffset); } +void MicrosoftCXXNameMangler::mangleMemberFunctionPointerInClassNTTP( + const CXXRecordDecl *RD, const CXXMethodDecl *MD) { + // ::= + // ::= N + // ::= E? + // ::= E? + + if (!MD) { + if (RD->getMSInheritanceModel() != MSInheritanceModel::Single) + return mangleMemberFunctionPointer(RD, MD, ""); + + Out << 'N'; + return; + } + + Out << "E?"; + if (MD->isVirtual()) { + MicrosoftVTableContext *VTContext = + cast(getASTContext().getVTableContext()); + MethodVFTableLocation ML = + VTContext->getMethodVFTableLocation(GlobalDecl(MD)); + mangleVirtualMemPtrThunk(MD, ML); + } else { + mangleName(MD); + mangleFunctionEncoding(MD, /*ShouldMangle=*/true); + } +} + void MicrosoftCXXNameMangler::mangleVirtualMemPtrThunk( const CXXMethodDecl *MD, const MethodVFTableLocation &ML) { // Get the vftable offset. @@ -1188,6 +1244,11 @@ void MicrosoftCXXNameMangler::mangleUnqualifiedName(GlobalDecl GD, // ::= [] void MicrosoftCXXNameMangler::mangleNestedName(GlobalDecl GD) { const NamedDecl *ND = cast(GD.getDecl()); + + if (const auto *ID = dyn_cast(ND)) + for (unsigned I = 1, IE = ID->getChainingSize(); I < IE; ++I) + mangleSourceName(""); + const DeclContext *DC = getEffectiveDeclContext(ND); while (!DC->isTranslationUnit()) { if (isa(ND) || isa(ND)) { @@ -1570,7 +1631,6 @@ void MicrosoftCXXNameMangler::mangleTemplateArg(const TemplateDecl *TD, // ::= 8 @ // ::= A # float // ::= B # double - // ::= E # reference to D // # pointer to member, by component value // ::= F // ::= G @@ -1615,7 +1675,7 @@ void MicrosoftCXXNameMangler::mangleTemplateArg(const TemplateDecl *TD, mangleTemplateArgValue(TPO->getType().getUnqualifiedType(), TPO->getValue()); } else { - mangle(ND, TA.getParamTypeForDecl()->isReferenceType() ? "$E?" : "$1?"); + mangle(ND, "$1?"); } break; } @@ -1744,46 +1804,62 @@ void MicrosoftCXXNameMangler::mangleTemplateArgValue(QualType T, // FIXME: This can only happen as an extension. Invent a mangling. break; } else if (auto *VD = Base.dyn_cast()) { - Out << (T->isReferenceType() ? "E" : "1"); + Out << "E"; mangle(VD); } else { break; } } else { - unsigned NumAts = 0; - if (T->isPointerType()) { + if (T->isPointerType()) Out << "5"; - ++NumAts; - } - QualType T = Base.getType(); + SmallVector EntryTypes; + SmallVector, 2> EntryManglers; + QualType ET = Base.getType(); for (APValue::LValuePathEntry E : V.getLValuePath()) { - // We don't know how to mangle array subscripting yet. - if (T->isArrayType()) - goto mangling_unknown; + if (auto *AT = ET->getAsArrayTypeUnsafe()) { + EntryTypes.push_back('C'); + EntryManglers.push_back([this, I = E.getAsArrayIndex()] { + Out << '0'; + mangleNumber(I); + Out << '@'; + }); + ET = AT->getElementType(); + continue; + } const Decl *D = E.getAsBaseOrMember().getPointer(); - auto *FD = dyn_cast(D); - // We don't know how to mangle derived-to-base conversions yet. - if (!FD) - goto mangling_unknown; - - Out << "6"; - ++NumAts; - T = FD->getType(); + if (auto *FD = dyn_cast(D)) { + ET = FD->getType(); + if (const auto *RD = ET->getAsRecordDecl()) + if (RD->isAnonymousStructOrUnion()) + continue; + } else { + ET = getASTContext().getRecordType(cast(D)); + // Bug in MSVC: fully qualified name of base class should be used for + // mangling to prevent collisions e.g. on base classes with same names + // in different namespaces. + } + + EntryTypes.push_back('6'); + EntryManglers.push_back([this, D] { + mangleUnqualifiedName(cast(D)); + Out << '@'; + }); } + for (auto I = EntryTypes.rbegin(), E = EntryTypes.rend(); I != E; ++I) + Out << *I; + auto *VD = Base.dyn_cast(); if (!VD) break; Out << "E"; mangle(VD); - for (APValue::LValuePathEntry E : V.getLValuePath()) { - const Decl *D = E.getAsBaseOrMember().getPointer(); - mangleUnqualifiedName(cast(D)); - } - for (unsigned I = 0; I != NumAts; ++I) + for (const std::function &Mangler : EntryManglers) + Mangler(); + if (T->isPointerType()) Out << '@'; } @@ -1794,20 +1870,14 @@ void MicrosoftCXXNameMangler::mangleTemplateArgValue(QualType T, if (WithScalarType) mangleType(T, SourceRange(), QMM_Escape); - // FIXME: The below manglings don't include a conversion, so bail if there - // would be one. MSVC mangles the (possibly converted) value of the - // pointer-to-member object as if it were a struct, leading to collisions - // in some cases. - if (!V.getMemberPointerPath().empty()) - break; - const CXXRecordDecl *RD = T->castAs()->getMostRecentCXXRecordDecl(); const ValueDecl *D = V.getMemberPointerDecl(); if (T->isMemberDataPointerType()) - mangleMemberDataPointer(RD, D, ""); + mangleMemberDataPointerInClassNTTP(RD, D); else - mangleMemberFunctionPointer(RD, cast_or_null(D), ""); + mangleMemberFunctionPointerInClassNTTP(RD, + cast_or_null(D)); return; } @@ -1895,7 +1965,6 @@ void MicrosoftCXXNameMangler::mangleTemplateArgValue(QualType T, break; } -mangling_unknown: DiagnosticsEngine &Diags = Context.getDiags(); unsigned DiagID = Diags.getCustomDiagID( DiagnosticsEngine::Error, "cannot mangle this template argument yet"); diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp index 3374b49f5d8e2..0ead0940479d8 100644 --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -594,7 +594,7 @@ void ODRHash::AddCXXRecordDecl(const CXXRecordDecl *Record) { ID.AddInteger(Record->getNumBases()); auto Bases = Record->bases(); - for (auto Base : Bases) { + for (const auto &Base : Bases) { AddQualType(Base.getType()); ID.AddInteger(Base.isVirtual()); ID.AddInteger(Base.getAccessSpecifierAsWritten()); diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index a5573c117e629..3817025c051c5 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -1495,6 +1495,9 @@ void TextNodeDumper::VisitVectorType(const VectorType *T) { case VectorType::SveFixedLengthPredicateVector: OS << " fixed-length sve predicate vector"; break; + case VectorType::RVVFixedLengthDataVector: + OS << " fixed-length rvv data vector"; + break; } OS << " " << T->getNumElements(); } diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index e1686a7c69d52..ed481a4c06962 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -46,6 +46,7 @@ #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MathExtras.h" +#include "llvm/TargetParser/RISCVTargetParser.h" #include #include #include @@ -1928,6 +1929,11 @@ bool Type::hasIntegerRepresentation() const { (VT->getKind() >= BuiltinType::SveInt8 && VT->getKind() <= BuiltinType::SveUint64); } + if (CanonicalType->isRVVVLSBuiltinType()) { + const auto *VT = cast(CanonicalType); + return (VT->getKind() >= BuiltinType::RvvInt8mf8 && + VT->getKind() <= BuiltinType::RvvUint64m8); + } return isIntegerType(); } @@ -2425,6 +2431,28 @@ QualType Type::getSveEltType(const ASTContext &Ctx) const { return Ctx.getBuiltinVectorTypeInfo(BTy).ElementType; } +bool Type::isRVVVLSBuiltinType() const { + if (const BuiltinType *BT = getAs()) { + switch (BT->getKind()) { + // FIXME: Support more than LMUL 1. +#define RVV_VECTOR_TYPE(Name, Id, SingletonId, NumEls, ElBits, NF, IsSigned, IsFP) \ + case BuiltinType::Id: \ + return NF == 1 && (NumEls * ElBits) == llvm::RISCV::RVVBitsPerBlock; +#include "clang/Basic/RISCVVTypes.def" + default: + return false; + } + } + return false; +} + +QualType Type::getRVVEltType(const ASTContext &Ctx) const { + assert(isRVVVLSBuiltinType() && "unsupported type!"); + + const BuiltinType *BTy = getAs(); + return Ctx.getBuiltinVectorTypeInfo(BTy).ElementType; +} + bool QualType::isPODType(const ASTContext &Context) const { // C++11 has a more relaxed definition of POD. if (Context.getLangOpts().CPlusPlus11) @@ -4630,6 +4658,7 @@ AutoType::AutoType(QualType DeducedAsType, AutoTypeKeyword Keyword, AutoTypeBits.Keyword = (unsigned)Keyword; AutoTypeBits.NumArgs = TypeConstraintArgs.size(); this->TypeConstraintConcept = TypeConstraintConcept; + assert(TypeConstraintConcept || AutoTypeBits.NumArgs == 0); if (TypeConstraintConcept) { auto *ArgBuffer = const_cast(getTypeConstraintArguments().data()); diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 02a354da56a42..408c7caa16c93 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -687,6 +687,19 @@ void TypePrinter::printVectorBefore(const VectorType *T, raw_ostream &OS) { else OS << T->getNumElements(); + OS << " * sizeof("; + print(T->getElementType(), OS, StringRef()); + // Multiply by 8 for the number of bits. + OS << ") * 8))) "; + printBefore(T->getElementType(), OS); + break; + case VectorType::RVVFixedLengthDataVector: + // FIXME: We prefer to print the size directly here, but have no way + // to get the size of the type. + OS << "__attribute__((__riscv_rvv_vector_bits__("; + + OS << T->getNumElements(); + OS << " * sizeof("; print(T->getElementType(), OS, StringRef()); // Multiply by 8 for the number of bits. @@ -759,6 +772,20 @@ void TypePrinter::printDependentVectorBefore( OS << "))) "; printBefore(T->getElementType(), OS); break; + case VectorType::RVVFixedLengthDataVector: + // FIXME: We prefer to print the size directly here, but have no way + // to get the size of the type. + OS << "__attribute__((__riscv_rvv_vector_bits__("; + if (T->getSizeExpr()) { + T->getSizeExpr()->printPretty(OS, nullptr, Policy); + OS << " * sizeof("; + print(T->getElementType(), OS, StringRef()); + // Multiply by 8 for the number of bits. + OS << ") * 8"; + } + OS << "))) "; + printBefore(T->getElementType(), OS); + break; } } diff --git a/clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp b/clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp index 6699a0fc9d79e..5520633da68ae 100644 --- a/clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp +++ b/clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp @@ -70,7 +70,7 @@ static llvm::BitVector findReachableBlocks(const CFG &Cfg) { llvm::Expected ControlFlowContext::build(const Decl *D, Stmt &S, ASTContext &C) { CFG::BuildOptions Options; - Options.PruneTriviallyFalseEdges = false; + Options.PruneTriviallyFalseEdges = true; Options.AddImplicitDtors = true; Options.AddTemporaryDtors = true; Options.AddInitializers = true; diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp index 35988af6d5bc6..a4d3768b8b189 100644 --- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp @@ -247,9 +247,9 @@ void Environment::initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl) { for (const VarDecl *D : Vars) { if (getStorageLocation(*D, SkipPast::None) != nullptr) continue; - auto &Loc = createStorageLocation(*D); + auto &Loc = createStorageLocation(D->getType().getNonReferenceType()); setStorageLocation(*D, Loc); - if (auto *Val = createValue(D->getType())) + if (auto *Val = createValue(D->getType().getNonReferenceType())) setValue(Loc, *Val); } @@ -291,10 +291,16 @@ Environment::Environment(DataflowAnalysisContext &DACtx, for (const auto *ParamDecl : FuncDecl->parameters()) { assert(ParamDecl != nullptr); - auto &ParamLoc = createStorageLocation(*ParamDecl); + // References aren't objects, so the reference itself doesn't have a + // storage location. Instead, the storage location for a reference refers + // directly to an object of the referenced type -- so strip off any + // reference from the type. + auto &ParamLoc = + createStorageLocation(ParamDecl->getType().getNonReferenceType()); setStorageLocation(*ParamDecl, ParamLoc); - if (Value *ParamVal = createValue(ParamDecl->getType())) - setValue(ParamLoc, *ParamVal); + if (Value *ParamVal = + createValue(ParamDecl->getType().getNonReferenceType())) + setValue(ParamLoc, *ParamVal); } QualType ReturnType = FuncDecl->getReturnType(); @@ -376,17 +382,19 @@ void Environment::pushCallInternal(const FunctionDecl *FuncDecl, continue; const VarDecl *Param = *ParamIt; - auto &Loc = createStorageLocation(*Param); - setStorageLocation(*Param, Loc); QualType ParamType = Param->getType(); if (ParamType->isReferenceType()) { - auto &Val = arena().create(*ArgLoc); - setValue(Loc, Val); - } else if (auto *ArgVal = getValue(*ArgLoc)) { - setValue(Loc, *ArgVal); - } else if (Value *Val = createValue(ParamType)) { - setValue(Loc, *Val); + setStorageLocation(*Param, *ArgLoc); + } else { + auto &Loc = createStorageLocation(*Param); + setStorageLocation(*Param, Loc); + + if (auto *ArgVal = getValue(*ArgLoc)) { + setValue(Loc, *ArgVal); + } else if (Value *Val = createValue(ParamType)) { + setValue(Loc, *Val); + } } } } @@ -518,6 +526,10 @@ LatticeJoinEffect Environment::join(const Environment &Other, JoinedEnv.ReturnLoc = ReturnLoc; JoinedEnv.ThisPointeeLoc = ThisPointeeLoc; + // FIXME: Once we're able to remove declarations from `DeclToLoc` when their + // lifetime ends, add an assertion that there aren't any entries in + // `DeclToLoc` and `Other.DeclToLoc` that map the same declaration to + // different storage locations. JoinedEnv.DeclToLoc = intersectDenseMaps(DeclToLoc, Other.DeclToLoc); if (DeclToLoc.size() != JoinedEnv.DeclToLoc.size()) Effect = LatticeJoinEffect::Changed; @@ -589,13 +601,23 @@ StorageLocation &Environment::createStorageLocation(const Expr &E) { void Environment::setStorageLocation(const ValueDecl &D, StorageLocation &Loc) { assert(!DeclToLoc.contains(&D)); + assert(!isa_and_nonnull(getValue(Loc))); DeclToLoc[&D] = &Loc; } StorageLocation *Environment::getStorageLocation(const ValueDecl &D, SkipPast SP) const { + assert(SP != SkipPast::ReferenceThenPointer); + auto It = DeclToLoc.find(&D); - return It == DeclToLoc.end() ? nullptr : &skip(*It->second, SP); + if (It == DeclToLoc.end()) + return nullptr; + + StorageLocation *Loc = It->second; + + assert(!isa_and_nonnull(getValue(*Loc))); + + return Loc; } void Environment::setStorageLocation(const Expr &E, StorageLocation &Loc) { @@ -662,6 +684,8 @@ Value *Environment::getValue(const StorageLocation &Loc) const { } Value *Environment::getValue(const ValueDecl &D, SkipPast SP) const { + assert(SP != SkipPast::ReferenceThenPointer); + auto *Loc = getStorageLocation(D, SP); if (Loc == nullptr) return nullptr; @@ -707,7 +731,7 @@ Value *Environment::createValueUnlessSelfReferential( // with integers, and so distinguishing them serves no purpose, but could // prevent convergence. CreatedValuesCount++; - return &arena().create(); + return &DACtx->arena().create(); } if (Type->isReferenceType() || Type->isPointerType()) { @@ -725,9 +749,9 @@ Value *Environment::createValueUnlessSelfReferential( } if (Type->isReferenceType()) - return &arena().create(PointeeLoc); + return &DACtx->arena().create(PointeeLoc); else - return &arena().create(PointeeLoc); + return &DACtx->arena().create(PointeeLoc); } if (Type->isRecordType()) { @@ -747,7 +771,7 @@ Value *Environment::createValueUnlessSelfReferential( Visited.erase(FieldType.getCanonicalType()); } - return &arena().create(std::move(FieldValues)); + return &DACtx->arena().create(std::move(FieldValues)); } return nullptr; diff --git a/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp b/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp index b229194bc8f44..14293a3043f98 100644 --- a/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp +++ b/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp @@ -67,6 +67,7 @@ #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/JSON.h" #include "llvm/Support/Program.h" +#include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/raw_ostream.h" // Defines assets: HTMLLogger_{html_js,css} #include "HTMLLogger.inc" @@ -79,6 +80,96 @@ llvm::Expected renderSVG(llvm::StringRef DotGraph); using StreamFactory = std::function()>; +// Recursively dumps Values/StorageLocations as JSON +class ModelDumper { +public: + ModelDumper(llvm::json::OStream &JOS, const Environment &Env) + : JOS(JOS), Env(Env) {} + + void dump(Value &V) { + JOS.attribute("value_id", llvm::to_string(&V)); + if (!Visited.insert(&V).second) + return; + + JOS.attribute("kind", debugString(V.getKind())); + + switch (V.getKind()) { + case Value::Kind::Integer: + case Value::Kind::TopBool: + case Value::Kind::AtomicBool: + break; + case Value::Kind::Reference: + JOS.attributeObject( + "referent", [&] { dump(cast(V).getReferentLoc()); }); + break; + case Value::Kind::Pointer: + JOS.attributeObject( + "pointee", [&] { dump(cast(V).getPointeeLoc()); }); + break; + case Value::Kind::Struct: + for (const auto &Child : cast(V).children()) + JOS.attributeObject("f:" + Child.first->getNameAsString(), + [&] { dump(*Child.second); }); + break; + case Value::Kind::Disjunction: { + auto &VV = cast(V); + JOS.attributeObject("lhs", [&] { dump(VV.getLeftSubValue()); }); + JOS.attributeObject("rhs", [&] { dump(VV.getRightSubValue()); }); + break; + } + case Value::Kind::Conjunction: { + auto &VV = cast(V); + JOS.attributeObject("lhs", [&] { dump(VV.getLeftSubValue()); }); + JOS.attributeObject("rhs", [&] { dump(VV.getRightSubValue()); }); + break; + } + case Value::Kind::Negation: { + auto &VV = cast(V); + JOS.attributeObject("not", [&] { dump(VV.getSubVal()); }); + break; + } + case Value::Kind::Implication: { + auto &VV = cast(V); + JOS.attributeObject("if", [&] { dump(VV.getLeftSubValue()); }); + JOS.attributeObject("then", [&] { dump(VV.getRightSubValue()); }); + break; + } + case Value::Kind::Biconditional: { + auto &VV = cast(V); + JOS.attributeObject("lhs", [&] { dump(VV.getLeftSubValue()); }); + JOS.attributeObject("rhs", [&] { dump(VV.getRightSubValue()); }); + break; + } + } + + for (const auto& Prop : V.properties()) + JOS.attributeObject(("p:" + Prop.first()).str(), + [&] { dump(*Prop.second); }); + + // Running the SAT solver is expensive, but knowing which booleans are + // guaranteed true/false here is valuable and hard to determine by hand. + if (auto *B = llvm::dyn_cast(&V)) { + JOS.attribute("truth", Env.flowConditionImplies(*B) ? "true" + : Env.flowConditionImplies(Env.makeNot(*B)) + ? "false" + : "unknown"); + } + } + void dump(const StorageLocation &L) { + JOS.attribute("location", llvm::to_string(&L)); + if (!Visited.insert(&L).second) + return; + + JOS.attribute("type", L.getType().getAsString()); + if (auto *V = Env.getValue(L)) + dump(*V); + } + + llvm::DenseSet Visited; + llvm::json::OStream &JOS; + const Environment &Env; +}; + class HTMLLogger : public Logger { StreamFactory Streams; std::unique_ptr OS; @@ -184,6 +275,16 @@ class HTMLLogger : public Logger { JOS->attribute("block", blockID(Block)); JOS->attribute("iter", Iter); JOS->attribute("element", ElementIndex); + + // If this state immediately follows an Expr, show its built-in model. + if (ElementIndex > 0) { + auto S = + Iters.back().first->Elements[ElementIndex - 1].getAs(); + if (const Expr *E = S ? llvm::dyn_cast(S->getStmt()) : nullptr) + if (auto *Loc = State.Env.getStorageLocation(*E, SkipPast::None)) + JOS->attributeObject( + "value", [&] { ModelDumper(*JOS, State.Env).dump(*Loc); }); + } if (!ContextLogs.empty()) { JOS->attribute("logs", ContextLogs); ContextLogs.clear(); diff --git a/clang/lib/Analysis/FlowSensitive/HTMLLogger.css b/clang/lib/Analysis/FlowSensitive/HTMLLogger.css index 4877c1264c83b..c8212df1f94b8 100644 --- a/clang/lib/Analysis/FlowSensitive/HTMLLogger.css +++ b/clang/lib/Analysis/FlowSensitive/HTMLLogger.css @@ -116,3 +116,27 @@ code.line:has(.bb-select):before { position: relative; margin-left: 0.5em; } + +.value { + border: 1px solid #888; + font-size: x-small; + flex-grow: 1; +} +.value summary { + background-color: #ace; + display: flex; + justify-content: space-between; +} +.value .address { + font-size: xx-small; + font-family: monospace; + color: #888; +} +.value .property { + display: flex; + margin-top: 0.5em; +} +.value .property .key { + font-weight: bold; + min-width: 5em; +} diff --git a/clang/lib/Analysis/FlowSensitive/HTMLLogger.html b/clang/lib/Analysis/FlowSensitive/HTMLLogger.html index c97f3ea8ac7d4..a60259a99cce0 100644 --- a/clang/lib/Analysis/FlowSensitive/HTMLLogger.html +++ b/clang/lib/Analysis/FlowSensitive/HTMLLogger.html @@ -10,6 +10,30 @@ + + + @@ -35,9 +59,15 @@ + @@ -50,6 +80,10 @@ +