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 class TT1> struct A {};
+ // old-note@-1 2{{previous template template parameter}}
+
+ template class TT2> void f(A);
+ // new-note@-1 {{explicit instantiation candidate}}
+ // old-note@-2 {{invalid explicitly-specified argument for template parameter 'TT2'}}
+
+ // FIXME: The default arguments on the TTP are not available during partial ordering.
+ template class TT3> void f(A) {};
+ // new-note@-1 {{explicit instantiation candidate}}
+ // old-error@-2 {{template template argument has different template parameters}}
+ // old-note@-3 {{too many template parameters}}
+
+ template struct B;
+ // old-note@-1 {{too many template parameters}}
+
+ template void f(A);
+ // new-error@-1 {{partial ordering for explicit instantiation of 'f' is ambiguous}}
+ // old-error@-2 {{template template argument has different template parameters}}
+ // old-error@-3 {{explicit instantiation of 'f' does not refer to a function template}}
+} // namespace ttp_defaults
diff --git a/clang/test/SemaTemplate/default-arguments.cpp b/clang/test/SemaTemplate/default-arguments.cpp
index a850d273ccba..d5d9687cc90f 100644
--- a/clang/test/SemaTemplate/default-arguments.cpp
+++ b/clang/test/SemaTemplate/default-arguments.cpp
@@ -112,15 +112,14 @@ template class X = T::template apply>
int array4[is_same,
X4 >::value? 1 : -1];
-template struct X5 {}; // expected-note{{has a different type 'int'}}
+template struct X5 {};
template struct X5b {};
template class B = X5> // expected-error{{template template argument has different}} \
- // expected-note{{previous non-type template parameter}}
+ template class B = X5>
struct X6 {};
X6 x6a;
-X6 x6b; // expected-note{{while checking a default template argument}}
+X6 x6b;
X6 x6c;
diff --git a/clang/test/SemaTemplate/instantiate-template-template-parm.cpp b/clang/test/SemaTemplate/instantiate-template-template-parm.cpp
index a70c7e8b081a..39aeeb1c1a6a 100644
--- a/clang/test/SemaTemplate/instantiate-template-template-parm.cpp
+++ b/clang/test/SemaTemplate/instantiate-template-template-parm.cpp
@@ -20,30 +20,29 @@ apply::type ir = i;
apply::type fr = i; // expected-error{{non-const lvalue reference to type 'float' cannot bind to a value of unrelated type 'int'}}
// Template template parameters
-template struct B; // expected-note{{has a different type 'int'}}
+template struct B;
-template class X> // expected-error{{cannot have type 'float'}} \
- // expected-note{{with type 'long'}}
+template class X> // expected-error{{cannot have type 'float'}}
struct X0 { };
X0 x0b1;
X0 x0b2; // expected-note{{while substituting}}
-X0 x0b3; // expected-error{{template template argument has different template parameters}}
+X0 x0b3;
-template class TT> // expected-note{{parameter with type 'int'}}
+template class TT>
struct X1 { };
template class TT>
struct X2 {
- X1 x1; // expected-error{{has different template parameters}}
+ X1 x1;
};
template struct X3i { };
-template struct X3l { }; // expected-note{{different type 'long'}}
+template struct X3l { };
X2 x2okay;
-X2 x2bad; // expected-note{{instantiation}}
+X2 x2okay2;
template class TT, class R = TT<1, 2> >
struct Comp {
diff --git a/clang/test/SemaTemplate/nested-template.cpp b/clang/test/SemaTemplate/nested-template.cpp
index efbde2076b9f..5bd388d4dff3 100644
--- a/clang/test/SemaTemplate/nested-template.cpp
+++ b/clang/test/SemaTemplate/nested-template.cpp
@@ -112,18 +112,16 @@ template struct X1::B;
// Template template parameters
template
struct X2 {
- template class> // expected-error{{cannot have type 'float'}} \
- // expected-note{{previous non-type template}}
+ template class> // expected-error{{cannot have type 'float'}}
struct Inner { };
};
-template // expected-note{{template non-type parameter}}
+template
struct X2_arg;
X2::Inner x2i1;
X2 x2a; // expected-note{{instantiation}}
-X2::Inner x2i3; // expected-error{{template template argument has different}}
+X2::Inner x2i3;
namespace PR10896 {
template
diff --git a/clang/test/SemaTemplate/temp_arg_template.cpp b/clang/test/SemaTemplate/temp_arg_template.cpp
index 3c2697329212..a7236669276a 100644
--- a/clang/test/SemaTemplate/temp_arg_template.cpp
+++ b/clang/test/SemaTemplate/temp_arg_template.cpp
@@ -5,11 +5,11 @@ template class X> struct A; // expected-note 2{{previous te
template class X> struct B; // expected-note{{previous template template parameter is here}}
-template class X> struct C; // expected-note 2{{previous non-type template parameter with type 'int' is here}}
+template class X> struct C; // expected-note {{previous non-type template parameter with type 'int' is here}}
template struct X; // expected-note{{too few template parameters in template template argument}}
template struct Y; // expected-note{{template parameter has a different kind in template argument}}
-template struct Ylong; // expected-note{{template non-type parameter has a different type 'long' in template argument}}
+template struct Ylong;
template struct Yref; // expected-note{{template non-type parameter has a different type 'const int &' in template argument}}
namespace N {
@@ -26,7 +26,7 @@ A *a4; // expected-error{{template template argument has different template p
A *a5; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}
B *a6; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}
C *a7;
-C *a8; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}
+C *a8;
C *a9; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}
template void f(int);
diff --git a/clang/test/SemaTemplate/temp_arg_template_cxx1z.cpp b/clang/test/SemaTemplate/temp_arg_template_cxx1z.cpp
index 03ef78f8cf14..372a00efc601 100644
--- a/clang/test/SemaTemplate/temp_arg_template_cxx1z.cpp
+++ b/clang/test/SemaTemplate/temp_arg_template_cxx1z.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z -frelaxed-template-template-args %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z %s
// expected-note@temp_arg_template_cxx1z.cpp:* 1+{{}}
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 260f74ded93c..b61399a26b1c 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -923,7 +923,7 @@ C++23, informally referred to as C++26.
You can use Clang in C++17 mode with the -std=c++17 option
(use -std=c++1z in Clang 4 and earlier).
-
+
List of features and minimum Clang version with support
@@ -1140,8 +1140,8 @@ You can use Clang in C++17 mode with the -std=c++17 option
| Matching template template parameters to compatible arguments |
- P0522R0 |
- Partial (10) |
+ P0522R0 (DR) |
+ Clang 19 (10) |
| Removing deprecated dynamic exception specifications |
@@ -1169,13 +1169,11 @@ functions using expression syntax are no longer guaranteed to be destroyed in
reverse construction order in that ABI.
This is not fully supported during constant expression evaluation until Clang 12.
-(10): Despite being the resolution to a Defect Report, this
-feature is disabled by default in all language versions, and can be enabled
-explicitly with the flag -frelaxed-template-template-args in Clang 4
-onwards.
-The change to the standard lacks a corresponding change for template partial
-ordering, resulting in ambiguity errors for reasonable and previously-valid
-code. This issue is expected to be rectified soon.
+(10): While this feature was initially implemented in Clang 4,
+it was not enabled by default prior to clang 19, but could be enabled with
+-frelaxed-template-template-args.
+Starting from Clang 19, the flag is deprecated and will be removed in a future
+version.
--
GitLab
From e3f42b02a4129947ca2dd820bfb63ffed83027b7 Mon Sep 17 00:00:00 2001
From: Yeting Kuo <46629943+yetingk@users.noreply.github.com>
Date: Thu, 2 May 2024 14:04:55 +0800
Subject: [PATCH 0012/1014] [RISCV] Expand PseudoTAIL with t2 instead of t1 for
Zicfilp. (#89014)
PseudoTail should be a software guarded branch in Ziciflp, since its
branch target is known in link time. JALR/C.JR/C.JALR with rs1 as t2 is
termed a software guarded branch. Such branches do not need to land on a
lpad instruction.
ABI Change PR: https://github.com/riscv-non-isa/riscv-asm-manual/pull/93
---
.../RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp | 4 +++
llvm/test/MC/RISCV/tail-call.s | 27 +++++++++++++++++++
2 files changed, 31 insertions(+)
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
index 5ea386c3c32a..0863345b0c6d 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
@@ -126,6 +126,10 @@ void RISCVMCCodeEmitter::expandFunctionCall(const MCInst &MI,
if (MI.getOpcode() == RISCV::PseudoTAIL) {
Func = MI.getOperand(0);
Ra = RISCV::X6;
+ // For Zicfilp, PseudoTAIL should be expanded to a software guarded branch.
+ // It means to use t2(x7) as rs1 of JALR to expand PseudoTAIL.
+ if (STI.hasFeature(RISCV::FeatureStdExtZicfilp))
+ Ra = RISCV::X7;
} else if (MI.getOpcode() == RISCV::PseudoCALLReg) {
Func = MI.getOperand(1);
Ra = MI.getOperand(0).getReg();
diff --git a/llvm/test/MC/RISCV/tail-call.s b/llvm/test/MC/RISCV/tail-call.s
index c94af672edda..7c9f28bdfacd 100644
--- a/llvm/test/MC/RISCV/tail-call.s
+++ b/llvm/test/MC/RISCV/tail-call.s
@@ -12,17 +12,36 @@
# RUN: llvm-mc -triple riscv64 < %s -show-encoding \
# RUN: | FileCheck -check-prefix=FIXUP %s
+# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+experimental-zicfilp < %s \
+# RUN: | llvm-objdump -d - | FileCheck --check-prefix=INSTR-ZICFILP %s
+# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+experimental-zicfilp < %s \
+# RUN: | llvm-readobj -r - | FileCheck -check-prefix=RELOC %s
+# RUN: llvm-mc -triple riscv32 -mattr=+experimental-zicfilp < %s -show-encoding \
+# RUN: | FileCheck -check-prefix=FIXUP %s
+
+# RUN: llvm-mc -filetype=obj -triple riscv64 -mattr=+experimental-zicfilp < %s \
+# RUN: | llvm-objdump -d - | FileCheck --check-prefix=INSTR-ZICFILP %s
+# RUN: llvm-mc -filetype=obj -triple riscv64 -mattr=+experimental-zicfilp < %s \
+# RUN: | llvm-readobj -r - | FileCheck -check-prefix=RELOC %s
+# RUN: llvm-mc -triple riscv64 -mattr=+experimental-zicfilp < %s -show-encoding \
+# RUN: | FileCheck -check-prefix=FIXUP %s
+
.long foo
tail foo
# RELOC: R_RISCV_CALL_PLT foo 0x0
# INSTR: auipc t1, 0
# INSTR: jr t1
+# INSTR-ZICFILP: auipc t2, 0
+# INSTR-ZICFILP: jr t2
# FIXUP: fixup A - offset: 0, value: foo, kind:
+
tail bar
# RELOC: R_RISCV_CALL_PLT bar 0x0
# INSTR: auipc t1, 0
# INSTR: jr t1
+# INSTR-ZICFILP: auipc t2, 0
+# INSTR-ZICFILP: jr t2
# FIXUP: fixup A - offset: 0, value: bar, kind:
# Ensure that tail calls to functions whose names coincide with register names
@@ -32,22 +51,30 @@ tail zero
# RELOC: R_RISCV_CALL_PLT zero 0x0
# INSTR: auipc t1, 0
# INSTR: jr t1
+# INSTR-ZICFILP: auipc t2, 0
+# INSTR-ZICFILP: jr t2
# FIXUP: fixup A - offset: 0, value: zero, kind:
tail f1
# RELOC: R_RISCV_CALL_PLT f1 0x0
# INSTR: auipc t1, 0
# INSTR: jr t1
+# INSTR-ZICFILP: auipc t2, 0
+# INSTR-ZICFILP: jr t2
# FIXUP: fixup A - offset: 0, value: f1, kind:
tail ra
# RELOC: R_RISCV_CALL_PLT ra 0x0
# INSTR: auipc t1, 0
# INSTR: jr t1
+# INSTR-ZICFILP: auipc t2, 0
+# INSTR-ZICFILP: jr t2
# FIXUP: fixup A - offset: 0, value: ra, kind:
tail foo@plt
# RELOC: R_RISCV_CALL_PLT foo 0x0
# INSTR: auipc t1, 0
# INSTR: jr t1
+# INSTR-ZICFILP: auipc t2, 0
+# INSTR-ZICFILP: jr t2
# FIXUP: fixup A - offset: 0, value: foo, kind: fixup_riscv_call_plt
--
GitLab
From 597a3150e932a9423c65b5ea4b53dd431aff5865 Mon Sep 17 00:00:00 2001
From: martinboehme
Date: Thu, 2 May 2024 08:35:13 +0200
Subject: [PATCH 0013/1014] [clang][dataflow] Don't propagate result objects in
unevaluated contexts (#90438)
Trying to do so can cause crashes -- see newly added test and the
comments in
the fix.
We're starting to see a repeating pattern here: We're getting crashes
because
`ResultObjectVisitor` and `getReferencedDecls()` don't agree on which
parts of
the AST to visit and, hence, which fields should be modeled.
I think we should ensure consistency between these two parts of the code
by
using a `RecursiveASTVisitor` in `getReferencedDecls()`[^1]; the
`Traverse...()` functions that control which parts of the AST we visit
would go
in a common base class that would be used for both `ResultObjectVisitor`
and
`getReferencedDecls()`.
I'd like to focus this PR, however, on a targeted fix for the current
crash and
postpone the refactoring to a later PR (which will be easier to revert
if there
are unintended side-effects).
[^1]: As an added bonus, this would make the code better structured and
more
efficient than the current sequence of `if (dyn_cast(...))`
statements).
---
.../FlowSensitive/DataflowEnvironment.cpp | 11 ++++
.../Analysis/FlowSensitive/TransferTest.cpp | 52 +++++++++++++++++++
2 files changed, 63 insertions(+)
diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
index d79e73440289..cb6c8b2ef107 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -350,6 +350,17 @@ public:
return RecursiveASTVisitor::TraverseDecl(D);
}
+ // Don't traverse expressions in unevaluated contexts, as we don't model
+ // fields that are only used in these.
+ // Note: The operand of the `noexcept` operator is an unevaluated operand, but
+ // nevertheless it appears in the Clang CFG, so we don't exclude it here.
+ bool TraverseDecltypeTypeLoc(DecltypeTypeLoc) { return true; }
+ bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc) { return true; }
+ bool TraverseCXXTypeidExpr(CXXTypeidExpr *) { return true; }
+ bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *) {
+ return true;
+ }
+
bool TraverseBindingDecl(BindingDecl *BD) {
// `RecursiveASTVisitor` doesn't traverse holding variables for
// `BindingDecl`s by itself, so we need to tell it to.
diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index 301bec32c0cf..95d5f569c0c8 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -3331,6 +3331,58 @@ TEST(TransferTest, ResultObjectLocationDontVisitNestedRecordDecl) {
ASTContext &ASTCtx) {});
}
+TEST(TransferTest, ResultObjectLocationDontVisitUnevaluatedContexts) {
+ // This is a crash repro.
+ // We used to crash because when propagating result objects, we would visit
+ // unevaluated contexts, but we don't model fields used only in these.
+
+ auto testFunction = [](llvm::StringRef Code, llvm::StringRef TargetFun) {
+ runDataflow(
+ Code,
+ [](const llvm::StringMap> &Results,
+ ASTContext &ASTCtx) {},
+ LangStandard::lang_gnucxx17,
+ /* ApplyBuiltinTransfer= */ true, TargetFun);
+ };
+
+ std::string Code = R"cc(
+ // Definitions needed for `typeid`.
+ namespace std {
+ class type_info {};
+ class bad_typeid {};
+ } // namespace std
+
+ struct S1 {};
+ struct S2 { S1 s1; };
+
+ // We test each type of unevaluated context from a different target
+ // function. Some types of unevaluated contexts may actually cause the
+ // field `s1` to be modeled, and we don't want this to "pollute" the tests
+ // for the other unevaluated contexts.
+ void decltypeTarget() {
+ decltype(S2{}) Dummy;
+ }
+ void typeofTarget() {
+ typeof(S2{}) Dummy;
+ }
+ void typeidTarget() {
+ typeid(S2{});
+ }
+ void sizeofTarget() {
+ sizeof(S2{});
+ }
+ void noexceptTarget() {
+ noexcept(S2{});
+ }
+ )cc";
+
+ testFunction(Code, "decltypeTarget");
+ testFunction(Code, "typeofTarget");
+ testFunction(Code, "typeidTarget");
+ testFunction(Code, "sizeofTarget");
+ testFunction(Code, "noexceptTarget");
+}
+
TEST(TransferTest, StaticCast) {
std::string Code = R"(
void target(int Foo) {
--
GitLab
From 376bc73b34d2cc007c8f8cf533e7cd7258d7a313 Mon Sep 17 00:00:00 2001
From: Matt Arsenault
Date: Thu, 2 May 2024 08:34:30 +0200
Subject: [PATCH 0014/1014] SystemZ: Fix accidentally commented out run line in
test
---
llvm/test/CodeGen/SystemZ/atomic-store-08.ll | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/llvm/test/CodeGen/SystemZ/atomic-store-08.ll b/llvm/test/CodeGen/SystemZ/atomic-store-08.ll
index 4d1693477f01..a0c7455b7408 100644
--- a/llvm/test/CodeGen/SystemZ/atomic-store-08.ll
+++ b/llvm/test/CodeGen/SystemZ/atomic-store-08.ll
@@ -2,7 +2,7 @@
; the AtomicExpand pass.
;
; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck -check-prefixes=CHECK,BASE %s
-; xUN: llc < %s -mtriple=s390x-linux-gnu -mcpu=z13 | FileCheck -check-prefixes=CHECK,Z13 %s
+; RUN: llc < %s -mtriple=s390x-linux-gnu -mcpu=z13 | FileCheck -check-prefixes=CHECK,Z13 %s
define void @f1(ptr %dst, ptr %src) {
; CHECK-LABEL: f1:
@@ -32,7 +32,7 @@ define void @f1_fpsrc(ptr %dst, ptr %src) {
; Z13-NEXT: vlgvg %r0, %v0, 0
; CHECK-NEXT: stpq %r0, 0(%r2)
-; CHECK-NEXT: bcr 15, %r0
+; CHECK-NEXT: bcr 1{{[45]}}, %r0
; CHECK-NEXT: br %r14
%val = load fp128, ptr %src, align 8
%add = fadd fp128 %val, %val
@@ -58,8 +58,13 @@ define void @f2_fpuse(ptr %dst, ptr %src) {
; CHECK-NEXT: .cfi_def_cfa_offset 336
; CHECK-NEXT: ld %f0, 0(%r3)
; CHECK-NEXT: ld %f2, 8(%r3)
-; CHECK-NEXT: lgr %r3, %r2
-; CHECK-NEXT: axbr %f0, %f0
+
+; BASE-NEXT: lgr %r3, %r2
+; BASE-NEXT: axbr %f0, %f0
+
+; Z13-NEXT: axbr %f0, %f0
+; Z13-NEXT: lgr %r3, %r2
+
; CHECK-NEXT: la %r4, 160(%r15)
; CHECK-NEXT: lghi %r2, 16
; CHECK-NEXT: lhi %r5, 5
--
GitLab
From 15027be6a5c5cf870f25af8de05d6d06c767238f Mon Sep 17 00:00:00 2001
From: Matt Arsenault
Date: Thu, 2 May 2024 08:37:08 +0200
Subject: [PATCH 0015/1014] SystemZ: Fix test failing the verifier
---
.../CodeGen/SystemZ/copy-phys-reg-gr128-to-vr128.mir | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/llvm/test/CodeGen/SystemZ/copy-phys-reg-gr128-to-vr128.mir b/llvm/test/CodeGen/SystemZ/copy-phys-reg-gr128-to-vr128.mir
index a2a07ac5c7f5..42bfadbd3c29 100644
--- a/llvm/test/CodeGen/SystemZ/copy-phys-reg-gr128-to-vr128.mir
+++ b/llvm/test/CodeGen/SystemZ/copy-phys-reg-gr128-to-vr128.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 4
-# RUN: llc -mtriple=s390x-ibm-linux -mcpu=z13 -run-pass=postrapseudos -o - %s | FileCheck %s
+# RUN: llc -mtriple=s390x-ibm-linux -mcpu=z13 -run-pass=postrapseudos -verify-machineinstrs -o - %s | FileCheck %s
---
name: copy_gr128_to_vr128__r0q_to_v0
@@ -51,9 +51,9 @@ name: copy_gr128_to_vr128__r0q_to_v0_subreg0
tracksRegLiveness: true
body: |
bb.0:
- liveins: $r0d
+ liveins: $r0q
; CHECK-LABEL: name: copy_gr128_to_vr128__r0q_to_v0_subreg0
- ; CHECK: liveins: $r0d
+ ; CHECK: liveins: $r0q
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: $v0 = VLVGP $r0d, $r1d
; CHECK-NEXT: Return implicit $v0
@@ -66,9 +66,9 @@ name: copy_gr128_to_vr128__r0q_to_v0_subreg1
tracksRegLiveness: true
body: |
bb.0:
- liveins: $r1d
+ liveins: $r0q
; CHECK-LABEL: name: copy_gr128_to_vr128__r0q_to_v0_subreg1
- ; CHECK: liveins: $r1d
+ ; CHECK: liveins: $r0q
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: $v0 = VLVGP $r0d, $r1d
; CHECK-NEXT: Return implicit $v0
--
GitLab
From 8bec96447ffee3909ef65241f999dd3e0ebe98e3 Mon Sep 17 00:00:00 2001
From: Christian Sigg
Date: Thu, 2 May 2024 08:56:03 +0200
Subject: [PATCH 0016/1014] [lldb][bazel] Fix BUILD after
dcbf0fcd0d5572f7001ebdd3bda6062593ec172b. (#90825)
---
.../llvm-project-overlay/lldb/BUILD.bazel | 34 ++++++++-----------
1 file changed, 14 insertions(+), 20 deletions(-)
diff --git a/utils/bazel/llvm-project-overlay/lldb/BUILD.bazel b/utils/bazel/llvm-project-overlay/lldb/BUILD.bazel
index b7b52f3ef59c..c06d0c1cfe46 100644
--- a/utils/bazel/llvm-project-overlay/lldb/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/lldb/BUILD.bazel
@@ -183,18 +183,17 @@ cc_binary(
],
)
-gentbl_cc_library(
+py_binary(
+ name = "generate-sbapi-dwarf-enum",
+ srcs = ["scripts/generate-sbapi-dwarf-enum.py"],
+)
+
+genrule(
name = "lldb-sbapi-dwarf-enums",
- strip_include_prefix = "include",
- tbl_outs = [
- (
- ["-gen-lldb-sbapi-dwarf-enum"],
- "include/lldb/API/SBLanguages.h",
- ),
- ],
- tblgen = ":lldb-tblgen",
- td_file = "//llvm:include/llvm/BinaryFormat/Dwarf.def",
- deps = [],
+ srcs = ["//llvm:include/llvm/BinaryFormat/Dwarf.def"],
+ outs = ["include/lldb/API/SBLanguages.h"],
+ cmd = "$(location :generate-sbapi-dwarf-enum) $(location //llvm:include/llvm/BinaryFormat/Dwarf.def) --output $@",
+ tools = [":generate-sbapi-dwarf-enum"],
)
cc_library(
@@ -203,10 +202,9 @@ cc_library(
"source/API/**/*.cpp",
"source/API/**/*.h",
]),
- hdrs = glob(["include/lldb/API/**/*.h"]),
+ hdrs = glob(["include/lldb/API/**/*.h"]) + [":lldb-sbapi-dwarf-enums"],
strip_include_prefix = "include",
deps = [
- ":lldb-sbapi-dwarf-enums",
":Breakpoint",
":Commands",
":Core",
@@ -284,10 +282,9 @@ cc_library(
cc_library(
name = "Expression",
srcs = glob(["source/Expression/**/*.cpp"]),
- hdrs = glob(["include/lldb/Expression/**/*.h"]),
+ hdrs = glob(["include/lldb/Expression/**/*.h"]) + [":lldb-sbapi-dwarf-enums"],
strip_include_prefix = "include",
deps = [
- ":lldb-sbapi-dwarf-enums",
":Core",
":Headers",
":Host",
@@ -361,12 +358,9 @@ cc_library(
cc_library(
name = "ExpressionHeaders",
- hdrs = glob(["include/lldb/Expression/**/*.h"]),
+ hdrs = glob(["include/lldb/Expression/**/*.h"]) + [":lldb-sbapi-dwarf-enums"],
strip_include_prefix = "include",
- deps = [
- ":lldb-sbapi-dwarf-enums",
- "//llvm:ExecutionEngine"
- ],
+ deps = ["//llvm:ExecutionEngine"],
)
cc_library(
--
GitLab
From cd132dcbeb0fc79fd657bd5e0a8e9244c3fb5da6 Mon Sep 17 00:00:00 2001
From: Oleksandr T
Date: Thu, 2 May 2024 10:22:59 +0300
Subject: [PATCH 0017/1014] [clang] fix(85447): clang 18.1.0 crashes in
clang::ASTContext::getTypeInfoImpl (#89850)
Fixes #85447
---
This PR resolves a crash in `ActOnUninitializedDecl` due to an oversight
in updating the `isInvalidDecl` state before invocation. The crash
occurs due to a missing invocation of `setInvalidDecl()` for an invalid
`Anon` declaration. To address this issue, the `setInvalidDecl()` method
is now properly invoked to mark the `Anon` declaration as invalid before
running `ActOnUninitializedDecl()`.
---
clang/docs/ReleaseNotes.rst | 1 +
clang/lib/Sema/SemaDecl.cpp | 3 +++
clang/test/Sema/incomplete-struct-decl.cpp | 10 ++++++++++
3 files changed, 14 insertions(+)
create mode 100644 clang/test/Sema/incomplete-struct-decl.cpp
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index d402babc8aaa..818dce8fb83b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -649,6 +649,7 @@ Bug Fixes to C++ Support
- Fix a bug on template partial specialization with issue on deduction of nontype template parameter
whose type is `decltype(auto)`. Fixes (#GH68885).
- Clang now correctly treats the noexcept-specifier of a friend function to be a complete-class context.
+- Fix an assertion failure when parsing an invalid members of an anonymous class. (#GH85447)
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 79fb6c0417e3..19968452f0d5 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -5790,6 +5790,9 @@ Decl *Sema::BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS,
Anon = VarDecl::Create(Context, Owner, DS.getBeginLoc(),
Record->getLocation(), /*IdentifierInfo=*/nullptr,
Context.getTypeDeclType(Record), TInfo, SC);
+ if (Invalid)
+ Anon->setInvalidDecl();
+
ProcessDeclAttributes(S, Anon, Dc);
// Default-initialize the implicit variable. This initialization will be
diff --git a/clang/test/Sema/incomplete-struct-decl.cpp b/clang/test/Sema/incomplete-struct-decl.cpp
new file mode 100644
index 000000000000..bc3bd6b2eae2
--- /dev/null
+++ b/clang/test/Sema/incomplete-struct-decl.cpp
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -x c++ -fsyntax-only -verify=cxx,expected %s
+
+template using __impl_of = a; // expected-note {{'__impl_of' declared here}} \
+ expected-note {{template is declared here}}
+struct { // expected-error {{anonymous structs and classes must be class members}} \
+ expected-note {{to match this '{'}}
+ __impl_; // expected-error {{no template named '__impl_'; did you mean '__impl_of'?}} \
+ expected-error {{cannot specify deduction guide for alias template '__impl_of'}} \
+ expected-error {{expected ';' after struct}}
+ // expected-error {{expected '}'}}
--
GitLab
From 16096325a5ff66d26b7e3424e7fac029c3dd35ea Mon Sep 17 00:00:00 2001
From: David Spickett
Date: Thu, 2 May 2024 08:44:32 +0100
Subject: [PATCH 0018/1014] [clang][Docs] Add release note for
{target}-none-{environment} triple normalization changes (#90734)
That were implemented by
https://github.com/llvm/llvm-project/pull/89638.
---
clang/docs/ReleaseNotes.rst | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 818dce8fb83b..85f7144a27ca 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -90,6 +90,25 @@ Clang Frontend Potentially Breaking Changes
of ``-Wno-gnu-binary-literal`` will no longer silence this pedantic warning,
which may break existing uses with ``-Werror``.
+- The normalization of 3 element target triples where ``-none-`` is the middle
+ element has changed. For example, ``armv7m-none-eabi`` previously normalized
+ to ``armv7m-none-unknown-eabi``, with ``none`` for the vendor and ``unknown``
+ for the operating system. It now normalizes to ``armv7m-unknown-none-eabi``,
+ which has ``unknown`` vendor and ``none`` operating system.
+
+ The affected triples are primarily for bare metal Arm where it is intended
+ that ``none`` means that there is no operating system. As opposed to an unknown
+ type of operating system.
+
+ This change my cause clang to not find libraries, or libraries to be built at
+ different file system locations. This can be fixed by changing your builds to
+ use the new normalized triple. However, we recommend instead getting the
+ normalized triple from clang itself, as this will make your builds more
+ robust in case of future changes::
+
+ $ clang --target= -print-target-triple
+
+
What's New in Clang |release|?
==============================
Some of the major new features and improvements to Clang are listed
--
GitLab
From e19f7221412f4b49f2aa2a3d8a07ffd3debab0d8 Mon Sep 17 00:00:00 2001
From: David Spickett
Date: Thu, 2 May 2024 08:45:48 +0100
Subject: [PATCH 0019/1014] [lldb][Docs] Use proper LLDB/GDB project branding
in tutorial (#90712)
Except when referring to the program binaries.
---
lldb/docs/use/tutorial.rst | 116 ++++++++++++++++++-------------------
1 file changed, 58 insertions(+), 58 deletions(-)
diff --git a/lldb/docs/use/tutorial.rst b/lldb/docs/use/tutorial.rst
index c7f89976156c..22354c6720e1 100644
--- a/lldb/docs/use/tutorial.rst
+++ b/lldb/docs/use/tutorial.rst
@@ -1,14 +1,14 @@
Tutorial
========
-This document describes how to use lldb if you are already familiar with
-gdb's command set. We will start with some details on lldb command structure and
+This document describes how to use LLDB if you are already familiar with
+GDB's command set. We will start with some details on LLDB command structure and
syntax.
Command Structure
-----------------
-Unlike gdb's quite free-form commands, lldb's are more structured. All commands
+Unlike GDB's quite free-form commands, LLDB's are more structured. All commands
are of the form:
::
@@ -24,11 +24,11 @@ all commands. The command syntax for basic commands is very simple.
* Escape backslashes and double quotes within arguments should be escaped
with a backslash ``\``.
-This makes lldb's commands more regular, but it also means you may have to quote
-some arguments in lldb that you would not in gdb.
+This makes LLDB's commands more regular, but it also means you may have to quote
+some arguments in LLDB that you would not in GDB.
-There is one other special quote character in lldb - the backtick `````.
-If you put backticks around an argument or option value, lldb will run the text
+There is one other special quote character in LLDB - the backtick `````.
+If you put backticks around an argument or option value, LLDB will run the text
of the value through the expression parser, and the result of the expression
will be passed to the command. So for instance, if ``len`` is a local
``int`` variable with the value ``5``, then the command:
@@ -40,7 +40,7 @@ will be passed to the command. So for instance, if ``len`` is a local
Will receive the value ``5`` for the count option, rather than the string ``len``.
Options can be placed anywhere on the command line, but if the arguments begin
-with a ``-`` then you have to tell lldb that you are done with options for the
+with a ``-`` then you have to tell LLDB that you are done with options for the
current command by adding an option termination: ``--``.
So for instance, if you want to launch a process and give the ``process launch``
@@ -53,7 +53,7 @@ to launch to be launched with the arguments ``-program_arg value``, you would ty
We also tried to reduce the number of special purpose argument parsers, which
sometimes forces the user to be explicit about their intentions. The first
-instance you willl see of this is the breakpoint command. In gdb, to set a
+instance you willl see of this is the breakpoint command. In GDB, to set a
breakpoint, you might enter:
::
@@ -71,17 +71,17 @@ from ``foo`` from ``foo.c::foo`` (which means the function ``foo`` in the file `
got more and more complex. Especially in C++ there are times where there is
really no way to specify the function you want to break on.
-The lldb commands are more verbose but also more precise and allow for
+The LLDB commands are more verbose but also more precise and allow for
intelligent auto completion.
-To set the same file and line breakpoint in lldb you can enter either of:
+To set the same file and line breakpoint in LLDB you can enter either of:
::
(lldb) breakpoint set --file foo.c --line 12
(lldb) breakpoint set -f foo.c -l 12
-To set a breakpoint on a function named ``foo`` in lldb you can enter either of:
+To set a breakpoint on a function named ``foo`` in LLDB you can enter either of:
::
@@ -96,7 +96,7 @@ conditions or commands without having to specify them multiple times:
(lldb) breakpoint set --name foo --name bar
-Setting breakpoints by name is even more specialized in lldb as you can specify
+Setting breakpoints by name is even more specialized in LLDB as you can specify
that you want to set a breakpoint at a function by method name. To set a
breakpoint on all C++ methods named ``foo`` you can enter either of:
@@ -125,7 +125,7 @@ The ``--shlib`` option can also be repeated to specify several shared libraries.
Suggestions on more interesting primitives of this sort are also very welcome.
-Just like gdb, the lldb command interpreter does a shortest unique string match
+Just like GDB, the LLDB command interpreter does a shortest unique string match
on command names, so the following two commands will both execute the same
command:
@@ -134,12 +134,12 @@ command:
(lldb) breakpoint set -n "-[SKTGraphicView alignLeftEdges:]"
(lldb) br s -n "-[SKTGraphicView alignLeftEdges:]"
-lldb also supports command completion for source file names, symbol names, file
+LLDB also supports command completion for source file names, symbol names, file
names, etc. Completion is initiated by hitting TAB. Individual options in a
command can have different completers, so for instance, the ``--file ``
option in ``breakpoint`` completes to source files, the ``--shlib `` option
to currently loaded shared libraries, etc. You can even do things like if you
-specify ``--shlib ``, and are completing on ``--file ``, lldb will only
+specify ``--shlib ``, and are completing on ``--file ``, LLDB will only
list source files in the shared library specified by ``--shlib ``.
The individual commands are pretty extensively documented. You can use the ``help``
@@ -162,23 +162,23 @@ You can do:
(lldb) command alias bfl breakpoint set -f %1 -l %2
(lldb) bfl foo.c 12
-lldb has a few aliases for commonly used commands (e.g. ``step``, ``next`` and
+LLDB has a few aliases for commonly used commands (e.g. ``step``, ``next`` and
``continue``) but it does not try to be exhaustive because in our experience it
is more convenient to make the basic commands unique down to a letter or two,
and then learn these sequences than to fill the namespace with lots of aliases,
and then have to type them all the way out.
-However, users are free to customize lldb's command set however they like, and
-since lldb reads the file ``~/.lldbinit`` at startup, you can store all your
+However, users are free to customize LLDB's command set however they like, and
+since LLDB reads the file ``~/.lldbinit`` at startup, you can store all your
aliases there and they will be generally available to you. Your aliases are
also documented in the ``help`` command so you can remind yourself of what you have
set up.
-One alias of note that lldb does include by popular demand is a weak emulator of
-gdb's ``break`` command. It does not try to do everything that gdb's break command
+One alias of note that LLDB does include by popular demand is a weak emulator of
+GDB's ``break`` command. It does not try to do everything that GDB's break command
does (for instance, it does not handle ``foo.c::bar``). But it mostly works, and
makes the transition easier. Also, by popular demand, it is aliased to ``b``. If you
-actually want to learn the lldb command set natively, that means it will get in
+actually want to learn the LLDB command set natively, that means it will get in
the way of the rest of the breakpoint commands. Fortunately, if you do not like
one of our aliases, you can easily get rid of it by running, for example:
@@ -192,9 +192,9 @@ You can also do:
(lldb) command alias b breakpoint
-So you can run the native lldb breakpoint command with just ``b``.
+So you can run the native LLDB breakpoint command with just ``b``.
-The lldb command parser also supports "raw" commands, where, after command
+The LLDB command parser also supports "raw" commands, where, after command
options are stripped off, the rest of the command string is passed
uninterpreted to the command. This is convenient for commands whose arguments
might be some complex expression that would be painful to backslash protect.
@@ -205,17 +205,17 @@ commands still can have options, if your command string has dashes in it,
you will have to indicate these are not option markers by putting ``--`` after the
command name, but before your command string.
-lldb also has a built-in Python interpreter, which is accessible by the
+LLDB also has a built-in Python interpreter, which is accessible by the
``"script`` command. All the functionality of the debugger is available as classes
-in the Python interpreter, so the more complex commands that in gdb you would
+in the Python interpreter, so the more complex commands that in GDB you would
introduce with the ``define`` command can be done by writing Python functions
-using the lldb-Python library, then loading the scripts into your running
+using the LLDB Python library, then loading the scripts into your running
session and accessing them with the ``script`` command.
-Loading a Program Into lldb
+Loading a Program Into LLDB
---------------------------
-First you need to set the program to debug. As with gdb, you can start lldb and
+First you need to set the program to debug. As with GDB, you can start LLDB and
specify the file you wish to debug on the command line:
::
@@ -273,16 +273,16 @@ address it corresponds to gets loaded into the program you are debugging. For
instance if you set a breakpoint in a shared library that then gets unloaded,
that breakpoint location will remain, but it will no longer be resolved.
-One other thing to note for gdb users is that lldb acts like gdb with:
+One other thing to note for GDB users is that LLDB acts like GDB with:
::
(gdb) set breakpoint pending on
-Which means that lldb will always make a breakpoint from your specification, even if it
+Which means that LLDB will always make a breakpoint from your specification, even if it
could not find any locations that match the specification. You can tell whether
the expression was resolved or not by checking the locations field in
-``breakpoint list``, and lldb reports the breakpoint as ``pending`` when you set it so
+``breakpoint list``, and LLDB reports the breakpoint as ``pending`` when you set it so
you can tell you have made a typo more easily, if that was indeed the reason no
locations were found:
@@ -304,12 +304,12 @@ command to print a backtrace when you hit this breakpoint you could do:
> bt
> DONE
-By default, the breakpoint command add command takes lldb command line
+By default, the breakpoint command add command takes LLDB command line
commands. You can also specify this explicitly by passing the ``--command``
option. Use ``--script`` if you want to implement your breakpoint command using
the Python script instead.
-This is a convenient point to bring up another feature of the lldb command
+This is a convenient point to bring up another feature of the LLDB command
``help``. Do:
::
@@ -447,7 +447,7 @@ a variable called ``global`` for write operation, but only stop if the condition
Starting or Attaching to Your Program
-------------------------------------
-To launch a program in lldb you will use the ``process launch`` command or one of
+To launch a program in LLDB you will use the ``process launch`` command or one of
its built in aliases:
::
@@ -457,7 +457,7 @@ its built in aliases:
(lldb) r
You can also attach to a process by process ID or process name. When attaching
-to a process by name, lldb also supports the ``--waitfor`` option which waits for
+to a process by name, LLDB also supports the ``--waitfor`` option which waits for
the next process that has that name to show up, and attaches to it
::
@@ -497,32 +497,32 @@ for process control all exist under the "thread" command:
At present you can only operate on one thread at a time, but the design will
ultimately support saying "step over the function in Thread 1, and step into the
-function in Thread 2, and continue Thread 3" etc. When lldb eventually supports
+function in Thread 2, and continue Thread 3" etc. When LLDB eventually supports
keeping some threads running while others are stopped this will be particularly
important. For convenience, however, all the stepping commands have easy aliases.
So ``thread continue`` is just ``c``, etc.
-The other program stepping commands are pretty much the same as in gdb. You have got:
+The other program stepping commands are pretty much the same as in GDB. You have got:
::
- (lldb) thread step-in // The same as gdb's "step" or "s"
- (lldb) thread step-over // The same as gdb's "next" or "n"
- (lldb) thread step-out // The same as gdb's "finish" or "f"
+ (lldb) thread step-in // The same as GDB's "step" or "s"
+ (lldb) thread step-over // The same as GDB's "next" or "n"
+ (lldb) thread step-out // The same as GDB's "finish" or "f"
-By default, lldb does defined aliases to all common gdb process control commands
-(``s``, ``step``, ``n``, ``next``, ``finish``). If lldb is missing any, please add
+By default, LLDB does defined aliases to all common GDB process control commands
+(``s``, ``step``, ``n``, ``next``, ``finish``). If LLDB is missing any, please add
them to your ``~/.lldbinit`` file using the ``command alias`` command.
-lldb also supports the step by instruction versions:
+LLDB also supports the step by instruction versions:
::
- (lldb) thread step-inst // The same as gdb's "stepi" / "si"
- (lldb) thread step-over-inst // The same as gdb's "nexti" / "ni"
+ (lldb) thread step-inst // The same as GDB's "stepi" / "si"
+ (lldb) thread step-over-inst // The same as GDB's "nexti" / "ni"
-Finally, lldb has a run until line or frame exit stepping mode:
+Finally, LLDB has a run until line or frame exit stepping mode:
::
@@ -530,16 +530,16 @@ Finally, lldb has a run until line or frame exit stepping mode:
This command will run the thread in the current frame until it reaches line 100
in this frame or stops if it leaves the current frame. This is a pretty close
-equivalent to gdb's ``until`` command.
+equivalent to GDB's ``until`` command.
-A process, by default, will share the lldb terminal with the inferior process.
-When in this mode, much like when debugging with gdb, when the process is
+A process, by default, will share the LLDB terminal with the inferior process.
+When in this mode, much like when debugging with GDB, when the process is
running anything you type will go to the ``STDIN`` of the inferior process. To
interrupt your inferior program, type ``CTRL+C``.
If you attach to a process, or launch a process with the ``--no-stdin`` option,
the command interpreter is always available to enter commands. It might be a
-little disconcerting to gdb users to always have an ``(lldb)`` prompt. This allows
+little disconcerting to GDB users to always have an ``(lldb)`` prompt. This allows
you to set a breakpoint, or use any other command without having to explicitly
interrupt the program you are debugging:
@@ -563,16 +563,16 @@ and memory reading and writing (``memory [read|write] ...``).
The question of disabling stdio when running brings up a good opportunity to
show how to set debugger properties. If you always want to run in
the ``--no-stdin`` mode, you can set this as a generic process property using the
-lldb ``settings`` command, which is equivalent to gdb's ``set`` command.
+LLDB ``settings`` command, which is equivalent to GDB's ``set`` command.
In this case you would say:
::
(lldb) settings set target.process.disable-stdio true
-Over time, gdb's ``set`` command became a wilderness of disordered options, so
-that there were useful options that even experienced gdb users did not know
-about because they were too hard to find. lldb instead organizes the settings
+Over time, GDB's ``set`` command became a wilderness of disordered options, so
+that there were useful options that even experienced GDB users did not know
+about because they were too hard to find. LLDB instead organizes the settings
hierarchically using the structure of the basic entities in the debugger. For
the most part anywhere you can specify a setting on a generic entity (threads,
for example) you can also apply the option to a particular instance. You can
@@ -582,7 +582,7 @@ on the settings command explaining how it works more generally.
Examining Thread State
----------------------
-Once you have stopped, lldb will choose a current thread, usually the one that
+Once you have stopped, LLDB will choose a current thread, usually the one that
stopped "for a reason", and a current frame in that thread (on stop this is
always the bottom-most frame). Many the commands for inspecting state work on
this current thread/frame.
@@ -685,7 +685,7 @@ pointers as arrays:
(char const *) argv[0] = 0x00007fff5fbffaf8 "/Projects/Sketch/build/Debug/Sketch.app/Contents/MacOS/Sketch"
The frame variable command will also perform "object printing" operations on
-variables (currently lldb only supports ObjC printing, using the object's
+variables (currently LLDB only supports ObjC printing, using the object's
``description`` method. Turn this on by passing the ``-o`` flag to frame variable:
::
@@ -697,4 +697,4 @@ variables (currently lldb only supports ObjC printing, using the object's
frame #9: 0x0000000100015ae3, where = Sketch`function1 + 33 at /Projects/Sketch/SKTFunctions.m:11
You can also move up and down the stack by passing the ``--relative`` (``-r``) option.
-We also have built-in aliases ``u`` and ``d`` which behave like their gdb equivalents.
+We also have built-in aliases ``u`` and ``d`` which behave like their GDB equivalents.
--
GitLab
From 176d6fbed3988032dbdabe7abfc5f54e1f0e2bbb Mon Sep 17 00:00:00 2001
From: David Spickett
Date: Thu, 2 May 2024 08:45:57 +0100
Subject: [PATCH 0020/1014] [lldb][Docs] Remove .txt copy of tutorial (#90585)
This was last modified in 4fd3347d6e4b0c873c789528e1c9a1b55990d1b6 in
2021 and was made obsolete by the RST version that
edb874b2310dc6eeaa27330ca1b1c013da7bdd65 added in 2019.
There are some differences but at this point, I'd bet the RST is the
correct one.
---
lldb/docs/lldb-for-gdb-users.txt | 488 -------------------------------
1 file changed, 488 deletions(-)
delete mode 100644 lldb/docs/lldb-for-gdb-users.txt
diff --git a/lldb/docs/lldb-for-gdb-users.txt b/lldb/docs/lldb-for-gdb-users.txt
deleted file mode 100644
index e5eae376bb48..000000000000
--- a/lldb/docs/lldb-for-gdb-users.txt
+++ /dev/null
@@ -1,488 +0,0 @@
-Here's a short precis of how to run lldb if you are familiar with the
-gdb command set:
-
-
-1) LLDB Command Structure:
-
-First some details on lldb command structure to help orient you...
-
-Unlike gdb's command set, which is rather free-form, we tried to make
-the lldb command syntax fairly structured. The commands are all of the
-form
-
- [-options [option-value]] [argument [argument...]]
-
-The command line parsing is done before command execution, so it is
-uniform across all the commands. The command syntax is very simple,
-basically arguments, options and option values are all white-space
-separated. If you need to put a backslash or double-quote character
-in an argument you back-slash it in the argument. That makes the
-command syntax more regular, but it also means you may have to
-quote some arguments in lldb that you wouldn't in gdb.
-
-Options can be placed anywhere on the command line, but if the arguments
-begin with a "-" then you have to tell lldb that you're done with options
-using the "--" option. So for instance, the "process launch" command takes
-the "-s" option to mean "stop the process at the first instruction". It's
-arguments are the arguments you are passing to the program. So if you wanted
-to pass an argument that contained a "-" you would have to do:
-
-(lldb) process launch -- -program_arg value
-
-We also tried to reduce the number of special purpose argument
-parsers, which sometimes forces the user to be a little more explicit
-about stating their intentions. The first instance you'll note of
-this is the breakpoint command. In gdb, to set a breakpoint, you
-would just say:
-
-(gdb) break foo.c:12
-
-or
-
-(gdb) break foo
-
-if foo is a function. As time went on, the parser that tells foo.c:12
-from foo from foo.c::foo (which means the function foo in the file
-foo.c) got more and more complex and bizarre, and especially in C++
-there are times where there's really no way to specify the function
-you want to break on. The lldb commands are more verbose but also precise.
-So you say:
-
-(lldb) breakpoint set -f foo.c -l 12
-
-to set a file & line breakpoint. To set a breakpoint on a function
-by name, you do:
-
-(lldb) breakpoint set -n foo
-
-This can allow us to be more expressive, so you can say:
-
-(lldb) breakpoint set -M foo
-
-to break on all C++ methods named foo, or:
-
-(lldb) breakpoint set -S alignLeftEdges:
-
-to set a breakpoint on all ObjC selectors called alignLeftEdges:. It
-also makes it easy to compose specifications, like:
-
-(lldb) breakpoint set -s foo.dylib -n foo
-
-for all functions called foo in the shared library foo.dylib. Suggestions
-on more interesting primitives of this sort are also very welcome.
-
-So for instance:
-
-(lldb) breakpoint set -n "-[SKTGraphicView alignLeftEdges:]"
-
-Just like gdb, the lldb command interpreter does a shortest unique
-string match on command names, so the previous command can also be
-typed:
-
-(lldb) b s -n "-[SKTGraphicView alignLeftEdges:]"
-
-lldb also supports command completion for source file names, symbol
-names, file names, etc. Completion is initiated by a hitting a .
-Individual options in a command can have different completers, so for
-instance the -f option in "breakpoint" completes to source files, the
--s option to currently loaded shared libraries, etc... We can even do
-things like if you specify -s, and are completing on -f, we will only
-list source files in the shared library specified by -s...
-
-The individual commands are pretty extensively documented, using
-the "help" command. And there is an "apropos" command that will
-search the help for a particular word and dump a summary help string
-for each matching command.
-
-Finally, there is a mechanism to construct aliases for commonly used
-commands. So for instance if you get annoyed typing
-
-(lldb) b s -f foo.c -l 12
-
-you can do:
-
-(lldb) command alias bfl breakpoint set -f %1 -l %2
-(lldb) bfl foo.c 12
-
-We have added a few aliases for commonly used commands (e.g. "step",
-"next" and "continue") but we haven't tried to be exhaustive because
-in our experience it is more convenient to make the basic commands
-unique down to a letter or two, and then learn these sequences than
-fill the namespace with lots of aliases, and then have to type them
-all the way out.
-
-However, users are free to customize lldb's command set however they
-like, and since lldb reads the file ~/.lldbinit at startup, you can
-store all your aliases there and they will be generally available to
-you. Your aliases are also documented in the help command so you can
-remind yourself of what you've set up.
-
-lldb also has a built-in Python interpreter, which is accessible by
-the "script" command. All the functionality of the debugger is
-available as classes in the Python interpreter, so the more complex
-commands that in gdb you would introduce with the "define" command can
-be done by writing Python functions using the lldb-Python library,
-then loading the scripts into your running session and accessing them
-with the "script" command.
-
-
-
-2) A typical session:
-
-
-a) Setting the program to debug:
-
-
-As with gdb, you can start lldb and specify the file you wish to debug
-on the command line:
-
-$ lldb /Projects/Sketch/build/Debug/Sketch.app
-Current executable set to '/Projects/Sketch/build/Debug/Sketch.app' (x86_64).
-
-or you can specify it after the fact with the "file" command:
-
-(lldb) file /Projects/Sketch/build/Debug/Sketch.app
-Current executable set to '/Projects/Sketch/build/Debug/Sketch.app' (x86_64).
-
-
-b) Setting breakpoints:
-
-
-We've discussed how to set breakpoints above. You can use "help break set"
-to see all the options for breakpoint setting. For instance, we might do:
-
-(lldb) b s -S alignLeftEdges:
-Breakpoint created: 1: name = 'alignLeftEdges:', locations = 1, resolved = 1
-
-You can find out about the breakpoints you've set with:
-
-(lldb) break list
-Current breakpoints:
-1: name = 'alignLeftEdges:', locations = 1, resolved = 1
- 1.1: where = Sketch`-[SKTGraphicView alignLeftEdges:] + 33 at /Projects/Sketch/SKTGraphicView.m:1405, address = 0x0000000100010d5b, resolved, hit count = 0
-
-Note that each "logical" breakpoint can have multiple "locations".
-The logical breakpoint has an integer id, and its locations have an
-id within their parent breakpoint (the two are joined by a ".",
-e.g. 1.1 in the example above.)
-
-Also the breakpoints remain "live" so that if another shared library
-were to be loaded that had another implementation of the
-"alignLeftEdges:" selector, the new location would be added to
-breakpoint 1 (e.g. a "1.2" breakpoint would be set on the newly loaded
-selector).
-
-The other piece of information in the breakpoint listing is whether the
-breakpoint location was "resolved" or not. A location gets resolved when
-the file address it corresponds to gets loaded into the program you are
-debugging. For instance if you set a breakpoint in a shared library that
-then gets unloaded, that breakpoint location will remain, but it will no
-longer be "resolved".
-
-One other thing to note for gdb users is that lldb acts like gdb with:
-
-(gdb) set breakpoint pending on
-
-That is, lldb should always make a breakpoint from your specification, even
-if it couldn't find any locations that match the specification. You can tell
-whether the expression was resolved or not by checking the locations field
-in "breakpoint list", and we report the breakpoint as "pending" when you
-set it so you can tell you've made a typo more easily, if that was indeed
-the reason no locations were found:
-
-(lldb) b s -f no_such_file.c -l 10000000
-Breakpoint created: 1: file ='no_such_file.c', line = 10000000, locations = 0 (pending)
-
-You can delete, disable, set conditions and ignore counts either on all the
-locations generated by your logical breakpoint, or on particular locations
-your specification resolved to. For instance if we wanted to add a command
-to print a backtrace when we hit this breakpoint we could do:
-
-(lldb) b command add -c 1.1
-Enter your debugger command(s). Type 'DONE' to end.
-> bt
-> DONE
-
-The "-c" option specifies that the breakpoint command is a set of lldb
-command interpreter commands. Use "-s" if you want to implement your
-breakpoint command using the Python interface instead.
-
-
-c) Running the program:
-
-Then you can either launch the process with the command:
-
-(lldb) process launch
-
-or its alias:
-
-(lldb) r
-
-Or you can attach to a process by name with:
-
-(lldb) process attach -n Sketch
-
-The "attach by name" also supports the "-w" option which waits for the
-next process of that name to show up, and attaches to that. You can also
-attach by PID:
-
-(lldb) process attach -p 12345
-Process 46915 Attaching
-(lldb) Process 46915 Stopped
-1 of 3 threads stopped with reasons:
-* thread #1: tid = 0x2c03, 0x00007fff85cac76a, where = libSystem.B.dylib`__getdirentries64 + 10, stop reason = signal = SIGSTOP, queue = com.apple.main-thread
-
-Note that we tell you that "1 of 3 threads stopped with reasons" and
-then list those threads. In a multi-threaded environment it is very
-common for more than one thread to hit your breakpoint(s) before the
-kernel actually returns control to the debugger. In that case, you
-will see all the threads that stopped for some interesting reason
-listed in the stop message.
-
-
-d) Controlling execution:
-
-
-After launching, we can continue until we hit our breakpoint. The primitive
-commands for process control all exist under the "thread" command:
-
-(lldb) thread continue
-Resuming thread 0x2c03 in process 46915
-Resuming process 46915
-(lldb)
-
-At present you can only operate on one thread at a time, but the
-design will ultimately support saying "step over the function in
-Thread 1, and step into the function in Thread 2, and continue Thread
-3" etc. When we eventually support keeping some threads running while
-others are stopped this will be particularly important. For
-convenience, however, all the stepping commands have easy aliases.
-So "thread continue" is just "c", etc.
-
-The other program stepping commands are pretty much the same as in gdb.
-You've got:
-
- 1. (lldb) thread step-in
- The same as gdb's "step" -- there is also the alias "s" in lldb
-
- 2. (lldb) thread step-over
- The same as gdb's "next" -- there is also the alias "n" in lldb
-
- 3. (lldb) thread step-out
- The same as gdb's "finish" -- there is also the alias "f" in lldb
-
-And the "by instruction" versions:
-
-(lldb) thread step-inst
-(lldb) thread step-over-inst
-
-Finally, there's:
-
-(lldb) thread until 100
-
-Which runs the thread in the current frame till it reaches line 100 in
-this frame or stops if it leaves the current frame. This is a pretty
-close equivalent to gdb's "until" command.
-
-
-One thing here that might be a little disconcerting to gdb users here is that
-when you resume process execution, you immediately get a prompt back. That's
-because the lldb interpreter remains live when you are running the target.
-This allows you to set a breakpoint, etc without having to explicitly interrupt
-the program you are debugging. We're still working out all the operations
-that it is safe to do while running. But this way of operation will set us
-up for "no stop" debugging when we get to implementing that.
-
-If you want to interrupt a running program do:
-
-(lldb) process interrupt
-
-To find out the state of the program, use:
-
-(lldb) process status
-Process 47958 is running.
-
-This is very convenient, but it does have the down-side that debugging
-programs that use stdin is no longer as straightforward. For now, you
-have to specify another tty to use as the program stdout & stdin using
-the appropriate options to "process launch", or start your program in
-another terminal and catch it with "process attach -w". We will come
-up with some more convenient way to juggle the terminal back & forth
-over time.
-
-
-e) Examining program state:
-
-Once you've stopped, lldb will choose a current thread, usually the
-one that stopped "for a reason", and a current frame in that thread.
-Many the commands for inspecting state work on this current
-thread/frame.
-
-To inspect the current state of your process, you can start with the
-threads:
-
-(lldb) thread list
-Process 46915 state is Stopped
-* thread #1: tid = 0x2c03, 0x00007fff85cac76a, where = libSystem.B.dylib`__getdirentries64 + 10, stop reason = signal = SIGSTOP, queue = com.apple.main-thread
- thread #2: tid = 0x2e03, 0x00007fff85cbb08a, where = libSystem.B.dylib`kevent + 10, queue = com.apple.libdispatch-manager
- thread #3: tid = 0x2f03, 0x00007fff85cbbeaa, where = libSystem.B.dylib`__workq_kernreturn + 10
-
-The * indicates that Thread 1 is the current thread. To get a
-backtrace for that thread, do:
-
-(lldb) thread backtrace
-thread #1: tid = 0x2c03, stop reason = breakpoint 1.1, queue = com.apple.main-thread
- frame #0: 0x0000000100010d5b, where = Sketch`-[SKTGraphicView alignLeftEdges:] + 33 at /Projects/Sketch/SKTGraphicView.m:1405
- frame #1: 0x00007fff8602d152, where = AppKit`-[NSApplication sendAction:to:from:] + 95
- frame #2: 0x00007fff860516be, where = AppKit`-[NSMenuItem _corePerformAction] + 365
- frame #3: 0x00007fff86051428, where = AppKit`-[NSCarbonMenuImpl performActionWithHighlightingForItemAtIndex:] + 121
- frame #4: 0x00007fff860370c1, where = AppKit`-[NSMenu performKeyEquivalent:] + 272
- frame #5: 0x00007fff86035e69, where = AppKit`-[NSApplication _handleKeyEquivalent:] + 559
- frame #6: 0x00007fff85f06aa1, where = AppKit`-[NSApplication sendEvent:] + 3630
- frame #7: 0x00007fff85e9d922, where = AppKit`-[NSApplication run] + 474
- frame #8: 0x00007fff85e965f8, where = AppKit`NSApplicationMain + 364
- frame #9: 0x0000000100015ae3, where = Sketch`main + 33 at /Projects/Sketch/SKTMain.m:11
- frame #10: 0x0000000100000f20, where = Sketch`start + 52
-
-You can also provide a list of threads to backtrace, or the keyword
-"all" to see all threads:
-
-(lldb) thread backtrace all
-
-Next task is inspecting data:
-
-The most convenient way to inspect a frame's arguments and local variables is:
-
-(lldb) frame variable
-self = (SKTGraphicView *) 0x0000000100208b40
-_cmd = (struct objc_selector *) 0x000000010001bae1
-sender = (id) 0x00000001001264e0
-selection = (NSArray *) 0x00000001001264e0
-i = (NSUInteger) 0x00000001001264e0
-c = (NSUInteger) 0x00000001001253b0
-
-You can also choose particular variables to view:
-
-(lldb) frame variable self
-(SKTGraphicView *) self = 0x0000000100208b40
-
-The frame variable command is not a full expression parser but it
-does support some common operations like dereferencing:
-
-(lldb) fr v *self
-(SKTGraphicView *) self = 0x0000000100208b40
- (NSView) NSView = {
- (NSResponder) NSResponder = {
-...
-
-and structure element references:
-
-(lldb) frame variable self.isa
-(struct objc_class *) self.isa = 0x0000000100023730
-
-The frame variable command will also perform "object printing" operations on
-variables (currently we only support NSPrintForDebugger) with:
-
-(lldb) fr v -o self
-(SKTGraphicView *) self = 0x0000000100208b40
-
-
-You can select another frame to view with:
-
-(lldb) frame select 9
-frame #9: 0x0000000100015ae3, where = Sketch`main + 33 at /Projects/Sketch/SKTMain.m:11
- 8
- 9
- 10 int main(int argc, const char *argv[]) {
- 11 -> return NSApplicationMain(argc, argv);
- 12 }
- 13
- 14
-
-Another neat trick that the variable list does is array references, so:
-
-(lldb) fr v argv[0]
-(char const *) argv[0] = 0x00007fff5fbffaf8 "/Projects/Sketch/build/Debug/Sketch.app/Contents/MacOS/Sketch"
-
-If you need to view more complex data or change program data, you can
-use the general "expression" command. It takes an expression and
-evaluates it in the scope of the currently selected frame. For instance:
-
-(lldb) expr self
-$0 = (SKTGraphicView *) 0x0000000100135430
-(lldb) expr self = 0x00
-$1 = (SKTGraphicView *) 0x0000000000000000
-(lldb) frame var self
-(SKTGraphicView *) self = 0x0000000000000000
-
-You can also call functions:
-
-(lldb) expr (int) printf ("I have a pointer 0x%llx.\n", self)
-$2 = (int) 22
-I have a pointer 0x0.
-
-One thing to note from this example is that lldb commands can be defined to
-take "raw" input. "expression" is one of these. So in the expression command,
-you don't have to quote your whole expression, nor backslash protect quotes,
-etc...
-
-Finally, the results of the expressions are stored in persistent variables
-(of the form $[0-9]+) that you can use in further expressions, like:
-
-(lldb) expr self = $0
-$4 = (SKTGraphicView *) 0x0000000100135430
-
-f) Customization:
-
-You can use the embedded Python interpreter to add the following 'pwd' and 'cd' commands
-for your lldb session:
-
-(lldb) script import os
-(lldb) command alias pwd script print os.getcwd()
-(lldb) command regex cd "s/^(.*)$/script os.chdir(os.path.expanduser('%1'))/"
-
-...
-
-(lldb) cd /tmp
-script os.chdir(os.path.expanduser('/tmp'))
-(lldb) pwd
-/private/tmp
-(lldb)
-
-Or for a more capable 'cd' command, create ~/utils.py like this:
-
-import os
-
-def chdir(debugger, args, result, dict):
- """Change the working directory, or cd to ${HOME}."""
- dir = args.strip()
- if dir:
- os.chdir(args)
- else:
- os.chdir(os.path.expanduser('~'))
- print "Current working directory: %s" % os.getcwd()
-
-and, have the following in your ~/.lldbinit file:
-
-script import os, sys
-script sys.path.append(os.path.expanduser('~'))
-script import utils
-command alias pwd script print os.getcwd()
-command script add -f utils.chdir cd
-
-and, then in your lldb session, you can have:
-
-(lldb) help cd
-
-Change the working directory, or cd to ${HOME}.
-Syntax: cd
-(lldb) cd
-Current working directory: /Volumes/data/Users/johnny
-(lldb) cd /tmp
-Current working directory: /private/tmp
-(lldb) pwd
-/private/tmp
-(lldb)
-
-For more examples of customization, look under the ToT/examples/customization
-directory.
--
GitLab
From 528b512b13e2ade4657b25dbb809bafd28c8b170 Mon Sep 17 00:00:00 2001
From: David Spickett
Date: Thu, 2 May 2024 08:47:40 +0100
Subject: [PATCH 0021/1014] [lldb][test][FreeBSD] Narrow vectorcall xfail to
x86 platforms (#84024)
This relates to #56084.
vectorcall only works on x86
https://clang.llvm.org/docs/AttributeReference.html#vectorcall so
elsewhere it fails to compile. Which is expected on AArch64 for example
so is treated as a pass.
---
.../API/lang/c/calling-conventions/TestCCallingConventions.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/lldb/test/API/lang/c/calling-conventions/TestCCallingConventions.py b/lldb/test/API/lang/c/calling-conventions/TestCCallingConventions.py
index 9483dfcd0401..0304482e899b 100644
--- a/lldb/test/API/lang/c/calling-conventions/TestCCallingConventions.py
+++ b/lldb/test/API/lang/c/calling-conventions/TestCCallingConventions.py
@@ -62,7 +62,10 @@ class TestCase(TestBase):
return
self.expect_expr("func(1, 2, 3, 4)", result_type="int", result_value="10")
+ # Fails on x86, passes elsewhere because clang doesn't support vectorcall on
+ # any other architectures.
@expectedFailureAll(
+ triple=re.compile("^(x86|i386)"),
oslist=["freebsd"], bugnumber="github.com/llvm/llvm-project/issues/56084"
)
def test_vectorcall(self):
--
GitLab
From a015f015db21e02cbce4ff9d15d0b293e45d0831 Mon Sep 17 00:00:00 2001
From: David Spickett
Date: Thu, 2 May 2024 08:48:40 +0100
Subject: [PATCH 0022/1014] [lldb][test][FreeBSD] Remove corefile test xfails
(#84022)
Fixes #48759
As stated on the issue this was fixed by a change in FreeBSD 13, and
I've confirmed that it passes on 14 as well.
---
.../functionalities/postmortem/elf-core/TestLinuxCore.py | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py b/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py
index 7ec5e0d7c830..8ec0cbdd0fdd 100644
--- a/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py
+++ b/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py
@@ -292,9 +292,7 @@ class LinuxCoreTestCase(TestBase):
self.dbg.DeleteTarget(target)
@skipIfLLVMTargetMissing("AArch64")
- @expectedFailureAll(
- archs=["aarch64"], oslist=["freebsd"], bugnumber="llvm.org/pr49415"
- )
+ # This test fails on FreeBSD 12 and earlier, see llvm.org/pr49415 for details.
def test_aarch64_regs(self):
# check 64 bit ARM core files
target = self.dbg.CreateTarget(None)
@@ -377,9 +375,7 @@ class LinuxCoreTestCase(TestBase):
self.expect("register read --all")
@skipIfLLVMTargetMissing("AArch64")
- @expectedFailureAll(
- archs=["aarch64"], oslist=["freebsd"], bugnumber="llvm.org/pr49415"
- )
+ # This test fails on FreeBSD 12 and earlier, see llvm.org/pr49415 for details.
def test_aarch64_sve_regs_fpsimd(self):
# check 64 bit ARM core files
target = self.dbg.CreateTarget(None)
--
GitLab
From c32a4f83b59c4293f0b91503ed217371ae1ab543 Mon Sep 17 00:00:00 2001
From: Lang Hames
Date: Wed, 1 May 2024 22:19:46 -0930
Subject: [PATCH 0023/1014] [ORC] Allow removal of ObjectLinkingLayer Plugins.
This adds a removePlugin operation to ObjectLinkingLayer. The removal of a
plugin will be visible in all links started after the removal (ongoing links
started before the removal will still use the removed plugin).
Coding my way home: 17.56037S, 149.61118W
---
.../ExecutionEngine/Orc/ObjectLinkingLayer.h | 15 +++-
.../Orc/ObjectLinkingLayerTest.cpp | 76 +++++++++++++++++++
2 files changed, 90 insertions(+), 1 deletion(-)
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h b/llvm/include/llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h
index e452b90598a0..689daba8ae73 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h
@@ -121,13 +121,26 @@ public:
this->ReturnObjectBuffer = std::move(ReturnObjectBuffer);
}
- /// Add a pass-config modifier.
+ /// Add a plugin.
ObjectLinkingLayer &addPlugin(std::shared_ptr P) {
std::lock_guard Lock(LayerMutex);
Plugins.push_back(std::move(P));
return *this;
}
+ /// Remove a plugin. This remove applies only to subsequent links (links
+ /// already underway will continue to use the plugin), and does not of itself
+ /// destroy the plugin -- destruction will happen once all shared pointers
+ /// (including those held by in-progress links) are destroyed.
+ void removePlugin(Plugin &P) {
+ std::lock_guard Lock(LayerMutex);
+ auto I = llvm::find_if(Plugins, [&](const std::shared_ptr &Elem) {
+ return Elem.get() == &P;
+ });
+ assert(I != Plugins.end() && "Plugin not present");
+ Plugins.erase(I);
+ }
+
/// Add a LinkGraph to the JITDylib targeted by the given tracker.
Error add(ResourceTrackerSP, std::unique_ptr G);
diff --git a/llvm/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp b/llvm/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp
index 7ab3e40df745..70570055fea9 100644
--- a/llvm/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp
+++ b/llvm/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp
@@ -178,6 +178,82 @@ TEST_F(ObjectLinkingLayerTest, HandleErrorDuringPostAllocationPass) {
EXPECT_THAT_EXPECTED(ES.lookup(&JD, "_anchor"), Failed());
}
+TEST_F(ObjectLinkingLayerTest, AddAndRemovePlugins) {
+ class TestPlugin : public ObjectLinkingLayer::Plugin {
+ public:
+ TestPlugin(size_t &ActivationCount, bool &PluginDestroyed)
+ : ActivationCount(ActivationCount), PluginDestroyed(PluginDestroyed) {}
+
+ ~TestPlugin() { PluginDestroyed = true; }
+
+ void modifyPassConfig(MaterializationResponsibility &MR,
+ jitlink::LinkGraph &G,
+ jitlink::PassConfiguration &Config) override {
+ ++ActivationCount;
+ }
+
+ Error notifyFailed(MaterializationResponsibility &MR) override {
+ ADD_FAILURE() << "TestPlugin::notifyFailed called unexpectedly";
+ return Error::success();
+ }
+
+ Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override {
+ return Error::success();
+ }
+
+ void notifyTransferringResources(JITDylib &JD, ResourceKey DstKey,
+ ResourceKey SrcKey) override {}
+
+ private:
+ size_t &ActivationCount;
+ bool &PluginDestroyed;
+ };
+
+ size_t ActivationCount = 0;
+ bool PluginDestroyed = false;
+
+ auto P = std::make_shared(ActivationCount, PluginDestroyed);
+
+ ObjLinkingLayer.addPlugin(P);
+
+ {
+ auto G1 = std::make_unique("G1", Triple("x86_64-apple-darwin"),
+ 8, llvm::endianness::little,
+ x86_64::getEdgeKindName);
+
+ auto &DataSec = G1->createSection("__data", MemProt::Read | MemProt::Write);
+ auto &DataBlock = G1->createContentBlock(DataSec, BlockContent,
+ orc::ExecutorAddr(0x1000), 8, 0);
+ G1->addDefinedSymbol(DataBlock, 4, "_anchor1", 4, Linkage::Weak,
+ Scope::Default, false, true);
+
+ EXPECT_THAT_ERROR(ObjLinkingLayer.add(JD, std::move(G1)), Succeeded());
+ EXPECT_THAT_EXPECTED(ES.lookup(&JD, "_anchor1"), Succeeded());
+ EXPECT_EQ(ActivationCount, 1U);
+ }
+
+ ObjLinkingLayer.removePlugin(*P);
+
+ {
+ auto G2 = std::make_unique("G2", Triple("x86_64-apple-darwin"),
+ 8, llvm::endianness::little,
+ x86_64::getEdgeKindName);
+
+ auto &DataSec = G2->createSection("__data", MemProt::Read | MemProt::Write);
+ auto &DataBlock = G2->createContentBlock(DataSec, BlockContent,
+ orc::ExecutorAddr(0x1000), 8, 0);
+ G2->addDefinedSymbol(DataBlock, 4, "_anchor2", 4, Linkage::Weak,
+ Scope::Default, false, true);
+
+ EXPECT_THAT_ERROR(ObjLinkingLayer.add(JD, std::move(G2)), Succeeded());
+ EXPECT_THAT_EXPECTED(ES.lookup(&JD, "_anchor2"), Succeeded());
+ EXPECT_EQ(ActivationCount, 1U);
+ }
+
+ P.reset();
+ EXPECT_TRUE(PluginDestroyed);
+}
+
TEST(ObjectLinkingLayerSearchGeneratorTest, AbsoluteSymbolsObjectLayer) {
class TestEPC : public UnsupportedExecutorProcessControl {
public:
--
GitLab
From d00ed836e77759414afd40c02eeca5651657438f Mon Sep 17 00:00:00 2001
From: David Spickett
Date: Thu, 2 May 2024 10:12:36 +0100
Subject: [PATCH 0024/1014] [lldb] Fix build on FreeBSD
Missing llvm:: namespace for StringRef.
---
.../Plugins/SymbolLocator/Default/SymbolLocatorDefault.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/source/Plugins/SymbolLocator/Default/SymbolLocatorDefault.cpp b/lldb/source/Plugins/SymbolLocator/Default/SymbolLocatorDefault.cpp
index edb1d59cf42f..919f26ba7012 100644
--- a/lldb/source/Plugins/SymbolLocator/Default/SymbolLocatorDefault.cpp
+++ b/lldb/source/Plugins/SymbolLocator/Default/SymbolLocatorDefault.cpp
@@ -157,7 +157,7 @@ std::optional SymbolLocatorDefault::LocateExecutableSymbolFile(
mib[1] = USER_LOCALBASE;
if (::sysctl(mib, 2, buf, &len, NULL, 0) == 0) {
FileSpec file_spec("/lib/debug");
- file_spec.PrependPathComponent(StringRef(buf));
+ file_spec.PrependPathComponent(llvm::StringRef(buf));
FileSystem::Instance().Resolve(file_spec);
debug_file_search_paths.AppendIfUnique(file_spec);
}
--
GitLab
From 171aeb20ad465a49065730719731aae405c1d167 Mon Sep 17 00:00:00 2001
From: zxc12523 <76193329+zxc12523@users.noreply.github.com>
Date: Thu, 2 May 2024 17:31:56 +0800
Subject: [PATCH 0025/1014] [DAG] SelectionDAG.computeKnownBits - add NSW/NUW
flags support to ISD::SHL handling (#89877)
fix #89414
---
.../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 11 +-
.../X86/fold-int-pow2-with-fmul-or-fdiv.ll | 65 +++-------
llvm/test/CodeGen/X86/known-never-zero.ll | 4 +-
llvm/test/CodeGen/X86/pr89877.ll | 115 ++++++++++++++++++
4 files changed, 142 insertions(+), 53 deletions(-)
create mode 100644 llvm/test/CodeGen/X86/pr89877.ll
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index d92976bc2f30..25a728bf9ba3 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -3527,16 +3527,23 @@ KnownBits SelectionDAG::computeKnownBits(SDValue Op, const APInt &DemandedElts,
Known.Zero.setBitsFrom(1);
break;
}
- case ISD::SHL:
+ case ISD::SHL: {
Known = computeKnownBits(Op.getOperand(0), DemandedElts, Depth + 1);
Known2 = computeKnownBits(Op.getOperand(1), DemandedElts, Depth + 1);
- Known = KnownBits::shl(Known, Known2);
+
+ bool NUW = Op->getFlags().hasNoUnsignedWrap();
+ bool NSW = Op->getFlags().hasNoSignedWrap();
+
+ bool ShAmtNonZero = Known2.isNonZero();
+
+ Known = KnownBits::shl(Known, Known2, NUW, NSW, ShAmtNonZero);
// Minimum shift low bits are known zero.
if (const APInt *ShMinAmt =
getValidMinimumShiftAmountConstant(Op, DemandedElts))
Known.Zero.setLowBits(ShMinAmt->getZExtValue());
break;
+ }
case ISD::SRL:
Known = computeKnownBits(Op.getOperand(0), DemandedElts, Depth + 1);
Known2 = computeKnownBits(Op.getOperand(1), DemandedElts, Depth + 1);
diff --git a/llvm/test/CodeGen/X86/fold-int-pow2-with-fmul-or-fdiv.ll b/llvm/test/CodeGen/X86/fold-int-pow2-with-fmul-or-fdiv.ll
index 8f875c70a25f..96b2e1ef9827 100644
--- a/llvm/test/CodeGen/X86/fold-int-pow2-with-fmul-or-fdiv.ll
+++ b/llvm/test/CodeGen/X86/fold-int-pow2-with-fmul-or-fdiv.ll
@@ -840,44 +840,18 @@ define double @fmul_pow_shl_cnt_fail_maybe_non_pow2(i64 %v, i64 %cnt) nounwind {
define <2 x float> @fmul_pow_shl_cnt_vec_fail_expensive_cast(<2 x i64> %cnt) nounwind {
; CHECK-SSE-LABEL: fmul_pow_shl_cnt_vec_fail_expensive_cast:
; CHECK-SSE: # %bb.0:
-; CHECK-SSE-NEXT: pshufd {{.*#+}} xmm2 = xmm0[2,3,2,3]
-; CHECK-SSE-NEXT: movdqa {{.*#+}} xmm3 = [2,2]
-; CHECK-SSE-NEXT: movdqa %xmm3, %xmm1
-; CHECK-SSE-NEXT: psllq %xmm2, %xmm1
-; CHECK-SSE-NEXT: psllq %xmm0, %xmm3
-; CHECK-SSE-NEXT: movq %xmm3, %rax
-; CHECK-SSE-NEXT: testq %rax, %rax
-; CHECK-SSE-NEXT: js .LBB12_1
-; CHECK-SSE-NEXT: # %bb.2:
-; CHECK-SSE-NEXT: xorps %xmm0, %xmm0
-; CHECK-SSE-NEXT: cvtsi2ss %rax, %xmm0
-; CHECK-SSE-NEXT: jmp .LBB12_3
-; CHECK-SSE-NEXT: .LBB12_1:
-; CHECK-SSE-NEXT: movq %rax, %rcx
-; CHECK-SSE-NEXT: shrq %rcx
-; CHECK-SSE-NEXT: andl $1, %eax
-; CHECK-SSE-NEXT: orq %rcx, %rax
+; CHECK-SSE-NEXT: pshufd {{.*#+}} xmm1 = xmm0[2,3,2,3]
+; CHECK-SSE-NEXT: movdqa {{.*#+}} xmm2 = [2,2]
+; CHECK-SSE-NEXT: movdqa %xmm2, %xmm3
+; CHECK-SSE-NEXT: psllq %xmm1, %xmm3
+; CHECK-SSE-NEXT: psllq %xmm0, %xmm2
+; CHECK-SSE-NEXT: movq %xmm2, %rax
; CHECK-SSE-NEXT: xorps %xmm0, %xmm0
; CHECK-SSE-NEXT: cvtsi2ss %rax, %xmm0
-; CHECK-SSE-NEXT: addss %xmm0, %xmm0
-; CHECK-SSE-NEXT: .LBB12_3:
-; CHECK-SSE-NEXT: pshufd {{.*#+}} xmm1 = xmm1[2,3,2,3]
+; CHECK-SSE-NEXT: pshufd {{.*#+}} xmm1 = xmm3[2,3,2,3]
; CHECK-SSE-NEXT: movq %xmm1, %rax
-; CHECK-SSE-NEXT: testq %rax, %rax
-; CHECK-SSE-NEXT: js .LBB12_4
-; CHECK-SSE-NEXT: # %bb.5:
-; CHECK-SSE-NEXT: xorps %xmm1, %xmm1
-; CHECK-SSE-NEXT: cvtsi2ss %rax, %xmm1
-; CHECK-SSE-NEXT: jmp .LBB12_6
-; CHECK-SSE-NEXT: .LBB12_4:
-; CHECK-SSE-NEXT: movq %rax, %rcx
-; CHECK-SSE-NEXT: shrq %rcx
-; CHECK-SSE-NEXT: andl $1, %eax
-; CHECK-SSE-NEXT: orq %rcx, %rax
; CHECK-SSE-NEXT: xorps %xmm1, %xmm1
; CHECK-SSE-NEXT: cvtsi2ss %rax, %xmm1
-; CHECK-SSE-NEXT: addss %xmm1, %xmm1
-; CHECK-SSE-NEXT: .LBB12_6:
; CHECK-SSE-NEXT: unpcklps {{.*#+}} xmm0 = xmm0[0],xmm1[0],xmm0[1],xmm1[1]
; CHECK-SSE-NEXT: mulps {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0
; CHECK-SSE-NEXT: retq
@@ -886,18 +860,11 @@ define <2 x float> @fmul_pow_shl_cnt_vec_fail_expensive_cast(<2 x i64> %cnt) nou
; CHECK-AVX2: # %bb.0:
; CHECK-AVX2-NEXT: vpmovsxbq {{.*#+}} xmm1 = [2,2]
; CHECK-AVX2-NEXT: vpsllvq %xmm0, %xmm1, %xmm0
-; CHECK-AVX2-NEXT: vpsrlq $1, %xmm0, %xmm1
-; CHECK-AVX2-NEXT: vblendvpd %xmm0, %xmm1, %xmm0, %xmm1
-; CHECK-AVX2-NEXT: vpextrq $1, %xmm1, %rax
-; CHECK-AVX2-NEXT: vcvtsi2ss %rax, %xmm2, %xmm2
-; CHECK-AVX2-NEXT: vmovq %xmm1, %rax
-; CHECK-AVX2-NEXT: vcvtsi2ss %rax, %xmm3, %xmm1
-; CHECK-AVX2-NEXT: vinsertps {{.*#+}} xmm1 = xmm1[0],xmm2[0],zero,zero
-; CHECK-AVX2-NEXT: vaddps %xmm1, %xmm1, %xmm2
-; CHECK-AVX2-NEXT: vpxor %xmm3, %xmm3, %xmm3
-; CHECK-AVX2-NEXT: vpcmpgtq %xmm0, %xmm3, %xmm0
-; CHECK-AVX2-NEXT: vpshufd {{.*#+}} xmm0 = xmm0[1,3,2,3]
-; CHECK-AVX2-NEXT: vblendvps %xmm0, %xmm2, %xmm1, %xmm0
+; CHECK-AVX2-NEXT: vpextrq $1, %xmm0, %rax
+; CHECK-AVX2-NEXT: vcvtsi2ss %rax, %xmm2, %xmm1
+; CHECK-AVX2-NEXT: vmovq %xmm0, %rax
+; CHECK-AVX2-NEXT: vcvtsi2ss %rax, %xmm2, %xmm0
+; CHECK-AVX2-NEXT: vinsertps {{.*#+}} xmm0 = xmm0[0],xmm1[0],zero,zero
; CHECK-AVX2-NEXT: vbroadcastss {{.*#+}} xmm1 = [1.5E+1,1.5E+1,1.5E+1,1.5E+1]
; CHECK-AVX2-NEXT: vmulps %xmm1, %xmm0, %xmm0
; CHECK-AVX2-NEXT: retq
@@ -907,9 +874,9 @@ define <2 x float> @fmul_pow_shl_cnt_vec_fail_expensive_cast(<2 x i64> %cnt) nou
; CHECK-NO-FASTFMA-NEXT: vpmovsxbq {{.*#+}} xmm1 = [2,2]
; CHECK-NO-FASTFMA-NEXT: vpsllvq %xmm0, %xmm1, %xmm0
; CHECK-NO-FASTFMA-NEXT: vpextrq $1, %xmm0, %rax
-; CHECK-NO-FASTFMA-NEXT: vcvtusi2ss %rax, %xmm2, %xmm1
+; CHECK-NO-FASTFMA-NEXT: vcvtsi2ss %rax, %xmm2, %xmm1
; CHECK-NO-FASTFMA-NEXT: vmovq %xmm0, %rax
-; CHECK-NO-FASTFMA-NEXT: vcvtusi2ss %rax, %xmm2, %xmm0
+; CHECK-NO-FASTFMA-NEXT: vcvtsi2ss %rax, %xmm2, %xmm0
; CHECK-NO-FASTFMA-NEXT: vinsertps {{.*#+}} xmm0 = xmm0[0],xmm1[0],zero,zero
; CHECK-NO-FASTFMA-NEXT: vbroadcastss {{.*#+}} xmm1 = [1.5E+1,1.5E+1,1.5E+1,1.5E+1]
; CHECK-NO-FASTFMA-NEXT: vmulps %xmm1, %xmm0, %xmm0
@@ -919,7 +886,7 @@ define <2 x float> @fmul_pow_shl_cnt_vec_fail_expensive_cast(<2 x i64> %cnt) nou
; CHECK-FMA: # %bb.0:
; CHECK-FMA-NEXT: vpbroadcastq {{.*#+}} xmm1 = [2,2]
; CHECK-FMA-NEXT: vpsllvq %xmm0, %xmm1, %xmm0
-; CHECK-FMA-NEXT: vcvtuqq2ps %xmm0, %xmm0
+; CHECK-FMA-NEXT: vcvtqq2ps %xmm0, %xmm0
; CHECK-FMA-NEXT: vmulps {{\.?LCPI[0-9]+_[0-9]+}}(%rip){1to4}, %xmm0, %xmm0
; CHECK-FMA-NEXT: retq
%shl = shl nsw nuw <2 x i64> , %cnt
@@ -986,7 +953,7 @@ define <4 x float> @fmul_pow_shl_cnt_vec_preserve_fma(<4 x i32> %cnt, <4 x float
; CHECK-FMA: # %bb.0:
; CHECK-FMA-NEXT: vpbroadcastd {{.*#+}} xmm2 = [2,2,2,2]
; CHECK-FMA-NEXT: vpsllvd %xmm0, %xmm2, %xmm0
-; CHECK-FMA-NEXT: vcvtudq2ps %xmm0, %xmm0
+; CHECK-FMA-NEXT: vcvtdq2ps %xmm0, %xmm0
; CHECK-FMA-NEXT: vfmadd132ps {{.*#+}} xmm0 = (xmm0 * mem) + xmm1
; CHECK-FMA-NEXT: retq
%shl = shl nsw nuw <4 x i32> , %cnt
diff --git a/llvm/test/CodeGen/X86/known-never-zero.ll b/llvm/test/CodeGen/X86/known-never-zero.ll
index 2f780e3c6fe1..f0504e7dbdb6 100644
--- a/llvm/test/CodeGen/X86/known-never-zero.ll
+++ b/llvm/test/CodeGen/X86/known-never-zero.ll
@@ -1612,7 +1612,7 @@ define i32 @sext_known_nonzero(i16 %xx) {
; X86-NEXT: movzbl {{[0-9]+}}(%esp), %ecx
; X86-NEXT: movl $256, %eax # imm = 0x100
; X86-NEXT: shll %cl, %eax
-; X86-NEXT: cwtl
+; X86-NEXT: movzwl %ax, %eax
; X86-NEXT: rep bsfl %eax, %eax
; X86-NEXT: retl
;
@@ -1622,7 +1622,7 @@ define i32 @sext_known_nonzero(i16 %xx) {
; X64-NEXT: movl $256, %eax # imm = 0x100
; X64-NEXT: # kill: def $cl killed $cl killed $ecx
; X64-NEXT: shll %cl, %eax
-; X64-NEXT: cwtl
+; X64-NEXT: movzwl %ax, %eax
; X64-NEXT: rep bsfl %eax, %eax
; X64-NEXT: retq
%x = shl nuw nsw i16 256, %xx
diff --git a/llvm/test/CodeGen/X86/pr89877.ll b/llvm/test/CodeGen/X86/pr89877.ll
new file mode 100644
index 000000000000..9820ec42f5b8
--- /dev/null
+++ b/llvm/test/CodeGen/X86/pr89877.ll
@@ -0,0 +1,115 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 3
+; RUN: llc < %s -mtriple=i686-unknown-unknown -mattr=+sse2 | FileCheck %s --check-prefixes=X86
+; RUN: llc < %s -mtriple=x86_64-unknown-unknown -mattr=+avx | FileCheck %s --check-prefixes=X64
+
+define i32 @sext_known_nonzero(i16 %xx) {
+; X86-LABEL: sext_known_nonzero:
+; X86: # %bb.0:
+; X86-NEXT: movzbl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT: movl $256, %eax # imm = 0x100
+; X86-NEXT: shll %cl, %eax
+; X86-NEXT: cwtl
+; X86-NEXT: testl %eax, %eax
+; X86-NEXT: je .LBB0_1
+; X86-NEXT: # %bb.2: # %cond.false
+; X86-NEXT: rep bsfl %eax, %eax
+; X86-NEXT: retl
+; X86-NEXT: .LBB0_1:
+; X86-NEXT: movl $32, %eax
+; X86-NEXT: retl
+;
+; X64-LABEL: sext_known_nonzero:
+; X64: # %bb.0:
+; X64-NEXT: movl %edi, %ecx
+; X64-NEXT: movl $256, %eax # imm = 0x100
+; X64-NEXT: # kill: def $cl killed $cl killed $ecx
+; X64-NEXT: shll %cl, %eax
+; X64-NEXT: cwtl
+; X64-NEXT: testl %eax, %eax
+; X64-NEXT: je .LBB0_1
+; X64-NEXT: # %bb.2: # %cond.false
+; X64-NEXT: rep bsfl %eax, %eax
+; X64-NEXT: retq
+; X64-NEXT: .LBB0_1:
+; X64-NEXT: movl $32, %eax
+; X64-NEXT: retq
+ %x = shl i16 256, %xx
+ %z = sext i16 %x to i32
+ %r = call i32 @llvm.cttz.i32(i32 %z, i1 false)
+ ret i32 %r
+}
+
+define i32 @sext_known_nonzero_nuw(i16 %xx) {
+; X86-LABEL: sext_known_nonzero_nuw:
+; X86: # %bb.0:
+; X86-NEXT: movzbl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT: movl $256, %eax # imm = 0x100
+; X86-NEXT: shll %cl, %eax
+; X86-NEXT: cwtl
+; X86-NEXT: rep bsfl %eax, %eax
+; X86-NEXT: retl
+;
+; X64-LABEL: sext_known_nonzero_nuw:
+; X64: # %bb.0:
+; X64-NEXT: movl %edi, %ecx
+; X64-NEXT: movl $256, %eax # imm = 0x100
+; X64-NEXT: # kill: def $cl killed $cl killed $ecx
+; X64-NEXT: shll %cl, %eax
+; X64-NEXT: cwtl
+; X64-NEXT: rep bsfl %eax, %eax
+; X64-NEXT: retq
+ %x = shl nuw i16 256, %xx
+ %z = sext i16 %x to i32
+ %r = call i32 @llvm.cttz.i32(i32 %z, i1 false)
+ ret i32 %r
+}
+
+define i32 @sext_known_nonzero_nsw(i16 %xx) {
+; X86-LABEL: sext_known_nonzero_nsw:
+; X86: # %bb.0:
+; X86-NEXT: movzbl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT: movl $256, %eax # imm = 0x100
+; X86-NEXT: shll %cl, %eax
+; X86-NEXT: movzwl %ax, %eax
+; X86-NEXT: rep bsfl %eax, %eax
+; X86-NEXT: retl
+;
+; X64-LABEL: sext_known_nonzero_nsw:
+; X64: # %bb.0:
+; X64-NEXT: movl %edi, %ecx
+; X64-NEXT: movl $256, %eax # imm = 0x100
+; X64-NEXT: # kill: def $cl killed $cl killed $ecx
+; X64-NEXT: shll %cl, %eax
+; X64-NEXT: movzwl %ax, %eax
+; X64-NEXT: rep bsfl %eax, %eax
+; X64-NEXT: retq
+ %x = shl nsw i16 256, %xx
+ %z = sext i16 %x to i32
+ %r = call i32 @llvm.cttz.i32(i32 %z, i1 false)
+ ret i32 %r
+}
+
+define i32 @sext_known_nonzero_nuw_nsw(i16 %xx) {
+; X86-LABEL: sext_known_nonzero_nuw_nsw:
+; X86: # %bb.0:
+; X86-NEXT: movzbl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT: movl $256, %eax # imm = 0x100
+; X86-NEXT: shll %cl, %eax
+; X86-NEXT: movzwl %ax, %eax
+; X86-NEXT: rep bsfl %eax, %eax
+; X86-NEXT: retl
+;
+; X64-LABEL: sext_known_nonzero_nuw_nsw:
+; X64: # %bb.0:
+; X64-NEXT: movl %edi, %ecx
+; X64-NEXT: movl $256, %eax # imm = 0x100
+; X64-NEXT: # kill: def $cl killed $cl killed $ecx
+; X64-NEXT: shll %cl, %eax
+; X64-NEXT: movzwl %ax, %eax
+; X64-NEXT: rep bsfl %eax, %eax
+; X64-NEXT: retq
+ %x = shl nuw nsw i16 256, %xx
+ %z = sext i16 %x to i32
+ %r = call i32 @llvm.cttz.i32(i32 %z, i1 false)
+ ret i32 %r
+}
--
GitLab
From f17b1fb6673e9ed6a7d9180230586113bb99748c Mon Sep 17 00:00:00 2001
From: Krishna Narayanan <84722531+Krishna-13-cyber@users.noreply.github.com>
Date: Thu, 2 May 2024 15:12:34 +0530
Subject: [PATCH 0026/1014] [Clang][CodeGen] Optimised LLVM IR for atomic
increments/decrements on floats (#89362)
Fixes #53079
---
clang/lib/CodeGen/CGExprScalar.cpp | 13 +
clang/test/CodeGen/X86/x86-atomic-float.c | 69 ++
.../test/CodeGen/X86/x86-atomic-long_double.c | 609 +++++++++---------
3 files changed, 382 insertions(+), 309 deletions(-)
create mode 100644 clang/test/CodeGen/X86/x86-atomic-float.c
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index af48e8d2b839..d84531959b50 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -2801,6 +2801,19 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV,
llvm::AtomicOrdering::SequentiallyConsistent);
return isPre ? Builder.CreateBinOp(op, old, amt) : old;
}
+ // Special case for atomic increment/decrement on floats
+ if (type->isFloatingType()) {
+ llvm::AtomicRMWInst::BinOp aop =
+ isInc ? llvm::AtomicRMWInst::FAdd : llvm::AtomicRMWInst::FSub;
+ llvm::Instruction::BinaryOps op =
+ isInc ? llvm::Instruction::FAdd : llvm::Instruction::FSub;
+ llvm::Value *amt = llvm::ConstantFP::get(
+ VMContext, llvm::APFloat(static_cast(1.0)));
+ llvm::Value *old =
+ Builder.CreateAtomicRMW(aop, LV.getAddress(CGF), amt,
+ llvm::AtomicOrdering::SequentiallyConsistent);
+ return isPre ? Builder.CreateBinOp(op, old, amt) : old;
+ }
value = EmitLoadOfLValue(LV, E->getExprLoc());
input = value;
// For every other atomic operation, we need to emit a load-op-cmpxchg loop
diff --git a/clang/test/CodeGen/X86/x86-atomic-float.c b/clang/test/CodeGen/X86/x86-atomic-float.c
new file mode 100644
index 000000000000..a7a5083b92ef
--- /dev/null
+++ b/clang/test/CodeGen/X86/x86-atomic-float.c
@@ -0,0 +1,69 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 4
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -target-cpu core2 %s -S -emit-llvm -o - | FileCheck -check-prefixes=CHECK,CHECK64 %s
+// RUN: %clang_cc1 -triple i686-linux-gnu -target-cpu core2 %s -S -emit-llvm -o - | FileCheck -check-prefixes=CHECK,CHECK32 %s
+
+
+// CHECK-LABEL: define dso_local i32 @test_int_inc(
+// CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[TMP0:%.*]] = atomicrmw add ptr @test_int_inc.n, i32 1 seq_cst, align 4
+// CHECK-NEXT: ret i32 [[TMP0]]
+//
+int test_int_inc()
+{
+ static _Atomic int n;
+ return n++;
+}
+
+// CHECK-LABEL: define dso_local float @test_float_post_inc(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[TMP0:%.*]] = atomicrmw fadd ptr @test_float_post_inc.n, float 1.000000e+00 seq_cst, align 4
+// CHECK-NEXT: ret float [[TMP0]]
+//
+float test_float_post_inc()
+{
+ static _Atomic float n;
+ return n++;
+}
+
+// CHECK-LABEL: define dso_local float @test_float_post_dc(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[TMP0:%.*]] = atomicrmw fsub ptr @test_float_post_dc.n, float 1.000000e+00 seq_cst, align 4
+// CHECK-NEXT: ret float [[TMP0]]
+//
+float test_float_post_dc()
+{
+ static _Atomic float n;
+ return n--;
+}
+
+// CHECK-LABEL: define dso_local float @test_float_pre_dc(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[TMP0:%.*]] = atomicrmw fsub ptr @test_float_pre_dc.n, float 1.000000e+00 seq_cst, align 4
+// CHECK-NEXT: [[TMP1:%.*]] = fsub float [[TMP0]], 1.000000e+00
+// CHECK-NEXT: ret float [[TMP1]]
+//
+float test_float_pre_dc()
+{
+ static _Atomic float n;
+ return --n;
+}
+
+// CHECK-LABEL: define dso_local float @test_float_pre_inc(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[TMP0:%.*]] = atomicrmw fadd ptr @test_float_pre_inc.n, float 1.000000e+00 seq_cst, align 4
+// CHECK-NEXT: [[TMP1:%.*]] = fadd float [[TMP0]], 1.000000e+00
+// CHECK-NEXT: ret float [[TMP1]]
+//
+float test_float_pre_inc()
+{
+ static _Atomic float n;
+ return ++n;
+}
+//// NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+// CHECK32: {{.*}}
+// CHECK64: {{.*}}
diff --git a/clang/test/CodeGen/X86/x86-atomic-long_double.c b/clang/test/CodeGen/X86/x86-atomic-long_double.c
index ca3fb6730fb6..ab061395f0b7 100644
--- a/clang/test/CodeGen/X86/x86-atomic-long_double.c
+++ b/clang/test/CodeGen/X86/x86-atomic-long_double.c
@@ -1,351 +1,342 @@
// RUN: %clang_cc1 -triple x86_64-linux-gnu -target-cpu core2 %s -S -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 -triple i686-linux-gnu -target-cpu core2 %s -S -emit-llvm -o - | FileCheck -check-prefix=CHECK32 %s
+// CHECK-LABEL: define dso_local x86_fp80 @testinc(
+// CHECK-SAME: ptr noundef [[ADDR:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[RETVAL:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT: [[ADDR_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT: store ptr [[ADDR]], ptr [[ADDR_ADDR]], align 8
+// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ADDR_ADDR]], align 8
+// CHECK-NEXT: [[TMP1:%.*]] = atomicrmw fadd ptr [[TMP0]], float 1.000000e+00 seq_cst, align 16
+// CHECK-NEXT: [[TMP2:%.*]] = fadd float [[TMP1]], 1.000000e+00
+// CHECK-NEXT: store float [[TMP2]], ptr [[RETVAL]], align 16
+// CHECK-NEXT: [[TMP3:%.*]] = load x86_fp80, ptr [[RETVAL]], align 16
+// CHECK-NEXT: ret x86_fp80 [[TMP3]]
+//
+// CHECK32-LABEL: define dso_local x86_fp80 @testinc(
+// CHECK32-SAME: ptr noundef [[ADDR:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK32-NEXT: entry:
+// CHECK32-NEXT: [[RETVAL:%.*]] = alloca x86_fp80, align 4
+// CHECK32-NEXT: [[ADDR_ADDR:%.*]] = alloca ptr, align 4
+// CHECK32-NEXT: store ptr [[ADDR]], ptr [[ADDR_ADDR]], align 4
+// CHECK32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ADDR_ADDR]], align 4
+// CHECK32-NEXT: [[TMP1:%.*]] = atomicrmw fadd ptr [[TMP0]], float 1.000000e+00 seq_cst, align 4
+// CHECK32-NEXT: [[TMP2:%.*]] = fadd float [[TMP1]], 1.000000e+00
+// CHECK32-NEXT: store float [[TMP2]], ptr [[RETVAL]], align 4
+// CHECK32-NEXT: [[TMP3:%.*]] = load x86_fp80, ptr [[RETVAL]], align 4
+// CHECK32-NEXT: ret x86_fp80 [[TMP3]]
+//
long double testinc(_Atomic long double *addr) {
- // CHECK-LABEL: @testinc
- // CHECK: store ptr %{{.+}}, ptr [[ADDR_ADDR:%.+]], align 8
- // CHECK: [[ADDR:%.+]] = load ptr, ptr [[ADDR_ADDR]], align 8
- // CHECK: [[INT_VALUE:%.+]] = load atomic i128, ptr [[ADDR]] seq_cst, align 16
- // CHECK: store i128 [[INT_VALUE]], ptr [[LD_ADDR:%.+]], align 16
- // CHECK: [[LD_VALUE:%.+]] = load x86_fp80, ptr [[LD_ADDR]], align 16
- // CHECK: br label %[[ATOMIC_OP:.+]]
- // CHECK: [[ATOMIC_OP]]
- // CHECK: [[OLD_VALUE:%.+]] = phi x86_fp80 [ [[LD_VALUE]], %{{.+}} ], [ [[LD_VALUE:%.+]], %[[ATOMIC_OP]] ]
- // CHECK: [[INC_VALUE:%.+]] = fadd x86_fp80 [[OLD_VALUE]],
- // CHECK: call void @llvm.memset.p0.i64(ptr align 16 [[OLD_VALUE_ADDR:%.+]], i8 0, i64 16, i1 false)
- // CHECK: store x86_fp80 [[OLD_VALUE]], ptr [[OLD_VALUE_ADDR]], align 16
- // CHECK: [[OLD_INT:%.+]] = load i128, ptr [[OLD_VALUE_ADDR]], align 16
- // CHECK: call void @llvm.memset.p0.i64(ptr align 16 [[NEW_VALUE_ADDR:%.+]], i8 0, i64 16, i1 false)
- // CHECK: store x86_fp80 [[INC_VALUE]], ptr [[NEW_VALUE_ADDR]], align 16
- // CHECK: [[NEW_INT:%.+]] = load i128, ptr [[NEW_VALUE_ADDR]], align 16
- // CHECK: [[RES:%.+]] = cmpxchg ptr [[ADDR]], i128 [[OLD_INT]], i128 [[NEW_INT]] seq_cst seq_cst, align 16
- // CHECK: [[OLD_VALUE:%.+]] = extractvalue { i128, i1 } [[RES]], 0
- // CHECK: [[FAIL_SUCCESS:%.+]] = extractvalue { i128, i1 } [[RES]], 1
- // CHECK: store i128 [[OLD_VALUE]], ptr [[OLD_VALUE_RES_PTR:%.+]], align 16
- // CHECK: [[LD_VALUE]] = load x86_fp80, ptr [[OLD_VALUE_RES_PTR]], align 16
- // CHECK: br i1 [[FAIL_SUCCESS]], label %[[ATOMIC_CONT:.+]], label %[[ATOMIC_OP]]
- // CHECK: [[ATOMIC_CONT]]
- // CHECK: ret x86_fp80 [[INC_VALUE]]
- // CHECK32-LABEL: @testinc
- // CHECK32: store ptr %{{.+}}, ptr [[ADDR_ADDR:%.+]], align 4
- // CHECK32: [[ADDR:%.+]] = load ptr, ptr [[ADDR_ADDR]], align 4
- // CHECK32: call void @__atomic_load(i32 noundef 12, ptr noundef [[ADDR]], ptr noundef [[TEMP_LD_ADDR:%.+]], i32 noundef 5)
- // CHECK32: [[LD_VALUE:%.+]] = load x86_fp80, ptr [[TEMP_LD_ADDR]], align 4
- // CHECK32: br label %[[ATOMIC_OP:.+]]
- // CHECK32: [[ATOMIC_OP]]
- // CHECK32: [[OLD_VALUE:%.+]] = phi x86_fp80 [ [[LD_VALUE]], %{{.+}} ], [ [[LD_VALUE:%.+]], %[[ATOMIC_OP]] ]
- // CHECK32: [[INC_VALUE:%.+]] = fadd x86_fp80 [[OLD_VALUE]],
- // CHECK32: call void @llvm.memset.p0.i64(ptr align 4 [[OLD_VALUE_ADDR:%.+]], i8 0, i64 12, i1 false)
- // CHECK32: store x86_fp80 [[OLD_VALUE]], ptr [[OLD_VALUE_ADDR]], align 4
- // CHECK32: call void @llvm.memset.p0.i64(ptr align 4 [[DESIRED_VALUE_ADDR:%.+]], i8 0, i64 12, i1 false)
- // CHECK32: store x86_fp80 [[INC_VALUE]], ptr [[DESIRED_VALUE_ADDR]], align 4
- // CHECK32: [[FAIL_SUCCESS:%.+]] = call zeroext i1 @__atomic_compare_exchange(i32 noundef 12, ptr noundef [[ADDR]], ptr noundef [[OLD_VALUE_ADDR]], ptr noundef [[DESIRED_VALUE_ADDR]], i32 noundef 5, i32 noundef 5)
- // CHECK32: [[LD_VALUE:%.+]] = load x86_fp80, ptr [[OLD_VALUE_ADDR]], align 4
- // CHECK32: br i1 [[FAIL_SUCCESS]], label %[[ATOMIC_CONT:.+]], label %[[ATOMIC_OP]]
- // CHECK32: [[ATOMIC_CONT]]
- // CHECK32: ret x86_fp80 [[INC_VALUE]]
return ++*addr;
}
+// CHECK-LABEL: define dso_local x86_fp80 @testdec(
+// CHECK-SAME: ptr noundef [[ADDR:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[RETVAL:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT: [[ADDR_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT: store ptr [[ADDR]], ptr [[ADDR_ADDR]], align 8
+// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ADDR_ADDR]], align 8
+// CHECK-NEXT: [[TMP1:%.*]] = atomicrmw fsub ptr [[TMP0]], float 1.000000e+00 seq_cst, align 16
+// CHECK-NEXT: store float [[TMP1]], ptr [[RETVAL]], align 16
+// CHECK-NEXT: [[TMP2:%.*]] = load x86_fp80, ptr [[RETVAL]], align 16
+// CHECK-NEXT: ret x86_fp80 [[TMP2]]
+//
+// CHECK32-LABEL: define dso_local x86_fp80 @testdec(
+// CHECK32-SAME: ptr noundef [[ADDR:%.*]]) #[[ATTR0]] {
+// CHECK32-NEXT: entry:
+// CHECK32-NEXT: [[RETVAL:%.*]] = alloca x86_fp80, align 4
+// CHECK32-NEXT: [[ADDR_ADDR:%.*]] = alloca ptr, align 4
+// CHECK32-NEXT: store ptr [[ADDR]], ptr [[ADDR_ADDR]], align 4
+// CHECK32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ADDR_ADDR]], align 4
+// CHECK32-NEXT: [[TMP1:%.*]] = atomicrmw fsub ptr [[TMP0]], float 1.000000e+00 seq_cst, align 4
+// CHECK32-NEXT: store float [[TMP1]], ptr [[RETVAL]], align 4
+// CHECK32-NEXT: [[TMP2:%.*]] = load x86_fp80, ptr [[RETVAL]], align 4
+// CHECK32-NEXT: ret x86_fp80 [[TMP2]]
+//
long double testdec(_Atomic long double *addr) {
- // CHECK-LABEL: @testdec
- // CHECK: store ptr %{{.+}}, ptr [[ADDR_ADDR:%.+]], align 8
- // CHECK: [[ADDR:%.+]] = load ptr, ptr [[ADDR_ADDR]], align 8
- // CHECK: [[INT_VALUE:%.+]] = load atomic i128, ptr [[ADDR]] seq_cst, align 16
- // CHECK: store i128 [[INT_VALUE]], ptr [[LD_ADDR:%.+]], align 16
- // CHECK: [[ORIG_LD_VALUE:%.+]] = load x86_fp80, ptr [[LD_ADDR]], align 16
- // CHECK: br label %[[ATOMIC_OP:.+]]
- // CHECK: [[ATOMIC_OP]]
- // CHECK: [[OLD_VALUE:%.+]] = phi x86_fp80 [ [[ORIG_LD_VALUE]], %{{.+}} ], [ [[LD_VALUE:%.+]], %[[ATOMIC_OP]] ]
- // CHECK: [[DEC_VALUE:%.+]] = fadd x86_fp80 [[OLD_VALUE]],
- // CHECK: call void @llvm.memset.p0.i64(ptr align 16 [[OLD_VALUE_ADDR:%.+]], i8 0, i64 16, i1 false)
- // CHECK: store x86_fp80 [[OLD_VALUE]], ptr [[OLD_VALUE_ADDR]], align 16
- // CHECK: [[OLD_INT:%.+]] = load i128, ptr [[OLD_VALUE_ADDR]], align 16
- // CHECK: call void @llvm.memset.p0.i64(ptr align 16 [[NEW_VALUE_ADDR:%.+]], i8 0, i64 16, i1 false)
- // CHECK: store x86_fp80 [[DEC_VALUE]], ptr [[NEW_VALUE_ADDR]], align 16
- // CHECK: [[NEW_INT:%.+]] = load i128, ptr [[NEW_VALUE_ADDR]], align 16
- // CHECK: [[RES:%.+]] = cmpxchg ptr [[ADDR]], i128 [[OLD_INT]], i128 [[NEW_INT]] seq_cst seq_cst, align 16
- // CHECK: [[OLD_VALUE:%.+]] = extractvalue { i128, i1 } [[RES]], 0
- // CHECK: [[FAIL_SUCCESS:%.+]] = extractvalue { i128, i1 } [[RES]], 1
- // CHECK: store i128 [[OLD_VALUE]], ptr [[OLD_VALUE_RES_PTR:%.+]], align 16
- // CHECK: [[LD_VALUE]] = load x86_fp80, ptr [[OLD_VALUE_RES_PTR]], align 16
- // CHECK: br i1 [[FAIL_SUCCESS]], label %[[ATOMIC_CONT:.+]], label %[[ATOMIC_OP]]
- // CHECK: [[ATOMIC_CONT]]
- // CHECK: ret x86_fp80 [[ORIG_LD_VALUE]]
- // CHECK32-LABEL: @testdec
- // CHECK32: store ptr %{{.+}}, ptr [[ADDR_ADDR:%.+]], align 4
- // CHECK32: [[ADDR:%.+]] = load ptr, ptr [[ADDR_ADDR]], align 4
- // CHECK32: call void @__atomic_load(i32 noundef 12, ptr noundef [[ADDR]], ptr noundef [[TEMP_LD_ADDR:%.+]], i32 noundef 5)
- // CHECK32: [[ORIG_LD_VALUE:%.+]] = load x86_fp80, ptr [[TEMP_LD_ADDR]], align 4
- // CHECK32: br label %[[ATOMIC_OP:.+]]
- // CHECK32: [[ATOMIC_OP]]
- // CHECK32: [[OLD_VALUE:%.+]] = phi x86_fp80 [ [[ORIG_LD_VALUE]], %{{.+}} ], [ [[LD_VALUE:%.+]], %[[ATOMIC_OP]] ]
- // CHECK32: [[DEC_VALUE:%.+]] = fadd x86_fp80 [[OLD_VALUE]],
- // CHECK32: call void @llvm.memset.p0.i64(ptr align 4 [[OLD_VALUE_ADDR:%.+]], i8 0, i64 12, i1 false)
- // CHECK32: store x86_fp80 [[OLD_VALUE]], ptr [[OLD_VALUE_ADDR]], align 4
- // CHECK32: call void @llvm.memset.p0.i64(ptr align 4 [[DESIRED_VALUE_ADDR:%.+]], i8 0, i64 12, i1 false)
- // CHECK32: store x86_fp80 [[DEC_VALUE]], ptr [[DESIRED_VALUE_ADDR]], align 4
- // CHECK32: [[FAIL_SUCCESS:%.+]] = call zeroext i1 @__atomic_compare_exchange(i32 noundef 12, ptr noundef [[ADDR]], ptr noundef [[OLD_VALUE_ADDR]], ptr noundef [[DESIRED_VALUE_ADDR]], i32 noundef 5, i32 noundef 5)
- // CHECK32: [[LD_VALUE]] = load x86_fp80, ptr [[OLD_VALUE_ADDR]], align 4
- // CHECK32: br i1 [[FAIL_SUCCESS]], label %[[ATOMIC_CONT:.+]], label %[[ATOMIC_OP]]
- // CHECK32: [[ATOMIC_CONT]]
- // CHECK32: ret x86_fp80 [[ORIG_LD_VALUE]]
return (*addr)--;
}
+// CHECK-LABEL: define dso_local x86_fp80 @testcompassign(
+// CHECK-SAME: ptr noundef [[ADDR:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[ADDR_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT: [[ATOMIC_TEMP:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT: [[ATOMIC_TEMP1:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT: [[ATOMIC_TEMP2:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT: [[ATOMIC_TEMP3:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT: [[ATOMIC_TEMP5:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT: store ptr [[ADDR]], ptr [[ADDR_ADDR]], align 8
+// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ADDR_ADDR]], align 8
+// CHECK-NEXT: [[ATOMIC_LOAD:%.*]] = load atomic i128, ptr [[TMP0]] seq_cst, align 16
+// CHECK-NEXT: store i128 [[ATOMIC_LOAD]], ptr [[ATOMIC_TEMP]], align 16
+// CHECK-NEXT: [[TMP1:%.*]] = load x86_fp80, ptr [[ATOMIC_TEMP]], align 16
+// CHECK-NEXT: br label [[ATOMIC_OP:%.*]]
+// CHECK: atomic_op:
+// CHECK-NEXT: [[TMP2:%.*]] = phi x86_fp80 [ [[TMP1]], [[ENTRY:%.*]] ], [ [[TMP8:%.*]], [[ATOMIC_OP]] ]
+// CHECK-NEXT: [[SUB:%.*]] = fsub x86_fp80 [[TMP2]], 0xK4003C800000000000000
+// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[ATOMIC_TEMP1]], i8 0, i64 16, i1 false)
+// CHECK-NEXT: store x86_fp80 [[TMP2]], ptr [[ATOMIC_TEMP1]], align 16
+// CHECK-NEXT: [[TMP3:%.*]] = load i128, ptr [[ATOMIC_TEMP1]], align 16
+// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[ATOMIC_TEMP2]], i8 0, i64 16, i1 false)
+// CHECK-NEXT: store x86_fp80 [[SUB]], ptr [[ATOMIC_TEMP2]], align 16
+// CHECK-NEXT: [[TMP4:%.*]] = load i128, ptr [[ATOMIC_TEMP2]], align 16
+// CHECK-NEXT: [[TMP5:%.*]] = cmpxchg ptr [[TMP0]], i128 [[TMP3]], i128 [[TMP4]] seq_cst seq_cst, align 16
+// CHECK-NEXT: [[TMP6:%.*]] = extractvalue { i128, i1 } [[TMP5]], 0
+// CHECK-NEXT: [[TMP7:%.*]] = extractvalue { i128, i1 } [[TMP5]], 1
+// CHECK-NEXT: store i128 [[TMP6]], ptr [[ATOMIC_TEMP3]], align 16
+// CHECK-NEXT: [[TMP8]] = load x86_fp80, ptr [[ATOMIC_TEMP3]], align 16
+// CHECK-NEXT: br i1 [[TMP7]], label [[ATOMIC_CONT:%.*]], label [[ATOMIC_OP]]
+// CHECK: atomic_cont:
+// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[ADDR_ADDR]], align 8
+// CHECK-NEXT: [[ATOMIC_LOAD4:%.*]] = load atomic i128, ptr [[TMP9]] seq_cst, align 16
+// CHECK-NEXT: store i128 [[ATOMIC_LOAD4]], ptr [[ATOMIC_TEMP5]], align 16
+// CHECK-NEXT: [[TMP10:%.*]] = load x86_fp80, ptr [[ATOMIC_TEMP5]], align 16
+// CHECK-NEXT: ret x86_fp80 [[TMP10]]
+//
+// CHECK32-LABEL: define dso_local x86_fp80 @testcompassign(
+// CHECK32-SAME: ptr noundef [[ADDR:%.*]]) #[[ATTR0]] {
+// CHECK32-NEXT: entry:
+// CHECK32-NEXT: [[ADDR_ADDR:%.*]] = alloca ptr, align 4
+// CHECK32-NEXT: [[ATOMIC_TEMP:%.*]] = alloca x86_fp80, align 4
+// CHECK32-NEXT: [[ATOMIC_TEMP1:%.*]] = alloca x86_fp80, align 4
+// CHECK32-NEXT: [[ATOMIC_TEMP2:%.*]] = alloca x86_fp80, align 4
+// CHECK32-NEXT: [[ATOMIC_TEMP3:%.*]] = alloca x86_fp80, align 4
+// CHECK32-NEXT: store ptr [[ADDR]], ptr [[ADDR_ADDR]], align 4
+// CHECK32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ADDR_ADDR]], align 4
+// CHECK32-NEXT: call void @__atomic_load(i32 noundef 12, ptr noundef [[TMP0]], ptr noundef [[ATOMIC_TEMP]], i32 noundef 5)
+// CHECK32-NEXT: [[TMP1:%.*]] = load x86_fp80, ptr [[ATOMIC_TEMP]], align 4
+// CHECK32-NEXT: br label [[ATOMIC_OP:%.*]]
+// CHECK32: atomic_op:
+// CHECK32-NEXT: [[TMP2:%.*]] = phi x86_fp80 [ [[TMP1]], [[ENTRY:%.*]] ], [ [[TMP3:%.*]], [[ATOMIC_OP]] ]
+// CHECK32-NEXT: [[SUB:%.*]] = fsub x86_fp80 [[TMP2]], 0xK4003C800000000000000
+// CHECK32-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[ATOMIC_TEMP1]], i8 0, i64 12, i1 false)
+// CHECK32-NEXT: store x86_fp80 [[TMP2]], ptr [[ATOMIC_TEMP1]], align 4
+// CHECK32-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[ATOMIC_TEMP2]], i8 0, i64 12, i1 false)
+// CHECK32-NEXT: store x86_fp80 [[SUB]], ptr [[ATOMIC_TEMP2]], align 4
+// CHECK32-NEXT: [[CALL:%.*]] = call zeroext i1 @__atomic_compare_exchange(i32 noundef 12, ptr noundef [[TMP0]], ptr noundef [[ATOMIC_TEMP1]], ptr noundef [[ATOMIC_TEMP2]], i32 noundef 5, i32 noundef 5)
+// CHECK32-NEXT: [[TMP3]] = load x86_fp80, ptr [[ATOMIC_TEMP1]], align 4
+// CHECK32-NEXT: br i1 [[CALL]], label [[ATOMIC_CONT:%.*]], label [[ATOMIC_OP]]
+// CHECK32: atomic_cont:
+// CHECK32-NEXT: [[TMP4:%.*]] = load ptr, ptr [[ADDR_ADDR]], align 4
+// CHECK32-NEXT: call void @__atomic_load(i32 noundef 12, ptr noundef [[TMP4]], ptr noundef [[ATOMIC_TEMP3]], i32 noundef 5)
+// CHECK32-NEXT: [[TMP5:%.*]] = load x86_fp80, ptr [[ATOMIC_TEMP3]], align 4
+// CHECK32-NEXT: ret x86_fp80 [[TMP5]]
+//
long double testcompassign(_Atomic long double *addr) {
*addr -= 25;
- // CHECK-LABEL: @testcompassign
- // CHECK: store ptr %{{.+}}, ptr [[ADDR_ADDR:%.+]], align 8
- // CHECK: [[ADDR:%.+]] = load ptr, ptr [[ADDR_ADDR]], align 8
- // CHECK: [[INT_VALUE:%.+]] = load atomic i128, ptr [[ADDR]] seq_cst, align 16
- // CHECK: store i128 [[INT_VALUE]], ptr [[LD_ADDR:%.+]], align 16
- // CHECK: [[LD_VALUE:%.+]] = load x86_fp80, ptr [[LD_ADDR]], align 16
- // CHECK: br label %[[ATOMIC_OP:.+]]
- // CHECK: [[ATOMIC_OP]]
- // CHECK: [[OLD_VALUE:%.+]] = phi x86_fp80 [ [[LD_VALUE]], %{{.+}} ], [ [[LD_VALUE:%.+]], %[[ATOMIC_OP]] ]
- // CHECK: [[SUB_VALUE:%.+]] = fsub x86_fp80 [[OLD_VALUE]],
- // CHECK: call void @llvm.memset.p0.i64(ptr align 16 [[OLD_VALUE_ADDR:%.+]], i8 0, i64 16, i1 false)
- // CHECK: store x86_fp80 [[OLD_VALUE]], ptr [[OLD_VALUE_ADDR]], align 16
- // CHECK: [[OLD_INT:%.+]] = load i128, ptr [[OLD_VALUE_ADDR]], align 16
- // CHECK: call void @llvm.memset.p0.i64(ptr align 16 [[NEW_VALUE_ADDR:%.+]], i8 0, i64 16, i1 false)
- // CHECK: store x86_fp80 [[SUB_VALUE]], ptr [[NEW_VALUE_ADDR]], align 16
- // CHECK: [[NEW_INT:%.+]] = load i128, ptr [[NEW_VALUE_ADDR]], align 16
- // CHECK: [[RES:%.+]] = cmpxchg ptr [[ADDR]], i128 [[OLD_INT]], i128 [[NEW_INT]] seq_cst seq_cst, align 16
- // CHECK: [[OLD_VALUE:%.+]] = extractvalue { i128, i1 } [[RES]], 0
- // CHECK: [[FAIL_SUCCESS:%.+]] = extractvalue { i128, i1 } [[RES]], 1
- // CHECK: store i128 [[OLD_VALUE]], ptr [[OLD_VALUE_RES_PTR:%.+]], align 16
- // CHECK: [[LD_VALUE]] = load x86_fp80, ptr [[OLD_VALUE_RES_PTR]], align 16
- // CHECK: br i1 [[FAIL_SUCCESS]], label %[[ATOMIC_CONT:.+]], label %[[ATOMIC_OP]]
- // CHECK: [[ATOMIC_CONT]]
- // CHECK: [[ADDR:%.+]] = load ptr, ptr %{{.+}}, align 8
- // CHECK: [[INT_VAL:%.+]] = load atomic i128, ptr [[ADDR]] seq_cst, align 16
- // CHECK: store i128 [[INT_VAL]], ptr [[INT_LD_TEMP:%.+]], align 16
- // CHECK: [[RET_VAL:%.+]] = load x86_fp80, ptr [[LD_TEMP:%.+]], align 16
- // CHECK: ret x86_fp80 [[RET_VAL]]
- // CHECK32-LABEL: @testcompassign
- // CHECK32: store ptr %{{.+}}, ptr [[ADDR_ADDR:%.+]], align 4
- // CHECK32: [[ADDR:%.+]] = load ptr, ptr [[ADDR_ADDR]], align 4
- // CHECK32: call void @__atomic_load(i32 noundef 12, ptr noundef [[ADDR]], ptr noundef [[TEMP_LD_ADDR:%.+]], i32 noundef 5)
- // CHECK32: [[LD_VALUE:%.+]] = load x86_fp80, ptr [[TEMP_LD_ADDR]], align 4
- // CHECK32: br label %[[ATOMIC_OP:.+]]
- // CHECK32: [[ATOMIC_OP]]
- // CHECK32: [[OLD_VALUE:%.+]] = phi x86_fp80 [ [[LD_VALUE]], %{{.+}} ], [ [[LD_VALUE:%.+]], %[[ATOMIC_OP]] ]
- // CHECK32: [[INC_VALUE:%.+]] = fsub x86_fp80 [[OLD_VALUE]],
- // CHECK32: call void @llvm.memset.p0.i64(ptr align 4 [[OLD_VALUE_ADDR:%.+]], i8 0, i64 12, i1 false)
- // CHECK32: store x86_fp80 [[OLD_VALUE]], ptr [[OLD_VALUE_ADDR]], align 4
- // CHECK32: call void @llvm.memset.p0.i64(ptr align 4 [[DESIRED_VALUE_ADDR:%.+]], i8 0, i64 12, i1 false)
- // CHECK32: store x86_fp80 [[INC_VALUE]], ptr [[DESIRED_VALUE_ADDR]], align 4
- // CHECK32: [[FAIL_SUCCESS:%.+]] = call zeroext i1 @__atomic_compare_exchange(i32 noundef 12, ptr noundef [[ADDR]], ptr noundef [[OLD_VALUE_ADDR]], ptr noundef [[DESIRED_VALUE_ADDR]], i32 noundef 5, i32 noundef 5)
- // CHECK32: [[LD_VALUE]] = load x86_fp80, ptr [[OLD_VALUE_ADDR]], align 4
- // CHECK32: br i1 [[FAIL_SUCCESS]], label %[[ATOMIC_CONT:.+]], label %[[ATOMIC_OP]]
- // CHECK32: [[ATOMIC_CONT]]
- // CHECK32: [[ADDR:%.+]] = load ptr, ptr %{{.+}}, align 4
- // CHECK32: call void @__atomic_load(i32 noundef 12, ptr noundef [[ADDR]], ptr noundef [[GET_ADDR:%.+]], i32 noundef 5)
- // CHECK32: [[RET_VAL:%.+]] = load x86_fp80, ptr [[GET_ADDR]], align 4
- // CHECK32: ret x86_fp80 [[RET_VAL]]
return *addr;
}
+// CHECK-LABEL: define dso_local x86_fp80 @testassign(
+// CHECK-SAME: ptr noundef [[ADDR:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[ADDR_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT: [[ATOMIC_TEMP:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT: [[ATOMIC_TEMP1:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT: store ptr [[ADDR]], ptr [[ADDR_ADDR]], align 8
+// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ADDR_ADDR]], align 8
+// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[ATOMIC_TEMP]], i8 0, i64 16, i1 false)
+// CHECK-NEXT: store x86_fp80 0xK4005E600000000000000, ptr [[ATOMIC_TEMP]], align 16
+// CHECK-NEXT: [[TMP1:%.*]] = load i128, ptr [[ATOMIC_TEMP]], align 16
+// CHECK-NEXT: store atomic i128 [[TMP1]], ptr [[TMP0]] seq_cst, align 16
+// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[ADDR_ADDR]], align 8
+// CHECK-NEXT: [[ATOMIC_LOAD:%.*]] = load atomic i128, ptr [[TMP2]] seq_cst, align 16
+// CHECK-NEXT: store i128 [[ATOMIC_LOAD]], ptr [[ATOMIC_TEMP1]], align 16
+// CHECK-NEXT: [[TMP3:%.*]] = load x86_fp80, ptr [[ATOMIC_TEMP1]], align 16
+// CHECK-NEXT: ret x86_fp80 [[TMP3]]
+//
+// CHECK32-LABEL: define dso_local x86_fp80 @testassign(
+// CHECK32-SAME: ptr noundef [[ADDR:%.*]]) #[[ATTR0]] {
+// CHECK32-NEXT: entry:
+// CHECK32-NEXT: [[ADDR_ADDR:%.*]] = alloca ptr, align 4
+// CHECK32-NEXT: [[ATOMIC_TEMP:%.*]] = alloca x86_fp80, align 4
+// CHECK32-NEXT: [[ATOMIC_TEMP1:%.*]] = alloca x86_fp80, align 4
+// CHECK32-NEXT: store ptr [[ADDR]], ptr [[ADDR_ADDR]], align 4
+// CHECK32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ADDR_ADDR]], align 4
+// CHECK32-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[ATOMIC_TEMP]], i8 0, i64 12, i1 false)
+// CHECK32-NEXT: store x86_fp80 0xK4005E600000000000000, ptr [[ATOMIC_TEMP]], align 4
+// CHECK32-NEXT: call void @__atomic_store(i32 noundef 12, ptr noundef [[TMP0]], ptr noundef [[ATOMIC_TEMP]], i32 noundef 5)
+// CHECK32-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ADDR_ADDR]], align 4
+// CHECK32-NEXT: call void @__atomic_load(i32 noundef 12, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP1]], i32 noundef 5)
+// CHECK32-NEXT: [[TMP2:%.*]] = load x86_fp80, ptr [[ATOMIC_TEMP1]], align 4
+// CHECK32-NEXT: ret x86_fp80 [[TMP2]]
+//
long double testassign(_Atomic long double *addr) {
- // CHECK-LABEL: @testassign
- // CHECK: store ptr %{{.+}}, ptr [[ADDR_ADDR:%.+]], align 8
- // CHECK: [[ADDR:%.+]] = load ptr, ptr [[ADDR_ADDR]], align 8
- // CHECK: call void @llvm.memset.p0.i64(ptr align 16 [[STORE_TEMP_PTR:%.+]], i8 0, i64 16, i1 false)
- // CHECK: store x86_fp80 {{.+}}, ptr [[STORE_TEMP_PTR]], align 16
- // CHECK: [[STORE_TEMP_INT:%.+]] = load i128, ptr [[STORE_TEMP_PTR]], align 16
- // CHECK: store atomic i128 [[STORE_TEMP_INT]], ptr [[ADDR]] seq_cst, align 16
- // CHECK32-LABEL: @testassign
- // CHECK32: store ptr %{{.+}}, ptr [[ADDR_ADDR:%.+]], align 4
- // CHECK32: [[ADDR:%.+]] = load ptr, ptr [[ADDR_ADDR]], align 4
- // CHECK32: call void @llvm.memset.p0.i64(ptr align 4 [[STORE_TEMP_PTR:%.+]], i8 0, i64 12, i1 false)
- // CHECK32: store x86_fp80 {{.+}}, ptr [[STORE_TEMP_PTR]], align 4
- // CHECK32: call void @__atomic_store(i32 noundef 12, ptr noundef [[ADDR]], ptr noundef [[STORE_TEMP_PTR]], i32 noundef 5)
*addr = 115;
- // CHECK: [[ADDR:%.+]] = load ptr, ptr %{{.+}}, align 8
- // CHECK: [[INT_VAL:%.+]] = load atomic i128, ptr [[ADDR]] seq_cst, align 16
- // CHECK: store i128 [[INT_VAL]], ptr [[INT_LD_TEMP:%.+]], align 16
- // CHECK: [[RET_VAL:%.+]] = load x86_fp80, ptr [[LD_TEMP:%.+]], align 16
- // CHECK: ret x86_fp80 [[RET_VAL]]
- // CHECK32: [[ADDR:%.+]] = load ptr, ptr %{{.+}}, align 4
- // CHECK32: call void @__atomic_load(i32 noundef 12, ptr noundef [[ADDR]], ptr noundef [[LD_TEMP:%.+]], i32 noundef 5)
- // CHECK32: [[RET_VAL:%.+]] = load x86_fp80, ptr [[LD_TEMP]], align 4
- // CHECK32: ret x86_fp80 [[RET_VAL]]
return *addr;
}
+// CHECK-LABEL: define dso_local x86_fp80 @test_volatile_inc(
+// CHECK-SAME: ptr noundef [[ADDR:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[RETVAL:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT: [[ADDR_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT: store ptr [[ADDR]], ptr [[ADDR_ADDR]], align 8
+// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ADDR_ADDR]], align 8
+// CHECK-NEXT: [[TMP1:%.*]] = atomicrmw fadd ptr [[TMP0]], float 1.000000e+00 seq_cst, align 16
+// CHECK-NEXT: [[TMP2:%.*]] = fadd float [[TMP1]], 1.000000e+00
+// CHECK-NEXT: store float [[TMP2]], ptr [[RETVAL]], align 16
+// CHECK-NEXT: [[TMP3:%.*]] = load x86_fp80, ptr [[RETVAL]], align 16
+// CHECK-NEXT: ret x86_fp80 [[TMP3]]
+//
+// CHECK32-LABEL: define dso_local x86_fp80 @test_volatile_inc(
+// CHECK32-SAME: ptr noundef [[ADDR:%.*]]) #[[ATTR0]] {
+// CHECK32-NEXT: entry:
+// CHECK32-NEXT: [[RETVAL:%.*]] = alloca x86_fp80, align 4
+// CHECK32-NEXT: [[ADDR_ADDR:%.*]] = alloca ptr, align 4
+// CHECK32-NEXT: store ptr [[ADDR]], ptr [[ADDR_ADDR]], align 4
+// CHECK32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ADDR_ADDR]], align 4
+// CHECK32-NEXT: [[TMP1:%.*]] = atomicrmw fadd ptr [[TMP0]], float 1.000000e+00 seq_cst, align 4
+// CHECK32-NEXT: [[TMP2:%.*]] = fadd float [[TMP1]], 1.000000e+00
+// CHECK32-NEXT: store float [[TMP2]], ptr [[RETVAL]], align 4
+// CHECK32-NEXT: [[TMP3:%.*]] = load x86_fp80, ptr [[RETVAL]], align 4
+// CHECK32-NEXT: ret x86_fp80 [[TMP3]]
+//
long double test_volatile_inc(volatile _Atomic long double *addr) {
- // CHECK-LABEL: @test_volatile_inc
- // CHECK: store ptr %{{.+}}, ptr [[ADDR_ADDR:%.+]], align 8
- // CHECK: [[ADDR:%.+]] = load ptr, ptr [[ADDR_ADDR]], align 8
- // CHECK: [[INT_VALUE:%.+]] = load atomic volatile i128, ptr [[ADDR]] seq_cst, align 16
- // CHECK: store i128 [[INT_VALUE]], ptr [[LD_ADDR:%.+]], align 16
- // CHECK: [[LD_VALUE:%.+]] = load x86_fp80, ptr [[LD_ADDR]], align 16
- // CHECK: br label %[[ATOMIC_OP:.+]]
- // CHECK: [[ATOMIC_OP]]
- // CHECK: [[OLD_VALUE:%.+]] = phi x86_fp80 [ [[LD_VALUE]], %{{.+}} ], [ [[LD_VALUE:%.+]], %[[ATOMIC_OP]] ]
- // CHECK: [[INC_VALUE:%.+]] = fadd x86_fp80 [[OLD_VALUE]],
- // CHECK: call void @llvm.memset.p0.i64(ptr align 16 [[OLD_VALUE_ADDR:%.+]], i8 0, i64 16, i1 false)
- // CHECK: store x86_fp80 [[OLD_VALUE]], ptr [[OLD_VALUE_ADDR]], align 16
- // CHECK: [[OLD_INT:%.+]] = load i128, ptr [[OLD_VALUE_ADDR]], align 16
- // CHECK: call void @llvm.memset.p0.i64(ptr align 16 [[NEW_VALUE_ADDR:%.+]], i8 0, i64 16, i1 false)
- // CHECK: store x86_fp80 [[INC_VALUE]], ptr [[NEW_VALUE_ADDR]], align 16
- // CHECK: [[NEW_INT:%.+]] = load i128, ptr [[NEW_VALUE_ADDR]], align 16
- // CHECK: [[RES:%.+]] = cmpxchg volatile ptr [[ADDR]], i128 [[OLD_INT]], i128 [[NEW_INT]] seq_cst seq_cst, align 16
- // CHECK: [[OLD_VALUE:%.+]] = extractvalue { i128, i1 } [[RES]], 0
- // CHECK: [[FAIL_SUCCESS:%.+]] = extractvalue { i128, i1 } [[RES]], 1
- // CHECK: store i128 [[OLD_VALUE]], ptr [[OLD_VALUE_RES_PTR:%.+]], align 16
- // CHECK: [[LD_VALUE]] = load x86_fp80, ptr [[OLD_VALUE_RES_PTR]], align 16
- // CHECK: br i1 [[FAIL_SUCCESS]], label %[[ATOMIC_CONT:.+]], label %[[ATOMIC_OP]]
- // CHECK: [[ATOMIC_CONT]]
- // CHECK: ret x86_fp80 [[INC_VALUE]]
- // CHECK32-LABEL: @test_volatile_inc
- // CHECK32: store ptr %{{.+}}, ptr [[ADDR_ADDR:%.+]], align 4
- // CHECK32: [[ADDR:%.+]] = load ptr, ptr [[ADDR_ADDR]], align 4
- // CHECK32: call void @__atomic_load(i32 noundef 12, ptr noundef [[ADDR]], ptr noundef [[TEMP_LD_ADDR:%.+]], i32 noundef 5)
- // CHECK32: [[LD_VALUE:%.+]] = load x86_fp80, ptr [[TEMP_LD_ADDR]], align 4
- // CHECK32: br label %[[ATOMIC_OP:.+]]
- // CHECK32: [[ATOMIC_OP]]
- // CHECK32: [[OLD_VALUE:%.+]] = phi x86_fp80 [ [[LD_VALUE]], %{{.+}} ], [ [[LD_VALUE:%.+]], %[[ATOMIC_OP]] ]
- // CHECK32: [[INC_VALUE:%.+]] = fadd x86_fp80 [[OLD_VALUE]],
- // CHECK32: call void @llvm.memset.p0.i64(ptr align 4 [[OLD_VALUE_ADDR:%.+]], i8 0, i64 12, i1 false)
- // CHECK32: store x86_fp80 [[OLD_VALUE]], ptr [[OLD_VALUE_ADDR]], align 4
- // CHECK32: call void @llvm.memset.p0.i64(ptr align 4 [[DESIRED_VALUE_ADDR:%.+]], i8 0, i64 12, i1 false)
- // CHECK32: store x86_fp80 [[INC_VALUE]], ptr [[DESIRED_VALUE_ADDR]], align 4
- // CHECK32: [[FAIL_SUCCESS:%.+]] = call zeroext i1 @__atomic_compare_exchange(i32 noundef 12, ptr noundef [[ADDR]], ptr noundef [[OLD_VALUE_ADDR]], ptr noundef [[DESIRED_VALUE_ADDR]], i32 noundef 5, i32 noundef 5)
- // CHECK32: [[LD_VALUE]] = load x86_fp80, ptr [[OLD_VALUE_ADDR]], align 4
- // CHECK32: br i1 [[FAIL_SUCCESS]], label %[[ATOMIC_CONT:.+]], label %[[ATOMIC_OP]]
- // CHECK32: [[ATOMIC_CONT]]
- // CHECK32: ret x86_fp80 [[INC_VALUE]]
return ++*addr;
}
+// CHECK-LABEL: define dso_local x86_fp80 @test_volatile_dec(
+// CHECK-SAME: ptr noundef [[ADDR:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[RETVAL:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT: [[ADDR_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT: store ptr [[ADDR]], ptr [[ADDR_ADDR]], align 8
+// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ADDR_ADDR]], align 8
+// CHECK-NEXT: [[TMP1:%.*]] = atomicrmw fsub ptr [[TMP0]], float 1.000000e+00 seq_cst, align 16
+// CHECK-NEXT: store float [[TMP1]], ptr [[RETVAL]], align 16
+// CHECK-NEXT: [[TMP2:%.*]] = load x86_fp80, ptr [[RETVAL]], align 16
+// CHECK-NEXT: ret x86_fp80 [[TMP2]]
+//
+// CHECK32-LABEL: define dso_local x86_fp80 @test_volatile_dec(
+// CHECK32-SAME: ptr noundef [[ADDR:%.*]]) #[[ATTR0]] {
+// CHECK32-NEXT: entry:
+// CHECK32-NEXT: [[RETVAL:%.*]] = alloca x86_fp80, align 4
+// CHECK32-NEXT: [[ADDR_ADDR:%.*]] = alloca ptr, align 4
+// CHECK32-NEXT: store ptr [[ADDR]], ptr [[ADDR_ADDR]], align 4
+// CHECK32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ADDR_ADDR]], align 4
+// CHECK32-NEXT: [[TMP1:%.*]] = atomicrmw fsub ptr [[TMP0]], float 1.000000e+00 seq_cst, align 4
+// CHECK32-NEXT: store float [[TMP1]], ptr [[RETVAL]], align 4
+// CHECK32-NEXT: [[TMP2:%.*]] = load x86_fp80, ptr [[RETVAL]], align 4
+// CHECK32-NEXT: ret x86_fp80 [[TMP2]]
+//
long double test_volatile_dec(volatile _Atomic long double *addr) {
- // CHECK-LABEL: @test_volatile_dec
- // CHECK: store ptr %{{.+}}, ptr [[ADDR_ADDR:%.+]], align 8
- // CHECK: [[ADDR:%.+]] = load ptr, ptr [[ADDR_ADDR]], align 8
- // CHECK: [[INT_VALUE:%.+]] = load atomic volatile i128, ptr [[ADDR]] seq_cst, align 16
- // CHECK: store i128 [[INT_VALUE]], ptr [[LD_ADDR:%.+]], align 16
- // CHECK: [[ORIG_LD_VALUE:%.+]] = load x86_fp80, ptr [[LD_ADDR]], align 16
- // CHECK: br label %[[ATOMIC_OP:.+]]
- // CHECK: [[ATOMIC_OP]]
- // CHECK: [[OLD_VALUE:%.+]] = phi x86_fp80 [ [[ORIG_LD_VALUE]], %{{.+}} ], [ [[LD_VALUE:%.+]], %[[ATOMIC_OP]] ]
- // CHECK: [[DEC_VALUE:%.+]] = fadd x86_fp80 [[OLD_VALUE]],
- // CHECK: call void @llvm.memset.p0.i64(ptr align 16 [[OLD_VALUE_ADDR:%.+]], i8 0, i64 16, i1 false)
- // CHECK: store x86_fp80 [[OLD_VALUE]], ptr [[OLD_VALUE_ADDR]], align 16
- // CHECK: [[OLD_INT:%.+]] = load i128, ptr [[OLD_VALUE_ADDR]], align 16
- // CHECK: call void @llvm.memset.p0.i64(ptr align 16 [[NEW_VALUE_ADDR:%.+]], i8 0, i64 16, i1 false)
- // CHECK: store x86_fp80 [[DEC_VALUE]], ptr [[NEW_VALUE_ADDR]], align 16
- // CHECK: [[NEW_INT:%.+]] = load i128, ptr [[NEW_VALUE_ADDR]], align 16
- // CHECK: [[RES:%.+]] = cmpxchg volatile ptr [[ADDR]], i128 [[OLD_INT]], i128 [[NEW_INT]] seq_cst seq_cst, align 16
- // CHECK: [[OLD_VALUE:%.+]] = extractvalue { i128, i1 } [[RES]], 0
- // CHECK: [[FAIL_SUCCESS:%.+]] = extractvalue { i128, i1 } [[RES]], 1
- // CHECK: store i128 [[OLD_VALUE]], ptr [[OLD_VALUE_RES_PTR:%.+]], align 16
- // CHECK: [[LD_VALUE]] = load x86_fp80, ptr [[OLD_VALUE_RES_PTR]], align 16
- // CHECK: br i1 [[FAIL_SUCCESS]], label %[[ATOMIC_CONT:.+]], label %[[ATOMIC_OP]]
- // CHECK: [[ATOMIC_CONT]]
- // CHECK: ret x86_fp80 [[ORIG_LD_VALUE]]
- // CHECK32-LABEL: @test_volatile_dec
- // CHECK32: store ptr %{{.+}}, ptr [[ADDR_ADDR:%.+]], align 4
- // CHECK32: [[ADDR:%.+]] = load ptr, ptr [[ADDR_ADDR]], align 4
- // CHECK32: call void @__atomic_load(i32 noundef 12, ptr noundef [[ADDR]], ptr noundef [[TEMP_LD_ADDR:%.+]], i32 noundef 5)
- // CHECK32: [[ORIG_LD_VALUE:%.+]] = load x86_fp80, ptr [[TEMP_LD_ADDR]], align 4
- // CHECK32: br label %[[ATOMIC_OP:.+]]
- // CHECK32: [[ATOMIC_OP]]
- // CHECK32: [[OLD_VALUE:%.+]] = phi x86_fp80 [ [[ORIG_LD_VALUE]], %{{.+}} ], [ [[LD_VALUE:%.+]], %[[ATOMIC_OP]] ]
- // CHECK32: [[DEC_VALUE:%.+]] = fadd x86_fp80 [[OLD_VALUE]],
- // CHECK32: call void @llvm.memset.p0.i64(ptr align 4 [[OLD_VALUE_ADDR:%.+]], i8 0, i64 12, i1 false)
- // CHECK32: store x86_fp80 [[OLD_VALUE]], ptr [[OLD_VALUE_ADDR]], align 4
- // CHECK32: call void @llvm.memset.p0.i64(ptr align 4 [[DESIRED_VALUE_ADDR:%.+]], i8 0, i64 12, i1 false)
- // CHECK32: store x86_fp80 [[DEC_VALUE]], ptr [[DESIRED_VALUE_ADDR]], align 4
- // CHECK32: [[FAIL_SUCCESS:%.+]] = call zeroext i1 @__atomic_compare_exchange(i32 noundef 12, ptr noundef [[ADDR]], ptr noundef [[OLD_VALUE_ADDR]], ptr noundef [[DESIRED_VALUE_ADDR]], i32 noundef 5, i32 noundef 5)
- // CHECK32: [[LD_VALUE]] = load x86_fp80, ptr [[OLD_VALUE_ADDR]], align 4
- // CHECK32: br i1 [[FAIL_SUCCESS]], label %[[ATOMIC_CONT:.+]], label %[[ATOMIC_OP]]
- // CHECK32: [[ATOMIC_CONT]]
- // CHECK32: ret x86_fp80 [[ORIG_LD_VALUE]]
return (*addr)--;
}
+// CHECK-LABEL: define dso_local x86_fp80 @test_volatile_compassign(
+// CHECK-SAME: ptr noundef [[ADDR:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[ADDR_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT: [[ATOMIC_TEMP:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT: [[ATOMIC_TEMP1:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT: [[ATOMIC_TEMP2:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT: [[ATOMIC_TEMP3:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT: [[ATOMIC_TEMP5:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT: store ptr [[ADDR]], ptr [[ADDR_ADDR]], align 8
+// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ADDR_ADDR]], align 8
+// CHECK-NEXT: [[ATOMIC_LOAD:%.*]] = load atomic volatile i128, ptr [[TMP0]] seq_cst, align 16
+// CHECK-NEXT: store i128 [[ATOMIC_LOAD]], ptr [[ATOMIC_TEMP]], align 16
+// CHECK-NEXT: [[TMP1:%.*]] = load x86_fp80, ptr [[ATOMIC_TEMP]], align 16
+// CHECK-NEXT: br label [[ATOMIC_OP:%.*]]
+// CHECK: atomic_op:
+// CHECK-NEXT: [[TMP2:%.*]] = phi x86_fp80 [ [[TMP1]], [[ENTRY:%.*]] ], [ [[TMP8:%.*]], [[ATOMIC_OP]] ]
+// CHECK-NEXT: [[SUB:%.*]] = fsub x86_fp80 [[TMP2]], 0xK4003C800000000000000
+// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[ATOMIC_TEMP1]], i8 0, i64 16, i1 false)
+// CHECK-NEXT: store x86_fp80 [[TMP2]], ptr [[ATOMIC_TEMP1]], align 16
+// CHECK-NEXT: [[TMP3:%.*]] = load i128, ptr [[ATOMIC_TEMP1]], align 16
+// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[ATOMIC_TEMP2]], i8 0, i64 16, i1 false)
+// CHECK-NEXT: store x86_fp80 [[SUB]], ptr [[ATOMIC_TEMP2]], align 16
+// CHECK-NEXT: [[TMP4:%.*]] = load i128, ptr [[ATOMIC_TEMP2]], align 16
+// CHECK-NEXT: [[TMP5:%.*]] = cmpxchg volatile ptr [[TMP0]], i128 [[TMP3]], i128 [[TMP4]] seq_cst seq_cst, align 16
+// CHECK-NEXT: [[TMP6:%.*]] = extractvalue { i128, i1 } [[TMP5]], 0
+// CHECK-NEXT: [[TMP7:%.*]] = extractvalue { i128, i1 } [[TMP5]], 1
+// CHECK-NEXT: store i128 [[TMP6]], ptr [[ATOMIC_TEMP3]], align 16
+// CHECK-NEXT: [[TMP8]] = load x86_fp80, ptr [[ATOMIC_TEMP3]], align 16
+// CHECK-NEXT: br i1 [[TMP7]], label [[ATOMIC_CONT:%.*]], label [[ATOMIC_OP]]
+// CHECK: atomic_cont:
+// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[ADDR_ADDR]], align 8
+// CHECK-NEXT: [[ATOMIC_LOAD4:%.*]] = load atomic volatile i128, ptr [[TMP9]] seq_cst, align 16
+// CHECK-NEXT: store i128 [[ATOMIC_LOAD4]], ptr [[ATOMIC_TEMP5]], align 16
+// CHECK-NEXT: [[TMP10:%.*]] = load x86_fp80, ptr [[ATOMIC_TEMP5]], align 16
+// CHECK-NEXT: ret x86_fp80 [[TMP10]]
+//
+// CHECK32-LABEL: define dso_local x86_fp80 @test_volatile_compassign(
+// CHECK32-SAME: ptr noundef [[ADDR:%.*]]) #[[ATTR0]] {
+// CHECK32-NEXT: entry:
+// CHECK32-NEXT: [[ADDR_ADDR:%.*]] = alloca ptr, align 4
+// CHECK32-NEXT: [[ATOMIC_TEMP:%.*]] = alloca x86_fp80, align 4
+// CHECK32-NEXT: [[ATOMIC_TEMP1:%.*]] = alloca x86_fp80, align 4
+// CHECK32-NEXT: [[ATOMIC_TEMP2:%.*]] = alloca x86_fp80, align 4
+// CHECK32-NEXT: [[ATOMIC_TEMP3:%.*]] = alloca x86_fp80, align 4
+// CHECK32-NEXT: store ptr [[ADDR]], ptr [[ADDR_ADDR]], align 4
+// CHECK32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ADDR_ADDR]], align 4
+// CHECK32-NEXT: call void @__atomic_load(i32 noundef 12, ptr noundef [[TMP0]], ptr noundef [[ATOMIC_TEMP]], i32 noundef 5)
+// CHECK32-NEXT: [[TMP1:%.*]] = load x86_fp80, ptr [[ATOMIC_TEMP]], align 4
+// CHECK32-NEXT: br label [[ATOMIC_OP:%.*]]
+// CHECK32: atomic_op:
+// CHECK32-NEXT: [[TMP2:%.*]] = phi x86_fp80 [ [[TMP1]], [[ENTRY:%.*]] ], [ [[TMP3:%.*]], [[ATOMIC_OP]] ]
+// CHECK32-NEXT: [[SUB:%.*]] = fsub x86_fp80 [[TMP2]], 0xK4003C800000000000000
+// CHECK32-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[ATOMIC_TEMP1]], i8 0, i64 12, i1 false)
+// CHECK32-NEXT: store x86_fp80 [[TMP2]], ptr [[ATOMIC_TEMP1]], align 4
+// CHECK32-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[ATOMIC_TEMP2]], i8 0, i64 12, i1 false)
+// CHECK32-NEXT: store x86_fp80 [[SUB]], ptr [[ATOMIC_TEMP2]], align 4
+// CHECK32-NEXT: [[CALL:%.*]] = call zeroext i1 @__atomic_compare_exchange(i32 noundef 12, ptr noundef [[TMP0]], ptr noundef [[ATOMIC_TEMP1]], ptr noundef [[ATOMIC_TEMP2]], i32 noundef 5, i32 noundef 5)
+// CHECK32-NEXT: [[TMP3]] = load x86_fp80, ptr [[ATOMIC_TEMP1]], align 4
+// CHECK32-NEXT: br i1 [[CALL]], label [[ATOMIC_CONT:%.*]], label [[ATOMIC_OP]]
+// CHECK32: atomic_cont:
+// CHECK32-NEXT: [[TMP4:%.*]] = load ptr, ptr [[ADDR_ADDR]], align 4
+// CHECK32-NEXT: call void @__atomic_load(i32 noundef 12, ptr noundef [[TMP4]], ptr noundef [[ATOMIC_TEMP3]], i32 noundef 5)
+// CHECK32-NEXT: [[TMP5:%.*]] = load x86_fp80, ptr [[ATOMIC_TEMP3]], align 4
+// CHECK32-NEXT: ret x86_fp80 [[TMP5]]
+//
long double test_volatile_compassign(volatile _Atomic long double *addr) {
*addr -= 25;
- // CHECK-LABEL: @test_volatile_compassign
- // CHECK: store ptr %{{.+}}, ptr [[ADDR_ADDR:%.+]], align 8
- // CHECK: [[ADDR:%.+]] = load ptr, ptr [[ADDR_ADDR]], align 8
- // CHECK: [[INT_VALUE:%.+]] = load atomic volatile i128, ptr [[ADDR]] seq_cst, align 16
- // CHECK: store i128 [[INT_VALUE]], ptr [[LD_ADDR:%.+]], align 16
- // CHECK: [[LD_VALUE:%.+]] = load x86_fp80, ptr [[LD_ADDR]], align 16
- // CHECK: br label %[[ATOMIC_OP:.+]]
- // CHECK: [[ATOMIC_OP]]
- // CHECK: [[OLD_VALUE:%.+]] = phi x86_fp80 [ [[LD_VALUE]], %{{.+}} ], [ [[LD_VALUE:%.+]], %[[ATOMIC_OP]] ]
- // CHECK: [[SUB_VALUE:%.+]] = fsub x86_fp80 [[OLD_VALUE]],
- // CHECK: call void @llvm.memset.p0.i64(ptr align 16 [[OLD_VALUE_ADDR:%.+]], i8 0, i64 16, i1 false)
- // CHECK: store x86_fp80 [[OLD_VALUE]], ptr [[OLD_VALUE_ADDR]], align 16
- // CHECK: [[OLD_INT:%.+]] = load i128, ptr [[OLD_VALUE_ADDR]], align 16
- // CHECK: call void @llvm.memset.p0.i64(ptr align 16 [[NEW_VALUE_ADDR:%.+]], i8 0, i64 16, i1 false)
- // CHECK: store x86_fp80 [[SUB_VALUE]], ptr [[NEW_VALUE_ADDR]], align 16
- // CHECK: [[NEW_INT:%.+]] = load i128, ptr [[NEW_VALUE_ADDR]], align 16
- // CHECK: [[RES:%.+]] = cmpxchg volatile ptr [[ADDR]], i128 [[OLD_INT]], i128 [[NEW_INT]] seq_cst seq_cst, align 16
- // CHECK: [[OLD_VALUE:%.+]] = extractvalue { i128, i1 } [[RES]], 0
- // CHECK: [[FAIL_SUCCESS:%.+]] = extractvalue { i128, i1 } [[RES]], 1
- // CHECK: store i128 [[OLD_VALUE]], ptr [[OLD_VALUE_RES_PTR:%.+]], align 16
- // CHECK: [[LD_VALUE]] = load x86_fp80, ptr [[OLD_VALUE_RES_PTR]], align 16
- // CHECK: br i1 [[FAIL_SUCCESS]], label %[[ATOMIC_CONT:.+]], label %[[ATOMIC_OP]]
- // CHECK: [[ATOMIC_CONT]]
- // CHECK: [[ADDR:%.+]] = load ptr, ptr %{{.+}}, align 8
- // CHECK: [[INT_VAL:%.+]] = load atomic volatile i128, ptr [[ADDR]] seq_cst, align 16
- // CHECK: store i128 [[INT_VAL]], ptr [[INT_LD_TEMP:%.+]], align 16
- // CHECK: [[RET_VAL:%.+]] = load x86_fp80, ptr [[LD_TEMP:%.+]], align 16
- // CHECK32-LABEL: @test_volatile_compassign
- // CHECK32: store ptr %{{.+}}, ptr [[ADDR_ADDR:%.+]], align 4
- // CHECK32: [[ADDR:%.+]] = load ptr, ptr [[ADDR_ADDR]], align 4
- // CHECK32: call void @__atomic_load(i32 noundef 12, ptr noundef [[ADDR]], ptr noundef [[TEMP_LD_ADDR:%.+]], i32 noundef 5)
- // CHECK32: [[LD_VALUE:%.+]] = load x86_fp80, ptr [[TEMP_LD_ADDR]], align 4
- // CHECK32: br label %[[ATOMIC_OP:.+]]
- // CHECK32: [[ATOMIC_OP]]
- // CHECK32: [[OLD_VALUE:%.+]] = phi x86_fp80 [ [[LD_VALUE]], %{{.+}} ], [ [[LD_VALUE:%.+]], %[[ATOMIC_OP]] ]
- // CHECK32: [[INC_VALUE:%.+]] = fsub x86_fp80 [[OLD_VALUE]],
- // CHECK32: call void @llvm.memset.p0.i64(ptr align 4 [[OLD_VALUE_ADDR:%.+]], i8 0, i64 12, i1 false)
- // CHECK32: store x86_fp80 [[OLD_VALUE]], ptr [[OLD_VALUE_ADDR]], align 4
- // CHECK32: call void @llvm.memset.p0.i64(ptr align 4 [[DESIRED_VALUE_ADDR:%.+]], i8 0, i64 12, i1 false)
- // CHECK32: store x86_fp80 [[INC_VALUE]], ptr [[DESIRED_VALUE_ADDR]], align 4
- // CHECK32: [[FAIL_SUCCESS:%.+]] = call zeroext i1 @__atomic_compare_exchange(i32 noundef 12, ptr noundef [[ADDR]], ptr noundef [[OLD_VALUE_ADDR]], ptr noundef [[DESIRED_VALUE_ADDR]], i32 noundef 5, i32 noundef 5)
- // CHECK32: [[LD_VALUE]] = load x86_fp80, ptr [[OLD_VALUE_ADDR]], align 4
- // CHECK32: br i1 [[FAIL_SUCCESS]], label %[[ATOMIC_CONT:.+]], label %[[ATOMIC_OP]]
- // CHECK32: [[ATOMIC_CONT]]
- // CHECK32: [[ADDR:%.+]] = load ptr, ptr %{{.+}}, align 4
- // CHECK32: call void @__atomic_load(i32 noundef 12, ptr noundef [[ADDR]], ptr noundef [[GET_ADDR:%.+]], i32 noundef 5)
- // CHECK32: [[RET_VAL:%.+]] = load x86_fp80, ptr [[GET_ADDR]], align 4
- // CHECK32: ret x86_fp80 [[RET_VAL]]
return *addr;
}
+// CHECK-LABEL: define dso_local x86_fp80 @test_volatile_assign(
+// CHECK-SAME: ptr noundef [[ADDR:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[ADDR_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT: [[ATOMIC_TEMP:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT: [[ATOMIC_TEMP1:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT: store ptr [[ADDR]], ptr [[ADDR_ADDR]], align 8
+// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ADDR_ADDR]], align 8
+// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[ATOMIC_TEMP]], i8 0, i64 16, i1 false)
+// CHECK-NEXT: store x86_fp80 0xK4005E600000000000000, ptr [[ATOMIC_TEMP]], align 16
+// CHECK-NEXT: [[TMP1:%.*]] = load i128, ptr [[ATOMIC_TEMP]], align 16
+// CHECK-NEXT: store atomic volatile i128 [[TMP1]], ptr [[TMP0]] seq_cst, align 16
+// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[ADDR_ADDR]], align 8
+// CHECK-NEXT: [[ATOMIC_LOAD:%.*]] = load atomic volatile i128, ptr [[TMP2]] seq_cst, align 16
+// CHECK-NEXT: store i128 [[ATOMIC_LOAD]], ptr [[ATOMIC_TEMP1]], align 16
+// CHECK-NEXT: [[TMP3:%.*]] = load x86_fp80, ptr [[ATOMIC_TEMP1]], align 16
+// CHECK-NEXT: ret x86_fp80 [[TMP3]]
+//
+// CHECK32-LABEL: define dso_local x86_fp80 @test_volatile_assign(
+// CHECK32-SAME: ptr noundef [[ADDR:%.*]]) #[[ATTR0]] {
+// CHECK32-NEXT: entry:
+// CHECK32-NEXT: [[ADDR_ADDR:%.*]] = alloca ptr, align 4
+// CHECK32-NEXT: [[ATOMIC_TEMP:%.*]] = alloca x86_fp80, align 4
+// CHECK32-NEXT: [[ATOMIC_TEMP1:%.*]] = alloca x86_fp80, align 4
+// CHECK32-NEXT: store ptr [[ADDR]], ptr [[ADDR_ADDR]], align 4
+// CHECK32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ADDR_ADDR]], align 4
+// CHECK32-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[ATOMIC_TEMP]], i8 0, i64 12, i1 false)
+// CHECK32-NEXT: store x86_fp80 0xK4005E600000000000000, ptr [[ATOMIC_TEMP]], align 4
+// CHECK32-NEXT: call void @__atomic_store(i32 noundef 12, ptr noundef [[TMP0]], ptr noundef [[ATOMIC_TEMP]], i32 noundef 5)
+// CHECK32-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ADDR_ADDR]], align 4
+// CHECK32-NEXT: call void @__atomic_load(i32 noundef 12, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP1]], i32 noundef 5)
+// CHECK32-NEXT: [[TMP2:%.*]] = load x86_fp80, ptr [[ATOMIC_TEMP1]], align 4
+// CHECK32-NEXT: ret x86_fp80 [[TMP2]]
+//
long double test_volatile_assign(volatile _Atomic long double *addr) {
- // CHECK-LABEL: @test_volatile_assign
- // CHECK: store ptr %{{.+}}, ptr [[ADDR_ADDR:%.+]], align 8
- // CHECK: [[ADDR:%.+]] = load ptr, ptr [[ADDR_ADDR]], align 8
- // CHECK: call void @llvm.memset.p0.i64(ptr align 16 [[STORE_TEMP_PTR:%.+]], i8 0, i64 16, i1 false)
- // CHECK: store x86_fp80 {{.+}}, ptr [[STORE_TEMP_PTR]], align 16
- // CHECK: [[STORE_TEMP_INT:%.+]] = load i128, ptr [[STORE_TEMP_PTR]], align 16
- // CHECK: store atomic volatile i128 [[STORE_TEMP_INT]], ptr [[ADDR]] seq_cst, align 16
- // CHECK32-LABEL: @test_volatile_assign
- // CHECK32: store ptr %{{.+}}, ptr [[ADDR_ADDR:%.+]], align 4
- // CHECK32: [[ADDR:%.+]] = load ptr, ptr [[ADDR_ADDR]], align 4
- // CHECK32: call void @llvm.memset.p0.i64(ptr align 4 [[STORE_TEMP_PTR:%.+]], i8 0, i64 12, i1 false)
- // CHECK32: store x86_fp80 {{.+}}, ptr [[STORE_TEMP_PTR]], align 4
- // CHECK32: call void @__atomic_store(i32 noundef 12, ptr noundef [[ADDR]], ptr noundef [[STORE_TEMP_PTR]], i32 noundef 5)
*addr = 115;
- // CHECK: [[ADDR:%.+]] = load ptr, ptr %{{.+}}, align 8
- // CHECK: [[INT_VAL:%.+]] = load atomic volatile i128, ptr [[ADDR]] seq_cst, align 16
- // CHECK: store i128 [[INT_VAL]], ptr [[INT_LD_TEMP:%.+]], align 16
- // CHECK: [[RET_VAL:%.+]] = load x86_fp80, ptr [[LD_TEMP:%.+]], align 16
- // CHECK: ret x86_fp80 [[RET_VAL]]
- // CHECK32: [[ADDR:%.+]] = load ptr, ptr %{{.+}}, align 4
- // CHECK32: call void @__atomic_load(i32 noundef 12, ptr noundef [[ADDR]], ptr noundef [[LD_TEMP:%.+]], i32 noundef 5)
- // CHECK32: [[RET_VAL:%.+]] = load x86_fp80, ptr [[LD_TEMP]], align 4
- // CHECK32: ret x86_fp80 [[RET_VAL]]
return *addr;
}
--
GitLab
From b6328db80b4294cf83a3471ecdfab33003fa742d Mon Sep 17 00:00:00 2001
From: Sven van Haastregt
Date: Thu, 2 May 2024 11:43:56 +0200
Subject: [PATCH 0027/1014] [clang][CodeGen] Put constant initializer globals
into constant addrspace (#90048)
Place constant initializer globals into the constant address space.
Clang generates such globals for e.g. larger array member initializers
of classes and then emits copy operations from the global to the
object(s). The globals are never written so they ought to be in the
constant address space.
---
clang/lib/CodeGen/CGExprAgg.cpp | 11 +++++++----
.../test/CodeGenOpenCLCXX/addrspace-with-class.clcpp | 5 ++++-
2 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index 44d476976a55..cd9936a6dc0b 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -537,9 +537,12 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType,
elementType.isTriviallyCopyableType(CGF.getContext())) {
CodeGen::CodeGenModule &CGM = CGF.CGM;
ConstantEmitter Emitter(CGF);
- LangAS AS = ArrayQTy.getAddressSpace();
+ QualType GVArrayQTy = CGM.getContext().getAddrSpaceQualType(
+ CGM.getContext().removeAddrSpaceQualType(ArrayQTy),
+ CGM.GetGlobalConstantAddressSpace());
+ LangAS AS = GVArrayQTy.getAddressSpace();
if (llvm::Constant *C =
- Emitter.tryEmitForInitializer(ExprToVisit, AS, ArrayQTy)) {
+ Emitter.tryEmitForInitializer(ExprToVisit, AS, GVArrayQTy)) {
auto GV = new llvm::GlobalVariable(
CGM.getModule(), C->getType(),
/* isConstant= */ true, llvm::GlobalValue::PrivateLinkage, C,
@@ -547,10 +550,10 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType,
/* InsertBefore= */ nullptr, llvm::GlobalVariable::NotThreadLocal,
CGM.getContext().getTargetAddressSpace(AS));
Emitter.finalize(GV);
- CharUnits Align = CGM.getContext().getTypeAlignInChars(ArrayQTy);
+ CharUnits Align = CGM.getContext().getTypeAlignInChars(GVArrayQTy);
GV->setAlignment(Align.getAsAlign());
Address GVAddr(GV, GV->getValueType(), Align);
- EmitFinalDestCopy(ArrayQTy, CGF.MakeAddrLValue(GVAddr, ArrayQTy));
+ EmitFinalDestCopy(ArrayQTy, CGF.MakeAddrLValue(GVAddr, GVArrayQTy));
return;
}
}
diff --git a/clang/test/CodeGenOpenCLCXX/addrspace-with-class.clcpp b/clang/test/CodeGenOpenCLCXX/addrspace-with-class.clcpp
index 18d97a989a43..a0ed03b25535 100644
--- a/clang/test/CodeGenOpenCLCXX/addrspace-with-class.clcpp
+++ b/clang/test/CodeGenOpenCLCXX/addrspace-with-class.clcpp
@@ -5,7 +5,7 @@
// for constructors, member functions and destructors.
// See also atexit.cl and global_init.cl for other specific tests.
-// CHECK: %struct.MyType = type { i32 }
+// CHECK: %struct.MyType = type { i32, [5 x i32] }
struct MyType {
MyType(int i) : i(i) {}
MyType(int i) __constant : i(i) {}
@@ -14,6 +14,7 @@ struct MyType {
int bar() { return i + 2; }
int bar() __constant { return i + 1; }
int i;
+ int a[5] = {42, 43, 44, 45, 46};
};
// CHECK: @const1 ={{.*}} addrspace(2) global %struct.MyType zeroinitializer
@@ -23,6 +24,8 @@ __constant MyType const2(2);
// CHECK: @glob ={{.*}} addrspace(1) global %struct.MyType zeroinitializer
MyType glob(1);
+// CHECK: @constinit ={{.*}} addrspace(2) constant [5 x i32] [i32 42, i32 43, i32 44, i32 45, i32 46]
+
// CHECK: call spir_func void @_ZNU3AS26MyTypeC1Ei(ptr addrspace(2) {{[^,]*}} @const1, i32 noundef 1)
// CHECK: call spir_func void @_ZNU3AS26MyTypeC1Ei(ptr addrspace(2) {{[^,]*}} @const2, i32 noundef 2)
// CHECK: call spir_func void @_ZNU3AS46MyTypeC1Ei(ptr addrspace(4) {{[^,]*}} addrspacecast (ptr addrspace(1) @glob to ptr addrspace(4)), i32 noundef 1)
--
GitLab
From 175d2971020ceaad3e1adcf9bb92e4ebaaa449ee Mon Sep 17 00:00:00 2001
From: Florian Hahn
Date: Thu, 2 May 2024 11:01:24 +0100
Subject: [PATCH 0028/1014] [LoopUnroll] Add CSE to remove redundant loads
after unrolling. (#83860)
This patch adds loadCSE support to simplifyLoopAfterUnroll. It is based
on EarlyCSE's implementation using ScopeHashTable and is using SCEV for
accessed pointers to check to find redundant loads after unrolling.
This applies to the late unroll pass only, for full unrolling those
redundant loads will be cleaned up by the regular pipeline.
The current approach constructs MSSA on-demand per-loop, but there is
still small but notable compile-time impact:
stage1-O3 +0.04%
stage1-ReleaseThinLTO +0.06%
stage1-ReleaseLTO-g +0.05%
stage1-O0-g +0.02%
stage2-O3 +0.09%
stage2-O0-g +0.04%
stage2-clang +0.02%
https://llvm-compile-time-tracker.com/compare.php?from=c089fa5a729e217d0c0d4647656386dac1a1b135&to=ec7c0f27cb5c12b600d9adfc8543d131765ec7be&stat=instructions:u
This benefits some workloads with runtime-unrolling disabled,
where users use pragmas to force unrolling, as well as with
runtime unrolling enabled.
On SPEC/MultiSource, this removes a number of loads after unrolling
on AArch64 with runtime unrolling enabled.
```
External/S...te/526.blender_r/526.blender_r 96
MultiSourc...rks/mediabench/gsm/toast/toast 39
SingleSource/Benchmarks/Misc/ffbench 4
External/SPEC/CINT2006/403.gcc/403.gcc 18
MultiSourc.../Applications/JM/ldecod/ldecod 4
MultiSourc.../mediabench/jpeg/jpeg-6a/cjpeg 6
MultiSourc...OE-ProxyApps-C/miniGMG/miniGMG 9
MultiSourc...e/Applications/ClamAV/clamscan 4
MultiSourc.../MallocBench/espresso/espresso 3
MultiSourc...dence-flt/LinearDependence-flt 2
MultiSourc...ch/office-ispell/office-ispell 4
MultiSourc...ch/consumer-jpeg/consumer-jpeg 6
MultiSourc...ench/security-sha/security-sha 11
MultiSourc...chmarks/McCat/04-bisect/bisect 3
SingleSour...tTests/2020-01-06-coverage-009 12
MultiSourc...ench/telecomm-gsm/telecomm-gsm 39
MultiSourc...lds-flt/CrossingThresholds-flt 24
MultiSourc...dence-dbl/LinearDependence-dbl 2
External/S...C/CINT2006/445.gobmk/445.gobmk 6
MultiSourc...enchmarks/mafft/pairlocalalign 53
External/S...31.deepsjeng_r/531.deepsjeng_r 3
External/S...rate/510.parest_r/510.parest_r 58
External/S...NT2006/464.h264ref/464.h264ref 29
External/S...NT2017rate/502.gcc_r/502.gcc_r 45
External/S...C/CINT2006/456.hmmer/456.hmmer 6
External/S...te/538.imagick_r/538.imagick_r 18
External/S.../CFP2006/447.dealII/447.dealII 4
MultiSourc...OE-ProxyApps-C++/miniFE/miniFE 12
External/S...2017rate/525.x264_r/525.x264_r 36
MultiSourc...Benchmarks/7zip/7zip-benchmark 33
MultiSourc...hmarks/ASC_Sequoia/AMGmk/AMGmk 2
MultiSourc...chmarks/VersaBench/8b10b/8b10b 1
MultiSourc.../Applications/JM/lencod/lencod 116
MultiSourc...lds-dbl/CrossingThresholds-dbl 24
MultiSource/Benchmarks/McCat/05-eks/eks 15
```
PR: https://github.com/llvm/llvm-project/pull/83860
---
llvm/include/llvm/Analysis/MemorySSA.h | 15 +-
.../llvm/Transforms/Utils/UnrollLoop.h | 7 +-
llvm/lib/Analysis/MemorySSA.cpp | 102 +++++++++---
llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp | 13 +-
llvm/lib/Transforms/Utils/LoopUnroll.cpp | 155 +++++++++++++++++-
.../LoopUnroll/shifted-tripcount.ll | 6 +-
.../Transforms/LoopUnroll/unroll-loads-cse.ll | 7 +-
.../AArch64/extra-unroll-simplifications.ll | 4 +-
8 files changed, 257 insertions(+), 52 deletions(-)
diff --git a/llvm/include/llvm/Analysis/MemorySSA.h b/llvm/include/llvm/Analysis/MemorySSA.h
index caf0e31fd37d..2ca5c281166c 100644
--- a/llvm/include/llvm/Analysis/MemorySSA.h
+++ b/llvm/include/llvm/Analysis/MemorySSA.h
@@ -110,6 +110,7 @@ namespace llvm {
template struct GraphTraits;
class BasicBlock;
class Function;
+class Loop;
class Instruction;
class LLVMContext;
class MemoryAccess;
@@ -700,6 +701,7 @@ DEFINE_TRANSPARENT_OPERAND_ACCESSORS(MemoryPhi, MemoryAccess)
class MemorySSA {
public:
MemorySSA(Function &, AliasAnalysis *, DominatorTree *);
+ MemorySSA(Loop &, AliasAnalysis *, DominatorTree *);
// MemorySSA must remain where it's constructed; Walkers it creates store
// pointers to it.
@@ -800,10 +802,11 @@ protected:
// Used by Memory SSA dumpers and wrapper pass
friend class MemorySSAUpdater;
+ template
void verifyOrderingDominationAndDefUses(
- Function &F, VerificationLevel = VerificationLevel::Fast) const;
- void verifyDominationNumbers(const Function &F) const;
- void verifyPrevDefInPhis(Function &F) const;
+ IterT Blocks, VerificationLevel = VerificationLevel::Fast) const;
+ template void verifyDominationNumbers(IterT Blocks) const;
+ template void verifyPrevDefInPhis(IterT Blocks) const;
// This is used by the use optimizer and updater.
AccessList *getWritableBlockAccesses(const BasicBlock *BB) const {
@@ -847,7 +850,8 @@ private:
class OptimizeUses;
CachingWalker *getWalkerImpl();
- void buildMemorySSA(BatchAAResults &BAA);
+ template
+ void buildMemorySSA(BatchAAResults &BAA, IterT Blocks);
void prepareForMoveTo(MemoryAccess *, BasicBlock *);
void verifyUseInDefs(MemoryAccess *, MemoryAccess *) const;
@@ -871,7 +875,8 @@ private:
void renumberBlock(const BasicBlock *) const;
AliasAnalysis *AA = nullptr;
DominatorTree *DT;
- Function &F;
+ Function *F = nullptr;
+ Loop *L = nullptr;
// Memory SSA mappings
DenseMap ValueToMemoryAccess;
diff --git a/llvm/include/llvm/Transforms/Utils/UnrollLoop.h b/llvm/include/llvm/Transforms/Utils/UnrollLoop.h
index e8b03f81b348..bd804dc11266 100644
--- a/llvm/include/llvm/Transforms/Utils/UnrollLoop.h
+++ b/llvm/include/llvm/Transforms/Utils/UnrollLoop.h
@@ -22,6 +22,7 @@
namespace llvm {
class AssumptionCache;
+class AAResults;
class BasicBlock;
class BlockFrequencyInfo;
class DependenceInfo;
@@ -79,7 +80,8 @@ LoopUnrollResult UnrollLoop(Loop *L, UnrollLoopOptions ULO, LoopInfo *LI,
AssumptionCache *AC,
const llvm::TargetTransformInfo *TTI,
OptimizationRemarkEmitter *ORE, bool PreserveLCSSA,
- Loop **RemainderLoop = nullptr);
+ Loop **RemainderLoop = nullptr,
+ AAResults *AA = nullptr);
bool UnrollRuntimeLoopRemainder(
Loop *L, unsigned Count, bool AllowExpensiveTripCount,
@@ -102,7 +104,8 @@ bool isSafeToUnrollAndJam(Loop *L, ScalarEvolution &SE, DominatorTree &DT,
void simplifyLoopAfterUnroll(Loop *L, bool SimplifyIVs, LoopInfo *LI,
ScalarEvolution *SE, DominatorTree *DT,
AssumptionCache *AC,
- const TargetTransformInfo *TTI);
+ const TargetTransformInfo *TTI,
+ AAResults *AA = nullptr);
MDNode *GetUnrollMetadata(MDNode *LoopID, StringRef Name);
diff --git a/llvm/lib/Analysis/MemorySSA.cpp b/llvm/lib/Analysis/MemorySSA.cpp
index 49450d85d742..48ef73e59045 100644
--- a/llvm/lib/Analysis/MemorySSA.cpp
+++ b/llvm/lib/Analysis/MemorySSA.cpp
@@ -25,6 +25,7 @@
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/CFGPrinter.h"
#include "llvm/Analysis/IteratedDominanceFrontier.h"
+#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/MemoryLocation.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/IR/AssemblyAnnotationWriter.h"
@@ -1230,7 +1231,7 @@ void MemorySSA::markUnreachableAsLiveOnEntry(BasicBlock *BB) {
}
MemorySSA::MemorySSA(Function &Func, AliasAnalysis *AA, DominatorTree *DT)
- : DT(DT), F(Func), LiveOnEntryDef(nullptr), Walker(nullptr),
+ : DT(DT), F(&Func), LiveOnEntryDef(nullptr), Walker(nullptr),
SkipWalker(nullptr) {
// Build MemorySSA using a batch alias analysis. This reuses the internal
// state that AA collects during an alias()/getModRefInfo() call. This is
@@ -1239,8 +1240,29 @@ MemorySSA::MemorySSA(Function &Func, AliasAnalysis *AA, DominatorTree *DT)
// make queries about all the instructions in the Function.
assert(AA && "No alias analysis?");
BatchAAResults BatchAA(*AA);
- buildMemorySSA(BatchAA);
- // Intentionally leave AA to nullptr while building so we don't accidently
+ buildMemorySSA(BatchAA, iterator_range(F->begin(), F->end()));
+ // Intentionally leave AA to nullptr while building so we don't accidentally
+ // use non-batch AliasAnalysis.
+ this->AA = AA;
+ // Also create the walker here.
+ getWalker();
+}
+
+MemorySSA::MemorySSA(Loop &L, AliasAnalysis *AA, DominatorTree *DT)
+ : DT(DT), L(&L), LiveOnEntryDef(nullptr), Walker(nullptr),
+ SkipWalker(nullptr) {
+ // Build MemorySSA using a batch alias analysis. This reuses the internal
+ // state that AA collects during an alias()/getModRefInfo() call. This is
+ // safe because there are no CFG changes while building MemorySSA and can
+ // significantly reduce the time spent by the compiler in AA, because we will
+ // make queries about all the instructions in the Function.
+ assert(AA && "No alias analysis?");
+ BatchAAResults BatchAA(*AA);
+ buildMemorySSA(
+ BatchAA, map_range(L.blocks(), [](const BasicBlock *BB) -> BasicBlock & {
+ return *const_cast(BB);
+ }));
+ // Intentionally leave AA to nullptr while building so we don't accidentally
// use non-batch AliasAnalysis.
this->AA = AA;
// Also create the walker here.
@@ -1493,16 +1515,17 @@ void MemorySSA::placePHINodes(
createMemoryPhi(BB);
}
-void MemorySSA::buildMemorySSA(BatchAAResults &BAA) {
+template
+void MemorySSA::buildMemorySSA(BatchAAResults &BAA, IterT Blocks) {
// We create an access to represent "live on entry", for things like
// arguments or users of globals, where the memory they use is defined before
// the beginning of the function. We do not actually insert it into the IR.
// We do not define a live on exit for the immediate uses, and thus our
// semantics do *not* imply that something with no immediate uses can simply
// be removed.
- BasicBlock &StartingPoint = F.getEntryBlock();
- LiveOnEntryDef.reset(new MemoryDef(F.getContext(), nullptr, nullptr,
- &StartingPoint, NextID++));
+ BasicBlock &StartingPoint = *Blocks.begin();
+ LiveOnEntryDef.reset(new MemoryDef(StartingPoint.getContext(), nullptr,
+ nullptr, &StartingPoint, NextID++));
// We maintain lists of memory accesses per-block, trading memory for time. We
// could just look up the memory access for every possible instruction in the
@@ -1510,7 +1533,7 @@ void MemorySSA::buildMemorySSA(BatchAAResults &BAA) {
SmallPtrSet DefiningBlocks;
// Go through each block, figure out where defs occur, and chain together all
// the accesses.
- for (BasicBlock &B : F) {
+ for (BasicBlock &B : Blocks) {
bool InsertIntoDef = false;
AccessList *Accesses = nullptr;
DefsList *Defs = nullptr;
@@ -1537,11 +1560,29 @@ void MemorySSA::buildMemorySSA(BatchAAResults &BAA) {
// Now do regular SSA renaming on the MemoryDef/MemoryUse. Visited will get
// filled in with all blocks.
SmallPtrSet Visited;
- renamePass(DT->getRootNode(), LiveOnEntryDef.get(), Visited);
+ if (L) {
+ // Only building MemorySSA for a single loop. placePHINodes may have
+ // inserted a MemoryPhi in the loop's preheader. As this is outside the
+ // scope of the loop, set them to LiveOnEntry.
+ if (auto *P = getMemoryAccess(L->getLoopPreheader())) {
+ for (Use &U : make_early_inc_range(P->uses()))
+ U.set(LiveOnEntryDef.get());
+ removeFromLists(P);
+ }
+ // Now rename accesses in the loop. Populate Visited with the exit blocks of
+ // the loop, to limit the scope of the renaming.
+ SmallVector ExitBlocks;
+ L->getExitBlocks(ExitBlocks);
+ Visited.insert(ExitBlocks.begin(), ExitBlocks.end());
+ renamePass(DT->getNode(L->getLoopPreheader()), LiveOnEntryDef.get(),
+ Visited);
+ } else {
+ renamePass(DT->getRootNode(), LiveOnEntryDef.get(), Visited);
+ }
// Mark the uses in unreachable blocks as live on entry, so that they go
// somewhere.
- for (auto &BB : F)
+ for (auto &BB : Blocks)
if (!Visited.count(&BB))
markUnreachableAsLiveOnEntry(&BB);
}
@@ -1851,7 +1892,10 @@ void MemorySSA::removeFromLists(MemoryAccess *MA, bool ShouldDelete) {
void MemorySSA::print(raw_ostream &OS) const {
MemorySSAAnnotatedWriter Writer(this);
- F.print(OS, &Writer);
+ Function *F = this->F;
+ if (L)
+ F = L->getHeader()->getParent();
+ F->print(OS, &Writer);
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
@@ -1864,10 +1908,23 @@ void MemorySSA::verifyMemorySSA(VerificationLevel VL) const {
#endif
#ifndef NDEBUG
- verifyOrderingDominationAndDefUses(F, VL);
- verifyDominationNumbers(F);
- if (VL == VerificationLevel::Full)
- verifyPrevDefInPhis(F);
+ if (F) {
+ auto Blocks = iterator_range(F->begin(), F->end());
+ verifyOrderingDominationAndDefUses(Blocks, VL);
+ verifyDominationNumbers(Blocks);
+ if (VL == VerificationLevel::Full)
+ verifyPrevDefInPhis(Blocks);
+ } else {
+ assert(L && "must either have loop or function");
+ auto Blocks =
+ map_range(L->blocks(), [](const BasicBlock *BB) -> BasicBlock & {
+ return *const_cast(BB);
+ });
+ verifyOrderingDominationAndDefUses(Blocks, VL);
+ verifyDominationNumbers(Blocks);
+ if (VL == VerificationLevel::Full)
+ verifyPrevDefInPhis(Blocks);
+ }
#endif
// Previously, the verification used to also verify that the clobberingAccess
// cached by MemorySSA is the same as the clobberingAccess found at a later
@@ -1881,8 +1938,9 @@ void MemorySSA::verifyMemorySSA(VerificationLevel VL) const {
// example, see test4 added in D51960.
}
-void MemorySSA::verifyPrevDefInPhis(Function &F) const {
- for (const BasicBlock &BB : F) {
+template
+void MemorySSA::verifyPrevDefInPhis(IterT Blocks) const {
+ for (const BasicBlock &BB : Blocks) {
if (MemoryPhi *Phi = getMemoryAccess(&BB)) {
for (unsigned I = 0, E = Phi->getNumIncomingValues(); I != E; ++I) {
auto *Pred = Phi->getIncomingBlock(I);
@@ -1917,12 +1975,13 @@ void MemorySSA::verifyPrevDefInPhis(Function &F) const {
/// Verify that all of the blocks we believe to have valid domination numbers
/// actually have valid domination numbers.
-void MemorySSA::verifyDominationNumbers(const Function &F) const {
+template
+void MemorySSA::verifyDominationNumbers(IterT Blocks) const {
if (BlockNumberingValid.empty())
return;
SmallPtrSet ValidBlocks = BlockNumberingValid;
- for (const BasicBlock &BB : F) {
+ for (const BasicBlock &BB : Blocks) {
if (!ValidBlocks.count(&BB))
continue;
@@ -1958,14 +2017,15 @@ void MemorySSA::verifyDominationNumbers(const Function &F) const {
/// Verify def-uses: the immediate use information - walk all the memory
/// accesses and verifying that, for each use, it appears in the appropriate
/// def's use list
-void MemorySSA::verifyOrderingDominationAndDefUses(Function &F,
+template
+void MemorySSA::verifyOrderingDominationAndDefUses(IterT Blocks,
VerificationLevel VL) const {
// Walk all the blocks, comparing what the lookups think and what the access
// lists think, as well as the order in the blocks vs the order in the access
// lists.
SmallVector ActualAccesses;
SmallVector ActualDefs;
- for (BasicBlock &B : F) {
+ for (BasicBlock &B : Blocks) {
const AccessList *AL = getBlockAccesses(&B);
const auto *DL = getBlockDefs(&B);
MemoryPhi *Phi = getMemoryAccess(&B);
diff --git a/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp b/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp
index 75fb8765061e..10fc9e9303e8 100644
--- a/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp
@@ -16,6 +16,7 @@
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/ScopedHashTable.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
@@ -27,6 +28,7 @@
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/LoopPass.h"
#include "llvm/Analysis/LoopUnrollAnalyzer.h"
+#include "llvm/Analysis/MemorySSA.h"
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
#include "llvm/Analysis/ProfileSummaryInfo.h"
#include "llvm/Analysis/ScalarEvolution.h"
@@ -1140,7 +1142,8 @@ tryToUnrollLoop(Loop *L, DominatorTree &DT, LoopInfo *LI, ScalarEvolution &SE,
std::optional ProvidedUpperBound,
std::optional ProvidedAllowPeeling,
std::optional ProvidedAllowProfileBasedPeeling,
- std::optional ProvidedFullUnrollMaxCount) {
+ std::optional ProvidedFullUnrollMaxCount,
+ AAResults *AA = nullptr) {
LLVM_DEBUG(dbgs() << "Loop Unroll: F["
<< L->getHeader()->getParent()->getName() << "] Loop %"
@@ -1292,7 +1295,7 @@ tryToUnrollLoop(Loop *L, DominatorTree &DT, LoopInfo *LI, ScalarEvolution &SE,
ValueToValueMapTy VMap;
if (peelLoop(L, PP.PeelCount, LI, &SE, DT, &AC, PreserveLCSSA, VMap)) {
- simplifyLoopAfterUnroll(L, true, LI, &SE, &DT, &AC, &TTI);
+ simplifyLoopAfterUnroll(L, true, LI, &SE, &DT, &AC, &TTI, nullptr);
// If the loop was peeled, we already "used up" the profile information
// we had, so we don't want to unroll or peel again.
if (PP.PeelProfiledIterations)
@@ -1325,7 +1328,7 @@ tryToUnrollLoop(Loop *L, DominatorTree &DT, LoopInfo *LI, ScalarEvolution &SE,
L,
{UP.Count, UP.Force, UP.Runtime, UP.AllowExpensiveTripCount,
UP.UnrollRemainder, ForgetAllSCEV},
- LI, &SE, &DT, &AC, &TTI, &ORE, PreserveLCSSA, &RemainderLoop);
+ LI, &SE, &DT, &AC, &TTI, &ORE, PreserveLCSSA, &RemainderLoop, AA);
if (UnrollResult == LoopUnrollResult::Unmodified)
return LoopUnrollResult::Unmodified;
@@ -1572,6 +1575,7 @@ PreservedAnalyses LoopUnrollPass::run(Function &F,
auto &DT = AM.getResult(F);
auto &AC = AM.getResult(F);
auto &ORE = AM.getResult(F);
+ AAResults &AA = AM.getResult(F);
LoopAnalysisManager *LAM = nullptr;
if (auto *LAMProxy = AM.getCachedResult(F))
@@ -1627,7 +1631,8 @@ PreservedAnalyses LoopUnrollPass::run(Function &F,
/*Count*/ std::nullopt,
/*Threshold*/ std::nullopt, UnrollOpts.AllowPartial,
UnrollOpts.AllowRuntime, UnrollOpts.AllowUpperBound, LocalAllowPeeling,
- UnrollOpts.AllowProfileBasedPeeling, UnrollOpts.FullUnrollMaxCount);
+ UnrollOpts.AllowProfileBasedPeeling, UnrollOpts.FullUnrollMaxCount,
+ &AA);
Changed |= Result != LoopUnrollResult::Unmodified;
// The parent must not be damaged by unrolling!
diff --git a/llvm/lib/Transforms/Utils/LoopUnroll.cpp b/llvm/lib/Transforms/Utils/LoopUnroll.cpp
index 6f0d00081572..e9969ae64147 100644
--- a/llvm/lib/Transforms/Utils/LoopUnroll.cpp
+++ b/llvm/lib/Transforms/Utils/LoopUnroll.cpp
@@ -18,17 +18,20 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/ScopedHashTable.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include "llvm/ADT/ilist_iterator.h"
+#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/AssumptionCache.h"
#include "llvm/Analysis/DomTreeUpdater.h"
#include "llvm/Analysis/InstructionSimplify.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/LoopIterator.h"
+#include "llvm/Analysis/MemorySSA.h"
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
#include "llvm/Analysis/ScalarEvolution.h"
#include "llvm/IR/BasicBlock.h"
@@ -209,13 +212,140 @@ static bool isEpilogProfitable(Loop *L) {
return false;
}
+struct LoadValue {
+ Instruction *DefI = nullptr;
+ unsigned Generation = 0;
+ LoadValue() = default;
+ LoadValue(Instruction *Inst, unsigned Generation)
+ : DefI(Inst), Generation(Generation) {}
+};
+
+class StackNode {
+ ScopedHashTable::ScopeTy LoadScope;
+ unsigned CurrentGeneration;
+ unsigned ChildGeneration;
+ DomTreeNode *Node;
+ DomTreeNode::const_iterator ChildIter;
+ DomTreeNode::const_iterator EndIter;
+ bool Processed = false;
+
+public:
+ StackNode(ScopedHashTable &AvailableLoads,
+ unsigned cg, DomTreeNode *N, DomTreeNode::const_iterator Child,
+ DomTreeNode::const_iterator End)
+ : LoadScope(AvailableLoads), CurrentGeneration(cg), ChildGeneration(cg),
+ Node(N), ChildIter(Child), EndIter(End) {}
+ // Accessors.
+ unsigned currentGeneration() const { return CurrentGeneration; }
+ unsigned childGeneration() const { return ChildGeneration; }
+ void childGeneration(unsigned generation) { ChildGeneration = generation; }
+ DomTreeNode *node() { return Node; }
+ DomTreeNode::const_iterator childIter() const { return ChildIter; }
+
+ DomTreeNode *nextChild() {
+ DomTreeNode *Child = *ChildIter;
+ ++ChildIter;
+ return Child;
+ }
+
+ DomTreeNode::const_iterator end() const { return EndIter; }
+ bool isProcessed() const { return Processed; }
+ void process() { Processed = true; }
+};
+
+Value *getMatchingValue(LoadValue LV, LoadInst *LI, unsigned CurrentGeneration,
+ BatchAAResults &BAA,
+ function_ref GetMSSA) {
+ if (!LV.DefI)
+ return nullptr;
+ if (LV.DefI->getType() != LI->getType())
+ return nullptr;
+ if (LV.Generation != CurrentGeneration) {
+ MemorySSA *MSSA = GetMSSA();
+ if (!MSSA)
+ return nullptr;
+ auto *EarlierMA = MSSA->getMemoryAccess(LV.DefI);
+ MemoryAccess *LaterDef =
+ MSSA->getWalker()->getClobberingMemoryAccess(LI, BAA);
+ if (!MSSA->dominates(LaterDef, EarlierMA))
+ return nullptr;
+ }
+ return LV.DefI;
+}
+
+void loadCSE(Loop *L, DominatorTree &DT, ScalarEvolution &SE, LoopInfo &LI,
+ BatchAAResults &BAA, function_ref GetMSSA) {
+ ScopedHashTable AvailableLoads;
+ SmallVector> NodesToProcess;
+ DomTreeNode *HeaderD = DT.getNode(L->getHeader());
+ NodesToProcess.emplace_back(new StackNode(AvailableLoads, 0, HeaderD,
+ HeaderD->begin(), HeaderD->end()));
+
+ unsigned CurrentGeneration = 0;
+ while (!NodesToProcess.empty()) {
+ StackNode *NodeToProcess = &*NodesToProcess.back();
+
+ CurrentGeneration = NodeToProcess->currentGeneration();
+
+ if (!NodeToProcess->isProcessed()) {
+ // Process the node.
+
+ // If this block has a single predecessor, then the predecessor is the
+ // parent
+ // of the domtree node and all of the live out memory values are still
+ // current in this block. If this block has multiple predecessors, then
+ // they could have invalidated the live-out memory values of our parent
+ // value. For now, just be conservative and invalidate memory if this
+ // block has multiple predecessors.
+ if (!NodeToProcess->node()->getBlock()->getSinglePredecessor())
+ ++CurrentGeneration;
+ for (auto &I : make_early_inc_range(*NodeToProcess->node()->getBlock())) {
+
+ auto *Load = dyn_cast(&I);
+ if (!Load || !Load->isSimple()) {
+ if (I.mayWriteToMemory())
+ CurrentGeneration++;
+ continue;
+ }
+
+ const SCEV *PtrSCEV = SE.getSCEV(Load->getPointerOperand());
+ LoadValue LV = AvailableLoads.lookup(PtrSCEV);
+ if (Value *M =
+ getMatchingValue(LV, Load, CurrentGeneration, BAA, GetMSSA)) {
+ if (LI.replacementPreservesLCSSAForm(Load, M)) {
+ Load->replaceAllUsesWith(M);
+ Load->eraseFromParent();
+ }
+ } else {
+ AvailableLoads.insert(PtrSCEV, LoadValue(Load, CurrentGeneration));
+ }
+ }
+ NodeToProcess->childGeneration(CurrentGeneration);
+ NodeToProcess->process();
+ } else if (NodeToProcess->childIter() != NodeToProcess->end()) {
+ // Push the next child onto the stack.
+ DomTreeNode *Child = NodeToProcess->nextChild();
+ if (!L->contains(Child->getBlock()))
+ continue;
+ NodesToProcess.emplace_back(
+ new StackNode(AvailableLoads, NodeToProcess->childGeneration(), Child,
+ Child->begin(), Child->end()));
+ } else {
+ // It has been processed, and there are no more children to process,
+ // so delete it and pop it off the stack.
+ NodesToProcess.pop_back();
+ }
+ }
+}
+
/// Perform some cleanup and simplifications on loops after unrolling. It is
/// useful to simplify the IV's in the new loop, as well as do a quick
/// simplify/dce pass of the instructions.
void llvm::simplifyLoopAfterUnroll(Loop *L, bool SimplifyIVs, LoopInfo *LI,
ScalarEvolution *SE, DominatorTree *DT,
AssumptionCache *AC,
- const TargetTransformInfo *TTI) {
+ const TargetTransformInfo *TTI,
+ AAResults *AA) {
using namespace llvm::PatternMatch;
// Simplify any new induction variables in the partially unrolled loop.
@@ -230,6 +360,16 @@ void llvm::simplifyLoopAfterUnroll(Loop *L, bool SimplifyIVs, LoopInfo *LI,
if (Instruction *Inst = dyn_cast_or_null(V))
RecursivelyDeleteTriviallyDeadInstructions(Inst);
}
+
+ if (AA) {
+ std::unique_ptr MSSA = nullptr;
+ BatchAAResults BAA(*AA);
+ loadCSE(L, *DT, *SE, *LI, BAA, [L, AA, DT, &MSSA]() -> MemorySSA * {
+ if (!MSSA)
+ MSSA.reset(new MemorySSA(*L, AA, DT));
+ return &*MSSA;
+ });
+ }
}
// At this point, the code is well formed. Perform constprop, instsimplify,
@@ -292,12 +432,11 @@ void llvm::simplifyLoopAfterUnroll(Loop *L, bool SimplifyIVs, LoopInfo *LI,
///
/// If RemainderLoop is non-null, it will receive the remainder loop (if
/// required and not fully unrolled).
-LoopUnrollResult llvm::UnrollLoop(Loop *L, UnrollLoopOptions ULO, LoopInfo *LI,
- ScalarEvolution *SE, DominatorTree *DT,
- AssumptionCache *AC,
- const TargetTransformInfo *TTI,
- OptimizationRemarkEmitter *ORE,
- bool PreserveLCSSA, Loop **RemainderLoop) {
+LoopUnrollResult
+llvm::UnrollLoop(Loop *L, UnrollLoopOptions ULO, LoopInfo *LI,
+ ScalarEvolution *SE, DominatorTree *DT, AssumptionCache *AC,
+ const TargetTransformInfo *TTI, OptimizationRemarkEmitter *ORE,
+ bool PreserveLCSSA, Loop **RemainderLoop, AAResults *AA) {
assert(DT && "DomTree is required");
if (!L->getLoopPreheader()) {
@@ -852,7 +991,7 @@ LoopUnrollResult llvm::UnrollLoop(Loop *L, UnrollLoopOptions ULO, LoopInfo *LI,
// At this point, the code is well formed. We now simplify the unrolled loop,
// doing constant propagation and dead code elimination as we go.
simplifyLoopAfterUnroll(L, !CompletelyUnroll && ULO.Count > 1, LI, SE, DT, AC,
- TTI);
+ TTI, AA);
NumCompletelyUnrolled += CompletelyUnroll;
++NumUnrolled;
diff --git a/llvm/test/Transforms/LoopUnroll/shifted-tripcount.ll b/llvm/test/Transforms/LoopUnroll/shifted-tripcount.ll
index 01ac4b77f2e4..4f3f861d308e 100644
--- a/llvm/test/Transforms/LoopUnroll/shifted-tripcount.ll
+++ b/llvm/test/Transforms/LoopUnroll/shifted-tripcount.ll
@@ -20,8 +20,7 @@ define void @latch_exit(ptr nocapture %p, i64 %n) nounwind {
; CHECK-NEXT: [[TMP16_1]] = add i64 [[I_013]], 2
; CHECK-NEXT: [[ARRAYIDX_1:%.*]] = getelementptr double, ptr [[P]], i64 [[TMP16_1]]
; CHECK-NEXT: [[TMP4_1:%.*]] = load double, ptr [[ARRAYIDX_1]], align 8
-; CHECK-NEXT: [[TMP8_1:%.*]] = load double, ptr [[ARRAYIDX7_1]], align 8
-; CHECK-NEXT: [[MUL9_1:%.*]] = fmul double [[TMP8_1]], [[TMP4_1]]
+; CHECK-NEXT: [[MUL9_1:%.*]] = fmul double [[TMP4]], [[TMP4_1]]
; CHECK-NEXT: store double [[MUL9_1]], ptr [[ARRAYIDX7_1]], align 8
; CHECK-NEXT: [[EXITCOND_1:%.*]] = icmp eq i64 [[TMP16_1]], [[MUL10]]
; CHECK-NEXT: br i1 [[EXITCOND_1]], label [[FOR_END:%.*]], label [[FOR_BODY]], !llvm.loop [[LOOP0:![0-9]+]]
@@ -70,8 +69,7 @@ define void @non_latch_exit(ptr nocapture %p, i64 %n) nounwind {
; CHECK-NEXT: [[TMP16_1]] = add i64 [[I_013]], 2
; CHECK-NEXT: [[ARRAYIDX_1:%.*]] = getelementptr double, ptr [[P]], i64 [[TMP16_1]]
; CHECK-NEXT: [[TMP4_1:%.*]] = load double, ptr [[ARRAYIDX_1]], align 8
-; CHECK-NEXT: [[TMP8_1:%.*]] = load double, ptr [[ARRAYIDX7_1]], align 8
-; CHECK-NEXT: [[MUL9_1:%.*]] = fmul double [[TMP8_1]], [[TMP4_1]]
+; CHECK-NEXT: [[MUL9_1:%.*]] = fmul double [[TMP4]], [[TMP4_1]]
; CHECK-NEXT: store double [[MUL9_1]], ptr [[ARRAYIDX7_1]], align 8
; CHECK-NEXT: [[EXITCOND_1:%.*]] = icmp eq i64 [[TMP16_1]], [[MUL10]]
; CHECK-NEXT: br i1 [[EXITCOND_1]], label [[FOR_END:%.*]], label [[LATCH_1]]
diff --git a/llvm/test/Transforms/LoopUnroll/unroll-loads-cse.ll b/llvm/test/Transforms/LoopUnroll/unroll-loads-cse.ll
index 109a1834c302..d4105254e531 100644
--- a/llvm/test/Transforms/LoopUnroll/unroll-loads-cse.ll
+++ b/llvm/test/Transforms/LoopUnroll/unroll-loads-cse.ll
@@ -29,9 +29,7 @@ define void @cse_matching_load_from_previous_unrolled_iteration(ptr %src, ptr no
; CHECK-NEXT: [[IV_NEXT:%.*]] = add nuw nsw i64 [[IV]], 1
; CHECK-NEXT: [[GEP_SRC_12_1:%.*]] = getelementptr i64, ptr [[SRC_12]], i64 [[IV_NEXT]]
; CHECK-NEXT: [[L_12_1:%.*]] = load i64, ptr [[GEP_SRC_12_1]], align 8
-; CHECK-NEXT: [[GEP_SRC_4_1:%.*]] = getelementptr i64, ptr [[SRC_4]], i64 [[IV_NEXT]]
-; CHECK-NEXT: [[L_4_1:%.*]] = load i64, ptr [[GEP_SRC_4_1]], align 8
-; CHECK-NEXT: [[MUL_1:%.*]] = mul i64 [[L_12_1]], [[L_4_1]]
+; CHECK-NEXT: [[MUL_1:%.*]] = mul i64 [[L_12_1]], [[L_12]]
; CHECK-NEXT: [[GEP_DST_1:%.*]] = getelementptr i64, ptr [[DST]], i64 [[IV_NEXT]]
; CHECK-NEXT: store i64 [[MUL_1]], ptr [[GEP_DST_1]], align 8
; CHECK-NEXT: [[IV_NEXT_1]] = add nuw nsw i64 [[IV]], 2
@@ -425,8 +423,7 @@ define void @loop_body_with_dead_blocks(ptr %src) {
; CHECK: loop.header.1:
; CHECK-NEXT: br label [[LOOP_BB_1:%.*]]
; CHECK: loop.bb.1:
-; CHECK-NEXT: [[L_1_1:%.*]] = load i32, ptr [[SRC]], align 8
-; CHECK-NEXT: [[C_1_1:%.*]] = icmp eq i32 [[L_1_1]], 0
+; CHECK-NEXT: [[C_1_1:%.*]] = icmp eq i32 [[L_2]], 0
; CHECK-NEXT: br i1 [[C_1_1]], label [[OUTER_HEADER_LOOPEXIT]], label [[LOOP_LATCH_1:%.*]]
; CHECK: loop.latch.1:
; CHECK-NEXT: call void @foo()
diff --git a/llvm/test/Transforms/PhaseOrdering/AArch64/extra-unroll-simplifications.ll b/llvm/test/Transforms/PhaseOrdering/AArch64/extra-unroll-simplifications.ll
index b32f4e2a258c..6c45442bdcd3 100644
--- a/llvm/test/Transforms/PhaseOrdering/AArch64/extra-unroll-simplifications.ll
+++ b/llvm/test/Transforms/PhaseOrdering/AArch64/extra-unroll-simplifications.ll
@@ -101,9 +101,7 @@ define void @cse_matching_load_from_previous_unrolled_iteration(i32 %N, ptr %src
; CHECK-NEXT: [[INDVARS_IV_NEXT:%.*]] = or disjoint i64 [[INDVARS_IV]], 1
; CHECK-NEXT: [[GEP_SRC_12_1:%.*]] = getelementptr <2 x i32>, ptr [[SRC_12]], i64 [[INDVARS_IV_NEXT]]
; CHECK-NEXT: [[L_12_1:%.*]] = load <2 x i32>, ptr [[GEP_SRC_12_1]], align 8
-; CHECK-NEXT: [[GEP_SRC_4_1:%.*]] = getelementptr <2 x i32>, ptr [[SRC_4]], i64 [[INDVARS_IV_NEXT]]
-; CHECK-NEXT: [[L_4_1:%.*]] = load <2 x i32>, ptr [[GEP_SRC_4_1]], align 8
-; CHECK-NEXT: [[MUL_1:%.*]] = mul <2 x i32> [[L_4_1]], [[L_12_1]]
+; CHECK-NEXT: [[MUL_1:%.*]] = mul <2 x i32> [[L_12]], [[L_12_1]]
; CHECK-NEXT: [[GEP_DST_1:%.*]] = getelementptr <2 x i32>, ptr [[DST]], i64 [[INDVARS_IV_NEXT]]
; CHECK-NEXT: store <2 x i32> [[MUL_1]], ptr [[GEP_DST_1]], align 8
; CHECK-NEXT: [[INDVARS_IV_NEXT_1]] = add nuw nsw i64 [[INDVARS_IV]], 2
--
GitLab
From d9fc5babb96ca9c75f36f9cf3d3f2a55ddc0ab4d Mon Sep 17 00:00:00 2001
From: Matt Arsenault
Date: Thu, 2 May 2024 12:08:52 +0200
Subject: [PATCH 0029/1014] DAG: Implement softening for fp atomic store
(#90840)
This will prevent SystemZ test regressions in a future change, tested by
#90826
---
.../SelectionDAG/LegalizeFloatTypes.cpp | 18 ++++++++++++++++++
llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h | 1 +
2 files changed, 19 insertions(+)
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp
index abe5be763825..ed838ae8775c 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp
@@ -946,6 +946,9 @@ bool DAGTypeLegalizer::SoftenFloatOperand(SDNode *N, unsigned OpNo) {
case ISD::STRICT_FSETCCS:
case ISD::SETCC: Res = SoftenFloatOp_SETCC(N); break;
case ISD::STORE: Res = SoftenFloatOp_STORE(N, OpNo); break;
+ case ISD::ATOMIC_STORE:
+ Res = SoftenFloatOp_ATOMIC_STORE(N, OpNo);
+ break;
case ISD::FCOPYSIGN: Res = SoftenFloatOp_FCOPYSIGN(N); break;
}
@@ -1172,6 +1175,21 @@ SDValue DAGTypeLegalizer::SoftenFloatOp_STORE(SDNode *N, unsigned OpNo) {
ST->getMemOperand());
}
+SDValue DAGTypeLegalizer::SoftenFloatOp_ATOMIC_STORE(SDNode *N, unsigned OpNo) {
+ assert(ISD::isUNINDEXEDStore(N) && "Indexed store during type legalization!");
+ assert(OpNo == 1 && "Can only soften the stored value!");
+ AtomicSDNode *ST = cast(N);
+ SDValue Val = ST->getVal();
+ EVT VT = Val.getValueType();
+ SDLoc dl(N);
+
+ assert(ST->getMemoryVT() == VT && "truncating atomic store not handled");
+
+ SDValue NewVal = GetSoftenedFloat(Val);
+ return DAG.getAtomic(ISD::ATOMIC_STORE, dl, VT, ST->getChain(), NewVal,
+ ST->getBasePtr(), ST->getMemOperand());
+}
+
SDValue DAGTypeLegalizer::SoftenFloatOp_FCOPYSIGN(SDNode *N) {
SDValue LHS = N->getOperand(0);
SDValue RHS = BitConvertToInteger(N->getOperand(1));
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
index 49be824deb51..617a4dd25d2b 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
@@ -592,6 +592,7 @@ private:
SDValue SoftenFloatOp_SELECT_CC(SDNode *N);
SDValue SoftenFloatOp_SETCC(SDNode *N);
SDValue SoftenFloatOp_STORE(SDNode *N, unsigned OpNo);
+ SDValue SoftenFloatOp_ATOMIC_STORE(SDNode *N, unsigned OpNo);
SDValue SoftenFloatOp_FCOPYSIGN(SDNode *N);
//===--------------------------------------------------------------------===//
--
GitLab
From 69e7cb587304e3706aac23c50b3d37651eb11330 Mon Sep 17 00:00:00 2001
From: Abid Qadeer
Date: Thu, 2 May 2024 11:43:46 +0100
Subject: [PATCH 0030/1014] [flang] Add a test for fir.real debug conversion.
(#90726)
This is an accompanying test for the fix done in #90683. It checks that
fir.real conversion works ok.
The failing fortran source in the #90683 generates fir.real type in the
target-rewrite pass which caused the original issue.
---
flang/test/Transforms/debug-90683.fir | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
create mode 100644 flang/test/Transforms/debug-90683.fir
diff --git a/flang/test/Transforms/debug-90683.fir b/flang/test/Transforms/debug-90683.fir
new file mode 100644
index 000000000000..9da0e5347d3f
--- /dev/null
+++ b/flang/test/Transforms/debug-90683.fir
@@ -0,0 +1,25 @@
+// RUN: fir-opt --add-debug-info --mlir-print-debuginfo %s -o - | FileCheck %s
+
+// This test checks that debug information for fir.real type works ok.
+
+module attributes {} {
+ func.func @_QPfn1(%arg0: !fir.ref> {fir.bindc_name = "a"} ) {
+ %0 = fir.declare %arg0 {uniq_name = "_QFfn1Ea"} : (!fir.ref>) -> !fir.ref>
+ %1 = fir.alloca f32 {bindc_name = "abserror", uniq_name = "_QFfn1Eabserror"}
+ %2 = fir.declare %1 {uniq_name = "_QFfn1Eabserror"} : (!fir.ref) -> !fir.ref
+ %3 = fir.load %0 : !fir.ref>
+ %4 = fir.extract_value %3, [0 : i32] : (!fir.complex<8>) -> !fir.real<8>
+ %5 = fir.extract_value %3, [1 : i32] : (!fir.complex<8>) -> !fir.real<8>
+ %6 = fir.call @cabs(%4, %5) : (!fir.real<8>, !fir.real<8>) -> f64
+ %7 = fir.convert %6 : (f64) -> f32
+ fir.store %7 to %2 : !fir.ref
+ return
+ } loc(#loc1)
+ func.func private @cabs(!fir.real<8>, !fir.real<8>) -> f64 attributes {fir.bindc_name = "cabs", fir.runtime}
+} loc(#loc)
+#loc1 = loc("test.f90":5:1)
+#loc = loc("test.f90":0:0)
+
+// CHECK-DAG: #[[TY:.*]] = #llvm.di_basic_type
+// CHECK-DAG: #[[TY1:.*]] = #llvm.di_subroutine_type
+// CHECK-DAG: #{{.*}} = #llvm.di_subprogram
--
GitLab
From c2f29caee4b8388cb185cab165a72fdb9ecef1e6 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser
Date: Thu, 2 May 2024 12:45:48 +0200
Subject: [PATCH 0031/1014] [libc++][NFC] Explicitly delete assignment operator
in tuple (#90604)
---
libcxx/include/tuple | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index c7fc5509a0f8..ace5f2328a45 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -304,7 +304,7 @@ class __tuple_leaf {
# endif
}
- _LIBCPP_CONSTEXPR_SINCE_CXX14 __tuple_leaf& operator=(const __tuple_leaf&);
+ _LIBCPP_CONSTEXPR_SINCE_CXX14 __tuple_leaf& operator=(const __tuple_leaf&) = delete;
public:
_LIBCPP_HIDE_FROM_ABI constexpr __tuple_leaf() _NOEXCEPT_(is_nothrow_default_constructible<_Hp>::value) : __value_() {
@@ -380,7 +380,7 @@ public:
template
class __tuple_leaf<_Ip, _Hp, true> : private _Hp {
- _LIBCPP_CONSTEXPR_SINCE_CXX14 __tuple_leaf& operator=(const __tuple_leaf&);
+ _LIBCPP_CONSTEXPR_SINCE_CXX14 __tuple_leaf& operator=(const __tuple_leaf&) = delete;
public:
_LIBCPP_HIDE_FROM_ABI constexpr __tuple_leaf() _NOEXCEPT_(is_nothrow_default_constructible<_Hp>::value) {}
@@ -1375,22 +1375,22 @@ inline _LIBCPP_HIDE_FROM_ABI constexpr _Tp __make_from_tuple_impl(_Tuple&& __t,
}
#else
template
-inline _LIBCPP_HIDE_FROM_ABI constexpr _Tp __make_from_tuple_impl(_Tuple&& __t, __tuple_indices<_Idx...>,
+inline _LIBCPP_HIDE_FROM_ABI constexpr _Tp __make_from_tuple_impl(_Tuple&& __t, __tuple_indices<_Idx...>,
enable_if_t(std::forward<_Tuple>(__t)))...>> * = nullptr)
_LIBCPP_NOEXCEPT_RETURN(_Tp(std::get<_Idx>(std::forward<_Tuple>(__t))...))
#endif // _LIBCPP_STD_VER >= 20
-template >>::type, class = void>
inline constexpr bool __can_make_from_tuple = false;
template
-inline constexpr bool __can_make_from_tuple<_Tp, _Tuple, __tuple_indices<_Idx...>,
+inline constexpr bool __can_make_from_tuple<_Tp, _Tuple, __tuple_indices<_Idx...>,
enable_if_t(std::declval<_Tuple>()))...>>> = true;
-// Based on LWG3528(https://wg21.link/LWG3528) and http://eel.is/c++draft/description#structure.requirements-9,
-// the standard allows to impose requirements, we constraint std::make_from_tuple to make std::make_from_tuple
-// SFINAE friendly and also avoid worse diagnostic messages. We still keep the constraints of std::__make_from_tuple_impl
+// Based on LWG3528(https://wg21.link/LWG3528) and http://eel.is/c++draft/description#structure.requirements-9,
+// the standard allows to impose requirements, we constraint std::make_from_tuple to make std::make_from_tuple
+// SFINAE friendly and also avoid worse diagnostic messages. We still keep the constraints of std::__make_from_tuple_impl
// so that std::__make_from_tuple_impl will have the same advantages when used alone.
#if _LIBCPP_STD_VER >= 20
template
--
GitLab
From 2252c5c42b95dd12dda0c7fb1b2811f943b21949 Mon Sep 17 00:00:00 2001
From: Weaver
Date: Thu, 2 May 2024 11:51:45 +0100
Subject: [PATCH 0032/1014] Revert "[clang][dataflow] Don't propagate result
objects in unevaluated contexts (#90438)"
This reverts commit 597a3150e932a9423c65b5ea4b53dd431aff5865.
Caused test failure on the following buildbot:
https://lab.llvm.org/buildbot/#/builders/216/builds/38446
---
.../FlowSensitive/DataflowEnvironment.cpp | 11 ----
.../Analysis/FlowSensitive/TransferTest.cpp | 52 -------------------
2 files changed, 63 deletions(-)
diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
index cb6c8b2ef107..d79e73440289 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -350,17 +350,6 @@ public:
return RecursiveASTVisitor::TraverseDecl(D);
}
- // Don't traverse expressions in unevaluated contexts, as we don't model
- // fields that are only used in these.
- // Note: The operand of the `noexcept` operator is an unevaluated operand, but
- // nevertheless it appears in the Clang CFG, so we don't exclude it here.
- bool TraverseDecltypeTypeLoc(DecltypeTypeLoc) { return true; }
- bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc) { return true; }
- bool TraverseCXXTypeidExpr(CXXTypeidExpr *) { return true; }
- bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *) {
- return true;
- }
-
bool TraverseBindingDecl(BindingDecl *BD) {
// `RecursiveASTVisitor` doesn't traverse holding variables for
// `BindingDecl`s by itself, so we need to tell it to.
diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index 95d5f569c0c8..301bec32c0cf 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -3331,58 +3331,6 @@ TEST(TransferTest, ResultObjectLocationDontVisitNestedRecordDecl) {
ASTContext &ASTCtx) {});
}
-TEST(TransferTest, ResultObjectLocationDontVisitUnevaluatedContexts) {
- // This is a crash repro.
- // We used to crash because when propagating result objects, we would visit
- // unevaluated contexts, but we don't model fields used only in these.
-
- auto testFunction = [](llvm::StringRef Code, llvm::StringRef TargetFun) {
- runDataflow(
- Code,
- [](const llvm::StringMap> &Results,
- ASTContext &ASTCtx) {},
- LangStandard::lang_gnucxx17,
- /* ApplyBuiltinTransfer= */ true, TargetFun);
- };
-
- std::string Code = R"cc(
- // Definitions needed for `typeid`.
- namespace std {
- class type_info {};
- class bad_typeid {};
- } // namespace std
-
- struct S1 {};
- struct S2 { S1 s1; };
-
- // We test each type of unevaluated context from a different target
- // function. Some types of unevaluated contexts may actually cause the
- // field `s1` to be modeled, and we don't want this to "pollute" the tests
- // for the other unevaluated contexts.
- void decltypeTarget() {
- decltype(S2{}) Dummy;
- }
- void typeofTarget() {
- typeof(S2{}) Dummy;
- }
- void typeidTarget() {
- typeid(S2{});
- }
- void sizeofTarget() {
- sizeof(S2{});
- }
- void noexceptTarget() {
- noexcept(S2{});
- }
- )cc";
-
- testFunction(Code, "decltypeTarget");
- testFunction(Code, "typeofTarget");
- testFunction(Code, "typeidTarget");
- testFunction(Code, "sizeofTarget");
- testFunction(Code, "noexceptTarget");
-}
-
TEST(TransferTest, StaticCast) {
std::string Code = R"(
void target(int Foo) {
--
GitLab
From 981aa6fcf68ffda877f19c8d59003c067cc6ef4b Mon Sep 17 00:00:00 2001
From: Valery Pykhtin
Date: Thu, 2 May 2024 12:59:31 +0200
Subject: [PATCH 0033/1014] [AMDGPU] Fix incorrect stepping in gdb for
amdgcn.end.cf intrinsic. (#83010)
After #73958 gdb.rocm/lane-execution.exp test started to fail due to
incorrect debug location. This is kind of a revert patch.
---
llvm/lib/Target/AMDGPU/SIAnnotateControlFlow.cpp | 8 ++++++--
llvm/test/CodeGen/AMDGPU/si-annotate-dbg-info.ll | 10 +++++-----
2 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/llvm/lib/Target/AMDGPU/SIAnnotateControlFlow.cpp b/llvm/lib/Target/AMDGPU/SIAnnotateControlFlow.cpp
index 58214f30bb8d..08e1d6b87b0d 100644
--- a/llvm/lib/Target/AMDGPU/SIAnnotateControlFlow.cpp
+++ b/llvm/lib/Target/AMDGPU/SIAnnotateControlFlow.cpp
@@ -336,8 +336,12 @@ bool SIAnnotateControlFlow::closeControlFlow(BasicBlock *BB) {
// Split edge to make Def dominate Use
FirstInsertionPt = SplitEdge(DefBB, BB, DT, LI)->getFirstInsertionPt();
}
- IRBuilder<>(FirstInsertionPt->getParent(), FirstInsertionPt)
- .CreateCall(EndCf, {Exec});
+ IRBuilder<> IRB(FirstInsertionPt->getParent(), FirstInsertionPt);
+ // TODO: StructurizeCFG 'Flow' blocks have debug locations from the
+ // condition, for now just avoid copying these DebugLocs so that stepping
+ // out of the then/else block in a debugger doesn't step to the condition.
+ IRB.SetCurrentDebugLocation(DebugLoc());
+ IRB.CreateCall(EndCf, {Exec});
}
return true;
diff --git a/llvm/test/CodeGen/AMDGPU/si-annotate-dbg-info.ll b/llvm/test/CodeGen/AMDGPU/si-annotate-dbg-info.ll
index a7af02017001..a7b4eee84cb9 100644
--- a/llvm/test/CodeGen/AMDGPU/si-annotate-dbg-info.ll
+++ b/llvm/test/CodeGen/AMDGPU/si-annotate-dbg-info.ll
@@ -23,9 +23,9 @@ define amdgpu_ps i32 @if_else(i32 %0) !dbg !5 {
; OPT-NEXT: br label [[FLOW]], !dbg [[DBG16:![0-9]+]]
; OPT: exit:
; OPT-NEXT: [[RET:%.*]] = phi i32 [ [[TMP5]], [[FLOW]] ], [ 42, [[TRUE]] ], !dbg [[DBG17:![0-9]+]]
-; OPT-NEXT: call void @llvm.amdgcn.end.cf.i64(i64 [[TMP8]]), !dbg [[DBG18:![0-9]+]]
+; OPT-NEXT: call void @llvm.amdgcn.end.cf.i64(i64 [[TMP8]])
; OPT-NEXT: tail call void @llvm.dbg.value(metadata i32 [[RET]], metadata [[META11:![0-9]+]], metadata !DIExpression()), !dbg [[DBG17]]
-; OPT-NEXT: ret i32 [[RET]], !dbg [[DBG18]]
+; OPT-NEXT: ret i32 [[RET]], !dbg [[DBG18:![0-9]+]]
;
%c = icmp eq i32 %0, 0, !dbg !13
tail call void @llvm.dbg.value(metadata i1 %c, metadata !9, metadata !DIExpression()), !dbg !13
@@ -65,13 +65,13 @@ define amdgpu_ps void @loop_if_break(i32 %n) !dbg !19 {
; OPT: Flow:
; OPT-NEXT: [[TMP3]] = phi i32 [ [[I_NEXT]], [[LOOP_BODY]] ], [ undef, [[LOOP]] ]
; OPT-NEXT: [[TMP4:%.*]] = phi i1 [ false, [[LOOP_BODY]] ], [ true, [[LOOP]] ]
-; OPT-NEXT: call void @llvm.amdgcn.end.cf.i64(i64 [[TMP2]]), !dbg [[DBG27]]
+; OPT-NEXT: call void @llvm.amdgcn.end.cf.i64(i64 [[TMP2]])
; OPT-NEXT: [[TMP5]] = call i64 @llvm.amdgcn.if.break.i64(i1 [[TMP4]], i64 [[PHI_BROKEN]]), !dbg [[DBG27]]
; OPT-NEXT: [[TMP6:%.*]] = call i1 @llvm.amdgcn.loop.i64(i64 [[TMP5]]), !dbg [[DBG27]]
; OPT-NEXT: br i1 [[TMP6]], label [[EXIT:%.*]], label [[LOOP]], !dbg [[DBG27]]
; OPT: exit:
-; OPT-NEXT: call void @llvm.amdgcn.end.cf.i64(i64 [[TMP5]]), !dbg [[DBG30:![0-9]+]]
-; OPT-NEXT: ret void, !dbg [[DBG30]]
+; OPT-NEXT: call void @llvm.amdgcn.end.cf.i64(i64 [[TMP5]])
+; OPT-NEXT: ret void, !dbg [[DBG30:![0-9]+]]
;
entry:
br label %loop, !dbg !24
--
GitLab
From 779f40c088c8f827b3cb83dd2b68dab1bd05b55a Mon Sep 17 00:00:00 2001
From: Kiran Chandramohan
Date: Thu, 2 May 2024 12:00:49 +0100
Subject: [PATCH 0034/1014] [Flang][OpenMP] Give better errors during parsing
failures (#90480)
Similar to OpenACC handling.
Fixes #90452
---
flang/lib/Parser/openmp-parsers.cpp | 49 +++++++++++---------
flang/test/Parser/OpenMP/fail-construct1.f90 | 5 ++
flang/test/Parser/OpenMP/fail-construct2.f90 | 5 ++
3 files changed, 36 insertions(+), 23 deletions(-)
create mode 100644 flang/test/Parser/OpenMP/fail-construct1.f90
create mode 100644 flang/test/Parser/OpenMP/fail-construct2.f90
diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index eae478416914..48f213794247 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -634,18 +634,20 @@ TYPE_PARSER(
// Declarative constructs
TYPE_PARSER(startOmpLine >>
- sourced(construct(
- Parser{}) ||
- construct(
- Parser{}) ||
- construct(
- Parser{}) ||
- construct(
- Parser{}) ||
- construct(
- Parser{}) ||
- construct(Parser{})) /
- endOmpLine)
+ withMessage("expected OpenMP construct"_err_en_US,
+ sourced(construct(
+ Parser{}) ||
+ construct(
+ Parser{}) ||
+ construct(
+ Parser{}) ||
+ construct(
+ Parser{}) ||
+ construct(
+ Parser{}) ||
+ construct(
+ Parser{})) /
+ endOmpLine))
// Block Construct
TYPE_PARSER(construct(
@@ -681,17 +683,18 @@ TYPE_PARSER(construct(
TYPE_CONTEXT_PARSER("OpenMP construct"_en_US,
startOmpLine >>
- first(construct(Parser{}),
- construct(Parser{}),
- construct(Parser{}),
- // OpenMPBlockConstruct is attempted before
- // OpenMPStandaloneConstruct to resolve !$OMP ORDERED
- construct(Parser{}),
- construct(Parser{}),
- construct(Parser{}),
- construct(Parser{}),
- construct(Parser{}),
- construct(Parser{})))
+ withMessage("expected OpenMP construct"_err_en_US,
+ first(construct(Parser{}),
+ construct(Parser{}),
+ construct(Parser{}),
+ // OpenMPBlockConstruct is attempted before
+ // OpenMPStandaloneConstruct to resolve !$OMP ORDERED
+ construct(Parser{}),
+ construct(Parser{}),
+ construct(Parser{}),
+ construct(Parser{}),
+ construct(Parser{}),
+ construct(Parser{}))))
// END OMP Block directives
TYPE_PARSER(
diff --git a/flang/test/Parser/OpenMP/fail-construct1.f90 b/flang/test/Parser/OpenMP/fail-construct1.f90
new file mode 100644
index 000000000000..f0ee22125cee
--- /dev/null
+++ b/flang/test/Parser/OpenMP/fail-construct1.f90
@@ -0,0 +1,5 @@
+! RUN: not %flang_fc1 -fsyntax-only -fopenmp %s 2>&1 | FileCheck %s
+
+! CHECK: error: expected OpenMP construct
+!$omp parallel
+end
diff --git a/flang/test/Parser/OpenMP/fail-construct2.f90 b/flang/test/Parser/OpenMP/fail-construct2.f90
new file mode 100644
index 000000000000..b7f5736d1329
--- /dev/null
+++ b/flang/test/Parser/OpenMP/fail-construct2.f90
@@ -0,0 +1,5 @@
+! RUN: not %flang_fc1 -fsyntax-only -fopenmp %s 2>&1 | FileCheck %s
+
+! CHECK: error: expected OpenMP construct
+!$omp dummy
+end
--
GitLab
From f2daa32fcaa061daa04546c343198d876e635def Mon Sep 17 00:00:00 2001
From: Christian Kandeler
Date: Thu, 2 May 2024 13:18:59 +0200
Subject: [PATCH 0035/1014] [clangd] Remove potential prefix from enum value
names (#83412)
... when converting unscoped to scoped enums.
With traditional enums, a popular technique to guard against potential
name clashes is to use the enum name as a pseudo-namespace, like this:
enum MyEnum { MyEnumValue1, MyEnumValue2 };
With scoped enums, this makes no sense, making it extremely unlikely
that the user wants to keep such a prefix when modernizing. Therefore,
our tweak now removes it.
---
.../clangd/refactor/tweaks/ScopifyEnum.cpp | 61 ++++++++++++-----
.../unittests/tweaks/ScopifyEnumTests.cpp | 66 +++++++++++++++++--
clang-tools-extra/docs/ReleaseNotes.rst | 3 +
3 files changed, 108 insertions(+), 22 deletions(-)
diff --git a/clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp b/clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp
index e36b3249bc7b..2be2bbc422af 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp
+++ b/clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp
@@ -40,15 +40,12 @@ namespace {
/// void f() { E e1 = EV1; }
///
/// After:
-/// enum class E { EV1, EV2 };
-/// void f() { E e1 = E::EV1; }
+/// enum class E { V1, V2 };
+/// void f() { E e1 = E::V1; }
///
/// Note that the respective project code might not compile anymore
/// if it made use of the now-gone implicit conversion to int.
/// This is out of scope for this tweak.
-///
-/// TODO: In the above example, we could detect that the values
-/// start with the enum name, and remove that prefix.
class ScopifyEnum : public Tweak {
const char *id() const final;
@@ -63,7 +60,8 @@ class ScopifyEnum : public Tweak {
std::function;
llvm::Error addClassKeywordToDeclarations();
llvm::Error scopifyEnumValues();
- llvm::Error scopifyEnumValue(const EnumConstantDecl &CD, StringRef Prefix);
+ llvm::Error scopifyEnumValue(const EnumConstantDecl &CD, StringRef EnumName,
+ bool StripPrefix);
llvm::Expected getContentForFile(StringRef FilePath);
unsigned getOffsetFromPosition(const Position &Pos, StringRef Content) const;
llvm::Error addReplacementForReference(const ReferencesResult::Reference &Ref,
@@ -125,25 +123,45 @@ llvm::Error ScopifyEnum::addClassKeywordToDeclarations() {
}
llvm::Error ScopifyEnum::scopifyEnumValues() {
- std::string PrefixToInsert(D->getName());
- PrefixToInsert += "::";
- for (auto E : D->enumerators()) {
- if (auto Err = scopifyEnumValue(*E, PrefixToInsert))
+ StringRef EnumName(D->getName());
+ bool StripPrefix = true;
+ for (const EnumConstantDecl *E : D->enumerators()) {
+ if (!E->getName().starts_with(EnumName)) {
+ StripPrefix = false;
+ break;
+ }
+ }
+ for (const EnumConstantDecl *E : D->enumerators()) {
+ if (auto Err = scopifyEnumValue(*E, EnumName, StripPrefix))
return Err;
}
return llvm::Error::success();
}
llvm::Error ScopifyEnum::scopifyEnumValue(const EnumConstantDecl &CD,
- StringRef Prefix) {
+ StringRef EnumName,
+ bool StripPrefix) {
for (const auto &Ref :
findReferences(*S->AST, getPosition(CD), 0, S->Index, false)
.References) {
- if (Ref.Attributes & ReferencesResult::Declaration)
+ if (Ref.Attributes & ReferencesResult::Declaration) {
+ if (StripPrefix) {
+ const auto MakeReplacement = [&EnumName](StringRef FilePath,
+ StringRef Content,
+ unsigned Offset) {
+ unsigned Length = EnumName.size();
+ if (Content[Offset + Length] == '_')
+ ++Length;
+ return tooling::Replacement(FilePath, Offset, Length, {});
+ };
+ if (auto Err = addReplacementForReference(Ref, MakeReplacement))
+ return Err;
+ }
continue;
+ }
- const auto MakeReplacement = [&Prefix](StringRef FilePath,
- StringRef Content, unsigned Offset) {
+ const auto MakeReplacement = [&](StringRef FilePath, StringRef Content,
+ unsigned Offset) {
const auto IsAlreadyScoped = [Content, Offset] {
if (Offset < 2)
return false;
@@ -164,9 +182,18 @@ llvm::Error ScopifyEnum::scopifyEnumValue(const EnumConstantDecl &CD,
}
return false;
};
- return IsAlreadyScoped()
- ? tooling::Replacement()
- : tooling::Replacement(FilePath, Offset, 0, Prefix);
+ if (StripPrefix) {
+ const int ExtraLength =
+ Content[Offset + EnumName.size()] == '_' ? 1 : 0;
+ if (IsAlreadyScoped())
+ return tooling::Replacement(FilePath, Offset,
+ EnumName.size() + ExtraLength, {});
+ return tooling::Replacement(FilePath, Offset + EnumName.size(),
+ ExtraLength, "::");
+ }
+ return IsAlreadyScoped() ? tooling::Replacement()
+ : tooling::Replacement(FilePath, Offset, 0,
+ EnumName.str() + "::");
};
if (auto Err = addReplacementForReference(Ref, MakeReplacement))
return Err;
diff --git a/clang-tools-extra/clangd/unittests/tweaks/ScopifyEnumTests.cpp b/clang-tools-extra/clangd/unittests/tweaks/ScopifyEnumTests.cpp
index b5a964a5a26d..5da059faaf5e 100644
--- a/clang-tools-extra/clangd/unittests/tweaks/ScopifyEnumTests.cpp
+++ b/clang-tools-extra/clangd/unittests/tweaks/ScopifyEnumTests.cpp
@@ -26,7 +26,7 @@ enum ^E;
)cpp");
}
-TEST_F(ScopifyEnumTest, ApplyTest) {
+TEST_F(ScopifyEnumTest, ApplyTestWithPrefix) {
std::string Original = R"cpp(
enum ^E { EV1, EV2, EV3 };
enum E;
@@ -39,13 +39,69 @@ E func(E in)
}
)cpp";
std::string Expected = R"cpp(
-enum class E { EV1, EV2, EV3 };
+enum class E { V1, V2, V3 };
enum class E;
E func(E in)
{
- E out = E::EV1;
- if (in == E::EV2)
- out = E::EV3;
+ E out = E::V1;
+ if (in == E::V2)
+ out = E::V3;
+ return out;
+}
+)cpp";
+ FileName = "Test.cpp";
+ SCOPED_TRACE(Original);
+ EXPECT_EQ(apply(Original), Expected);
+}
+
+TEST_F(ScopifyEnumTest, ApplyTestWithPrefixAndUnderscore) {
+ std::string Original = R"cpp(
+enum ^E { E_V1, E_V2, E_V3 };
+enum E;
+E func(E in)
+{
+ E out = E_V1;
+ if (in == E_V2)
+ out = E::E_V3;
+ return out;
+}
+)cpp";
+ std::string Expected = R"cpp(
+enum class E { V1, V2, V3 };
+enum class E;
+E func(E in)
+{
+ E out = E::V1;
+ if (in == E::V2)
+ out = E::V3;
+ return out;
+}
+)cpp";
+ FileName = "Test.cpp";
+ SCOPED_TRACE(Original);
+ EXPECT_EQ(apply(Original), Expected);
+}
+
+TEST_F(ScopifyEnumTest, ApplyTestWithoutPrefix) {
+ std::string Original = R"cpp(
+enum ^E { V1, V2, V3 };
+enum E;
+E func(E in)
+{
+ E out = V1;
+ if (in == V2)
+ out = E::V3;
+ return out;
+}
+)cpp";
+ std::string Expected = R"cpp(
+enum class E { V1, V2, V3 };
+enum class E;
+E func(E in)
+{
+ E out = E::V1;
+ if (in == E::V2)
+ out = E::V3;
return out;
}
)cpp";
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index d59da3a61b7b..fb7c45aa759c 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -69,6 +69,9 @@ Code completion
Code actions
^^^^^^^^^^^^
+- The tweak for turning unscoped into scoped enums now removes redundant prefixes
+ from the enum values.
+
Signature help
^^^^^^^^^^^^^^
--
GitLab
From 5d9889a6c6c97c92380c8eee48eaa35067f63766 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?=
Date: Tue, 30 Apr 2024 19:49:58 +0200
Subject: [PATCH 0036/1014] [clang][Interp] Fix zero-initializing records with
non-trivial ctors
We need to do the zero-init and then subsequently call the constructor.
---
clang/lib/AST/Interp/ByteCodeExprGen.cpp | 12 +++++++++---
clang/test/AST/Interp/records.cpp | 10 +++++++---
2 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index f1a51e81a92c..2dfa726cc752 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -2095,10 +2095,16 @@ bool ByteCodeExprGen::VisitCXXConstructExpr(
if (T->isRecordType()) {
const CXXConstructorDecl *Ctor = E->getConstructor();
- // Trivial zero initialization.
- if (E->requiresZeroInitialization() && Ctor->isTrivial()) {
+ // Zero initialization.
+ if (E->requiresZeroInitialization()) {
const Record *R = getRecord(E->getType());
- return this->visitZeroRecordInitializer(R, E);
+
+ if (!this->visitZeroRecordInitializer(R, E))
+ return false;
+
+ // If the constructor is trivial anyway, we're done.
+ if (Ctor->isTrivial())
+ return true;
}
const Function *Func = getFunction(Ctor);
diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp
index 771e5adfca34..26efd6896a9f 100644
--- a/clang/test/AST/Interp/records.cpp
+++ b/clang/test/AST/Interp/records.cpp
@@ -999,10 +999,9 @@ namespace TemporaryObjectExpr {
F f{12};
};
constexpr int foo(S x) {
- return x.a; // expected-note {{read of uninitialized object}}
+ return x.a;
}
- static_assert(foo(S()) == 0, ""); // expected-error {{not an integral constant expression}} \
- // expected-note {{in call to}}
+ static_assert(foo(S()) == 0, "");
};
#endif
}
@@ -1425,6 +1424,11 @@ namespace ZeroInit {
};
constexpr S3 s3d; // both-error {{default initialization of an object of const type 'const S3' without a user-provided default constructor}}
static_assert(s3d.n == 0, "");
+
+ struct P {
+ int a = 10;
+ };
+ static_assert(P().a == 10, "");
}
namespace {
--
GitLab
From 35e73e7cc8c4078ea25151da3958a114591bf700 Mon Sep 17 00:00:00 2001
From: Simon Pilgrim
Date: Thu, 2 May 2024 12:28:59 +0100
Subject: [PATCH 0037/1014] [X86] Regenerate memcpy-scoped-aa.ll
---
llvm/test/CodeGen/X86/memcpy-scoped-aa.ll | 94 +++++++++++++++++------
1 file changed, 70 insertions(+), 24 deletions(-)
diff --git a/llvm/test/CodeGen/X86/memcpy-scoped-aa.ll b/llvm/test/CodeGen/X86/memcpy-scoped-aa.ll
index 7765297dc673..d3b86786a630 100644
--- a/llvm/test/CodeGen/X86/memcpy-scoped-aa.ll
+++ b/llvm/test/CodeGen/X86/memcpy-scoped-aa.ll
@@ -1,3 +1,4 @@
+; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 4
; RUN: llc -mtriple=x86_64-linux-gnu -stop-after=finalize-isel -o - %s | FileCheck --check-prefix=MIR %s
; Ensure that the scoped AA is attached on loads/stores lowered from mem ops.
@@ -10,12 +11,21 @@
; MIR-DAG: ![[SET0:[0-9]+]] = !{![[SCOPE0]]}
; MIR-DAG: ![[SET1:[0-9]+]] = !{![[SCOPE1]]}
-; MIR-LABEL: name: test_memcpy
-; MIR: %2:gr64 = MOV64rm %0, 1, $noreg, 16, $noreg :: (load (s64) from %ir.p1, align 4, !alias.scope ![[SET0]], !noalias ![[SET1]])
-; MIR-NEXT: %3:gr64 = MOV64rm %0, 1, $noreg, 24, $noreg :: (load (s64) from %ir.p1 + 8, align 4, !alias.scope ![[SET0]], !noalias ![[SET1]])
-; MIR-NEXT: MOV64mr %0, 1, $noreg, 8, $noreg, killed %3 :: (store (s64) into %ir.p0 + 8, align 4, !alias.scope ![[SET0]], !noalias ![[SET1]])
-; MIR-NEXT: MOV64mr %0, 1, $noreg, 0, $noreg, killed %2 :: (store (s64) into %ir.p0, align 4, !alias.scope ![[SET0]], !noalias ![[SET1]])
define i32 @test_memcpy(ptr nocapture %p, ptr nocapture readonly %q) {
+ ; MIR-LABEL: name: test_memcpy
+ ; MIR: bb.0 (%ir-block.0):
+ ; MIR-NEXT: liveins: $rdi, $rsi
+ ; MIR-NEXT: {{ $}}
+ ; MIR-NEXT: [[COPY:%[0-9]+]]:gr64 = COPY $rsi
+ ; MIR-NEXT: [[COPY1:%[0-9]+]]:gr64 = COPY $rdi
+ ; MIR-NEXT: [[MOV64rm:%[0-9]+]]:gr64 = MOV64rm [[COPY1]], 1, $noreg, 16, $noreg :: (load (s64) from %ir.p1, align 4, !alias.scope !0, !noalias !3)
+ ; MIR-NEXT: [[MOV64rm1:%[0-9]+]]:gr64 = MOV64rm [[COPY1]], 1, $noreg, 24, $noreg :: (load (s64) from %ir.p1 + 8, align 4, !alias.scope !0, !noalias !3)
+ ; MIR-NEXT: MOV64mr [[COPY1]], 1, $noreg, 8, $noreg, killed [[MOV64rm1]] :: (store (s64) into %ir.p0 + 8, align 4, !alias.scope !0, !noalias !3)
+ ; MIR-NEXT: MOV64mr [[COPY1]], 1, $noreg, 0, $noreg, killed [[MOV64rm]] :: (store (s64) into %ir.p0, align 4, !alias.scope !0, !noalias !3)
+ ; MIR-NEXT: [[MOV32rm:%[0-9]+]]:gr32 = MOV32rm [[COPY]], 1, $noreg, 0, $noreg :: (load (s32) from %ir.q, !alias.scope !3, !noalias !0)
+ ; MIR-NEXT: [[ADD32rm:%[0-9]+]]:gr32 = ADD32rm [[MOV32rm]], [[COPY]], 1, $noreg, 4, $noreg, implicit-def dead $eflags :: (load (s32) from %ir.q1, !alias.scope !3, !noalias !0)
+ ; MIR-NEXT: $eax = COPY [[ADD32rm]]
+ ; MIR-NEXT: RET 0, $eax
%p0 = bitcast ptr %p to ptr
%add.ptr = getelementptr inbounds i32, ptr %p, i64 4
%p1 = bitcast ptr %add.ptr to ptr
@@ -27,12 +37,21 @@ define i32 @test_memcpy(ptr nocapture %p, ptr nocapture readonly %q) {
ret i32 %add
}
-; MIR-LABEL: name: test_memcpy_inline
-; MIR: %2:gr64 = MOV64rm %0, 1, $noreg, 16, $noreg :: (load (s64) from %ir.p1, align 4, !alias.scope ![[SET0]], !noalias ![[SET1]])
-; MIR-NEXT: %3:gr64 = MOV64rm %0, 1, $noreg, 24, $noreg :: (load (s64) from %ir.p1 + 8, align 4, !alias.scope ![[SET0]], !noalias ![[SET1]])
-; MIR-NEXT: MOV64mr %0, 1, $noreg, 8, $noreg, killed %3 :: (store (s64) into %ir.p0 + 8, align 4, !alias.scope ![[SET0]], !noalias ![[SET1]])
-; MIR-NEXT: MOV64mr %0, 1, $noreg, 0, $noreg, killed %2 :: (store (s64) into %ir.p0, align 4, !alias.scope ![[SET0]], !noalias ![[SET1]])
define i32 @test_memcpy_inline(ptr nocapture %p, ptr nocapture readonly %q) {
+ ; MIR-LABEL: name: test_memcpy_inline
+ ; MIR: bb.0 (%ir-block.0):
+ ; MIR-NEXT: liveins: $rdi, $rsi
+ ; MIR-NEXT: {{ $}}
+ ; MIR-NEXT: [[COPY:%[0-9]+]]:gr64 = COPY $rsi
+ ; MIR-NEXT: [[COPY1:%[0-9]+]]:gr64 = COPY $rdi
+ ; MIR-NEXT: [[MOV64rm:%[0-9]+]]:gr64 = MOV64rm [[COPY1]], 1, $noreg, 16, $noreg :: (load (s64) from %ir.p1, align 4, !alias.scope !0, !noalias !3)
+ ; MIR-NEXT: [[MOV64rm1:%[0-9]+]]:gr64 = MOV64rm [[COPY1]], 1, $noreg, 24, $noreg :: (load (s64) from %ir.p1 + 8, align 4, !alias.scope !0, !noalias !3)
+ ; MIR-NEXT: MOV64mr [[COPY1]], 1, $noreg, 8, $noreg, killed [[MOV64rm1]] :: (store (s64) into %ir.p0 + 8, align 4, !alias.scope !0, !noalias !3)
+ ; MIR-NEXT: MOV64mr [[COPY1]], 1, $noreg, 0, $noreg, killed [[MOV64rm]] :: (store (s64) into %ir.p0, align 4, !alias.scope !0, !noalias !3)
+ ; MIR-NEXT: [[MOV32rm:%[0-9]+]]:gr32 = MOV32rm [[COPY]], 1, $noreg, 0, $noreg :: (load (s32) from %ir.q, !alias.scope !3, !noalias !0)
+ ; MIR-NEXT: [[ADD32rm:%[0-9]+]]:gr32 = ADD32rm [[MOV32rm]], [[COPY]], 1, $noreg, 4, $noreg, implicit-def dead $eflags :: (load (s32) from %ir.q1, !alias.scope !3, !noalias !0)
+ ; MIR-NEXT: $eax = COPY [[ADD32rm]]
+ ; MIR-NEXT: RET 0, $eax
%p0 = bitcast ptr %p to ptr
%add.ptr = getelementptr inbounds i32, ptr %p, i64 4
%p1 = bitcast ptr %add.ptr to ptr
@@ -44,12 +63,21 @@ define i32 @test_memcpy_inline(ptr nocapture %p, ptr nocapture readonly %q) {
ret i32 %add
}
-; MIR-LABEL: name: test_memmove
-; MIR: %2:gr64 = MOV64rm %0, 1, $noreg, 16, $noreg :: (load (s64) from %ir.p1, align 4, !alias.scope ![[SET0]], !noalias ![[SET1]])
-; MIR-NEXT: %3:gr64 = MOV64rm %0, 1, $noreg, 24, $noreg :: (load (s64) from %ir.p1 + 8, align 4, !alias.scope ![[SET0]], !noalias ![[SET1]])
-; MIR-NEXT: MOV64mr %0, 1, $noreg, 0, $noreg, killed %2 :: (store (s64) into %ir.p0, align 4, !alias.scope ![[SET0]], !noalias ![[SET1]])
-; MIR-NEXT: MOV64mr %0, 1, $noreg, 8, $noreg, killed %3 :: (store (s64) into %ir.p0 + 8, align 4, !alias.scope ![[SET0]], !noalias ![[SET1]])
define i32 @test_memmove(ptr nocapture %p, ptr nocapture readonly %q) {
+ ; MIR-LABEL: name: test_memmove
+ ; MIR: bb.0 (%ir-block.0):
+ ; MIR-NEXT: liveins: $rdi, $rsi
+ ; MIR-NEXT: {{ $}}
+ ; MIR-NEXT: [[COPY:%[0-9]+]]:gr64 = COPY $rsi
+ ; MIR-NEXT: [[COPY1:%[0-9]+]]:gr64 = COPY $rdi
+ ; MIR-NEXT: [[MOV64rm:%[0-9]+]]:gr64 = MOV64rm [[COPY1]], 1, $noreg, 16, $noreg :: (load (s64) from %ir.p1, align 4, !alias.scope !0, !noalias !3)
+ ; MIR-NEXT: [[MOV64rm1:%[0-9]+]]:gr64 = MOV64rm [[COPY1]], 1, $noreg, 24, $noreg :: (load (s64) from %ir.p1 + 8, align 4, !alias.scope !0, !noalias !3)
+ ; MIR-NEXT: MOV64mr [[COPY1]], 1, $noreg, 0, $noreg, killed [[MOV64rm]] :: (store (s64) into %ir.p0, align 4, !alias.scope !0, !noalias !3)
+ ; MIR-NEXT: MOV64mr [[COPY1]], 1, $noreg, 8, $noreg, killed [[MOV64rm1]] :: (store (s64) into %ir.p0 + 8, align 4, !alias.scope !0, !noalias !3)
+ ; MIR-NEXT: [[MOV32rm:%[0-9]+]]:gr32 = MOV32rm [[COPY]], 1, $noreg, 0, $noreg :: (load (s32) from %ir.q, !alias.scope !3, !noalias !0)
+ ; MIR-NEXT: [[ADD32rm:%[0-9]+]]:gr32 = ADD32rm [[MOV32rm]], [[COPY]], 1, $noreg, 4, $noreg, implicit-def dead $eflags :: (load (s32) from %ir.q1, !alias.scope !3, !noalias !0)
+ ; MIR-NEXT: $eax = COPY [[ADD32rm]]
+ ; MIR-NEXT: RET 0, $eax
%p0 = bitcast ptr %p to ptr
%add.ptr = getelementptr inbounds i32, ptr %p, i64 4
%p1 = bitcast ptr %add.ptr to ptr
@@ -61,11 +89,20 @@ define i32 @test_memmove(ptr nocapture %p, ptr nocapture readonly %q) {
ret i32 %add
}
-; MIR-LABEL: name: test_memset
-; MIR: %2:gr64 = MOV64ri -6148914691236517206
-; MIR-NEXT: MOV64mr %0, 1, $noreg, 8, $noreg, %2 :: (store (s64) into %ir.p0 + 8, align 4, !alias.scope ![[SET0]], !noalias ![[SET1]])
-; MIR-NEXT: MOV64mr %0, 1, $noreg, 0, $noreg, %2 :: (store (s64) into %ir.p0, align 4, !alias.scope ![[SET0]], !noalias ![[SET1]])
define i32 @test_memset(ptr nocapture %p, ptr nocapture readonly %q) {
+ ; MIR-LABEL: name: test_memset
+ ; MIR: bb.0 (%ir-block.0):
+ ; MIR-NEXT: liveins: $rdi, $rsi
+ ; MIR-NEXT: {{ $}}
+ ; MIR-NEXT: [[COPY:%[0-9]+]]:gr64 = COPY $rsi
+ ; MIR-NEXT: [[COPY1:%[0-9]+]]:gr64 = COPY $rdi
+ ; MIR-NEXT: [[MOV64ri:%[0-9]+]]:gr64 = MOV64ri -6148914691236517206
+ ; MIR-NEXT: MOV64mr [[COPY1]], 1, $noreg, 8, $noreg, [[MOV64ri]] :: (store (s64) into %ir.p0 + 8, align 4, !alias.scope !0, !noalias !3)
+ ; MIR-NEXT: MOV64mr [[COPY1]], 1, $noreg, 0, $noreg, [[MOV64ri]] :: (store (s64) into %ir.p0, align 4, !alias.scope !0, !noalias !3)
+ ; MIR-NEXT: [[MOV32rm:%[0-9]+]]:gr32 = MOV32rm [[COPY]], 1, $noreg, 0, $noreg :: (load (s32) from %ir.q, !alias.scope !3, !noalias !0)
+ ; MIR-NEXT: [[ADD32rm:%[0-9]+]]:gr32 = ADD32rm [[MOV32rm]], [[COPY]], 1, $noreg, 4, $noreg, implicit-def dead $eflags :: (load (s32) from %ir.q1, !alias.scope !3, !noalias !0)
+ ; MIR-NEXT: $eax = COPY [[ADD32rm]]
+ ; MIR-NEXT: RET 0, $eax
%p0 = bitcast ptr %p to ptr
tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 4 dereferenceable(16) %p0, i8 170, i64 16, i1 false), !alias.scope !2, !noalias !4
%v0 = load i32, ptr %q, align 4, !alias.scope !4, !noalias !2
@@ -75,12 +112,21 @@ define i32 @test_memset(ptr nocapture %p, ptr nocapture readonly %q) {
ret i32 %add
}
-; MIR-LABEL: name: test_mempcpy
-; MIR: %2:gr64 = MOV64rm %0, 1, $noreg, 16, $noreg :: (load (s64) from %ir.p1, align 1, !alias.scope ![[SET0]], !noalias ![[SET1]])
-; MIR-NEXT: %3:gr64 = MOV64rm %0, 1, $noreg, 24, $noreg :: (load (s64) from %ir.p1 + 8, align 1, !alias.scope ![[SET0]], !noalias ![[SET1]])
-; MIR-NEXT: MOV64mr %0, 1, $noreg, 8, $noreg, killed %3 :: (store (s64) into %ir.p0 + 8, align 1, !alias.scope ![[SET0]], !noalias ![[SET1]])
-; MIR-NEXT: MOV64mr %0, 1, $noreg, 0, $noreg, killed %2 :: (store (s64) into %ir.p0, align 1, !alias.scope ![[SET0]], !noalias ![[SET1]])
define i32 @test_mempcpy(ptr nocapture %p, ptr nocapture readonly %q) {
+ ; MIR-LABEL: name: test_mempcpy
+ ; MIR: bb.0 (%ir-block.0):
+ ; MIR-NEXT: liveins: $rdi, $rsi
+ ; MIR-NEXT: {{ $}}
+ ; MIR-NEXT: [[COPY:%[0-9]+]]:gr64 = COPY $rsi
+ ; MIR-NEXT: [[COPY1:%[0-9]+]]:gr64 = COPY $rdi
+ ; MIR-NEXT: [[MOV64rm:%[0-9]+]]:gr64 = MOV64rm [[COPY1]], 1, $noreg, 16, $noreg :: (load (s64) from %ir.p1, align 1, !alias.scope !0, !noalias !3)
+ ; MIR-NEXT: [[MOV64rm1:%[0-9]+]]:gr64 = MOV64rm [[COPY1]], 1, $noreg, 24, $noreg :: (load (s64) from %ir.p1 + 8, align 1, !alias.scope !0, !noalias !3)
+ ; MIR-NEXT: MOV64mr [[COPY1]], 1, $noreg, 8, $noreg, killed [[MOV64rm1]] :: (store (s64) into %ir.p0 + 8, align 1, !alias.scope !0, !noalias !3)
+ ; MIR-NEXT: MOV64mr [[COPY1]], 1, $noreg, 0, $noreg, killed [[MOV64rm]] :: (store (s64) into %ir.p0, align 1, !alias.scope !0, !noalias !3)
+ ; MIR-NEXT: [[MOV32rm:%[0-9]+]]:gr32 = MOV32rm [[COPY]], 1, $noreg, 0, $noreg :: (load (s32) from %ir.q, !alias.scope !3, !noalias !0)
+ ; MIR-NEXT: [[ADD32rm:%[0-9]+]]:gr32 = ADD32rm [[MOV32rm]], [[COPY]], 1, $noreg, 4, $noreg, implicit-def dead $eflags :: (load (s32) from %ir.q1, !alias.scope !3, !noalias !0)
+ ; MIR-NEXT: $eax = COPY [[ADD32rm]]
+ ; MIR-NEXT: RET 0, $eax
%p0 = bitcast ptr %p to ptr
%add.ptr = getelementptr inbounds i32, ptr %p, i64 4
%p1 = bitcast ptr %add.ptr to ptr
--
GitLab
From b6d24cb018777d8e54e629114abbaadfefd95d8a Mon Sep 17 00:00:00 2001
From: Matt Arsenault
Date: Thu, 2 May 2024 13:38:37 +0200
Subject: [PATCH 0038/1014] DAG: Implement softening for fp atomic load
(#90839)
---
.../SelectionDAG/LegalizeFloatTypes.cpp | 21 +++++++++++++++++++
llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h | 1 +
2 files changed, 22 insertions(+)
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp
index ed838ae8775c..c04ca5ddc409 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp
@@ -134,6 +134,7 @@ void DAGTypeLegalizer::SoftenFloatResult(SDNode *N, unsigned ResNo) {
case ISD::STRICT_FTRUNC:
case ISD::FTRUNC: R = SoftenFloatRes_FTRUNC(N); break;
case ISD::LOAD: R = SoftenFloatRes_LOAD(N); break;
+ case ISD::ATOMIC_LOAD: R = SoftenFloatRes_ATOMIC_LOAD(N); break;
case ISD::ATOMIC_SWAP: R = BitcastToInt_ATOMIC_SWAP(N); break;
case ISD::SELECT: R = SoftenFloatRes_SELECT(N); break;
case ISD::SELECT_CC: R = SoftenFloatRes_SELECT_CC(N); break;
@@ -815,6 +816,26 @@ SDValue DAGTypeLegalizer::SoftenFloatRes_LOAD(SDNode *N) {
return BitConvertToInteger(ExtendNode);
}
+SDValue DAGTypeLegalizer::SoftenFloatRes_ATOMIC_LOAD(SDNode *N) {
+ AtomicSDNode *L = cast(N);
+ EVT VT = N->getValueType(0);
+ EVT NVT = TLI.getTypeToTransformTo(*DAG.getContext(), VT);
+ SDLoc dl(N);
+
+ if (L->getExtensionType() == ISD::NON_EXTLOAD) {
+ SDValue NewL =
+ DAG.getAtomic(ISD::ATOMIC_LOAD, dl, NVT, DAG.getVTList(NVT, MVT::Other),
+ {L->getChain(), L->getBasePtr()}, L->getMemOperand());
+
+ // Legalized the chain result - switch anything that used the old chain to
+ // use the new one.
+ ReplaceValueWith(SDValue(N, 1), NewL.getValue(1));
+ return NewL;
+ }
+
+ report_fatal_error("softening fp extending atomic load not handled");
+}
+
SDValue DAGTypeLegalizer::SoftenFloatRes_SELECT(SDNode *N) {
SDValue LHS = GetSoftenedFloat(N->getOperand(1));
SDValue RHS = GetSoftenedFloat(N->getOperand(2));
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
index 617a4dd25d2b..0252e3d6febc 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
@@ -569,6 +569,7 @@ private:
SDValue SoftenFloatRes_FSUB(SDNode *N);
SDValue SoftenFloatRes_FTRUNC(SDNode *N);
SDValue SoftenFloatRes_LOAD(SDNode *N);
+ SDValue SoftenFloatRes_ATOMIC_LOAD(SDNode *N);
SDValue SoftenFloatRes_SELECT(SDNode *N);
SDValue SoftenFloatRes_SELECT_CC(SDNode *N);
SDValue SoftenFloatRes_UNDEF(SDNode *N);
--
GitLab
From e5fb6564358f10c01d7533f2f805eedd7d663417 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?=
Date: Wed, 1 May 2024 10:20:47 +0200
Subject: [PATCH 0039/1014] [clang][Interp] Handle RecoveryExprs
Instead of checking containsErrors() for every expression, just handle
RecoveryExprs directly.
---
clang/lib/AST/Interp/ByteCodeExprGen.cpp | 14 +++++---------
clang/lib/AST/Interp/ByteCodeExprGen.h | 1 +
2 files changed, 6 insertions(+), 9 deletions(-)
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 2dfa726cc752..b096d4ddd882 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -2487,10 +2487,12 @@ bool ByteCodeExprGen::VisitPackIndexingExpr(
return this->delegate(E->getSelectedExpr());
}
-template bool ByteCodeExprGen::discard(const Expr *E) {
- if (E->containsErrors())
- return false;
+template
+bool ByteCodeExprGen::VisitRecoveryExpr(const RecoveryExpr *E) {
+ return this->emitError(E);
+}
+template bool ByteCodeExprGen::discard(const Expr *E) {
OptionScope Scope(this, /*NewDiscardResult=*/true,
/*NewInitializing=*/false);
return this->Visit(E);
@@ -2498,9 +2500,6 @@ template bool ByteCodeExprGen::discard(const Expr *E) {
template
bool ByteCodeExprGen::delegate(const Expr *E) {
- if (E->containsErrors())
- return this->emitError(E);
-
// We're basically doing:
// OptionScope Scope(this, DicardResult, Initializing);
// but that's unnecessary of course.
@@ -2508,9 +2507,6 @@ bool ByteCodeExprGen::delegate(const Expr *E) {
}
template bool ByteCodeExprGen::visit(const Expr *E) {
- if (E->containsErrors())
- return this->emitError(E);
-
if (E->getType()->isVoidType())
return this->discard(E);
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h
index a89e37c67aa6..0cd8cf1cb397 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.h
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -121,6 +121,7 @@ public:
bool VisitCXXRewrittenBinaryOperator(const CXXRewrittenBinaryOperator *E);
bool VisitPseudoObjectExpr(const PseudoObjectExpr *E);
bool VisitPackIndexingExpr(const PackIndexingExpr *E);
+ bool VisitRecoveryExpr(const RecoveryExpr *E);
protected:
bool visitExpr(const Expr *E) override;
--
GitLab
From 3d8a44d542b15ac9bc21f9fd3494f1649fca1aa9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?=
Date: Thu, 2 May 2024 05:58:20 +0200
Subject: [PATCH 0040/1014] [clang][Interp][NFC] Refactor if condition
Move the declaration into the condition.
---
clang/lib/AST/Interp/InterpFrame.cpp | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/clang/lib/AST/Interp/InterpFrame.cpp b/clang/lib/AST/Interp/InterpFrame.cpp
index ba957546473e..515b1f5fde1a 100644
--- a/clang/lib/AST/Interp/InterpFrame.cpp
+++ b/clang/lib/AST/Interp/InterpFrame.cpp
@@ -209,10 +209,8 @@ Pointer InterpFrame::getLocalPointer(unsigned Offset) const {
Pointer InterpFrame::getParamPointer(unsigned Off) {
// Return the block if it was created previously.
- auto Pt = Params.find(Off);
- if (Pt != Params.end()) {
+ if (auto Pt = Params.find(Off); Pt != Params.end())
return Pointer(reinterpret_cast(Pt->second.get()));
- }
// Allocate memory to store the parameter and the block metadata.
const auto &Desc = Func->getParamDescriptor(Off);
--
GitLab
From de04e6cd90b891215f1dfc83ec886d037a7c2ed0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?=
Date: Thu, 2 May 2024 06:45:59 +0200
Subject: [PATCH 0041/1014] [clang][Interp][NFC] Save source location of
evaluating expression
We don't have a source location to point to when the evaluation
of a destructor of a temporary created via an expression fails.
---
clang/lib/AST/Interp/EvalEmitter.cpp | 1 +
clang/lib/AST/Interp/InterpFrame.cpp | 5 ++++-
clang/lib/AST/Interp/InterpState.h | 4 ++++
3 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/clang/lib/AST/Interp/EvalEmitter.cpp b/clang/lib/AST/Interp/EvalEmitter.cpp
index d764b4b6f6d1..388c3612f292 100644
--- a/clang/lib/AST/Interp/EvalEmitter.cpp
+++ b/clang/lib/AST/Interp/EvalEmitter.cpp
@@ -34,6 +34,7 @@ EvalEmitter::~EvalEmitter() {
EvaluationResult EvalEmitter::interpretExpr(const Expr *E,
bool ConvertResultToRValue) {
+ S.setEvalLocation(E->getExprLoc());
this->ConvertResultToRValue = ConvertResultToRValue;
EvalResult.setSource(E);
diff --git a/clang/lib/AST/Interp/InterpFrame.cpp b/clang/lib/AST/Interp/InterpFrame.cpp
index 515b1f5fde1a..cf296b4e64f9 100644
--- a/clang/lib/AST/Interp/InterpFrame.cpp
+++ b/clang/lib/AST/Interp/InterpFrame.cpp
@@ -191,8 +191,11 @@ Frame *InterpFrame::getCaller() const {
}
SourceRange InterpFrame::getCallRange() const {
- if (!Caller->Func)
+ if (!Caller->Func) {
+ if (S.EvalLocation.isValid())
+ return S.EvalLocation;
return S.getRange(nullptr, {});
+ }
return S.getRange(Caller->Func, RetPC - sizeof(uintptr_t));
}
diff --git a/clang/lib/AST/Interp/InterpState.h b/clang/lib/AST/Interp/InterpState.h
index c17cfad11b1e..d483c60c58e2 100644
--- a/clang/lib/AST/Interp/InterpState.h
+++ b/clang/lib/AST/Interp/InterpState.h
@@ -98,6 +98,8 @@ public:
Context &getContext() const { return Ctx; }
+ void setEvalLocation(SourceLocation SL) { this->EvalLocation = SL; }
+
private:
/// AST Walker state.
State &Parent;
@@ -115,6 +117,8 @@ public:
Context &Ctx;
/// The current frame.
InterpFrame *Current = nullptr;
+ /// Source location of the evaluating expression
+ SourceLocation EvalLocation;
};
} // namespace interp
--
GitLab
From 1c7673b91d4d3bab4e296f5c67751d3879fb21a2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?=
Date: Thu, 2 May 2024 11:49:15 +0200
Subject: [PATCH 0042/1014] [clang][Interp] Fix locals created in
ExprWithCleanups
Save the declaration we're creating a scope for (if applicable),
so we can attach a local temporary variable to the right scope.
---
clang/lib/AST/Interp/ByteCodeExprGen.cpp | 60 +++++++++++-------------
clang/lib/AST/Interp/ByteCodeExprGen.h | 36 +++++++++-----
clang/test/AST/Interp/records.cpp | 15 ++++++
3 files changed, 67 insertions(+), 44 deletions(-)
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index b096d4ddd882..30627e6300ca 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -29,15 +29,11 @@ namespace interp {
template class DeclScope final : public VariableScope {
public:
DeclScope(ByteCodeExprGen *Ctx, const ValueDecl *VD)
- : VariableScope(Ctx), Scope(Ctx->P, VD),
+ : VariableScope(Ctx, nullptr), Scope(Ctx->P, VD),
OldGlobalDecl(Ctx->GlobalDecl) {
Ctx->GlobalDecl = Context::shouldBeGloballyIndexed(VD);
}
- void addExtended(const Scope::Local &Local) override {
- return this->addLocal(Local);
- }
-
~DeclScope() { this->Ctx->GlobalDecl = OldGlobalDecl; }
private:
@@ -85,8 +81,7 @@ bool ByteCodeExprGen::VisitCastExpr(const CastExpr *CE) {
std::optional SubExprT = classify(SubExpr->getType());
// Prepare storage for the result.
if (!Initializing && !SubExprT) {
- std::optional LocalIndex =
- allocateLocal(SubExpr, /*IsExtended=*/false);
+ std::optional LocalIndex = allocateLocal(SubExpr);
if (!LocalIndex)
return false;
if (!this->emitGetPtrLocal(*LocalIndex, CE))
@@ -362,8 +357,7 @@ bool ByteCodeExprGen::VisitCastExpr(const CastExpr *CE) {
// We're creating a complex value here, so we need to
// allocate storage for it.
if (!Initializing) {
- std::optional LocalIndex =
- allocateLocal(CE, /*IsExtended=*/true);
+ std::optional LocalIndex = allocateLocal(CE);
if (!LocalIndex)
return false;
if (!this->emitGetPtrLocal(*LocalIndex, CE))
@@ -390,8 +384,7 @@ bool ByteCodeExprGen::VisitCastExpr(const CastExpr *CE) {
return this->discard(SubExpr);
if (!Initializing) {
- std::optional LocalIndex =
- allocateLocal(CE, /*IsExtended=*/true);
+ std::optional LocalIndex = allocateLocal(CE);
if (!LocalIndex)
return false;
if (!this->emitGetPtrLocal(*LocalIndex, CE))
@@ -492,7 +485,7 @@ bool ByteCodeExprGen::VisitImaginaryLiteral(
return true;
if (!Initializing) {
- std::optional LocalIndex = allocateLocal(E, /*IsExtended=*/false);
+ std::optional LocalIndex = allocateLocal(E);
if (!LocalIndex)
return false;
if (!this->emitGetPtrLocal(*LocalIndex, E))
@@ -561,7 +554,7 @@ bool ByteCodeExprGen::VisitBinaryOperator(const BinaryOperator *BO) {
// We need a temporary variable holding our return value.
if (!Initializing) {
- std::optional ResultIndex = this->allocateLocal(BO, false);
+ std::optional ResultIndex = this->allocateLocal(BO);
if (!this->emitGetPtrLocal(*ResultIndex, BO))
return false;
}
@@ -784,7 +777,7 @@ template
bool ByteCodeExprGen::VisitComplexBinOp(const BinaryOperator *E) {
// Prepare storage for result.
if (!Initializing) {
- std::optional LocalIndex = allocateLocal(E, /*IsExtended=*/false);
+ std::optional LocalIndex = allocateLocal(E);
if (!LocalIndex)
return false;
if (!this->emitGetPtrLocal(*LocalIndex, E))
@@ -1841,11 +1834,12 @@ bool ByteCodeExprGen::VisitCompoundAssignOperator(
template
bool ByteCodeExprGen::VisitExprWithCleanups(
const ExprWithCleanups *E) {
+ ExprScope ES(this);
const Expr *SubExpr = E->getSubExpr();
assert(E->getNumObjects() == 0 && "TODO: Implement cleanups");
- return this->delegate(SubExpr);
+ return this->delegate(SubExpr) && ES.destroyLocals();
}
template
@@ -1910,9 +1904,8 @@ bool ByteCodeExprGen::VisitMaterializeTemporaryExpr(
return this->emitGetPtrLocal(LocalIndex, E);
} else {
const Expr *Inner = E->getSubExpr()->skipRValueSubobjectAdjustments();
-
if (std::optional LocalIndex =
- allocateLocal(Inner, /*IsExtended=*/true)) {
+ allocateLocal(Inner, E->getExtendingDecl())) {
if (!this->emitGetPtrLocal(*LocalIndex, E))
return false;
return this->visitInitializer(SubExpr);
@@ -2119,8 +2112,7 @@ bool ByteCodeExprGen::VisitCXXConstructExpr(
// to allocate a variable and call the constructor and destructor.
if (DiscardResult) {
assert(!Initializing);
- std::optional LocalIndex =
- allocateLocal(E, /*IsExtended=*/true);
+ std::optional LocalIndex = allocateLocal(E);
if (!LocalIndex)
return false;
@@ -2300,8 +2292,7 @@ bool ByteCodeExprGen::VisitCXXScalarValueInitExpr(
if (const auto *CT = Ty->getAs()) {
if (!Initializing) {
- std::optional LocalIndex =
- allocateLocal(E, /*IsExtended=*/false);
+ std::optional LocalIndex = allocateLocal(E);
if (!LocalIndex)
return false;
if (!this->emitGetPtrLocal(*LocalIndex, E))
@@ -2324,8 +2315,7 @@ bool ByteCodeExprGen::VisitCXXScalarValueInitExpr(
if (const auto *VT = Ty->getAs()) {
// FIXME: Code duplication with the _Complex case above.
if (!Initializing) {
- std::optional LocalIndex =
- allocateLocal(E, /*IsExtended=*/false);
+ std::optional LocalIndex = allocateLocal(E);
if (!LocalIndex)
return false;
if (!this->emitGetPtrLocal(*LocalIndex, E))
@@ -2513,7 +2503,7 @@ template bool ByteCodeExprGen::visit(const Expr *E) {
// Create local variable to hold the return value.
if (!E->isGLValue() && !E->getType()->isAnyComplexType() &&
!classify(E->getType())) {
- std::optional LocalIndex = allocateLocal(E, /*IsExtended=*/true);
+ std::optional LocalIndex = allocateLocal(E);
if (!LocalIndex)
return false;
@@ -2767,7 +2757,8 @@ unsigned ByteCodeExprGen::allocateLocalPrimitive(DeclTy &&Src,
template
std::optional
-ByteCodeExprGen::allocateLocal(DeclTy &&Src, bool IsExtended) {
+ByteCodeExprGen::allocateLocal(DeclTy &&Src,
+ const ValueDecl *ExtendingDecl) {
// Make sure we don't accidentally register the same decl twice.
if ([[maybe_unused]] const auto *VD =
dyn_cast_if_present(Src.dyn_cast())) {
@@ -2800,7 +2791,10 @@ ByteCodeExprGen::allocateLocal(DeclTy &&Src, bool IsExtended) {
Scope::Local Local = this->createLocal(D);
if (Key)
Locals.insert({Key, Local});
- VarScope->add(Local, IsExtended);
+ if (ExtendingDecl)
+ VarScope->addExtended(Local, ExtendingDecl);
+ else
+ VarScope->add(Local, false);
return Local.Offset;
}
@@ -2835,14 +2829,14 @@ bool ByteCodeExprGen