From d484c4d3501a7ff3d00a6e0cfad026a3b01d320c Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 2 May 2024 09:38:09 +0900 Subject: [PATCH 0001/1014] [InterleavedLoadCombine] Bail out on non-byte-sized vector element type (#90705) Vectors are always tightly packed, and elements of non-byte-sized usually do not have a well-defined (byte) offset. Fixes https://github.com/llvm/llvm-project/issues/90695. --- .../CodeGen/InterleavedLoadCombinePass.cpp | 3 +++ .../interleaved-load-combine-pr90695.ll | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 llvm/test/CodeGen/AArch64/interleaved-load-combine-pr90695.ll diff --git a/llvm/lib/CodeGen/InterleavedLoadCombinePass.cpp b/llvm/lib/CodeGen/InterleavedLoadCombinePass.cpp index e5f164b18272..a9b59e738c00 100644 --- a/llvm/lib/CodeGen/InterleavedLoadCombinePass.cpp +++ b/llvm/lib/CodeGen/InterleavedLoadCombinePass.cpp @@ -877,6 +877,9 @@ public: if (LI->isAtomic()) return false; + if (!DL.typeSizeEqualsStoreSize(Result.VTy->getElementType())) + return false; + // Get the base polynomial computePolynomialFromPointer(*LI->getPointerOperand(), Offset, BasePtr, DL); diff --git a/llvm/test/CodeGen/AArch64/interleaved-load-combine-pr90695.ll b/llvm/test/CodeGen/AArch64/interleaved-load-combine-pr90695.ll new file mode 100644 index 000000000000..ee75b3a083f7 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/interleaved-load-combine-pr90695.ll @@ -0,0 +1,19 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4 +; RUN: opt -S -passes=interleaved-load-combine < %s | FileCheck %s + +target triple = "aarch64-unknown-windows-gnu" + +; Make sure we don't crash on loads of vectors of non-byte-sized types. +define <4 x i1> @test(ptr %p) { +; CHECK-LABEL: define <4 x i1> @test( +; CHECK-SAME: ptr [[P:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[LOAD:%.*]] = load <2 x i1>, ptr [[P]], align 1 +; CHECK-NEXT: [[SHUF:%.*]] = shufflevector <2 x i1> [[LOAD]], <2 x i1> zeroinitializer, <4 x i32> +; CHECK-NEXT: ret <4 x i1> [[SHUF]] +; +entry: + %load = load <2 x i1>, ptr %p, align 1 + %shuf = shufflevector <2 x i1> %load, <2 x i1> zeroinitializer, <4 x i32> + ret <4 x i1> %shuf +} -- GitLab From ad7ee900c70ba4e8dd442b35cddf390369698a59 Mon Sep 17 00:00:00 2001 From: Maksim Panchenko Date: Wed, 1 May 2024 18:22:38 -0700 Subject: [PATCH 0002/1014] [BOLT][NFC] Add BOLTReserved to BinaryContext (#90766) Use BOLTReserved to track binary space preallocated for BOLT. --- bolt/include/bolt/Core/BinaryContext.h | 4 +++ bolt/lib/Rewrite/RewriteInstance.cpp | 38 ++++++++++++++------------ 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h index 8b1af9e81539..75765819ac46 100644 --- a/bolt/include/bolt/Core/BinaryContext.h +++ b/bolt/include/bolt/Core/BinaryContext.h @@ -20,6 +20,7 @@ #include "bolt/Core/JumpTable.h" #include "bolt/Core/MCPlusBuilder.h" #include "bolt/RuntimeLibs/RuntimeLibrary.h" +#include "llvm/ADT/AddressRanges.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/iterator.h" @@ -726,6 +727,9 @@ public: uint64_t OldTextSectionOffset{0}; uint64_t OldTextSectionSize{0}; + /// Area in the input binary reserved for BOLT. + AddressRange BOLTReserved; + /// Address of the code/function that is executed before any other code in /// the binary. std::optional StartFunctionAddress; diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp index 23f79e3c135a..62759b7222a7 100644 --- a/bolt/lib/Rewrite/RewriteInstance.cpp +++ b/bolt/lib/Rewrite/RewriteInstance.cpp @@ -3628,13 +3628,19 @@ void RewriteInstance::mapFileSections(BOLTLinker::SectionMapper MapSection) { } if (StartBD) { + if (StartBD->getAddress() >= EndBD->getAddress()) { + BC->errs() << "BOLT-ERROR: invalid reserved space boundaries\n"; + exit(1); + } + BC->BOLTReserved = AddressRange(StartBD->getAddress(), EndBD->getAddress()); + BC->outs() + << "BOLT-INFO: using reserved space for allocating new sections\n"; + PHDRTableOffset = 0; PHDRTableAddress = 0; NewTextSegmentAddress = 0; NewTextSegmentOffset = 0; - NextAvailableAddress = StartBD->getAddress(); - BC->outs() - << "BOLT-INFO: using reserved space for allocating new sections\n"; + NextAvailableAddress = BC->BOLTReserved.start(); } // If no new .eh_frame was written, remove relocated original .eh_frame. @@ -3657,12 +3663,12 @@ void RewriteInstance::mapFileSections(BOLTLinker::SectionMapper MapSection) { // Map the rest of the sections. mapAllocatableSections(MapSection); - if (StartBD) { - const uint64_t ReservedSpace = EndBD->getAddress() - StartBD->getAddress(); - const uint64_t AllocatedSize = NextAvailableAddress - StartBD->getAddress(); - if (ReservedSpace < AllocatedSize) { - BC->errs() << "BOLT-ERROR: reserved space (" << ReservedSpace << " byte" - << (ReservedSpace == 1 ? "" : "s") + if (!BC->BOLTReserved.empty()) { + const uint64_t AllocatedSize = + NextAvailableAddress - BC->BOLTReserved.start(); + if (BC->BOLTReserved.size() < AllocatedSize) { + BC->errs() << "BOLT-ERROR: reserved space (" << BC->BOLTReserved.size() + << " byte" << (BC->BOLTReserved.size() == 1 ? "" : "s") << ") is smaller than required for new allocations (" << AllocatedSize << " bytes)\n"; exit(1); @@ -5852,13 +5858,11 @@ void RewriteInstance::writeEHFrameHeader() { NextAvailableAddress += EHFrameHdrSec.getOutputSize(); - if (const BinaryData *ReservedEnd = - BC->getBinaryDataByName(getBOLTReservedEnd())) { - if (NextAvailableAddress > ReservedEnd->getAddress()) { - BC->errs() << "BOLT-ERROR: unable to fit " << getEHFrameHdrSectionName() - << " into reserved space\n"; - exit(1); - } + if (!BC->BOLTReserved.empty() && + (NextAvailableAddress > BC->BOLTReserved.end())) { + BC->errs() << "BOLT-ERROR: unable to fit " << getEHFrameHdrSectionName() + << " into reserved space\n"; + exit(1); } // Merge new .eh_frame with the relocated original so that gdb can locate all @@ -5892,7 +5896,7 @@ uint64_t RewriteInstance::getNewValueForSymbol(const StringRef Name) { uint64_t RewriteInstance::getFileOffsetForAddress(uint64_t Address) const { // Check if it's possibly part of the new segment. - if (Address >= NewTextSegmentAddress) + if (NewTextSegmentAddress && Address >= NewTextSegmentAddress) return Address - NewTextSegmentAddress + NewTextSegmentOffset; // Find an existing segment that matches the address. -- GitLab From 3d65bd935a91439c483c56a966edc283a2b1130d Mon Sep 17 00:00:00 2001 From: Andreas Jonson Date: Thu, 2 May 2024 03:48:23 +0200 Subject: [PATCH 0003/1014] [NFC] Reduce copies created of ConstantRange when getting ConstantRangeAttributes (#90335) Think that it can be good to reduce the number of copies created when working with ConstantRangeAttributes. --- llvm/include/llvm/IR/Attributes.h | 4 ++-- llvm/lib/IR/AttributeImpl.h | 4 ++-- llvm/lib/IR/Attributes.cpp | 8 ++++---- llvm/lib/IR/Verifier.cpp | 3 ++- llvm/lib/Transforms/Utils/FunctionComparator.cpp | 4 ++-- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h index 5e3ba1f32e6a..dd1195571489 100644 --- a/llvm/include/llvm/IR/Attributes.h +++ b/llvm/include/llvm/IR/Attributes.h @@ -224,7 +224,7 @@ public: /// Return the attribute's value as a ConstantRange. This requires the /// attribute to be a ConstantRange attribute. - ConstantRange getValueAsConstantRange() const; + const ConstantRange &getValueAsConstantRange() const; /// Returns the alignment field of an attribute as a byte alignment /// value. @@ -265,7 +265,7 @@ public: FPClassTest getNoFPClass() const; /// Returns the value of the range attribute. - ConstantRange getRange() const; + const ConstantRange &getRange() const; /// The Attribute is converted to a string of equivalent mnemonic. This /// is, presumably, for writing out the mnemonics for the assembly writer. diff --git a/llvm/lib/IR/AttributeImpl.h b/llvm/lib/IR/AttributeImpl.h index 58dc14588f41..dc5b80b6da68 100644 --- a/llvm/lib/IR/AttributeImpl.h +++ b/llvm/lib/IR/AttributeImpl.h @@ -77,7 +77,7 @@ public: Type *getValueAsType() const; - ConstantRange getValueAsConstantRange() const; + const ConstantRange &getValueAsConstantRange() const; /// Used when sorting the attributes. bool operator<(const AttributeImpl &AI) const; @@ -219,7 +219,7 @@ public: ConstantRangeAttributeImpl(Attribute::AttrKind Kind, const ConstantRange &CR) : EnumAttributeImpl(ConstantRangeAttrEntry, Kind), CR(CR) {} - ConstantRange getConstantRangeValue() const { return CR; } + const ConstantRange &getConstantRangeValue() const { return CR; } }; class AttributeBitSet { diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp index c6e511b46e51..c8d6bdd42387 100644 --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -360,7 +360,7 @@ Type *Attribute::getValueAsType() const { return pImpl->getValueAsType(); } -ConstantRange Attribute::getValueAsConstantRange() const { +const ConstantRange &Attribute::getValueAsConstantRange() const { assert(isConstantRangeAttribute() && "Invalid attribute type to get the value as a ConstantRange!"); return pImpl->getValueAsConstantRange(); @@ -444,7 +444,7 @@ FPClassTest Attribute::getNoFPClass() const { return static_cast(pImpl->getValueAsInt()); } -ConstantRange Attribute::getRange() const { +const ConstantRange &Attribute::getRange() const { assert(hasAttribute(Attribute::Range) && "Trying to get range args from non-range attribute"); return pImpl->getValueAsConstantRange(); @@ -607,7 +607,7 @@ std::string Attribute::getAsString(bool InAttrGrp) const { if (hasAttribute(Attribute::Range)) { std::string Result; raw_string_ostream OS(Result); - ConstantRange CR = getValueAsConstantRange(); + const ConstantRange &CR = getValueAsConstantRange(); OS << "range("; OS << "i" << CR.getBitWidth() << " "; OS << CR.getLower() << ", " << CR.getUpper(); @@ -735,7 +735,7 @@ Type *AttributeImpl::getValueAsType() const { return static_cast(this)->getTypeValue(); } -ConstantRange AttributeImpl::getValueAsConstantRange() const { +const ConstantRange &AttributeImpl::getValueAsConstantRange() const { assert(isConstantRangeAttribute()); return static_cast(this) ->getConstantRangeValue(); diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 16ed167bd671..41d3fce7eef7 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -2065,7 +2065,8 @@ void Verifier::verifyParameterAttrs(AttributeSet Attrs, Type *Ty, "Invalid value for 'nofpclass' test mask", V); } if (Attrs.hasAttribute(Attribute::Range)) { - auto CR = Attrs.getAttribute(Attribute::Range).getValueAsConstantRange(); + const ConstantRange &CR = + Attrs.getAttribute(Attribute::Range).getValueAsConstantRange(); Check(Ty->isIntOrIntVectorTy(CR.getBitWidth()), "Range bit width must match type bit width!", V); } diff --git a/llvm/lib/Transforms/Utils/FunctionComparator.cpp b/llvm/lib/Transforms/Utils/FunctionComparator.cpp index 67aeba7048f8..d95248c84b86 100644 --- a/llvm/lib/Transforms/Utils/FunctionComparator.cpp +++ b/llvm/lib/Transforms/Utils/FunctionComparator.cpp @@ -148,8 +148,8 @@ int FunctionComparator::cmpAttrs(const AttributeList L, if (LA.getKindAsEnum() != RA.getKindAsEnum()) return cmpNumbers(LA.getKindAsEnum(), RA.getKindAsEnum()); - ConstantRange LCR = LA.getRange(); - ConstantRange RCR = RA.getRange(); + const ConstantRange &LCR = LA.getRange(); + const ConstantRange &RCR = RA.getRange(); if (int Res = cmpAPInts(LCR.getLower(), RCR.getLower())) return Res; if (int Res = cmpAPInts(LCR.getUpper(), RCR.getUpper())) -- GitLab From 06449095c22097508854c00f06d27bdccf1ed667 Mon Sep 17 00:00:00 2001 From: Yeting Kuo <46629943+yetingk@users.noreply.github.com> Date: Thu, 2 May 2024 10:32:40 +0800 Subject: [PATCH 0004/1014] [RISCV] Avoid using x7/t2 for indirect branches which need landing pad. (#68292) When Zicfilp enabled, this avoids selecting indirect jumps to PseudoBRIND/PseudoCALLIndirect/PseudoTAILIndirect, since they may uses X7 as rs1 and be identified as a software guarded jump. There is an another PR #66762 to use software guarded jump for jumptable branch. --- .../RISCV/GISel/RISCVRegisterBankInfo.cpp | 2 + llvm/lib/Target/RISCV/RISCVFeatures.td | 2 + llvm/lib/Target/RISCV/RISCVInstrInfo.td | 34 +++++++++++-- llvm/lib/Target/RISCV/RISCVRegisterInfo.td | 3 ++ .../CodeGen/RISCV/zicfilp-indirect-branch.ll | 48 +++++++++++++++++++ 5 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 llvm/test/CodeGen/RISCV/zicfilp-indirect-branch.ll diff --git a/llvm/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.cpp b/llvm/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.cpp index cc534f29685f..686c8d89a732 100644 --- a/llvm/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.cpp +++ b/llvm/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.cpp @@ -117,7 +117,9 @@ RISCVRegisterBankInfo::getRegBankFromRegClass(const TargetRegisterClass &RC, case RISCV::GPRNoX0RegClassID: case RISCV::GPRNoX0X2RegClassID: case RISCV::GPRJALRRegClassID: + case RISCV::GPRJALRNonX7RegClassID: case RISCV::GPRTCRegClassID: + case RISCV::GPRTCNonX7RegClassID: case RISCV::GPRC_and_GPRTCRegClassID: case RISCV::GPRCRegClassID: case RISCV::GPRC_and_SR07RegClassID: diff --git a/llvm/lib/Target/RISCV/RISCVFeatures.td b/llvm/lib/Target/RISCV/RISCVFeatures.td index c3dc4ea53697..eab1863fdc32 100644 --- a/llvm/lib/Target/RISCV/RISCVFeatures.td +++ b/llvm/lib/Target/RISCV/RISCVFeatures.td @@ -156,6 +156,8 @@ def FeatureStdExtZicfilp def HasStdExtZicfilp : Predicate<"Subtarget->hasStdExtZicfilp()">, AssemblerPredicate<(all_of FeatureStdExtZicfilp), "'Zicfilp' (Landing pad)">; +def NoStdExtZicfilp : Predicate<"!Subtarget->hasStdExtZicfilp()">, + AssemblerPredicate<(all_of (not FeatureStdExtZicfilp))>; def FeatureStdExtZicfiss : RISCVExperimentalExtension<"zicfiss", 0, 4, diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td index da4020758eb6..b867eccf4266 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td @@ -1448,13 +1448,29 @@ let isBarrier = 1, isBranch = 1, isTerminator = 1 in def PseudoBR : Pseudo<(outs), (ins simm21_lsb0_jal:$imm20), [(br bb:$imm20)]>, PseudoInstExpansion<(JAL X0, simm21_lsb0_jal:$imm20)>; -let isBarrier = 1, isBranch = 1, isIndirectBranch = 1, isTerminator = 1 in +let Predicates = [NoStdExtZicfilp], + isBarrier = 1, isBranch = 1, isIndirectBranch = 1, isTerminator = 1 in def PseudoBRIND : Pseudo<(outs), (ins GPRJALR:$rs1, simm12:$imm12), []>, PseudoInstExpansion<(JALR X0, GPR:$rs1, simm12:$imm12)>; +let Predicates = [HasStdExtZicfilp], + isBarrier = 1, isBranch = 1, isIndirectBranch = 1, isTerminator = 1 in +def PseudoBRINDNonX7 : Pseudo<(outs), (ins GPRJALRNonX7:$rs1, simm12:$imm12), []>, + PseudoInstExpansion<(JALR X0, GPR:$rs1, simm12:$imm12)>; + +// For Zicfilp, need to avoid using X7/T2 for indirect branches which need +// landing pad. +let Predicates = [HasStdExtZicfilp] in { +def : Pat<(brind GPRJALRNonX7:$rs1), (PseudoBRINDNonX7 GPRJALRNonX7:$rs1, 0)>; +def : Pat<(brind (add GPRJALRNonX7:$rs1, simm12:$imm12)), + (PseudoBRINDNonX7 GPRJALRNonX7:$rs1, simm12:$imm12)>; +} + +let Predicates = [NoStdExtZicfilp] in { def : Pat<(brind GPRJALR:$rs1), (PseudoBRIND GPRJALR:$rs1, 0)>; def : Pat<(brind (add GPRJALR:$rs1, simm12:$imm12)), (PseudoBRIND GPRJALR:$rs1, simm12:$imm12)>; +} // PseudoCALLReg is a generic pseudo instruction for calls which will eventually // expand to auipc and jalr while encoding, with any given register used as the @@ -1484,10 +1500,16 @@ def : Pat<(riscv_call texternalsym:$func), (PseudoCALL texternalsym:$func)>; def : Pat<(riscv_sret_glue), (SRET (XLenVT X0), (XLenVT X0))>; def : Pat<(riscv_mret_glue), (MRET (XLenVT X0), (XLenVT X0))>; -let isCall = 1, Defs = [X1] in +let isCall = 1, Defs = [X1] in { +let Predicates = [NoStdExtZicfilp] in def PseudoCALLIndirect : Pseudo<(outs), (ins GPRJALR:$rs1), [(riscv_call GPRJALR:$rs1)]>, PseudoInstExpansion<(JALR X1, GPR:$rs1, 0)>; +let Predicates = [HasStdExtZicfilp] in +def PseudoCALLIndirectNonX7 : Pseudo<(outs), (ins GPRJALRNonX7:$rs1), + [(riscv_call GPRJALRNonX7:$rs1)]>, + PseudoInstExpansion<(JALR X1, GPR:$rs1, 0)>; +} let isBarrier = 1, isReturn = 1, isTerminator = 1 in def PseudoRET : Pseudo<(outs), (ins), [(riscv_ret_glue)]>, @@ -1502,10 +1524,16 @@ def PseudoTAIL : Pseudo<(outs), (ins call_symbol:$dst), [], "tail", "$dst">, Sched<[WriteIALU, WriteJalr, ReadJalr]>; -let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, Uses = [X2] in +let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, Uses = [X2] in { +let Predicates = [NoStdExtZicfilp] in def PseudoTAILIndirect : Pseudo<(outs), (ins GPRTC:$rs1), [(riscv_tail GPRTC:$rs1)]>, PseudoInstExpansion<(JALR X0, GPR:$rs1, 0)>; +let Predicates = [HasStdExtZicfilp] in +def PseudoTAILIndirectNonX7 : Pseudo<(outs), (ins GPRTCNonX7:$rs1), + [(riscv_tail GPRTCNonX7:$rs1)]>, + PseudoInstExpansion<(JALR X0, GPR:$rs1, 0)>; +} def : Pat<(riscv_tail (iPTR tglobaladdr:$dst)), (PseudoTAIL tglobaladdr:$dst)>; diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.td b/llvm/lib/Target/RISCV/RISCVRegisterInfo.td index 316daf2763ca..90e62dc39e6a 100644 --- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.td +++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.td @@ -173,6 +173,8 @@ def GPRNoX0X2 : GPRRegisterClass<(sub GPR, X0, X2)>; // by tablegen. def GPRJALR : GPRRegisterClass<(sub GPR, (sequence "X%u", 0, 5))>; +def GPRJALRNonX7 : GPRRegisterClass<(sub GPRJALR, X7)>; + def GPRC : GPRRegisterClass<(add (sequence "X%u", 10, 15), (sequence "X%u", 8, 9))>; @@ -183,6 +185,7 @@ def GPRC : GPRRegisterClass<(add (sequence "X%u", 10, 15), def GPRTC : GPRRegisterClass<(add (sequence "X%u", 6, 7), (sequence "X%u", 10, 17), (sequence "X%u", 28, 31))>; +def GPRTCNonX7 : GPRRegisterClass<(sub GPRTC, X7)>; def SP : GPRRegisterClass<(add X2)>; diff --git a/llvm/test/CodeGen/RISCV/zicfilp-indirect-branch.ll b/llvm/test/CodeGen/RISCV/zicfilp-indirect-branch.ll new file mode 100644 index 000000000000..bccd28ee7e2b --- /dev/null +++ b/llvm/test/CodeGen/RISCV/zicfilp-indirect-branch.ll @@ -0,0 +1,48 @@ +; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 3 +; RUN: llc -mtriple=riscv64 -stop-after=finalize-isel < %s | FileCheck %s +; RUN: llc -mtriple=riscv64 -mattr=+experimental-zicfilp -stop-after=finalize-isel < %s | FileCheck -check-prefixes=ZICFILP %s + +@brind.arr = internal unnamed_addr constant [2 x ptr] [ptr blockaddress(@brind, %5), ptr blockaddress(@brind, %8)], align 8 +@x = dso_local global i32 0, align 4 + +define void @brind(i32 noundef signext %0) { + ; CHECK-LABEL: name: brind + ; CHECK: PseudoBRIND killed [[VAR:%.*]], 0 + ; ZICFILP-LABEL: name: brind + ; ZICFILP: PseudoBRINDNonX7 killed [[VAR:%.*]], 0 + %2 = sext i32 %0 to i64 + %3 = getelementptr inbounds [2 x ptr], ptr @brind.arr, i64 0, i64 %2 + %4 = load ptr, ptr %3, align 8 + indirectbr ptr %4, [label %5, label %8] + +5: ; preds = %1 + %6 = load i32, ptr @x, align 4 + %7 = add nsw i32 %6, 2 + store i32 %7, ptr @x, align 4 + br label %8 + +8: ; preds = %5, %1 + %9 = load i32, ptr @x, align 4 + %10 = add nsw i32 %9, 1 + store i32 %10, ptr @x, align 4 + ret void +} + +define i32 @indirect_call(ptr %0) { + ; CHECK-LABEL: name: indirect_call + ; CHECK: PseudoCALLIndirect + ; ZICFILP-LABEL: name: indirect_call + ; ZICFILP: PseudoCALLIndirectNonX7 + call void %0() + ret i32 0 +} + + +define void @indirect_tail(ptr %0) { + ; CHECK-LABEL: name: indirect_tail + ; CHECK: PseudoTAILIndirect + ; ZICFILP-LABEL: name: indirect_tail + ; ZICFILP: PseudoTAILIndirectNonX7 + tail call void %0() + ret void +} -- GitLab From a370d57b9ff9e385e9a51bf6b1d366890f4091cd Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Thu, 2 May 2024 10:41:39 +0800 Subject: [PATCH 0005/1014] [NFC][clang-tidy] update check list document (#90813) --- clang-tools-extra/docs/clang-tidy/checks/list.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 49747ff896ba..5cdaf9e35b6a 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -120,7 +120,7 @@ Clang-Tidy Checks :doc:`bugprone-posix-return `, "Yes" :doc:`bugprone-redundant-branch-condition `, "Yes" :doc:`bugprone-reserved-identifier `, "Yes" - :doc:`bugprone-return-const-ref-from-parameter ` + :doc:`bugprone-return-const-ref-from-parameter `, :doc:`bugprone-shared-ptr-array-mismatch `, "Yes" :doc:`bugprone-signal-handler `, :doc:`bugprone-signed-char-misuse `, -- GitLab From 889e60db2daf93c0bb3f7ae0db0a60bfefb86d89 Mon Sep 17 00:00:00 2001 From: Piotr Zegar Date: Thu, 2 May 2024 04:42:42 +0200 Subject: [PATCH 0006/1014] [clang-tidy] Ignore casts from void to void in bugprone-casting-through-void (#90566) Improved bugprone-casting-through-void check by ignoring casts where source is already a void pointer, making middle void pointer casts bug-free. Closes #87069 --- .../clang-tidy/bugprone/CastingThroughVoidCheck.cpp | 5 ++--- clang-tools-extra/docs/ReleaseNotes.rst | 5 +++++ .../clang-tidy/checkers/bugprone/casting-through-void.cpp | 7 +++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/clang-tools-extra/clang-tidy/bugprone/CastingThroughVoidCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/CastingThroughVoidCheck.cpp index 4c2416a89aef..9e714b4be4df 100644 --- a/clang-tools-extra/clang-tidy/bugprone/CastingThroughVoidCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/CastingThroughVoidCheck.cpp @@ -7,12 +7,10 @@ //===----------------------------------------------------------------------===// #include "CastingThroughVoidCheck.h" -#include "clang/AST/ASTContext.h" #include "clang/AST/Expr.h" #include "clang/AST/Type.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" -#include "llvm/ADT/StringSet.h" using namespace clang::ast_matchers; @@ -27,7 +25,8 @@ void CastingThroughVoidCheck::registerMatchers(MatchFinder *Finder) { hasSourceExpression( explicitCastExpr( hasSourceExpression( - expr(hasType(qualType().bind("source_type")))), + expr(hasType(qualType(unless(pointsTo(voidType()))) + .bind("source_type")))), hasDestinationType( qualType(pointsTo(voidType())).bind("void_type"))) .bind("cast"))), diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 5956ccb92548..6c9745d585b1 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -173,6 +173,11 @@ Changes in existing checks ` check by detecting side effect from calling a method with non-const reference parameters. +- Improved :doc:`bugprone-casting-through-void + ` check by ignoring casts + where source is already a ``void``` pointer, making middle ``void`` pointer + casts bug-free. + - Improved :doc:`bugprone-forwarding-reference-overload ` check to ignore deleted constructors which won't hide other overloads. diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/casting-through-void.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/casting-through-void.cpp index 3913d2d8a295..a784e4988587 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/casting-through-void.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/casting-through-void.cpp @@ -89,3 +89,10 @@ void bit_cast() { __builtin_bit_cast(int *, static_cast(&d)); // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: do not cast 'double *' to 'int *' through 'void *' [bugprone-casting-through-void] } + +namespace PR87069 { + void castconstVoidToVoid() { + const void* ptr = nullptr; + int* numberPtr = static_cast(const_cast(ptr)); + } +} -- GitLab From 1f1a417925624a67cb6cb2bbbdd901e0e90ee237 Mon Sep 17 00:00:00 2001 From: Piotr Zegar Date: Thu, 2 May 2024 04:43:35 +0200 Subject: [PATCH 0007/1014] [clang-tidy] Relax readability-const-return-type (#90560) From now readability-const-return-type won't provide warnings for returning const types, where const is not on top level. In such case const there is a performance issue, but not a readability. Closes #73270 --- .../readability/ConstReturnTypeCheck.cpp | 21 ++++--------------- clang-tools-extra/docs/ReleaseNotes.rst | 4 ++++ .../readability/const-return-type.cpp | 20 +++++++++++++++--- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/clang-tools-extra/clang-tidy/readability/ConstReturnTypeCheck.cpp b/clang-tools-extra/clang-tidy/readability/ConstReturnTypeCheck.cpp index e92350632b55..c13a8010c222 100644 --- a/clang-tools-extra/clang-tidy/readability/ConstReturnTypeCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/ConstReturnTypeCheck.cpp @@ -55,14 +55,6 @@ AST_MATCHER(QualType, isLocalConstQualified) { return Node.isLocalConstQualified(); } -AST_MATCHER(QualType, isTypeOfType) { - return isa(Node.getTypePtr()); -} - -AST_MATCHER(QualType, isTypeOfExprType) { - return isa(Node.getTypePtr()); -} - struct CheckResult { // Source range of the relevant `const` token in the definition being checked. CharSourceRange ConstRange; @@ -110,16 +102,11 @@ void ConstReturnTypeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { void ConstReturnTypeCheck::registerMatchers(MatchFinder *Finder) { // Find all function definitions for which the return types are `const` // qualified, ignoring decltype types. - auto NonLocalConstType = - qualType(unless(isLocalConstQualified()), - anyOf(decltypeType(), autoType(), isTypeOfType(), - isTypeOfExprType(), substTemplateTypeParmType())); Finder->addMatcher( - functionDecl( - returns(allOf(isConstQualified(), unless(NonLocalConstType))), - anyOf(isDefinition(), cxxMethodDecl(isPure())), - // Overridden functions are not actionable. - unless(cxxMethodDecl(isOverride()))) + functionDecl(returns(isLocalConstQualified()), + anyOf(isDefinition(), cxxMethodDecl(isPure())), + // Overridden functions are not actionable. + unless(cxxMethodDecl(isOverride()))) .bind("func"), this); } diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 6c9745d585b1..d59da3a61b7b 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -318,6 +318,10 @@ Changes in existing checks ` check by adding fix-its. +- Improved :doc:`readability-const-return-type + ` check to eliminate false + positives when returning types with const not at the top level. + - Improved :doc:`readability-duplicate-include ` check by excluding include directives that form the filename using macro. diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/const-return-type.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/const-return-type.cpp index 10b2858c9caa..76a3555663b1 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/const-return-type.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/const-return-type.cpp @@ -215,11 +215,9 @@ CREATE_FUNCTION(); using ty = const int; ty p21() {} -// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'ty' (aka 'const int') is typedef const int ty2; ty2 p22() {} -// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'ty2' (aka 'const int') i // Declaration uses a macro, while definition doesn't. In this case, we won't // fix the declaration, and will instead issue a warning. @@ -249,7 +247,6 @@ auto p27() -> int const { return 3; } // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu std::add_const::type p28() { return 3; } -// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'std::add_const::typ // p29, p30 are based on // llvm/projects/test-suite/SingleSource/Benchmarks/Misc-C++-EH/spirit.cpp: @@ -355,3 +352,20 @@ struct p41 { // CHECK-FIXES: T foo() const { return 2; } }; template struct p41; + +namespace PR73270 { + template + struct Pair { + using first_type = const K; + using second_type = V; + }; + + template + typename PairType::first_type getFirst() { + return {}; + } + + void test() { + getFirst>(); + } +} -- GitLab From df91cde4da62aec22e4d384b1bc800590c7f561a Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Wed, 1 May 2024 20:29:25 -0700 Subject: [PATCH 0008/1014] [alpha.webkit.UncountedCallArgsChecker] Ignore methods of WTF String classes. (#90704) --- .../WebKit/UncountedCallArgsChecker.cpp | 13 +- .../WebKit/call-args-wtf-containers.cpp | 118 ++++++++++++++++++ .../Analysis/Checkers/WebKit/mock-types.h | 9 +- 3 files changed, 134 insertions(+), 6 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index ae494de58da3..0f40ecc7ba30 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -227,10 +227,17 @@ public: return NamespaceName == "WTF" && (MethodName == "find" || MethodName == "findIf" || MethodName == "reverseFind" || MethodName == "reverseFindIf" || - MethodName == "get" || MethodName == "inlineGet" || - MethodName == "contains" || MethodName == "containsIf") && + MethodName == "findIgnoringASCIICase" || MethodName == "get" || + MethodName == "inlineGet" || MethodName == "contains" || + MethodName == "containsIf" || + MethodName == "containsIgnoringASCIICase" || + MethodName == "startsWith" || MethodName == "endsWith" || + MethodName == "startsWithIgnoringASCIICase" || + MethodName == "endsWithIgnoringASCIICase" || + MethodName == "substring") && (ClsName.ends_with("Vector") || ClsName.ends_with("Set") || - ClsName.ends_with("Map")); + ClsName.ends_with("Map") || ClsName == "StringImpl" || + ClsName.ends_with("String")); } void reportBug(const Expr *CallArg, const ParmVarDecl *Param) const { diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp index 0a63a7898561..17e25d9a6270 100644 --- a/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp +++ b/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp @@ -4,6 +4,92 @@ namespace WTF { + constexpr unsigned long notFound = static_cast(-1); + + class String; + class StringImpl; + + class StringView { + public: + StringView(const String&); + private: + RefPtr m_impl; + }; + + class StringImpl { + public: + void ref() const { ++m_refCount; } + void deref() const { + if (!--m_refCount) + delete this; + } + + static constexpr unsigned s_flagIs8Bit = 1u << 0; + bool is8Bit() const { return m_hashAndFlags & s_flagIs8Bit; } + const char* characters8() const { return m_char8; } + const short* characters16() const { return m_char16; } + unsigned length() const { return m_length; } + Ref substring(unsigned position, unsigned length) const; + + unsigned long find(char) const; + unsigned long find(StringView) const; + unsigned long contains(StringView) const; + unsigned long findIgnoringASCIICase(StringView) const; + + bool startsWith(StringView) const; + bool startsWithIgnoringASCIICase(StringView) const; + bool endsWith(StringView) const; + bool endsWithIgnoringASCIICase(StringView) const; + + private: + mutable unsigned m_refCount { 0 }; + unsigned m_length { 0 }; + union { + const char* m_char8; + const short* m_char16; + }; + unsigned m_hashAndFlags { 0 }; + }; + + class String { + public: + String() = default; + String(StringImpl& impl) : m_impl(&impl) { } + String(StringImpl* impl) : m_impl(impl) { } + String(Ref&& impl) : m_impl(impl.get()) { } + StringImpl* impl() { return m_impl.get(); } + unsigned length() const { return m_impl ? m_impl->length() : 0; } + const char* characters8() const { return m_impl ? m_impl->characters8() : nullptr; } + const short* characters16() const { return m_impl ? m_impl->characters16() : nullptr; } + + bool is8Bit() const { return !m_impl || m_impl->is8Bit(); } + + unsigned long find(char character) const { return m_impl ? m_impl->find(character) : notFound; } + unsigned long find(StringView str) const { return m_impl ? m_impl->find(str) : notFound; } + unsigned long findIgnoringASCIICase(StringView) const; + + bool contains(char character) const { return find(character) != notFound; } + bool contains(StringView) const; + bool containsIgnoringASCIICase(StringView) const; + + bool startsWith(StringView) const; + bool startsWithIgnoringASCIICase(StringView) const; + bool endsWith(StringView) const; + bool endsWithIgnoringASCIICase(StringView) const; + + String substring(unsigned position, unsigned length) const + { + if (!m_impl) + return { }; + if (!position && length >= m_impl->length()) + return *this; + return m_impl->substring(position, length); + } + + private: + RefPtr m_impl; + }; + template class HashSet { public: @@ -89,6 +175,9 @@ namespace WTF { } +using WTF::StringView; +using WTF::StringImpl; +using WTF::String; using WTF::HashSet; using WTF::HashMap; using WTF::WeakHashSet; @@ -101,8 +190,37 @@ public: }; RefCounted* object(); +StringImpl* strImpl(); +String* str(); +StringView strView(); void test() { + strImpl()->is8Bit(); + strImpl()->characters8(); + strImpl()->characters16(); + strImpl()->length(); + strImpl()->substring(2, 4); + strImpl()->find(strView()); + strImpl()->contains(strView()); + strImpl()->findIgnoringASCIICase(strView()); + strImpl()->startsWith(strView()); + strImpl()->startsWithIgnoringASCIICase(strView()); + strImpl()->endsWith(strView()); + strImpl()->endsWithIgnoringASCIICase(strView()); + + str()->is8Bit(); + str()->characters8(); + str()->characters16(); + str()->length(); + str()->substring(2, 4); + str()->find(strView()); + str()->contains(strView()); + str()->findIgnoringASCIICase(strView()); + str()->startsWith(strView()); + str()->startsWithIgnoringASCIICase(strView()); + str()->endsWith(strView()); + str()->endsWithIgnoringASCIICase(strView()); + HashSet> set; set.find(*object()); set.contains(*object()); diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h index c27ea9baaf3b..c427b22fd683 100644 --- a/clang/test/Analysis/Checkers/WebKit/mock-types.h +++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h @@ -47,7 +47,7 @@ template , typename RefDerefTra typename PtrTraits::StorageType t; Ref() : t{} {}; - Ref(T &t) : t(RefDerefTraits::refIfNotNull(t)) { } + Ref(T &t) : t(&RefDerefTraits::ref(t)) { } Ref(const Ref& o) : t(RefDerefTraits::refIfNotNull(PtrTraits::unwrap(o.t))) { } ~Ref() { RefDerefTraits::derefIfNotNull(PtrTraits::exchange(t, nullptr)); } T &get() { return *PtrTraits::unwrap(t); } @@ -55,7 +55,7 @@ template , typename RefDerefTra T *operator->() { return PtrTraits::unwrap(t); } operator const T &() const { return *PtrTraits::unwrap(t); } operator T &() { return *PtrTraits::unwrap(t); } - T* leakRef() { PtrTraits::exchange(t, nullptr); } + T* leakRef() { return PtrTraits::exchange(t, nullptr); } }; template struct RefPtr { @@ -67,6 +67,9 @@ template struct RefPtr { if (t) t->ref(); } + RefPtr(Ref&& o) + : t(o.leakRef()) + { } ~RefPtr() { if (t) t->deref(); @@ -76,7 +79,7 @@ template struct RefPtr { const T *operator->() const { return t; } T &operator*() { return *t; } RefPtr &operator=(T *) { return *this; } - operator bool() { return t; } + operator bool() const { return t; } }; template bool operator==(const RefPtr &, const RefPtr &) { -- GitLab From dd09a7db03f8ebaeb3f49203bb53ac730666c1b4 Mon Sep 17 00:00:00 2001 From: Maksim Panchenko Date: Wed, 1 May 2024 21:45:44 -0700 Subject: [PATCH 0009/1014] [BOLT] Add split function support for the Linux kernel (#90541) While rewriting the Linux kernel, we try to fit optimized functions into their original boundaries. When a function becomes larger, we skip it during the rewrite and end up with less than optimal code layout. To overcome that issue, add support for --split-function option so that hot part of the function could be fit into the original space. The cold part should go to reserved space in the binary. --- bolt/lib/Passes/SplitFunctions.cpp | 13 +++++++ bolt/lib/Rewrite/LinuxKernelRewriter.cpp | 44 +++++++++++++++++++----- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/bolt/lib/Passes/SplitFunctions.cpp b/bolt/lib/Passes/SplitFunctions.cpp index f9e634d15a97..bd0b6dea0e06 100644 --- a/bolt/lib/Passes/SplitFunctions.cpp +++ b/bolt/lib/Passes/SplitFunctions.cpp @@ -715,6 +715,12 @@ Error SplitFunctions::runOnFunctions(BinaryContext &BC) { if (!opts::SplitFunctions) return Error::success(); + if (BC.IsLinuxKernel && BC.BOLTReserved.empty()) { + BC.errs() << "BOLT-ERROR: split functions require reserved space in the " + "Linux kernel binary\n"; + exit(1); + } + // If split strategy is not CDSplit, then a second run of the pass is not // needed after function reordering. if (BC.HasFinalizedFunctionOrder && @@ -829,6 +835,13 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) { } } } + + // Outlining blocks with dynamic branches is not supported yet. + if (BC.IsLinuxKernel) { + if (llvm::any_of( + *BB, [&](MCInst &Inst) { return BC.MIB->isDynamicBranch(Inst); })) + BB->setCanOutline(false); + } } BF.getLayout().updateLayoutIndices(); diff --git a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp index 17077b4fa248..b976699cef17 100644 --- a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp +++ b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp @@ -783,11 +783,9 @@ Error LinuxKernelRewriter::rewriteORCTables() { }; // Emit new ORC entries for the emitted function. - auto emitORC = [&](const BinaryFunction &BF) -> Error { - assert(!BF.isSplit() && "Split functions not supported by ORC writer yet."); - + auto emitORC = [&](const FunctionFragment &FF) -> Error { ORCState CurrentState = NullORC; - for (BinaryBasicBlock *BB : BF.getLayout().blocks()) { + for (BinaryBasicBlock *BB : FF) { for (MCInst &Inst : *BB) { ErrorOr ErrorOrState = BC.MIB->tryGetAnnotationAs(Inst, "ORC"); @@ -808,7 +806,36 @@ Error LinuxKernelRewriter::rewriteORCTables() { return Error::success(); }; + // Emit ORC entries for cold fragments. We assume that these fragments are + // emitted contiguously in memory using reserved space in the kernel. This + // assumption is validated in post-emit pass validateORCTables() where we + // check that ORC entries are sorted by their addresses. + auto emitColdORC = [&]() -> Error { + for (BinaryFunction &BF : + llvm::make_second_range(BC.getBinaryFunctions())) { + if (!BC.shouldEmit(BF)) + continue; + for (FunctionFragment &FF : BF.getLayout().getSplitFragments()) + if (Error E = emitORC(FF)) + return E; + } + + return Error::success(); + }; + + bool ShouldEmitCold = !BC.BOLTReserved.empty(); for (ORCListEntry &Entry : ORCEntries) { + if (ShouldEmitCold && Entry.IP > BC.BOLTReserved.start()) { + if (Error E = emitColdORC()) + return E; + + // Emit terminator entry at the end of the reserved region. + if (Error E = emitORCEntry(BC.BOLTReserved.end(), NullORC)) + return E; + + ShouldEmitCold = false; + } + // Emit original entries for functions that we haven't modified. if (!Entry.BF || !BC.shouldEmit(*Entry.BF)) { // Emit terminator only if it marks the start of a function. @@ -822,7 +849,7 @@ Error LinuxKernelRewriter::rewriteORCTables() { // Emit all ORC entries for a function referenced by an entry and skip over // the rest of entries for this function by resetting its ORC attribute. if (Entry.BF->hasORC()) { - if (Error E = emitORC(*Entry.BF)) + if (Error E = emitORC(Entry.BF->getLayout().getMainFragment())) return E; Entry.BF->setHasORC(false); } @@ -831,10 +858,9 @@ Error LinuxKernelRewriter::rewriteORCTables() { LLVM_DEBUG(dbgs() << "BOLT-DEBUG: emitted " << NumEmitted << " ORC entries\n"); - // Replicate terminator entry at the end of sections to match the original - // table sizes. - const BinaryFunction &LastBF = BC.getBinaryFunctions().rbegin()->second; - const uint64_t LastIP = LastBF.getAddress() + LastBF.getMaxSize(); + // Populate ORC tables with a terminator entry with max address to match the + // original table sizes. + const uint64_t LastIP = std::numeric_limits::max(); while (UnwindWriter.bytesRemaining()) { if (Error E = emitORCEntry(LastIP, NullORC, nullptr, /*Force*/ true)) return E; -- GitLab From 59ab29213deffb8a18a18d2077ed268f5254b7f2 Mon Sep 17 00:00:00 2001 From: Maksim Panchenko Date: Wed, 1 May 2024 21:56:55 -0700 Subject: [PATCH 0010/1014] [BOLT] Register Linux kernel dynamic branch offsets (#90677) To match profile data to code we need to know branch instruction offsets within a function. For this reason, we mark branches with the "Offset" annotation while disassembling the code. However, _dynamic_ branches in the Linux kernel could be NOPs in disassembled code, and we ignore them while adding annotations. We need to explicitly add the "Offset" annotation while creating dynamic branches. Note that without this change, `getInstructionAtOffset()` would still return a branch instruction if the offset matched the last instruction in a basic block (and the profile data was matched correctly). However, the function failed for cases when the searched instruction was followed by an unconditional jump. "Offset" annotation solves this case. --- bolt/lib/Rewrite/LinuxKernelRewriter.cpp | 3 +++ bolt/test/X86/linux-static-keys.s | 29 +++++++++++++++++++----- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp index b976699cef17..0aee9521c5c7 100644 --- a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp +++ b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp @@ -1722,6 +1722,9 @@ Error LinuxKernelRewriter::readStaticKeysJumpTable() { if (!BC.MIB->getSize(*Inst)) BC.MIB->setSize(*Inst, Size); + if (!BC.MIB->getOffset(*Inst)) + BC.MIB->setOffset(*Inst, JumpAddress - BF->getAddress()); + if (opts::LongJumpLabels) BC.MIB->setSize(*Inst, 5); } diff --git a/bolt/test/X86/linux-static-keys.s b/bolt/test/X86/linux-static-keys.s index 08454bf97631..fb419e0f7627 100644 --- a/bolt/test/X86/linux-static-keys.s +++ b/bolt/test/X86/linux-static-keys.s @@ -3,6 +3,8 @@ ## Check that BOLT correctly updates the Linux kernel static keys jump table. # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o +# RUN: link_fdata %s %t.o %t.fdata +# RUN: llvm-strip --strip-unneeded %t.o # RUN: %clang %cflags -nostdlib %t.o -o %t.exe \ # RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr @@ -11,6 +13,12 @@ # RUN: llvm-bolt %t.exe --print-normalized -o %t.out --keep-nops=0 \ # RUN: --bolt-info=0 |& FileCheck %s +## Verify that profile is matched correctly. + +# RUN: llvm-bolt %t.exe --print-normalized -o %t.out --keep-nops=0 \ +# RUN: --bolt-info=0 --data %t.fdata |& \ +# RUN: FileCheck --check-prefix=CHECK-FDATA %s + ## Verify the bindings again on the rewritten binary with nops removed. # RUN: llvm-bolt %t.out -o %t.out.1 --print-normalized |& FileCheck %s @@ -25,15 +33,24 @@ _start: # CHECK: Binary Function "_start" nop .L0: - jmp .L1 + jmp L1 # CHECK: jit # CHECK-SAME: # ID: 1 {{.*}} # Likely: 0 # InitValue: 1 nop -.L1: +L1: .nops 5 + jmp .L0 # CHECK: jit # CHECK-SAME: # ID: 2 {{.*}} # Likely: 1 # InitValue: 1 -.L2: + +## Check that a branch profile associated with a NOP is handled properly when +## dynamic branch is created. + +# FDATA: 1 _start #L1# 1 _start #L2# 3 42 +# CHECK-FDATA: jit {{.*}} # ID: 2 +# CHECK-FDATA-NEXT: jmp +# CHECK-FDATA-NEXT: Successors: {{.*}} (mispreds: 3, count: 42) +L2: nop .size _start, .-_start @@ -51,11 +68,11 @@ foo: __start___jump_table: .long .L0 - . # Jump address - .long .L1 - . # Target address + .long L1 - . # Target address .quad 1 # Key address - .long .L1 - . # Jump address - .long .L2 - . # Target address + .long L1 - . # Jump address + .long L2 - . # Target address .quad 0 # Key address .globl __stop___jump_table -- GitLab From b86e0992bfa6c58be077d82d824016f590ac5d90 Mon Sep 17 00:00:00 2001 From: Matheus Izvekov Date: Thu, 2 May 2024 02:02:35 -0300 Subject: [PATCH 0011/1014] [clang] Enable C++17 relaxed template template argument matching by default (#89807) This patch will finally allow us to mark C++17 support in clang as complete. In order to implement this as a DR and avoid breaking reasonable code that worked before P0522, this patch implements a provisional resolution for CWG2398: When deducing template template parameters against each other, and the argument side names a template specialization, instead of just deducing A, we deduce a synthesized template template parameter based on A, but with it's parameters using the template specialization's arguments as defaults. The driver flag is deprecated with a warning. Fixes https://github.com/llvm/llvm-project/issues/36505 --- clang/docs/ReleaseNotes.rst | 19 +++ .../clang/Basic/DiagnosticDriverKinds.td | 2 +- clang/include/clang/Basic/LangOptions.def | 2 +- clang/include/clang/Driver/Options.td | 8 +- clang/lib/Driver/SanitizerArgs.cpp | 9 +- clang/lib/Driver/ToolChains/Clang.cpp | 16 +- clang/lib/Sema/SemaTemplate.cpp | 3 - clang/lib/Sema/SemaTemplateDeduction.cpp | 107 +++++++++++++- .../temp/temp.arg/temp.arg.template/p3-2a.cpp | 2 +- clang/test/CodeGenCXX/mangle-concept.cpp | 4 +- .../frelaxed-template-template-args.cpp | 5 + clang/test/Lexer/cxx-features.cpp | 6 +- clang/test/SemaTemplate/cwg2398.cpp | 139 ++++++++++++++++++ clang/test/SemaTemplate/default-arguments.cpp | 7 +- .../instantiate-template-template-parm.cpp | 17 +-- clang/test/SemaTemplate/nested-template.cpp | 8 +- clang/test/SemaTemplate/temp_arg_template.cpp | 6 +- .../SemaTemplate/temp_arg_template_cxx1z.cpp | 2 +- clang/www/cxx_status.html | 18 +-- 19 files changed, 317 insertions(+), 63 deletions(-) create mode 100644 clang/test/Driver/frelaxed-template-template-args.cpp create mode 100644 clang/test/SemaTemplate/cwg2398.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 0e3f7cf89ca8..d402babc8aaa 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -48,6 +48,11 @@ C++ Specific Potentially Breaking Changes - Clang now diagnoses function/variable templates that shadow their own template parameters, e.g. ``template void T();``. This error can be disabled via `-Wno-strict-primary-template-shadow` for compatibility with previous versions of clang. +- The behavior controlled by the `-frelaxed-template-template-args` flag is now + on by default, and the flag is deprecated. Until the flag is finally removed, + it's negative spelling can be used to obtain compatibility with previous + versions of clang. + ABI Changes in This Version --------------------------- - Fixed Microsoft name mangling of implicitly defined variables used for thread @@ -94,6 +99,17 @@ sections with improvements to Clang's support for those languages. C++ Language Changes -------------------- +- C++17 support is now completed, with the enablement of the + relaxed temlate template argument matching rules introduced in P0522, + which was retroactively applied as a defect report. + While the implementation already existed since Clang 4, it was turned off by + default, and was controlled with the `-frelaxed-template-template-args` flag. + In this release, we implement provisional wording for a core defect on + P0522 (CWG2398), which avoids the most serious compatibility issues caused + by it, allowing us to enable it by default in this release. + The flag is now deprecated, and will be removed in the next release, but can + still be used to turn it off and regain compatibility with previous versions + (#GH36505). - Implemented ``_BitInt`` literal suffixes ``__wb`` or ``__WB`` as a Clang extension with ``unsigned`` modifiers also allowed. (#GH85223). C++17 Feature Support @@ -173,6 +189,9 @@ Resolutions to C++ Defect Reports - Clang now diagnoses declarative nested-name-specifiers with pack-index-specifiers. (`CWG2858: Declarative nested-name-specifiers and pack-index-specifiers `_). +- P0522 implementation is enabled by default in all language versions, and + provisional wording for CWG2398 is implemented. + C Language Changes ------------------ diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index ed3fd9b1c4a5..9781fcaa4ff5 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -435,7 +435,7 @@ def warn_drv_diagnostics_misexpect_requires_pgo : Warning< def warn_drv_clang_unsupported : Warning< "the clang compiler does not support '%0'">; def warn_drv_deprecated_arg : Warning< - "argument '%0' is deprecated, use '%1' instead">, InGroup; + "argument '%0' is deprecated%select{|, use '%2' instead}1">, InGroup; def warn_drv_deprecated_custom : Warning< "argument '%0' is deprecated, %1">, InGroup; def warn_drv_assuming_mfloat_abi_is : Warning< diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 55c81eab1ec1..c967d8b22292 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -158,7 +158,7 @@ LANGOPT(GNUAsm , 1, 1, "GNU-style inline assembly") LANGOPT(Coroutines , 1, 0, "C++20 coroutines") LANGOPT(CoroAlignedAllocation, 1, 0, "prefer Aligned Allocation according to P2014 Option 2") LANGOPT(DllExportInlines , 1, 1, "dllexported classes dllexport inline methods") -LANGOPT(RelaxedTemplateTemplateArgs, 1, 0, "C++17 relaxed matching of template template arguments") +LANGOPT(RelaxedTemplateTemplateArgs, 1, 1, "C++17 relaxed matching of template template arguments") LANGOPT(ExperimentalLibrary, 1, 0, "enable unstable and experimental library features") LANGOPT(PointerAuthIntrinsics, 1, 0, "pointer authentication intrinsics") diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 864da4e1157f..953f6fc649e6 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -3383,10 +3383,10 @@ defm application_extension : BoolFOption<"application-extension", "Restrict code to those available for App Extensions">, NegFlag>; defm relaxed_template_template_args : BoolFOption<"relaxed-template-template-args", - LangOpts<"RelaxedTemplateTemplateArgs">, DefaultFalse, - PosFlag, - NegFlag>; + LangOpts<"RelaxedTemplateTemplateArgs">, DefaultTrue, + PosFlag, + NegFlag, + BothFlags<[], [ClangOption], " C++17 relaxed template template argument matching">>; defm sized_deallocation : BoolFOption<"sized-deallocation", LangOpts<"SizedDeallocation">, DefaultFalse, PosFlagclaim(); if (LegacySanitizeCoverage != 0 && DiagnoseErrors) { D.Diag(diag::warn_drv_deprecated_arg) - << Arg->getAsString(Args) << "-fsanitize-coverage=trace-pc-guard"; + << Arg->getAsString(Args) << /*hasReplacement=*/true + << "-fsanitize-coverage=trace-pc-guard"; } continue; } @@ -833,11 +834,11 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, // enabled. if (CoverageFeatures & CoverageTraceBB) D.Diag(clang::diag::warn_drv_deprecated_arg) - << "-fsanitize-coverage=trace-bb" + << "-fsanitize-coverage=trace-bb" << /*hasReplacement=*/true << "-fsanitize-coverage=trace-pc-guard"; if (CoverageFeatures & Coverage8bitCounters) D.Diag(clang::diag::warn_drv_deprecated_arg) - << "-fsanitize-coverage=8bit-counters" + << "-fsanitize-coverage=8bit-counters" << /*hasReplacement=*/true << "-fsanitize-coverage=trace-pc-guard"; } @@ -849,7 +850,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, if ((CoverageFeatures & InsertionPointTypes) && !(CoverageFeatures & InstrumentationTypes) && DiagnoseErrors) { D.Diag(clang::diag::warn_drv_deprecated_arg) - << "-fsanitize-coverage=[func|bb|edge]" + << "-fsanitize-coverage=[func|bb|edge]" << /*hasReplacement=*/true << "-fsanitize-coverage=[func|bb|edge],[trace-pc-guard|trace-pc],[" "control-flow]"; } diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 1f08c5958dfb..2cb0c7213544 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -6513,7 +6513,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, if (const Arg *A = Args.getLastArg(options::OPT_fvisibility_global_new_delete_hidden)) { D.Diag(diag::warn_drv_deprecated_arg) - << A->getAsString(Args) + << A->getAsString(Args) << /*hasReplacement=*/true << "-fvisibility-global-new-delete=force-hidden"; } @@ -7240,11 +7240,15 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.addOptOutFlag(CmdArgs, options::OPT_fassume_unique_vtables, options::OPT_fno_assume_unique_vtables); - // -frelaxed-template-template-args is off by default, as it is a severe - // breaking change until a corresponding change to template partial ordering - // is provided. - Args.addOptInFlag(CmdArgs, options::OPT_frelaxed_template_template_args, - options::OPT_fno_relaxed_template_template_args); + // -frelaxed-template-template-args is deprecated. + if (Arg *A = + Args.getLastArg(options::OPT_frelaxed_template_template_args, + options::OPT_fno_relaxed_template_template_args)) { + D.Diag(diag::warn_drv_deprecated_arg) + << A->getAsString(Args) << /*hasReplacement=*/false; + if (A->getOption().matches(options::OPT_fno_relaxed_template_template_args)) + CmdArgs.push_back("-fno-relaxed-template-template-args"); + } // -fsized-deallocation is off by default, as it is an ABI-breaking change for // most platforms. diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 7f18631c6096..989f3995ca59 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -8334,9 +8334,6 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, // C++1z [temp.arg.template]p3: (DR 150) // A template-argument matches a template template-parameter P when P // is at least as specialized as the template-argument A. - // FIXME: We should enable RelaxedTemplateTemplateArgs by default as it is a - // defect report resolution from C++17 and shouldn't be introduced by - // concepts. if (getLangOpts().RelaxedTemplateTemplateArgs) { // Quick check for the common case: // If P contains a parameter pack, then A [...] matches P if each of A's diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index e93f7bd842e4..9f9e44228271 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -507,10 +507,70 @@ static TemplateDeductionResult DeduceNonTypeTemplateArgument( S, TemplateParams, NTTP, DeducedTemplateArgument(New), T, Info, Deduced); } +/// Create a shallow copy of a given template parameter declaration, with +/// empty source locations and using the given TemplateArgument as it's +/// default argument. +/// +/// \returns The new template parameter declaration. +static NamedDecl *getTemplateParameterWithDefault(Sema &S, NamedDecl *A, + TemplateArgument Default) { + switch (A->getKind()) { + case Decl::TemplateTypeParm: { + auto *T = cast(A); + // FIXME: A TemplateTypeParmDecl's DefaultArgument can't hold a full + // TemplateArgument, so there is currently no way to specify a pack as a + // default argument for these. + if (T->isParameterPack()) + return A; + auto *R = TemplateTypeParmDecl::Create( + S.Context, A->getDeclContext(), SourceLocation(), SourceLocation(), + T->getDepth(), T->getIndex(), T->getIdentifier(), + T->wasDeclaredWithTypename(), /*ParameterPack=*/false, + T->hasTypeConstraint()); + R->setDefaultArgument( + S.Context.getTrivialTypeSourceInfo(Default.getAsType())); + if (R->hasTypeConstraint()) { + auto *C = R->getTypeConstraint(); + R->setTypeConstraint(C->getConceptReference(), + C->getImmediatelyDeclaredConstraint()); + } + return R; + } + case Decl::NonTypeTemplateParm: { + auto *T = cast(A); + // FIXME: Ditto, as above for TemplateTypeParm case. + if (T->isParameterPack()) + return A; + auto *R = NonTypeTemplateParmDecl::Create( + S.Context, A->getDeclContext(), SourceLocation(), SourceLocation(), + T->getDepth(), T->getIndex(), T->getIdentifier(), T->getType(), + /*ParameterPack=*/false, T->getTypeSourceInfo()); + R->setDefaultArgument(Default.getAsExpr()); + if (auto *PTC = T->getPlaceholderTypeConstraint()) + R->setPlaceholderTypeConstraint(PTC); + return R; + } + case Decl::TemplateTemplateParm: { + auto *T = cast(A); + auto *R = TemplateTemplateParmDecl::Create( + S.Context, A->getDeclContext(), SourceLocation(), T->getDepth(), + T->getIndex(), T->isParameterPack(), T->getIdentifier(), + T->wasDeclaredWithTypename(), T->getTemplateParameters()); + R->setDefaultArgument( + S.Context, + S.getTrivialTemplateArgumentLoc(Default, QualType(), SourceLocation())); + return R; + } + default: + llvm_unreachable("Unexpected Decl Kind"); + } +} + static TemplateDeductionResult DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, TemplateName Param, TemplateName Arg, TemplateDeductionInfo &Info, + ArrayRef DefaultArguments, SmallVectorImpl &Deduced) { TemplateDecl *ParamDecl = Param.getAsTemplateDecl(); if (!ParamDecl) { @@ -519,13 +579,45 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, return TemplateDeductionResult::Success; } - if (TemplateTemplateParmDecl *TempParam - = dyn_cast(ParamDecl)) { + if (auto *TempParam = dyn_cast(ParamDecl)) { // If we're not deducing at this depth, there's nothing to deduce. if (TempParam->getDepth() != Info.getDeducedDepth()) return TemplateDeductionResult::Success; - DeducedTemplateArgument NewDeduced(S.Context.getCanonicalTemplateName(Arg)); + auto NewDeduced = DeducedTemplateArgument(Arg); + // Provisional resolution for CWG2398: If Arg is also a template template + // param, and it names a template specialization, then we deduce a + // synthesized template template parameter based on A, but using the TS's + // arguments as defaults. + if (auto *TempArg = dyn_cast_or_null( + Arg.getAsTemplateDecl())) { + assert(Arg.getKind() == TemplateName::Template); + assert(!TempArg->isExpandedParameterPack()); + + TemplateParameterList *As = TempArg->getTemplateParameters(); + if (DefaultArguments.size() != 0) { + assert(DefaultArguments.size() <= As->size()); + SmallVector Params(As->size()); + for (unsigned I = 0; I < DefaultArguments.size(); ++I) + Params[I] = getTemplateParameterWithDefault(S, As->getParam(I), + DefaultArguments[I]); + for (unsigned I = DefaultArguments.size(); I < As->size(); ++I) + Params[I] = As->getParam(I); + // FIXME: We could unique these, and also the parameters, but we don't + // expect programs to contain a large enough amount of these deductions + // for that to be worthwhile. + auto *TPL = TemplateParameterList::Create( + S.Context, SourceLocation(), SourceLocation(), Params, + SourceLocation(), As->getRequiresClause()); + NewDeduced = DeducedTemplateArgument( + TemplateName(TemplateTemplateParmDecl::Create( + S.Context, TempArg->getDeclContext(), SourceLocation(), + TempArg->getDepth(), TempArg->getPosition(), + TempArg->isParameterPack(), TempArg->getIdentifier(), + TempArg->wasDeclaredWithTypename(), TPL))); + } + } + DeducedTemplateArgument Result = checkDeducedTemplateArguments(S.Context, Deduced[TempParam->getIndex()], NewDeduced); @@ -604,7 +696,8 @@ DeduceTemplateSpecArguments(Sema &S, TemplateParameterList *TemplateParams, // Perform template argument deduction for the template name. if (auto Result = - DeduceTemplateArguments(S, TemplateParams, TNP, TNA, Info, Deduced); + DeduceTemplateArguments(S, TemplateParams, TNP, TNA, Info, + SA->template_arguments(), Deduced); Result != TemplateDeductionResult::Success) return Result; // Perform template argument deduction on each template @@ -630,7 +723,8 @@ DeduceTemplateSpecArguments(Sema &S, TemplateParameterList *TemplateParams, // Perform template argument deduction for the template name. if (auto Result = DeduceTemplateArguments( S, TemplateParams, TP->getTemplateName(), - TemplateName(SA->getSpecializedTemplate()), Info, Deduced); + TemplateName(SA->getSpecializedTemplate()), Info, + SA->getTemplateArgs().asArray(), Deduced); Result != TemplateDeductionResult::Success) return Result; @@ -2323,7 +2417,8 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, case TemplateArgument::Template: if (A.getKind() == TemplateArgument::Template) return DeduceTemplateArguments(S, TemplateParams, P.getAsTemplate(), - A.getAsTemplate(), Info, Deduced); + A.getAsTemplate(), Info, + /*DefaultArguments=*/{}, Deduced); Info.FirstArg = P; Info.SecondArg = A; return TemplateDeductionResult::NonDeducedMismatch; diff --git a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp index f58606963861..342ffba53dbf 100644 --- a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp +++ b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c++2a -frelaxed-template-template-args -verify %s +// RUN: %clang_cc1 -std=c++2a -verify %s template concept C = T::f(); // #C template concept D = C && T::g(); diff --git a/clang/test/CodeGenCXX/mangle-concept.cpp b/clang/test/CodeGenCXX/mangle-concept.cpp index bbd2cf6555e3..e9c46d87635a 100644 --- a/clang/test/CodeGenCXX/mangle-concept.cpp +++ b/clang/test/CodeGenCXX/mangle-concept.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -verify -frelaxed-template-template-args -std=c++20 -emit-llvm -triple %itanium_abi_triple -o - %s -fclang-abi-compat=latest | FileCheck %s -// RUN: %clang_cc1 -verify -frelaxed-template-template-args -std=c++20 -emit-llvm -triple %itanium_abi_triple -o - %s -fclang-abi-compat=16 | FileCheck %s --check-prefix=CLANG16 +// RUN: %clang_cc1 -verify -std=c++20 -emit-llvm -triple %itanium_abi_triple -o - %s -fclang-abi-compat=latest | FileCheck %s +// RUN: %clang_cc1 -verify -std=c++20 -emit-llvm -triple %itanium_abi_triple -o - %s -fclang-abi-compat=16 | FileCheck %s --check-prefix=CLANG16 // expected-no-diagnostics namespace test1 { diff --git a/clang/test/Driver/frelaxed-template-template-args.cpp b/clang/test/Driver/frelaxed-template-template-args.cpp new file mode 100644 index 000000000000..dd6265ba8375 --- /dev/null +++ b/clang/test/Driver/frelaxed-template-template-args.cpp @@ -0,0 +1,5 @@ +// RUN: %clang -fsyntax-only -frelaxed-template-template-args %s 2>&1 | FileCheck --check-prefix=CHECK-ON %s +// RUN: %clang -fsyntax-only -fno-relaxed-template-template-args %s 2>&1 | FileCheck --check-prefix=CHECK-OFF %s + +// CHECK-ON: warning: argument '-frelaxed-template-template-args' is deprecated [-Wdeprecated] +// CHECK-OFF: warning: argument '-fno-relaxed-template-template-args' is deprecated [-Wdeprecated] diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp index 4a08eb61cd39..41550cf02aa3 100644 --- a/clang/test/Lexer/cxx-features.cpp +++ b/clang/test/Lexer/cxx-features.cpp @@ -7,7 +7,7 @@ // RUN: %clang_cc1 -std=c++2c -fcxx-exceptions -fsized-deallocation -verify %s // -// RUN: %clang_cc1 -std=c++17 -fcxx-exceptions -fsized-deallocation -frelaxed-template-template-args -DRELAXED_TEMPLATE_TEMPLATE_ARGS=1 -verify %s +// RUN: %clang_cc1 -std=c++17 -fcxx-exceptions -fsized-deallocation -fno-relaxed-template-template-args -DNO_RELAXED_TEMPLATE_TEMPLATE_ARGS=1 -verify %s // RUN: %clang_cc1 -std=c++17 -fcxx-exceptions -fsized-deallocation -DCONCEPTS_TS=1 -verify %s // RUN: %clang_cc1 -std=c++14 -fno-rtti -fno-threadsafe-statics -verify %s -DNO_EXCEPTIONS -DNO_RTTI -DNO_THREADSAFE_STATICS -fsized-deallocation // RUN: %clang_cc1 -std=c++14 -fchar8_t -DNO_EXCEPTIONS -DCHAR8_T -verify -fsized-deallocation %s @@ -231,8 +231,8 @@ #error "wrong value for __cpp_nontype_template_args" #endif -#if defined(RELAXED_TEMPLATE_TEMPLATE_ARGS) \ - ? check(template_template_args, 0, 0, 0, 201611, 201611, 201611, 201611) \ +#if !defined(NO_RELAXED_TEMPLATE_TEMPLATE_ARGS) \ + ? check(template_template_args, 201611, 201611, 201611, 201611, 201611, 201611, 201611) \ : check(template_template_args, 0, 0, 0, 0, 0, 0, 0) #error "wrong value for __cpp_template_template_args" #endif diff --git a/clang/test/SemaTemplate/cwg2398.cpp b/clang/test/SemaTemplate/cwg2398.cpp new file mode 100644 index 000000000000..a20155486b12 --- /dev/null +++ b/clang/test/SemaTemplate/cwg2398.cpp @@ -0,0 +1,139 @@ +// RUN: %clang_cc1 %s -fsyntax-only -std=c++23 -verify=expected,new +// RUN: %clang_cc1 %s -fsyntax-only -std=c++23 -fno-relaxed-template-template-args -verify=expected,old + +namespace issue1 { + template class B {}; + template class P, class T> void f(P); + // new-note@-1 {{deduced type 'B<[...], (default) int>' of 1st parameter does not match adjusted type 'B<[...], float>' of argument [with P = issue1::B, T = int]}} + // old-note@-2 2{{template template argument has different template parameters}} + + void g() { + f(B()); // old-error {{no matching function for call}} + f(B()); // expected-error {{no matching function for call}} + } +} // namespace issue1 + +namespace issue2 { + template struct match; + + template class t,typename T> struct match>; + + template class t,typename T0,typename T1> + struct match> {}; + + template struct other {}; + template struct match>; +} // namespace issue2 + +namespace type { + template struct A; + + template struct B; + template class TT1, class T5 > struct B> ; + template class TT2, class T8, class T9> struct B> {}; + template struct B>; +} // namespace type + +namespace value { + template struct A; + + template struct B; + template class TT1, class T4 > struct B> ; + template class TT2, class T6, int V3> struct B> {}; + template struct B>; +} // namespace value + +namespace templ { + template struct A; + + template class T4 = A> struct B {}; + + template struct C; + + template class TT1, class T7> struct C>; + + template class> class TT2, + class T10, template class TT3> + struct C> {}; + + template struct C>; +} // namespace templ + +namespace type_pack1 { + template struct A; + template class TT1, class T4> struct A> ; + // new-note@-1 {{partial specialization matches}} + template class TT2, class T6> struct A> {}; + // new-note@-1 {{partial specialization matches}} + + template struct B; + template struct A>; + // new-error@-1 {{ambiguous partial specialization}} +} // namespace type_pack1 + +namespace type_pack2 { + template struct A; + template class TT1, class ...T4> struct A> ; + // new-note@-1 {{partial specialization matches}} + template class TT2, class ...T6> struct A> {}; + // new-note@-1 {{partial specialization matches}} + + template struct B; + template struct A>; + // new-error@-1 {{ambiguous partial specialization}} +} // namespace type_pack2 + +namespace type_pack3 { + template struct A; + + template struct B; + + template class TT1, class T5 > struct B>; + // new-note@-1 {{template is declared here}} + template class TT2, class T8, class ...T9s> struct B>; + // old-note@-1 {{template is declared here}} + + template struct B>; + // expected-error@-1 {{explicit instantiation of undefined template}} +} // namespace type_pack3 + +namespace gcc_issue { + template struct A; + + template class TT1, class T2> struct A, typename TT1::type>; + // new-note@-1 {{partial specialization matches}} + + template class TT2, class T5, class T6> + struct A, typename TT2::type>; + // new-note@-1 {{partial specialization matches}} + // old-note@-2 {{template is declared here}} + + template struct B { using type = int; }; + + template struct A, int>; + // new-error@-1 {{ambiguous partial specializations}} + // old-error@-2 {{explicit instantiation of undefined template}} +} // namespace gcc_issue + +namespace ttp_defaults { + template