From bef6687f9bc753245117f00e298919bcb834868a Mon Sep 17 00:00:00 2001 From: Alexey Bataev Date: Thu, 25 Apr 2024 15:49:37 -0700 Subject: [PATCH 001/293] [SLP][NFC]Add a test with the incorrect comparison after minbiwidth analysis. --- .../RISCV/unsigned-icmp-signed-op.ll | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 llvm/test/Transforms/SLPVectorizer/RISCV/unsigned-icmp-signed-op.ll diff --git a/llvm/test/Transforms/SLPVectorizer/RISCV/unsigned-icmp-signed-op.ll b/llvm/test/Transforms/SLPVectorizer/RISCV/unsigned-icmp-signed-op.ll new file mode 100644 index 000000000000..bfeb7805ae9f --- /dev/null +++ b/llvm/test/Transforms/SLPVectorizer/RISCV/unsigned-icmp-signed-op.ll @@ -0,0 +1,41 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4 +; RUN: opt -S --passes=slp-vectorizer -mtriple=riscv64-unknown-linux-gnu -mattr=+v < %s | FileCheck %s + +define i32 @test(ptr %f, i16 %0) { +; CHECK-LABEL: define i32 @test( +; CHECK-SAME: ptr [[F:%.*]], i16 [[TMP0:%.*]]) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[F]], align 2 +; CHECK-NEXT: [[TMP2:%.*]] = insertelement <4 x i16> , i16 [[TMP0]], i32 1 +; CHECK-NEXT: [[TMP3:%.*]] = insertelement <4 x i16> , i16 [[TMP1]], i32 1 +; CHECK-NEXT: [[TMP4:%.*]] = icmp ule <4 x i16> [[TMP3]], [[TMP2]] +; CHECK-NEXT: [[TMP5:%.*]] = call i1 @llvm.vector.reduce.and.v4i1(<4 x i1> [[TMP4]]) +; CHECK-NEXT: [[ZEXT_4:%.*]] = zext i1 [[TMP5]] to i32 +; CHECK-NEXT: ret i32 [[ZEXT_4]] +; +entry: + %1 = load i16, ptr %f, align 2 + + %zext.0 = zext i16 %1 to i32 + %sext.0 = sext i16 %0 to i32 + + %zext.1 = zext i16 0 to i32 + %sext.1 = sext i16 0 to i32 + %zext.2 = zext i16 0 to i32 + %sext.2 = sext i16 0 to i32 + %zext.3 = zext i16 0 to i32 + %sext.3 = sext i16 0 to i32 + + %cmp.0 = icmp ule i32 %zext.0, %sext.0 + %cmp.1 = icmp ule i32 %zext.1, %sext.1 + %cmp.2 = icmp ule i32 %zext.2, %sext.2 + %cmp.3 = icmp ule i32 %zext.3, %sext.3 + + %and.0 = and i1 %cmp.0, %cmp.1 + %and.1 = and i1 %and.0, %cmp.2 + %and.2 = and i1 %and.1, %cmp.3 + + %zext.4 = zext i1 %and.2 to i32 + + ret i32 %zext.4 +} -- GitLab From f758bb66e8acfe0daa1725ab4d87ae944a4c53d2 Mon Sep 17 00:00:00 2001 From: Alexey Bataev Date: Thu, 25 Apr 2024 16:10:24 -0700 Subject: [PATCH 002/293] [SLP]Fix PR89988: do extra analysis of the icmp args to correctly handle signed/unsigned comparison. If operands of icmp has different signedness, need to consider extending unsigned operands to correctly handle comparison with the signed operands. --- llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp | 11 ++++++++--- .../SLPVectorizer/RISCV/unsigned-icmp-signed-op.ll | 4 +++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp index a1a28076881c..0cd7bd777222 100644 --- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp +++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp @@ -15072,11 +15072,16 @@ void BoUpSLP::computeMinimumValueSizes() { IsSignedCmp = NodeIdx < VectorizableTree.size() && any_of(VectorizableTree[NodeIdx]->UserTreeIndices, - [](const EdgeInfo &EI) { + [&](const EdgeInfo &EI) { return EI.UserTE->getOpcode() == Instruction::ICmp && - any_of(EI.UserTE->Scalars, [](Value *V) { + any_of(EI.UserTE->Scalars, [&](Value *V) { auto *IC = dyn_cast(V); - return IC && IC->isSigned(); + return IC && + (IC->isSigned() || + !isKnownNonNegative(IC->getOperand(0), + SimplifyQuery(*DL)) || + !isKnownNonNegative(IC->getOperand(1), + SimplifyQuery(*DL))); }); }); } diff --git a/llvm/test/Transforms/SLPVectorizer/RISCV/unsigned-icmp-signed-op.ll b/llvm/test/Transforms/SLPVectorizer/RISCV/unsigned-icmp-signed-op.ll index bfeb7805ae9f..5ec6b4f1040d 100644 --- a/llvm/test/Transforms/SLPVectorizer/RISCV/unsigned-icmp-signed-op.ll +++ b/llvm/test/Transforms/SLPVectorizer/RISCV/unsigned-icmp-signed-op.ll @@ -8,7 +8,9 @@ define i32 @test(ptr %f, i16 %0) { ; CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[F]], align 2 ; CHECK-NEXT: [[TMP2:%.*]] = insertelement <4 x i16> , i16 [[TMP0]], i32 1 ; CHECK-NEXT: [[TMP3:%.*]] = insertelement <4 x i16> , i16 [[TMP1]], i32 1 -; CHECK-NEXT: [[TMP4:%.*]] = icmp ule <4 x i16> [[TMP3]], [[TMP2]] +; CHECK-NEXT: [[TMP6:%.*]] = zext <4 x i16> [[TMP3]] to <4 x i32> +; CHECK-NEXT: [[TMP7:%.*]] = sext <4 x i16> [[TMP2]] to <4 x i32> +; CHECK-NEXT: [[TMP4:%.*]] = icmp ule <4 x i32> [[TMP6]], [[TMP7]] ; CHECK-NEXT: [[TMP5:%.*]] = call i1 @llvm.vector.reduce.and.v4i1(<4 x i1> [[TMP4]]) ; CHECK-NEXT: [[ZEXT_4:%.*]] = zext i1 [[TMP5]] to i32 ; CHECK-NEXT: ret i32 [[ZEXT_4]] -- GitLab From 9221f3af8f832d990be986c05d964ad37e5a2356 Mon Sep 17 00:00:00 2001 From: Paul Kirth Date: Fri, 26 Apr 2024 01:20:52 +0200 Subject: [PATCH 003/293] [RISCV] Support RISCV Atomics ABI attributes (#84597) This patch adds support for the `atomic_abi` attribute, specifid in https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#tag_riscv_atomic_abi-14-uleb128version. The atomics_abi tag merging is conducted as follows: - UNKNOWN is safe to merge with all other values. - A6C is compatible with A6S, and results in the A6C ABI. - A6C is incompatible with A7, and results in an error. - A6S and A7 are compatible, and merging results in the A7 ABI. Note: the A7 is not yet supported in either LLVM or in any current hardware, and is therefore ommited from attribute generation in RISCVTargetStreamer. --- lld/ELF/Arch/RISCV.cpp | 63 ++++++ lld/test/ELF/riscv-attributes.s | 202 ++++++++++++++++++ .../llvm/Support/RISCVAttributeParser.h | 1 + llvm/include/llvm/Support/RISCVAttributes.h | 13 ++ llvm/lib/Support/RISCVAttributeParser.cpp | 13 +- llvm/lib/Support/RISCVAttributes.cpp | 1 + .../MCTargetDesc/RISCVTargetStreamer.cpp | 7 + llvm/test/CodeGen/RISCV/attributes.ll | 10 +- llvm/test/MC/RISCV/attribute.s | 3 + llvm/test/MC/RISCV/invalid-attribute.s | 3 + 10 files changed, 314 insertions(+), 2 deletions(-) diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp index 20088d92bafa..7b9c9c6c6c38 100644 --- a/lld/ELF/Arch/RISCV.cpp +++ b/lld/ELF/Arch/RISCV.cpp @@ -1084,10 +1084,62 @@ static void mergeArch(RISCVISAInfo::OrderedExtensionMap &mergedExts, } } +static void mergeAtomic(DenseMap::iterator it, + const InputSectionBase *oldSection, + const InputSectionBase *newSection, unsigned int oldTag, + unsigned int newTag) { + using RISCVAttrs::RISCVAtomicAbiTag::AtomicABI; + // Same tags stay the same, and UNKNOWN is compatible with anything + if (oldTag == newTag || newTag == AtomicABI::UNKNOWN) + return; + + switch (oldTag) { + case AtomicABI::UNKNOWN: + it->getSecond() = newTag; + return; + case AtomicABI::A6C: + switch (newTag) { + case AtomicABI::A6S: + it->getSecond() = AtomicABI::A6C; + return; + case AtomicABI::A7: + error(toString(oldSection) + " has atomic_abi=" + Twine(oldTag) + + " but " + toString(newSection) + + " has atomic_abi=" + Twine(newTag)); + return; + }; + + case AtomicABI::A6S: + switch (newTag) { + case AtomicABI::A6C: + it->getSecond() = AtomicABI::A6C; + return; + case AtomicABI::A7: + it->getSecond() = AtomicABI::A7; + return; + }; + + case AtomicABI::A7: + switch (newTag) { + case AtomicABI::A6S: + it->getSecond() = AtomicABI::A7; + return; + case AtomicABI::A6C: + error(toString(oldSection) + " has atomic_abi=" + Twine(oldTag) + + " but " + toString(newSection) + + " has atomic_abi=" + Twine(newTag)); + return; + }; + default: + llvm_unreachable("unknown AtomicABI"); + }; +} + static RISCVAttributesSection * mergeAttributesSection(const SmallVector §ions) { RISCVISAInfo::OrderedExtensionMap exts; const InputSectionBase *firstStackAlign = nullptr; + const InputSectionBase *firstAtomicAbi = nullptr; unsigned firstStackAlignValue = 0, xlen = 0; bool hasArch = false; @@ -1134,6 +1186,17 @@ mergeAttributesSection(const SmallVector §ions) { case RISCVAttrs::PRIV_SPEC_MINOR: case RISCVAttrs::PRIV_SPEC_REVISION: break; + + case llvm::RISCVAttrs::AttrType::ATOMIC_ABI: + if (auto i = parser.getAttributeValue(tag.attr)) { + auto r = merged.intAttr.try_emplace(tag.attr, *i); + if (r.second) { + firstAtomicAbi = sec; + } else { + mergeAtomic(r.first, firstAtomicAbi, sec, r.first->getSecond(), *i); + } + } + continue; } // Fallback for deprecated priv_spec* and other unknown attributes: retain diff --git a/lld/test/ELF/riscv-attributes.s b/lld/test/ELF/riscv-attributes.s index d0ce0941269e..77c2c3cb263f 100644 --- a/lld/test/ELF/riscv-attributes.s +++ b/lld/test/ELF/riscv-attributes.s @@ -44,6 +44,39 @@ # RUN: not ld.lld a.o b.o c.o diff_stack_align.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=STACK_ALIGN --implicit-check-not=error: # STACK_ALIGN: error: diff_stack_align.o:(.riscv.attributes) has stack_align=32 but a.o:(.riscv.attributes) has stack_align=16 +## merging atomic_abi values for A6C and A7 lead to an error. +# RUN: llvm-mc -filetype=obj -triple=riscv64 atomic_abi_A6C.s -o atomic_abi_A6C.o +# RUN: llvm-mc -filetype=obj -triple=riscv64 atomic_abi_A7.s -o atomic_abi_A7.o +# RUN: not ld.lld atomic_abi_A6C.o atomic_abi_A7.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=ATOMIC_ABI_ERROR --implicit-check-not=error: +# ATOMIC_ABI_ERROR: error: atomic_abi_A6C.o:(.riscv.attributes) has atomic_abi=1 but atomic_abi_A7.o:(.riscv.attributes) has atomic_abi=3 + + +# RUN: llvm-mc -filetype=obj -triple=riscv64 atomic_abi_A6S.s -o atomic_abi_A6S.o +# RUN: ld.lld atomic_abi_A6S.o atomic_abi_A6C.o -o atomic_abi_A6C_A6S +# RUN: llvm-readobj -A atomic_abi_A6C_A6S | FileCheck %s --check-prefix=A6C_A6S + +# RUN: ld.lld atomic_abi_A6S.o atomic_abi_A7.o -o atomic_abi_A6S_A7 +# RUN: llvm-readobj -A atomic_abi_A6S_A7 | FileCheck %s --check-prefix=A6S_A7 + +# RUN: llvm-mc -filetype=obj -triple=riscv64 atomic_abi_unknown.s -o atomic_abi_unknown.o +# RUN: ld.lld atomic_abi_unknown.o atomic_abi_A6C.o -o atomic_abi_A6C_unknown +# RUN: llvm-readobj -A atomic_abi_A6C_unknown | FileCheck %s --check-prefixes=UNKNOWN_A6C + +# RUN: ld.lld atomic_abi_unknown.o diff_stack_align.o -o atomic_abi_none_unknown +# RUN: llvm-readobj -A atomic_abi_none_unknown | FileCheck %s --check-prefixes=UNKNOWN_NONE + +# RUN: ld.lld diff_stack_align.o atomic_abi_A6C.o -o atomic_abi_A6C_none +# RUN: llvm-readobj -A atomic_abi_A6C_none | FileCheck %s --check-prefixes=NONE_A6C + +# RUN: ld.lld atomic_abi_unknown.o atomic_abi_A6S.o -o atomic_abi_A6S_unknown +# RUN: llvm-readobj -A atomic_abi_A6S_unknown | FileCheck %s --check-prefix=UNKNOWN_A6S + +# RUN: ld.lld atomic_abi_unknown.o atomic_abi_A7.o -o atomic_abi_A7_unknown +# RUN: llvm-readobj -A atomic_abi_A7_unknown | FileCheck %s --check-prefix=UNKNOWN_A7 + +# RUN: ld.lld diff_stack_align.o atomic_abi_A7.o -o atomic_abi_A7_none +# RUN: llvm-readobj -A atomic_abi_A7_none | FileCheck %s --check-prefix=NONE_A7 + ## The deprecated priv_spec is not handled as GNU ld does. ## Differing priv_spec attributes lead to an absent attribute. # RUN: llvm-mc -filetype=obj -triple=riscv64 diff_priv_spec.s -o diff_priv_spec.o @@ -286,6 +319,175 @@ .attribute priv_spec, 3 .attribute priv_spec_minor, 3 +#--- atomic_abi_unknown.s +.attribute atomic_abi, 0 + +#--- atomic_abi_A6C.s +.attribute atomic_abi, 1 + +#--- atomic_abi_A6S.s +.attribute atomic_abi, 2 + +#--- atomic_abi_A7.s +.attribute atomic_abi, 3 + +# UNKNOWN_NONE: BuildAttributes { +# UNKNOWN_NONE-NEXT: FormatVersion: 0x41 +# UNKNOWN_NONE-NEXT: Section 1 { +# UNKNOWN_NONE-NEXT: SectionLength: 17 +# UNKNOWN_NONE-NEXT: Vendor: riscv +# UNKNOWN_NONE-NEXT: Tag: Tag_File (0x1) +# UNKNOWN_NONE-NEXT: Size: 7 +# UNKNOWN_NONE-NEXT: FileAttributes { +# UNKNOWN_NONE-NEXT: Attribute { +# UNKNOWN_NONE-NEXT: Tag: 4 +# UNKNOWN_NONE-NEXT: Value: 32 +# UNKNOWN_NONE-NEXT: TagName: stack_align +# UNKNOWN_NONE-NEXT: Description: Stack alignment is 32-bytes +# UNKNOWN_NONE-NEXT: } +# UNKNOWN_NONE-NEXT: } +# UNKNOWN_NONE-NEXT: } +# UNKNOWN_NONE-NEXT: } + +# NONE_A6C: BuildAttributes { +# NONE_A6C-NEXT: FormatVersion: 0x41 +# NONE_A6C-NEXT: Section 1 { +# NONE_A6C-NEXT: SectionLength: 19 +# NONE_A6C-NEXT: Vendor: riscv +# NONE_A6C-NEXT: Tag: Tag_File (0x1) +# NONE_A6C-NEXT: Size: 9 +# NONE_A6C-NEXT: FileAttributes { +# NONE_A6C-NEXT: Attribute { +# NONE_A6C-NEXT: Tag: 14 +# NONE_A6C-NEXT: Value: 1 +# NONE_A6C-NEXT: TagName: atomic_abi +# NONE_A6C-NEXT: Description: Atomic ABI is 1 +# NONE_A6C-NEXT: } +# NONE_A6C-NEXT: Attribute { +# NONE_A6C-NEXT: Tag: 4 +# NONE_A6C-NEXT: Value: 32 +# NONE_A6C-NEXT: TagName: stack_align +# NONE_A6C-NEXT: Description: Stack alignment is 32-bytes +# NONE_A6C-NEXT: } +# NONE_A6C-NEXT: } +# NONE_A6C-NEXT: } +# NONE_A6C-NEXT: } + +# UNKNOWN_A6C: BuildAttributes { +# UNKNOWN_A6C-NEXT: FormatVersion: 0x41 +# UNKNOWN_A6C-NEXT: Section 1 { +# UNKNOWN_A6C-NEXT: SectionLength: 17 +# UNKNOWN_A6C-NEXT: Vendor: riscv +# UNKNOWN_A6C-NEXT: Tag: Tag_File (0x1) +# UNKNOWN_A6C-NEXT: Size: 7 +# UNKNOWN_A6C-NEXT: FileAttributes { +# UNKNOWN_A6C-NEXT: Attribute { +# UNKNOWN_A6C-NEXT: Tag: 14 +# UNKNOWN_A6C-NEXT: Value: 1 +# UNKNOWN_A6C-NEXT: TagName: atomic_abi +# UNKNOWN_A6C-NEXT: Description: Atomic ABI is 1 +# UNKNOWN_A6C-NEXT: } +# UNKNOWN_A6C-NEXT: } +# UNKNOWN_A6C-NEXT: } +# UNKNOWN_A6C-NEXT: } + +# UNKNOWN_A6S: BuildAttributes { +# UNKNOWN_A6S-NEXT: FormatVersion: 0x41 +# UNKNOWN_A6S-NEXT: Section 1 { +# UNKNOWN_A6S-NEXT: SectionLength: +# UNKNOWN_A6S-NEXT: Vendor: riscv +# UNKNOWN_A6S-NEXT: Tag: Tag_File (0x1) +# UNKNOWN_A6S-NEXT: Size: 7 +# UNKNOWN_A6S-NEXT: FileAttributes { +# UNKNOWN_A6S-NEXT: Attribute { +# UNKNOWN_A6S-NEXT: Tag: 14 +# UNKNOWN_A6S-NEXT: Value: 2 +# UNKNOWN_A6S-NEXT: TagName: atomic_abi +# UNKNOWN_A6S-NEXT: Description: Atomic ABI is 2 +# UNKNOWN_A6S-NEXT: } +# UNKNOWN_A6S-NEXT: } +# UNKNOWN_A6S-NEXT: } +# UNKNOWN_A6S-NEXT: } + +# NONE_A7: BuildAttributes { +# NONE_A7-NEXT: FormatVersion: 0x41 +# NONE_A7-NEXT: Section 1 { +# NONE_A7-NEXT: SectionLength: 19 +# NONE_A7-NEXT: Vendor: riscv +# NONE_A7-NEXT: Tag: Tag_File (0x1) +# NONE_A7-NEXT: Size: 9 +# NONE_A7-NEXT: FileAttributes { +# NONE_A7-NEXT: Attribute { +# NONE_A7-NEXT: Tag: 14 +# NONE_A7-NEXT: Value: 3 +# NONE_A7-NEXT: TagName: atomic_abi +# NONE_A7-NEXT: Description: Atomic ABI is 3 +# NONE_A7-NEXT: } +# NONE_A7-NEXT: Attribute { +# NONE_A7-NEXT: Tag: 4 +# NONE_A7-NEXT: Value: 32 +# NONE_A7-NEXT: TagName: stack_align +# NONE_A7-NEXT: Description: Stack alignment is 32-bytes +# NONE_A7-NEXT: } +# NONE_A7-NEXT: } +# NONE_A7-NEXT: } +# NONE_A7-NEXT: } + + +# UNKNOWN_A7: BuildAttributes { +# UNKNOWN_A7-NEXT: FormatVersion: 0x41 +# UNKNOWN_A7-NEXT: Section 1 { +# UNKNOWN_A7-NEXT: SectionLength: 17 +# UNKNOWN_A7-NEXT: Vendor: riscv +# UNKNOWN_A7-NEXT: Tag: Tag_File (0x1) +# UNKNOWN_A7-NEXT: Size: 7 +# UNKNOWN_A7-NEXT: FileAttributes { +# UNKNOWN_A7-NEXT: Attribute { +# UNKNOWN_A7-NEXT: Tag: 14 +# UNKNOWN_A7-NEXT: Value: 3 +# UNKNOWN_A7-NEXT: TagName: atomic_abi +# UNKNOWN_A7-NEXT: Description: Atomic ABI is 3 +# UNKNOWN_A7-NEXT: } +# UNKNOWN_A7-NEXT: } +# UNKNOWN_A7-NEXT: } +# UNKNOWN_A7-NEXT: } + +# A6C_A6S: BuildAttributes { +# A6C_A6S-NEXT: FormatVersion: 0x41 +# A6C_A6S-NEXT: Section 1 { +# A6C_A6S-NEXT: SectionLength: 17 +# A6C_A6S-NEXT: Vendor: riscv +# A6C_A6S-NEXT: Tag: Tag_File (0x1) +# A6C_A6S-NEXT: Size: 7 +# A6C_A6S-NEXT: FileAttributes { +# A6C_A6S-NEXT: Attribute { +# A6C_A6S-NEXT: Tag: 14 +# A6C_A6S-NEXT: Value: 1 +# A6C_A6S-NEXT: TagName: atomic_abi +# A6C_A6S-NEXT: Description: Atomic ABI is 1 +# A6C_A6S-NEXT: } +# A6C_A6S-NEXT: } +# A6C_A6S-NEXT: } +# A6C_A6S-NEXT: } + +# A6S_A7: BuildAttributes { +# A6S_A7-NEXT: FormatVersion: 0x41 +# A6S_A7-NEXT: Section 1 { +# A6S_A7-NEXT: SectionLength: 17 +# A6S_A7-NEXT: Vendor: riscv +# A6S_A7-NEXT: Tag: Tag_File (0x1) +# A6S_A7-NEXT: Size: 7 +# A6S_A7-NEXT: FileAttributes { +# A6S_A7-NEXT: Attribute { +# A6S_A7-NEXT: Tag: 14 +# A6S_A7-NEXT: Value: 3 +# A6S_A7-NEXT: TagName: atomic_abi +# A6S_A7-NEXT: Description: Atomic ABI is 3 +# A6S_A7-NEXT: } +# A6S_A7-NEXT: } +# A6S_A7-NEXT: } +# A6S_A7-NEXT: } + #--- unknown13.s .attribute 13, "0" #--- unknown13a.s diff --git a/llvm/include/llvm/Support/RISCVAttributeParser.h b/llvm/include/llvm/Support/RISCVAttributeParser.h index 305adffbe851..9f295504de95 100644 --- a/llvm/include/llvm/Support/RISCVAttributeParser.h +++ b/llvm/include/llvm/Support/RISCVAttributeParser.h @@ -24,6 +24,7 @@ class RISCVAttributeParser : public ELFAttributeParser { Error unalignedAccess(unsigned tag); Error stackAlign(unsigned tag); + Error atomicAbi(unsigned tag); public: RISCVAttributeParser(ScopedPrinter *sw) diff --git a/llvm/include/llvm/Support/RISCVAttributes.h b/llvm/include/llvm/Support/RISCVAttributes.h index 18f5a84d21f2..5def890a7273 100644 --- a/llvm/include/llvm/Support/RISCVAttributes.h +++ b/llvm/include/llvm/Support/RISCVAttributes.h @@ -32,8 +32,21 @@ enum AttrType : unsigned { PRIV_SPEC = 8, PRIV_SPEC_MINOR = 10, PRIV_SPEC_REVISION = 12, + ATOMIC_ABI = 14, }; +namespace RISCVAtomicAbiTag { +enum AtomicABI : unsigned { + // Values for Tag_RISCV_atomic_abi + // Defined at + // https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#tag_riscv_atomic_abi-14-uleb128version + UNKNOWN = 0, + A6C = 1, + A6S = 2, + A7 = 3, +}; +} // namespace RISCVAtomicAbiTag + enum { NOT_ALLOWED = 0, ALLOWED = 1 }; } // namespace RISCVAttrs diff --git a/llvm/lib/Support/RISCVAttributeParser.cpp b/llvm/lib/Support/RISCVAttributeParser.cpp index 7ce4b6ab161c..19c5a0e06903 100644 --- a/llvm/lib/Support/RISCVAttributeParser.cpp +++ b/llvm/lib/Support/RISCVAttributeParser.cpp @@ -36,7 +36,18 @@ const RISCVAttributeParser::DisplayHandler { RISCVAttrs::UNALIGNED_ACCESS, &RISCVAttributeParser::unalignedAccess, - }}; + }, + { + RISCVAttrs::ATOMIC_ABI, + &RISCVAttributeParser::atomicAbi, + }, +}; + +Error RISCVAttributeParser::atomicAbi(unsigned Tag) { + uint64_t Value = de.getULEB128(cursor); + printAttribute(Tag, Value, "Atomic ABI is " + utostr(Value)); + return Error::success(); +} Error RISCVAttributeParser::unalignedAccess(unsigned tag) { static const char *strings[] = {"No unaligned access", "Unaligned access"}; diff --git a/llvm/lib/Support/RISCVAttributes.cpp b/llvm/lib/Support/RISCVAttributes.cpp index 9e629760d3d8..dc70d65acba0 100644 --- a/llvm/lib/Support/RISCVAttributes.cpp +++ b/llvm/lib/Support/RISCVAttributes.cpp @@ -18,6 +18,7 @@ static constexpr TagNameItem tagData[] = { {PRIV_SPEC, "Tag_priv_spec"}, {PRIV_SPEC_MINOR, "Tag_priv_spec_minor"}, {PRIV_SPEC_REVISION, "Tag_priv_spec_revision"}, + {ATOMIC_ABI, "Tag_atomic_abi"}, }; constexpr TagNameMap RISCVAttributeTags{tagData}; diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp index 0f92e9ed6a64..6f5f12cc7286 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp @@ -75,6 +75,13 @@ void RISCVTargetStreamer::emitTargetAttributes(const MCSubtargetInfo &STI, auto &ISAInfo = *ParseResult; emitTextAttribute(RISCVAttrs::ARCH, ISAInfo->toString()); } + + if (STI.hasFeature(RISCV::FeatureStdExtA)) { + unsigned AtomicABITag = STI.hasFeature(RISCV::FeatureTrailingSeqCstFence) + ? RISCVAttrs::RISCVAtomicAbiTag::AtomicABI::A6S + : RISCVAttrs::RISCVAtomicAbiTag::AtomicABI::A6C; + emitAttribute(RISCVAttrs::ATOMIC_ABI, AtomicABITag); + } } // This part is for ascii assembly output diff --git a/llvm/test/CodeGen/RISCV/attributes.ll b/llvm/test/CodeGen/RISCV/attributes.ll index 141d5ea41828..1aff3e8b83f4 100644 --- a/llvm/test/CodeGen/RISCV/attributes.ll +++ b/llvm/test/CodeGen/RISCV/attributes.ll @@ -129,7 +129,8 @@ ; RUN: llc -mtriple=riscv64 -mattr=+m %s -o - | FileCheck --check-prefixes=CHECK,RV64M %s ; RUN: llc -mtriple=riscv64 -mattr=+zmmul %s -o - | FileCheck --check-prefixes=CHECK,RV64ZMMUL %s ; RUN: llc -mtriple=riscv64 -mattr=+m,+zmmul %s -o - | FileCheck --check-prefixes=CHECK,RV64MZMMUL %s -; RUN: llc -mtriple=riscv64 -mattr=+a %s -o - | FileCheck --check-prefixes=CHECK,RV64A %s +; RUN: llc -mtriple=riscv64 -mattr=+a %s -o - | FileCheck --check-prefixes=CHECK,RV64A,A6C %s +; RUN: llc -mtriple=riscv64 -mattr=+a,+seq-cst-trailing-fence %s -o - | FileCheck --check-prefixes=CHECK,RV64A,A6S %s ; RUN: llc -mtriple=riscv64 -mattr=+f %s -o - | FileCheck --check-prefixes=CHECK,RV64F %s ; RUN: llc -mtriple=riscv64 -mattr=+d %s -o - | FileCheck --check-prefixes=CHECK,RV64D %s ; RUN: llc -mtriple=riscv64 -mattr=+c %s -o - | FileCheck --check-prefixes=CHECK,RV64C %s @@ -516,3 +517,10 @@ define i32 @addi(i32 %a) { %1 = add i32 %a, 1 ret i32 %1 } + +define i8 @atomic_load_i8_seq_cst(ptr %a) nounwind { + %1 = load atomic i8, ptr %a seq_cst, align 1 + ret i8 %1 +; A6S: .attribute 14, 2 +; A6C: .attribute 14, 1 +} diff --git a/llvm/test/MC/RISCV/attribute.s b/llvm/test/MC/RISCV/attribute.s index 56f0cb1daf17..75b9c65ed1cc 100644 --- a/llvm/test/MC/RISCV/attribute.s +++ b/llvm/test/MC/RISCV/attribute.s @@ -24,3 +24,6 @@ .attribute priv_spec_revision, 0 # CHECK: attribute 12, 0 + +.attribute atomic_abi, 0 +# CHECK: attribute 14, 0 diff --git a/llvm/test/MC/RISCV/invalid-attribute.s b/llvm/test/MC/RISCV/invalid-attribute.s index 1d732af83cda..2ebf7ddc9aff 100644 --- a/llvm/test/MC/RISCV/invalid-attribute.s +++ b/llvm/test/MC/RISCV/invalid-attribute.s @@ -33,3 +33,6 @@ .attribute arch, 30 # CHECK: [[@LINE-1]]:18: error: expected string constant + +.attribute atomic_abi, "16" +# CHECK: [[@LINE-1]]:24: error: expected numeric constant -- GitLab From 733b271db793ce30c504a1b5c4ae7a8775b0a6a2 Mon Sep 17 00:00:00 2001 From: Paul Kirth Date: Fri, 26 Apr 2024 01:33:10 +0200 Subject: [PATCH 004/293] [llvm][RISCV] Enable trailing fences for seq-cst stores by default (#87376) With the tag merging in place, we can safely change the default for +seq-cst-trailing-fence to the default, according to the recommendation in https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-atomic.adoc This tag changes the default for the feature flag, and moves to more consistent naming with respect to existing features. --- llvm/docs/ReleaseNotes.rst | 5 +++++ .../RISCV/MCTargetDesc/RISCVTargetStreamer.cpp | 6 +++--- llvm/lib/Target/RISCV/RISCVFeatures.td | 8 ++++---- llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 2 +- llvm/test/CodeGen/RISCV/atomic-load-store.ll | 16 ++++++++-------- llvm/test/CodeGen/RISCV/attributes.ll | 4 ++-- llvm/test/CodeGen/RISCV/forced-atomics.ll | 12 ++++++------ 7 files changed, 29 insertions(+), 24 deletions(-) diff --git a/llvm/docs/ReleaseNotes.rst b/llvm/docs/ReleaseNotes.rst index 6ef6ec20da67..1be8db602a15 100644 --- a/llvm/docs/ReleaseNotes.rst +++ b/llvm/docs/ReleaseNotes.rst @@ -112,6 +112,11 @@ Changes to the RISC-V Backend * The experimental Ssqosid extension is supported. * Zacas is no longer experimental. * Added the CSR names from the Resumable Non-Maskable Interrupts (Smrnmi) extension. +* The default atomics mapping was changed to emit an additional trailing fence + for sequentially consistent stores, offering compatibility with a future + mapping using load-acquire and store-release instructions while remaining + fully compatible with objects produced prior to this change. The mapping + (ABI) used is recorded as an ELF attribute. Changes to the WebAssembly Backend ---------------------------------- diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp index 6f5f12cc7286..adb17cec28c2 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp @@ -77,9 +77,9 @@ void RISCVTargetStreamer::emitTargetAttributes(const MCSubtargetInfo &STI, } if (STI.hasFeature(RISCV::FeatureStdExtA)) { - unsigned AtomicABITag = STI.hasFeature(RISCV::FeatureTrailingSeqCstFence) - ? RISCVAttrs::RISCVAtomicAbiTag::AtomicABI::A6S - : RISCVAttrs::RISCVAtomicAbiTag::AtomicABI::A6C; + unsigned AtomicABITag = STI.hasFeature(RISCV::FeatureNoTrailingSeqCstFence) + ? RISCVAttrs::RISCVAtomicAbiTag::AtomicABI::A6C + : RISCVAttrs::RISCVAtomicAbiTag::AtomicABI::A6S; emitAttribute(RISCVAttrs::ATOMIC_ABI, AtomicABITag); } } diff --git a/llvm/lib/Target/RISCV/RISCVFeatures.td b/llvm/lib/Target/RISCV/RISCVFeatures.td index c3dc4ea53697..deb983528f32 100644 --- a/llvm/lib/Target/RISCV/RISCVFeatures.td +++ b/llvm/lib/Target/RISCV/RISCVFeatures.td @@ -1216,10 +1216,10 @@ foreach i = {1-31} in def FeatureSaveRestore : SubtargetFeature<"save-restore", "EnableSaveRestore", "true", "Enable save/restore.">; -def FeatureTrailingSeqCstFence : SubtargetFeature<"seq-cst-trailing-fence", - "EnableSeqCstTrailingFence", - "true", - "Enable trailing fence for seq-cst store.">; +def FeatureNoTrailingSeqCstFence : SubtargetFeature<"no-trailing-seq-cst-fence", + "EnableTrailingSeqCstFence", + "false", + "Disable trailing fence for seq-cst store.">; def FeatureUnalignedScalarMem : SubtargetFeature<"unaligned-scalar-mem", "EnableUnalignedScalarMem", diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index 539aa3525545..769c465d56f9 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -20192,7 +20192,7 @@ Instruction *RISCVTargetLowering::emitTrailingFence(IRBuilderBase &Builder, if (isa(Inst) && isAcquireOrStronger(Ord)) return Builder.CreateFence(AtomicOrdering::Acquire); - if (Subtarget.enableSeqCstTrailingFence() && isa(Inst) && + if (Subtarget.enableTrailingSeqCstFence() && isa(Inst) && Ord == AtomicOrdering::SequentiallyConsistent) return Builder.CreateFence(AtomicOrdering::SequentiallyConsistent); return nullptr; diff --git a/llvm/test/CodeGen/RISCV/atomic-load-store.ll b/llvm/test/CodeGen/RISCV/atomic-load-store.ll index 2d1fc21cda89..1586a133568b 100644 --- a/llvm/test/CodeGen/RISCV/atomic-load-store.ll +++ b/llvm/test/CodeGen/RISCV/atomic-load-store.ll @@ -1,26 +1,26 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \ ; RUN: | FileCheck -check-prefix=RV32I %s -; RUN: llc -mtriple=riscv32 -mattr=+a -verify-machineinstrs < %s \ +; RUN: llc -mtriple=riscv32 -mattr=+a,+no-trailing-seq-cst-fence -verify-machineinstrs < %s \ ; RUN: | FileCheck -check-prefixes=RV32IA,RV32IA-WMO %s -; RUN: llc -mtriple=riscv32 -mattr=+a,+experimental-ztso -verify-machineinstrs < %s \ +; RUN: llc -mtriple=riscv32 -mattr=+a,+experimental-ztso,+no-trailing-seq-cst-fence -verify-machineinstrs < %s \ ; RUN: | FileCheck -check-prefixes=RV32IA,RV32IA-TSO %s ; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s \ ; RUN: | FileCheck -check-prefix=RV64I %s -; RUN: llc -mtriple=riscv64 -mattr=+a -verify-machineinstrs < %s \ +; RUN: llc -mtriple=riscv64 -mattr=+a,+no-trailing-seq-cst-fence -verify-machineinstrs < %s \ ; RUN: | FileCheck -check-prefixes=RV64IA,RV64IA-WMO %s -; RUN: llc -mtriple=riscv64 -mattr=+a,+experimental-ztso -verify-machineinstrs < %s \ +; RUN: llc -mtriple=riscv64 -mattr=+a,+experimental-ztso,+no-trailing-seq-cst-fence -verify-machineinstrs < %s \ ; RUN: | FileCheck -check-prefixes=RV64IA,RV64IA-TSO %s -; RUN: llc -mtriple=riscv32 -mattr=+a,+seq-cst-trailing-fence -verify-machineinstrs < %s \ +; RUN: llc -mtriple=riscv32 -mattr=+a -verify-machineinstrs < %s \ ; RUN: | FileCheck -check-prefixes=RV32IA,RV32IA-WMO-TRAILING-FENCE %s -; RUN: llc -mtriple=riscv32 -mattr=+a,+experimental-ztso,+seq-cst-trailing-fence -verify-machineinstrs < %s \ +; RUN: llc -mtriple=riscv32 -mattr=+a,+experimental-ztso -verify-machineinstrs < %s \ ; RUN: | FileCheck -check-prefixes=RV32IA,RV32IA-TSO-TRAILING-FENCE %s -; RUN: llc -mtriple=riscv64 -mattr=+a,+seq-cst-trailing-fence -verify-machineinstrs < %s \ +; RUN: llc -mtriple=riscv64 -mattr=+a -verify-machineinstrs < %s \ ; RUN: | FileCheck -check-prefixes=RV64IA,RV64IA-WMO-TRAILING-FENCE %s -; RUN: llc -mtriple=riscv64 -mattr=+a,+experimental-ztso,+seq-cst-trailing-fence -verify-machineinstrs < %s \ +; RUN: llc -mtriple=riscv64 -mattr=+a,+experimental-ztso -verify-machineinstrs < %s \ ; RUN: | FileCheck -check-prefixes=RV64IA,RV64IA-TSO-TRAILING-FENCE %s diff --git a/llvm/test/CodeGen/RISCV/attributes.ll b/llvm/test/CodeGen/RISCV/attributes.ll index 1aff3e8b83f4..61b5e50c6d52 100644 --- a/llvm/test/CodeGen/RISCV/attributes.ll +++ b/llvm/test/CodeGen/RISCV/attributes.ll @@ -129,8 +129,8 @@ ; RUN: llc -mtriple=riscv64 -mattr=+m %s -o - | FileCheck --check-prefixes=CHECK,RV64M %s ; RUN: llc -mtriple=riscv64 -mattr=+zmmul %s -o - | FileCheck --check-prefixes=CHECK,RV64ZMMUL %s ; RUN: llc -mtriple=riscv64 -mattr=+m,+zmmul %s -o - | FileCheck --check-prefixes=CHECK,RV64MZMMUL %s -; RUN: llc -mtriple=riscv64 -mattr=+a %s -o - | FileCheck --check-prefixes=CHECK,RV64A,A6C %s -; RUN: llc -mtriple=riscv64 -mattr=+a,+seq-cst-trailing-fence %s -o - | FileCheck --check-prefixes=CHECK,RV64A,A6S %s +; RUN: llc -mtriple=riscv64 -mattr=+a,no-trailing-seq-cst-fence %s -o - | FileCheck --check-prefixes=CHECK,RV64A,A6C %s +; RUN: llc -mtriple=riscv64 -mattr=+a %s -o - | FileCheck --check-prefixes=CHECK,RV64A,A6S %s ; RUN: llc -mtriple=riscv64 -mattr=+f %s -o - | FileCheck --check-prefixes=CHECK,RV64F %s ; RUN: llc -mtriple=riscv64 -mattr=+d %s -o - | FileCheck --check-prefixes=CHECK,RV64D %s ; RUN: llc -mtriple=riscv64 -mattr=+c %s -o - | FileCheck --check-prefixes=CHECK,RV64C %s diff --git a/llvm/test/CodeGen/RISCV/forced-atomics.ll b/llvm/test/CodeGen/RISCV/forced-atomics.ll index c303690aadff..44db3c49db8c 100644 --- a/llvm/test/CodeGen/RISCV/forced-atomics.ll +++ b/llvm/test/CodeGen/RISCV/forced-atomics.ll @@ -1,12 +1,12 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py -; RUN: llc -mtriple=riscv32 -mattr=+seq-cst-trailing-fence < %s | FileCheck %s --check-prefixes=RV32,RV32-NO-ATOMIC +; RUN: llc -mtriple=riscv32 -mattr=+no-trailing-seq-cst-fence < %s | FileCheck %s --check-prefixes=RV32,RV32-NO-ATOMIC ; RUN: llc -mtriple=riscv32 < %s | FileCheck %s --check-prefixes=RV32,RV32-NO-ATOMIC -; RUN: llc -mtriple=riscv32 -mattr=+forced-atomics < %s | FileCheck %s --check-prefixes=RV32,RV32-ATOMIC -; RUN: llc -mtriple=riscv32 -mattr=+forced-atomics,+seq-cst-trailing-fence < %s | FileCheck %s --check-prefixes=RV32,RV32-ATOMIC-TRAILING +; RUN: llc -mtriple=riscv32 -mattr=+forced-atomics,+no-trailing-seq-cst-fence < %s | FileCheck %s --check-prefixes=RV32,RV32-ATOMIC +; RUN: llc -mtriple=riscv32 -mattr=+forced-atomics < %s | FileCheck %s --check-prefixes=RV32,RV32-ATOMIC-TRAILING +; RUN: llc -mtriple=riscv64 -mattr=+no-trailing-seq-cst-fence < %s | FileCheck %s --check-prefixes=RV64,RV64-NO-ATOMIC ; RUN: llc -mtriple=riscv64 < %s | FileCheck %s --check-prefixes=RV64,RV64-NO-ATOMIC -; RUN: llc -mtriple=riscv64 -mattr=+seq-cst-trailing-fence < %s | FileCheck %s --check-prefixes=RV64,RV64-NO-ATOMIC -; RUN: llc -mtriple=riscv64 -mattr=+forced-atomics < %s | FileCheck %s --check-prefixes=RV64,RV64-ATOMIC -; RUN: llc -mtriple=riscv64 -mattr=+forced-atomics,+seq-cst-trailing-fence < %s | FileCheck %s --check-prefixes=RV64,RV64-ATOMIC-TRAILING +; RUN: llc -mtriple=riscv64 -mattr=+forced-atomics,+no-trailing-seq-cst-fence < %s | FileCheck %s --check-prefixes=RV64,RV64-ATOMIC +; RUN: llc -mtriple=riscv64 -mattr=+forced-atomics < %s | FileCheck %s --check-prefixes=RV64,RV64-ATOMIC-TRAILING define i8 @load8(ptr %p) nounwind { ; RV32-NO-ATOMIC-LABEL: load8: -- GitLab From 5f67ce5611ba007ed363b6a78b9c4eac85b70837 Mon Sep 17 00:00:00 2001 From: Min-Yih Hsu Date: Thu, 25 Apr 2024 16:36:11 -0700 Subject: [PATCH 005/293] [RISCV][MachineCombiner] Add reassociation optimizations for RVV instructions (#88307) This patch covers a really basic reassociation optimizations for VADD_VV and VMUL_VV. --- llvm/lib/Target/RISCV/RISCVInstrInfo.cpp | 266 ++++++++++++++++++ llvm/lib/Target/RISCV/RISCVInstrInfo.h | 14 + .../RISCV/rvv/vector-reassociations.ll | 27 +- 3 files changed, 293 insertions(+), 14 deletions(-) diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp index 5c1f154efa99..3efd09aeae87 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -1633,8 +1633,230 @@ static bool isFMUL(unsigned Opc) { } } +bool RISCVInstrInfo::isVectorAssociativeAndCommutative(const MachineInstr &Inst, + bool Invert) const { +#define OPCODE_LMUL_CASE(OPC) \ + case RISCV::OPC##_M1: \ + case RISCV::OPC##_M2: \ + case RISCV::OPC##_M4: \ + case RISCV::OPC##_M8: \ + case RISCV::OPC##_MF2: \ + case RISCV::OPC##_MF4: \ + case RISCV::OPC##_MF8 + +#define OPCODE_LMUL_MASK_CASE(OPC) \ + case RISCV::OPC##_M1_MASK: \ + case RISCV::OPC##_M2_MASK: \ + case RISCV::OPC##_M4_MASK: \ + case RISCV::OPC##_M8_MASK: \ + case RISCV::OPC##_MF2_MASK: \ + case RISCV::OPC##_MF4_MASK: \ + case RISCV::OPC##_MF8_MASK + + unsigned Opcode = Inst.getOpcode(); + if (Invert) { + if (auto InvOpcode = getInverseOpcode(Opcode)) + Opcode = *InvOpcode; + else + return false; + } + + // clang-format off + switch (Opcode) { + default: + return false; + OPCODE_LMUL_CASE(PseudoVADD_VV): + OPCODE_LMUL_MASK_CASE(PseudoVADD_VV): + OPCODE_LMUL_CASE(PseudoVMUL_VV): + OPCODE_LMUL_MASK_CASE(PseudoVMUL_VV): + return true; + } + // clang-format on + +#undef OPCODE_LMUL_MASK_CASE +#undef OPCODE_LMUL_CASE +} + +bool RISCVInstrInfo::areRVVInstsReassociable(const MachineInstr &Root, + const MachineInstr &Prev) const { + if (!areOpcodesEqualOrInverse(Root.getOpcode(), Prev.getOpcode())) + return false; + + assert(Root.getMF() == Prev.getMF()); + const MachineRegisterInfo *MRI = &Root.getMF()->getRegInfo(); + const TargetRegisterInfo *TRI = MRI->getTargetRegisterInfo(); + + // Make sure vtype operands are also the same. + const MCInstrDesc &Desc = get(Root.getOpcode()); + const uint64_t TSFlags = Desc.TSFlags; + + auto checkImmOperand = [&](unsigned OpIdx) { + return Root.getOperand(OpIdx).getImm() == Prev.getOperand(OpIdx).getImm(); + }; + + auto checkRegOperand = [&](unsigned OpIdx) { + return Root.getOperand(OpIdx).getReg() == Prev.getOperand(OpIdx).getReg(); + }; + + // PassThru + // TODO: Potentially we can loosen the condition to consider Root to be + // associable with Prev if Root has NoReg as passthru. In which case we + // also need to loosen the condition on vector policies between these. + if (!checkRegOperand(1)) + return false; + + // SEW + if (RISCVII::hasSEWOp(TSFlags) && + !checkImmOperand(RISCVII::getSEWOpNum(Desc))) + return false; + + // Mask + if (RISCVII::usesMaskPolicy(TSFlags)) { + const MachineBasicBlock *MBB = Root.getParent(); + const MachineBasicBlock::const_reverse_iterator It1(&Root); + const MachineBasicBlock::const_reverse_iterator It2(&Prev); + Register MI1VReg; + + bool SeenMI2 = false; + for (auto End = MBB->rend(), It = It1; It != End; ++It) { + if (It == It2) { + SeenMI2 = true; + if (!MI1VReg.isValid()) + // There is no V0 def between Root and Prev; they're sharing the + // same V0. + break; + } + + if (It->modifiesRegister(RISCV::V0, TRI)) { + Register SrcReg = It->getOperand(1).getReg(); + // If it's not VReg it'll be more difficult to track its defs, so + // bailing out here just to be safe. + if (!SrcReg.isVirtual()) + return false; + + if (!MI1VReg.isValid()) { + // This is the V0 def for Root. + MI1VReg = SrcReg; + continue; + } + + // Some random mask updates. + if (!SeenMI2) + continue; + + // This is the V0 def for Prev; check if it's the same as that of + // Root. + if (MI1VReg != SrcReg) + return false; + else + break; + } + } + + // If we haven't encountered Prev, it's likely that this function was + // called in a wrong way (e.g. Root is before Prev). + assert(SeenMI2 && "Prev is expected to appear before Root"); + } + + // Tail / Mask policies + if (RISCVII::hasVecPolicyOp(TSFlags) && + !checkImmOperand(RISCVII::getVecPolicyOpNum(Desc))) + return false; + + // VL + if (RISCVII::hasVLOp(TSFlags)) { + unsigned OpIdx = RISCVII::getVLOpNum(Desc); + const MachineOperand &Op1 = Root.getOperand(OpIdx); + const MachineOperand &Op2 = Prev.getOperand(OpIdx); + if (Op1.getType() != Op2.getType()) + return false; + switch (Op1.getType()) { + case MachineOperand::MO_Register: + if (Op1.getReg() != Op2.getReg()) + return false; + break; + case MachineOperand::MO_Immediate: + if (Op1.getImm() != Op2.getImm()) + return false; + break; + default: + llvm_unreachable("Unrecognized VL operand type"); + } + } + + // Rounding modes + if (RISCVII::hasRoundModeOp(TSFlags) && + !checkImmOperand(RISCVII::getVLOpNum(Desc) - 1)) + return false; + + return true; +} + +// Most of our RVV pseudos have passthru operand, so the real operands +// start from index = 2. +bool RISCVInstrInfo::hasReassociableVectorSibling(const MachineInstr &Inst, + bool &Commuted) const { + const MachineBasicBlock *MBB = Inst.getParent(); + const MachineRegisterInfo &MRI = MBB->getParent()->getRegInfo(); + assert(RISCVII::isFirstDefTiedToFirstUse(get(Inst.getOpcode())) && + "Expect the present of passthrough operand."); + MachineInstr *MI1 = MRI.getUniqueVRegDef(Inst.getOperand(2).getReg()); + MachineInstr *MI2 = MRI.getUniqueVRegDef(Inst.getOperand(3).getReg()); + + // If only one operand has the same or inverse opcode and it's the second + // source operand, the operands must be commuted. + Commuted = !areRVVInstsReassociable(Inst, *MI1) && + areRVVInstsReassociable(Inst, *MI2); + if (Commuted) + std::swap(MI1, MI2); + + return areRVVInstsReassociable(Inst, *MI1) && + (isVectorAssociativeAndCommutative(*MI1) || + isVectorAssociativeAndCommutative(*MI1, /* Invert */ true)) && + hasReassociableOperands(*MI1, MBB) && + MRI.hasOneNonDBGUse(MI1->getOperand(0).getReg()); +} + +bool RISCVInstrInfo::hasReassociableOperands( + const MachineInstr &Inst, const MachineBasicBlock *MBB) const { + if (!isVectorAssociativeAndCommutative(Inst) && + !isVectorAssociativeAndCommutative(Inst, /*Invert=*/true)) + return TargetInstrInfo::hasReassociableOperands(Inst, MBB); + + const MachineOperand &Op1 = Inst.getOperand(2); + const MachineOperand &Op2 = Inst.getOperand(3); + const MachineRegisterInfo &MRI = MBB->getParent()->getRegInfo(); + + // We need virtual register definitions for the operands that we will + // reassociate. + MachineInstr *MI1 = nullptr; + MachineInstr *MI2 = nullptr; + if (Op1.isReg() && Op1.getReg().isVirtual()) + MI1 = MRI.getUniqueVRegDef(Op1.getReg()); + if (Op2.isReg() && Op2.getReg().isVirtual()) + MI2 = MRI.getUniqueVRegDef(Op2.getReg()); + + // And at least one operand must be defined in MBB. + return MI1 && MI2 && (MI1->getParent() == MBB || MI2->getParent() == MBB); +} + +void RISCVInstrInfo::getReassociateOperandIndices( + const MachineInstr &Root, unsigned Pattern, + std::array &OperandIndices) const { + TargetInstrInfo::getReassociateOperandIndices(Root, Pattern, OperandIndices); + if (RISCV::getRVVMCOpcode(Root.getOpcode())) { + // Skip the passthrough operand, so increment all indices by one. + for (unsigned I = 0; I < 5; ++I) + ++OperandIndices[I]; + } +} + bool RISCVInstrInfo::hasReassociableSibling(const MachineInstr &Inst, bool &Commuted) const { + if (isVectorAssociativeAndCommutative(Inst) || + isVectorAssociativeAndCommutative(Inst, /*Invert=*/true)) + return hasReassociableVectorSibling(Inst, Commuted); + if (!TargetInstrInfo::hasReassociableSibling(Inst, Commuted)) return false; @@ -1654,6 +1876,9 @@ bool RISCVInstrInfo::hasReassociableSibling(const MachineInstr &Inst, bool RISCVInstrInfo::isAssociativeAndCommutative(const MachineInstr &Inst, bool Invert) const { + if (isVectorAssociativeAndCommutative(Inst, Invert)) + return true; + unsigned Opc = Inst.getOpcode(); if (Invert) { auto InverseOpcode = getInverseOpcode(Opc); @@ -1706,6 +1931,38 @@ bool RISCVInstrInfo::isAssociativeAndCommutative(const MachineInstr &Inst, std::optional RISCVInstrInfo::getInverseOpcode(unsigned Opcode) const { +#define RVV_OPC_LMUL_CASE(OPC, INV) \ + case RISCV::OPC##_M1: \ + return RISCV::INV##_M1; \ + case RISCV::OPC##_M2: \ + return RISCV::INV##_M2; \ + case RISCV::OPC##_M4: \ + return RISCV::INV##_M4; \ + case RISCV::OPC##_M8: \ + return RISCV::INV##_M8; \ + case RISCV::OPC##_MF2: \ + return RISCV::INV##_MF2; \ + case RISCV::OPC##_MF4: \ + return RISCV::INV##_MF4; \ + case RISCV::OPC##_MF8: \ + return RISCV::INV##_MF8 + +#define RVV_OPC_LMUL_MASK_CASE(OPC, INV) \ + case RISCV::OPC##_M1_MASK: \ + return RISCV::INV##_M1_MASK; \ + case RISCV::OPC##_M2_MASK: \ + return RISCV::INV##_M2_MASK; \ + case RISCV::OPC##_M4_MASK: \ + return RISCV::INV##_M4_MASK; \ + case RISCV::OPC##_M8_MASK: \ + return RISCV::INV##_M8_MASK; \ + case RISCV::OPC##_MF2_MASK: \ + return RISCV::INV##_MF2_MASK; \ + case RISCV::OPC##_MF4_MASK: \ + return RISCV::INV##_MF4_MASK; \ + case RISCV::OPC##_MF8_MASK: \ + return RISCV::INV##_MF8_MASK + switch (Opcode) { default: return std::nullopt; @@ -1729,7 +1986,16 @@ RISCVInstrInfo::getInverseOpcode(unsigned Opcode) const { return RISCV::SUBW; case RISCV::SUBW: return RISCV::ADDW; + // clang-format off + RVV_OPC_LMUL_CASE(PseudoVADD_VV, PseudoVSUB_VV); + RVV_OPC_LMUL_MASK_CASE(PseudoVADD_VV, PseudoVSUB_VV); + RVV_OPC_LMUL_CASE(PseudoVSUB_VV, PseudoVADD_VV); + RVV_OPC_LMUL_MASK_CASE(PseudoVSUB_VV, PseudoVADD_VV); + // clang-format on } + +#undef RVV_OPC_LMUL_MASK_CASE +#undef RVV_OPC_LMUL_CASE } static bool canCombineFPFusedMultiply(const MachineInstr &Root, diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.h b/llvm/lib/Target/RISCV/RISCVInstrInfo.h index 3b03d5efde6e..170f813eb10d 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.h +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.h @@ -266,6 +266,9 @@ public: SmallVectorImpl &DelInstrs, DenseMap &InstrIdxForVirtReg) const override; + bool hasReassociableOperands(const MachineInstr &Inst, + const MachineBasicBlock *MBB) const override; + bool hasReassociableSibling(const MachineInstr &Inst, bool &Commuted) const override; @@ -274,6 +277,10 @@ public: std::optional getInverseOpcode(unsigned Opcode) const override; + void getReassociateOperandIndices( + const MachineInstr &Root, unsigned Pattern, + std::array &OperandIndices) const override; + ArrayRef> getSerializableMachineMemOperandTargetFlags() const override; @@ -297,6 +304,13 @@ protected: private: unsigned getInstBundleLength(const MachineInstr &MI) const; + + bool isVectorAssociativeAndCommutative(const MachineInstr &MI, + bool Invert = false) const; + bool areRVVInstsReassociable(const MachineInstr &MI1, + const MachineInstr &MI2) const; + bool hasReassociableVectorSibling(const MachineInstr &Inst, + bool &Commuted) const; }; namespace RISCV { diff --git a/llvm/test/CodeGen/RISCV/rvv/vector-reassociations.ll b/llvm/test/CodeGen/RISCV/rvv/vector-reassociations.ll index 3cb6f3c35286..6435c1c14e06 100644 --- a/llvm/test/CodeGen/RISCV/rvv/vector-reassociations.ll +++ b/llvm/test/CodeGen/RISCV/rvv/vector-reassociations.ll @@ -31,7 +31,7 @@ define @simple_vadd_vv( %0, ; CHECK: # %bb.0: # %entry ; CHECK-NEXT: vsetvli zero, a0, e8, mf8, ta, ma ; CHECK-NEXT: vadd.vv v9, v8, v9 -; CHECK-NEXT: vadd.vv v9, v8, v9 +; CHECK-NEXT: vadd.vv v8, v8, v8 ; CHECK-NEXT: vadd.vv v8, v8, v9 ; CHECK-NEXT: ret entry: @@ -61,7 +61,7 @@ define @simple_vadd_vsub_vv( %0, @simple_vmul_vv( %0, ; CHECK: # %bb.0: # %entry ; CHECK-NEXT: vsetvli zero, a0, e8, mf8, ta, ma ; CHECK-NEXT: vmul.vv v9, v8, v9 -; CHECK-NEXT: vmul.vv v9, v8, v9 +; CHECK-NEXT: vmul.vv v8, v8, v8 ; CHECK-NEXT: vmul.vv v8, v8, v9 ; CHECK-NEXT: ret entry: @@ -124,8 +124,8 @@ define @vadd_vv_passthru( %0, @llvm.riscv.vadd.nxv1i8.nxv1i8( @@ -187,8 +187,8 @@ define @vadd_vv_mask( %0, % ; CHECK-NEXT: vmv1r.v v10, v8 ; CHECK-NEXT: vadd.vv v10, v8, v9, v0.t ; CHECK-NEXT: vmv1r.v v9, v8 -; CHECK-NEXT: vadd.vv v9, v8, v10, v0.t -; CHECK-NEXT: vadd.vv v8, v8, v9, v0.t +; CHECK-NEXT: vadd.vv v9, v8, v8, v0.t +; CHECK-NEXT: vadd.vv v8, v9, v10, v0.t ; CHECK-NEXT: ret entry: %a = call @llvm.riscv.vadd.mask.nxv1i8.nxv1i8( @@ -215,15 +215,16 @@ entry: ret %c } -define @vadd_vv_mask_negative( %0, %1, i32 %2, %m) nounwind { +define @vadd_vv_mask_negative( %0, %1, i32 %2, %m, %m2) nounwind { ; CHECK-LABEL: vadd_vv_mask_negative: ; CHECK: # %bb.0: # %entry ; CHECK-NEXT: vsetvli zero, a0, e8, mf8, ta, mu -; CHECK-NEXT: vmv1r.v v10, v8 -; CHECK-NEXT: vadd.vv v10, v8, v9, v0.t +; CHECK-NEXT: vmv1r.v v11, v8 +; CHECK-NEXT: vadd.vv v11, v8, v9, v0.t ; CHECK-NEXT: vmv1r.v v9, v8 -; CHECK-NEXT: vadd.vv v9, v8, v10, v0.t -; CHECK-NEXT: vadd.vv v8, v8, v9 +; CHECK-NEXT: vadd.vv v9, v8, v11, v0.t +; CHECK-NEXT: vmv1r.v v0, v10 +; CHECK-NEXT: vadd.vv v8, v8, v9, v0.t ; CHECK-NEXT: ret entry: %a = call @llvm.riscv.vadd.mask.nxv1i8.nxv1i8( @@ -240,8 +241,6 @@ entry: %m, i32 %2, i32 1) - %splat = insertelement poison, i1 1, i32 0 - %m2 = shufflevector %splat, poison, zeroinitializer %c = call @llvm.riscv.vadd.mask.nxv1i8.nxv1i8( %0, %0, -- GitLab From eb7dc991841489e2f8f18467705944c9136b06d2 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 25 Apr 2024 16:42:33 -0700 Subject: [PATCH 006/293] [lldb] Add SBValue::GetValueAsAddress API (#90144) I previously added this API via https://reviews.llvm.org/D142792 in 2023, along with changes to the ValueObject class to treat pointer types as addresses, and to annotate those ValueObjects with the original uint64_t byte sequence AND the name of the symbol once stripped, if that points to a symbol. I did this unconditionally for all pointer type ValueObjects, and it caused several regressions in the Objective-C data formatters which have a ValueObject of an object, it has the address of its class -- but with ObjC, sometimes it is a "tagged pointer" which is metadata, not an actual pointer. (e.g. a small NSInteger value is stored entirely in the tagged pointer, instead of a separate object) Treating these not-addresses as addresses -- clearing the non-addressable-bits -- is invalid. The original version of this patch we're using downstream only does this bits clearing for pointer types that are specifically decorated with the pointerauth typequal, but not all of those clang changes are upstreamed to github main yet, so I tried this simpler approach and hit the tagged pointer issue and bailed on the whole patch. This patch, however, is simply adding SBValue::GetValueAsAddress so script writers who know that an SBValue has an address in memory, can strip off any metadata. It's an important API to have for script writers when AArch64 ptrauth is in use, so I'm going to put this part of the patch back on github main now until we can get the rest of that original patch upstreamed. --- lldb/bindings/interface/SBValueDocstrings.i | 20 +++++++ lldb/include/lldb/API/SBValue.h | 2 + lldb/source/API/SBValue.cpp | 19 ++++++ .../Makefile | 3 + .../TestClearSBValueNonAddressableBits.py | 59 +++++++++++++++++++ .../clear-sbvalue-nonaddressable-bits/main.c | 27 +++++++++ 6 files changed, 130 insertions(+) create mode 100644 lldb/test/API/clear-sbvalue-nonaddressable-bits/Makefile create mode 100644 lldb/test/API/clear-sbvalue-nonaddressable-bits/TestClearSBValueNonAddressableBits.py create mode 100644 lldb/test/API/clear-sbvalue-nonaddressable-bits/main.c diff --git a/lldb/bindings/interface/SBValueDocstrings.i b/lldb/bindings/interface/SBValueDocstrings.i index 6bab923e8b35..59fa807f5ec9 100644 --- a/lldb/bindings/interface/SBValueDocstrings.i +++ b/lldb/bindings/interface/SBValueDocstrings.i @@ -135,6 +135,26 @@ linked list." %feature("docstring", "Expands nested expressions like .a->b[0].c[1]->d." ) lldb::SBValue::GetValueForExpressionPath; +%feature("docstring", " + Return the value as an address. On failure, LLDB_INVALID_ADDRESS + will be returned. On architectures like AArch64, where the + top (unaddressable) bits can be used for authentication, + memory tagging, or top byte ignore, this method will return + the value with those top bits cleared. + + GetValueAsUnsigned returns the actual value, with the + authentication/Top Byte Ignore/Memory Tagging Extension bits. + + Calling this on a random value which is not a pointer is + incorrect. Call GetType().IsPointerType() if in doubt. + + An SB API program may want to show both the literal byte value + and the address it refers to in memory. These two SBValue + methods allow SB API writers to behave appropriately for their + interface." +) lldb::SBValue::GetValueAsAddress; + + %feature("doctstring", " Returns the number for children. diff --git a/lldb/include/lldb/API/SBValue.h b/lldb/include/lldb/API/SBValue.h index 67f55ce7da28..8f4c4fd56dfb 100644 --- a/lldb/include/lldb/API/SBValue.h +++ b/lldb/include/lldb/API/SBValue.h @@ -68,6 +68,8 @@ public: uint64_t GetValueAsUnsigned(uint64_t fail_value = 0); + lldb::addr_t GetValueAsAddress(); + ValueType GetValueType(); // If you call this on a newly created ValueObject, it will always return diff --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp index 94a8f3ea319e..c53ec5a74648 100644 --- a/lldb/source/API/SBValue.cpp +++ b/lldb/source/API/SBValue.cpp @@ -909,6 +909,25 @@ uint64_t SBValue::GetValueAsUnsigned(uint64_t fail_value) { return fail_value; } +lldb::addr_t SBValue::GetValueAsAddress() { + addr_t fail_value = LLDB_INVALID_ADDRESS; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) { + bool success = true; + uint64_t ret_val = fail_value; + ret_val = value_sp->GetValueAsUnsigned(fail_value, &success); + if (!success) + return fail_value; + ProcessSP process_sp = m_opaque_sp->GetProcessSP(); + if (!process_sp) + return ret_val; + return process_sp->FixDataAddress(ret_val); + } + + return fail_value; +} + bool SBValue::MightHaveChildren() { LLDB_INSTRUMENT_VA(this); diff --git a/lldb/test/API/clear-sbvalue-nonaddressable-bits/Makefile b/lldb/test/API/clear-sbvalue-nonaddressable-bits/Makefile new file mode 100644 index 000000000000..10495940055b --- /dev/null +++ b/lldb/test/API/clear-sbvalue-nonaddressable-bits/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c + +include Makefile.rules diff --git a/lldb/test/API/clear-sbvalue-nonaddressable-bits/TestClearSBValueNonAddressableBits.py b/lldb/test/API/clear-sbvalue-nonaddressable-bits/TestClearSBValueNonAddressableBits.py new file mode 100644 index 000000000000..382b0e7a81d2 --- /dev/null +++ b/lldb/test/API/clear-sbvalue-nonaddressable-bits/TestClearSBValueNonAddressableBits.py @@ -0,0 +1,59 @@ +"""Test that SBValue clears non-addressable bits""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestClearSBValueNonAddressableBits(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + # On AArch64 systems, the top bits that are not used for + # addressing may be used for TBI, MTE, and/or pointer + # authentication. + @skipIf(archs=no_match(["aarch64", "arm64", "arm64e"])) + + # Only run this test on systems where TBI is known to be + # enabled, so the address mask will clear the TBI bits. + @skipUnlessPlatform(["linux"] + lldbplatformutil.getDarwinOSTriples()) + def test(self): + self.source = "main.c" + self.build() + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "break here", lldb.SBFileSpec(self.source, False) + ) + + if self.TraceOn(): + self.runCmd("frame variable") + self.runCmd("frame variable &count &global") + + frame = thread.GetFrameAtIndex(0) + + count_p = frame.FindVariable("count_p") + count_invalid_p = frame.FindVariable("count_invalid_p") + self.assertEqual( + count_p.GetValueAsUnsigned(), count_invalid_p.GetValueAsAddress() + ) + self.assertNotEqual( + count_invalid_p.GetValueAsUnsigned(), count_invalid_p.GetValueAsAddress() + ) + self.assertEqual(5, count_p.Dereference().GetValueAsUnsigned()) + self.assertEqual(5, count_invalid_p.Dereference().GetValueAsUnsigned()) + + global_p = frame.FindVariable("global_p") + global_invalid_p = frame.FindVariable("global_invalid_p") + self.assertEqual( + global_p.GetValueAsUnsigned(), global_invalid_p.GetValueAsAddress() + ) + self.assertNotEqual( + global_invalid_p.GetValueAsUnsigned(), global_invalid_p.GetValueAsAddress() + ) + self.assertEqual(10, global_p.Dereference().GetValueAsUnsigned()) + self.assertEqual(10, global_invalid_p.Dereference().GetValueAsUnsigned()) + + main_p = frame.FindVariable("main_p") + main_invalid_p = frame.FindVariable("main_invalid_p") + self.assertEqual( + main_p.GetValueAsUnsigned(), main_invalid_p.GetValueAsAddress() + ) diff --git a/lldb/test/API/clear-sbvalue-nonaddressable-bits/main.c b/lldb/test/API/clear-sbvalue-nonaddressable-bits/main.c new file mode 100644 index 000000000000..1b0e42c50dd6 --- /dev/null +++ b/lldb/test/API/clear-sbvalue-nonaddressable-bits/main.c @@ -0,0 +1,27 @@ +#include + +int global = 10; + +int main() { + int count = 5; + int *count_p = &count; + + // Add some metadata in the top byte (this will crash unless the + // test is running with TBI enabled, but we won't dereference it) + + intptr_t scratch = (intptr_t)count_p; + scratch |= (3ULL << 60); + int *count_invalid_p = (int *)scratch; + + int (*main_p)() = main; + scratch = (intptr_t)main_p; + scratch |= (3ULL << 60); + int (*main_invalid_p)() = (int (*)())scratch; + + int *global_p = &global; + scratch = (intptr_t)global_p; + scratch |= (3ULL << 60); + int *global_invalid_p = (int *)scratch; + + return count; // break here +} -- GitLab From bb2b04c73332f017955115fedb94790dac3608b9 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 25 Apr 2024 16:42:47 -0700 Subject: [PATCH 007/293] [lldb] Recognize DW_TAG_LLVM_ptrauth_type as a type qual (#90140) Jonas upstreamed recognition of DW_TAG_LLVM_ptrauth_type https://reviews.llvm.org/D130215 but it isn't recognized as a type qualifier tag in DWARFASTParserClang::ParseTypeFromDWARF. --- lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 41d81fbcf1b0..12dafd3f5d5d 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -495,6 +495,7 @@ TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc, case DW_TAG_const_type: case DW_TAG_restrict_type: case DW_TAG_volatile_type: + case DW_TAG_LLVM_ptrauth_type: case DW_TAG_atomic_type: case DW_TAG_unspecified_type: { type_sp = ParseTypeModifier(sc, die, attrs); -- GitLab From 5350052632fa3362a1ce89821703a96bc0066f26 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Fri, 26 Apr 2024 08:03:10 +0800 Subject: [PATCH 008/293] [RISCV] Move doUnion into DemandedFields. NFC Keep the DemandedFields logic grouped together in the struct --- llvm/lib/Target/RISCV/RISCVInsertVSETVLI.cpp | 23 ++++++++++---------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/llvm/lib/Target/RISCV/RISCVInsertVSETVLI.cpp b/llvm/lib/Target/RISCV/RISCVInsertVSETVLI.cpp index 3d598dd6f708..3c528a098d7a 100644 --- a/llvm/lib/Target/RISCV/RISCVInsertVSETVLI.cpp +++ b/llvm/lib/Target/RISCV/RISCVInsertVSETVLI.cpp @@ -262,6 +262,17 @@ struct DemandedFields { VLZeroness = true; } + // Make this the result of demanding both the fields in this and B. + void doUnion(const DemandedFields &B) { + VLAny |= B.VLAny; + VLZeroness |= B.VLZeroness; + SEW = std::max(SEW, B.SEW); + LMUL |= B.LMUL; + SEWLMULRatio |= B.SEWLMULRatio; + TailPolicy |= B.TailPolicy; + MaskPolicy |= B.MaskPolicy; + } + #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) /// Support for debugging, callable in GDB: V->dump() LLVM_DUMP_METHOD void dump() const { @@ -1547,16 +1558,6 @@ void RISCVInsertVSETVLI::doPRE(MachineBasicBlock &MBB) { AvailableInfo, OldExit); } -static void doUnion(DemandedFields &A, DemandedFields B) { - A.VLAny |= B.VLAny; - A.VLZeroness |= B.VLZeroness; - A.SEW = std::max(A.SEW, B.SEW); - A.LMUL |= B.LMUL; - A.SEWLMULRatio |= B.SEWLMULRatio; - A.TailPolicy |= B.TailPolicy; - A.MaskPolicy |= B.MaskPolicy; -} - // Return true if we can mutate PrevMI to match MI without changing any the // fields which would be observed. static bool canMutatePriorConfig(const MachineInstr &PrevMI, @@ -1606,7 +1607,7 @@ bool RISCVCoalesceVSETVLI::coalesceVSETVLIs(MachineBasicBlock &MBB) { for (MachineInstr &MI : make_range(MBB.rbegin(), MBB.rend())) { if (!isVectorConfigInstr(MI)) { - doUnion(Used, getDemanded(MI, MRI, ST)); + Used.doUnion(getDemanded(MI, MRI, ST)); if (MI.isCall() || MI.isInlineAsm() || MI.modifiesRegister(RISCV::VL, /*TRI=*/nullptr) || MI.modifiesRegister(RISCV::VTYPE, /*TRI=*/nullptr)) -- GitLab From 8feedd5e067a5ea13e36c39dc634da5c34284ddd Mon Sep 17 00:00:00 2001 From: Prashant Kumar Date: Fri, 26 Apr 2024 05:39:00 +0530 Subject: [PATCH 009/293] [mlir][linalg] Fix the semantic use of a flag (#90081) `useInBoundsInsteadOfMasking` was doing the opposite i.e., when set to true; was updating the mask instead of updating the inBounds. --- mlir/include/mlir/Dialect/Vector/Utils/VectorUtils.h | 2 +- mlir/lib/Dialect/Linalg/Transforms/Vectorization.cpp | 10 ++++++---- mlir/lib/Dialect/Vector/Utils/VectorUtils.cpp | 4 ++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/mlir/include/mlir/Dialect/Vector/Utils/VectorUtils.h b/mlir/include/mlir/Dialect/Vector/Utils/VectorUtils.h index 8a57c6094c41..030be328e97f 100644 --- a/mlir/include/mlir/Dialect/Vector/Utils/VectorUtils.h +++ b/mlir/include/mlir/Dialect/Vector/Utils/VectorUtils.h @@ -194,7 +194,7 @@ bool isLinearizableVector(VectorType type); /// for each dimension of the passed in tensor. Value createReadOrMaskedRead(OpBuilder &builder, Location loc, Value source, ArrayRef readShape, Value padValue, - bool useInBoundsInsteadOfMasking = true); + bool useInBoundsInsteadOfMasking); /// Returns success if `inputVectorSizes` is a valid masking configuraion for /// given `shape`, i.e., it meets: diff --git a/mlir/lib/Dialect/Linalg/Transforms/Vectorization.cpp b/mlir/lib/Dialect/Linalg/Transforms/Vectorization.cpp index e836f0dc63b4..ef9a30be9a01 100644 --- a/mlir/lib/Dialect/Linalg/Transforms/Vectorization.cpp +++ b/mlir/lib/Dialect/Linalg/Transforms/Vectorization.cpp @@ -1499,11 +1499,11 @@ vectorizeAsTensorPackOp(RewriterBase &rewriter, tensor::PackOp packOp, // If the input vector sizes are not provided, then the vector sizes are // determined by the result tensor shape. In case the vector sizes aren't // provided, we update the inBounds attribute instead of masking. - bool useInBoundsInsteadOfMasking = true; + bool useInBoundsInsteadOfMasking = false; if (inputVectorSizes.empty()) { ArrayRef resultTensorShape = packOp.getDestType().getShape(); inputVectorSizes = resultTensorShape.take_front(packOp.getSourceRank()); - useInBoundsInsteadOfMasking = false; + useInBoundsInsteadOfMasking = true; } // Create masked TransferReadOp. @@ -1612,7 +1612,8 @@ vectorizeAsTensorUnpackOp(RewriterBase &rewriter, tensor::UnPackOp unpackOp, // to shape of source, then a mask is necessary. Value readResult = vector::createReadOrMaskedRead( rewriter, loc, unpackOp.getSource(), - ArrayRef(readMaskShape.begin(), readMaskShape.end()), padValue); + ArrayRef(readMaskShape.begin(), readMaskShape.end()), padValue, + /*useInBoundsInsteadOfMasking=*/false); PackingMetadata packMetadata; SmallVector lastDimToInsertPosPerm = @@ -1669,7 +1670,8 @@ vectorizeAsTensorPadOp(RewriterBase &rewriter, tensor::PadOp padOp, (void)status; // prevent unused variable warning on non-assert builds assert(succeeded(status) && "failed to reify result shapes"); auto maskedRead = vector::createReadOrMaskedRead( - rewriter, loc, padOp.getSource(), inputVectorSizes, padValue); + rewriter, loc, padOp.getSource(), inputVectorSizes, padValue, + /*useInBoundsInsteadOfMasking=*/false); Operation *write = createWriteOrMaskedWrite( rewriter, loc, maskedRead, reifiedReturnShapes[0], inputVectorSizes); newResults.push_back(write->getResult(0)); diff --git a/mlir/lib/Dialect/Vector/Utils/VectorUtils.cpp b/mlir/lib/Dialect/Vector/Utils/VectorUtils.cpp index fcaf1ec944b4..6727f3f46172 100644 --- a/mlir/lib/Dialect/Vector/Utils/VectorUtils.cpp +++ b/mlir/lib/Dialect/Vector/Utils/VectorUtils.cpp @@ -345,7 +345,7 @@ Value vector::createReadOrMaskedRead(OpBuilder &builder, Location loc, int64_t readRank = readShape.size(); auto zero = builder.create(loc, 0); SmallVector inBoundsVal(readRank, true); - if (!useInBoundsInsteadOfMasking) { + if (useInBoundsInsteadOfMasking) { // Update the inBounds attribute. for (unsigned i = 0; i < readRank; i++) inBoundsVal[i] = (sourceShape[i] == readShape[i]) && @@ -359,7 +359,7 @@ Value vector::createReadOrMaskedRead(OpBuilder &builder, Location loc, /*padding=*/padValue, /*inBounds=*/inBoundsVal); - if (llvm::equal(readShape, sourceShape) || !useInBoundsInsteadOfMasking) + if (llvm::equal(readShape, sourceShape) || useInBoundsInsteadOfMasking) return transferReadOp; SmallVector mixedSourceDims = tensor::getMixedSizes(builder, loc, source); -- GitLab From 45fc0e6b38b62a61b0ddcda2e7fe9b4fee7e3e58 Mon Sep 17 00:00:00 2001 From: Pranav Kant Date: Fri, 26 Apr 2024 00:17:28 +0000 Subject: [PATCH 010/293] Revert "[Clang][Sema] Fix warnings after #84050 (#90104)" This reverts commit 6dd2617c80d5133b92fdff679364f2d8fcd93b47. --- clang/lib/Sema/SemaExprMember.cpp | 1 + clang/lib/Sema/SemaLookup.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index 14dde1bff8fb..0eeb7b1faa0a 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -1793,6 +1793,7 @@ ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base, DecomposeUnqualifiedId(Id, TemplateArgsBuffer, NameInfo, TemplateArgs); + DeclarationName Name = NameInfo.getName(); bool IsArrow = (OpKind == tok::arrow); if (getLangOpts().HLSL && IsArrow) diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 2f6ad49fc08b..a537eccc2eba 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -2791,7 +2791,7 @@ bool Sema::LookupParsedName(LookupResult &R, Scope *S, CXXScopeSpec *SS, return LookupInSuper(R, NNS->getAsRecordDecl()); // This nested-name-specifier occurs after another nested-name-specifier, // so long into the context associated with the prior nested-name-specifier. - if ((DC = computeDeclContext(*SS, EnteringContext))) { + if (DC = computeDeclContext(*SS, EnteringContext)) { // The declaration context must be complete. if (!DC->isDependentContext() && RequireCompleteDeclContext(*SS, DC)) return false; -- GitLab From 0c6e1ca1c704a3a0fb53ae54f7e3723736f477c7 Mon Sep 17 00:00:00 2001 From: Pranav Kant Date: Fri, 26 Apr 2024 00:18:08 +0000 Subject: [PATCH 011/293] Revert "[Clang][Sema] Diagnose class member access expressions naming non-existent members of the current instantiation prior to instantiation in the absence of dependent base classes (#84050)" This reverts commit a8fd0d029dca7d17eee72d0445223c2fe1ee7758. --- .../clangd/unittests/FindTargetTests.cpp | 8 +- .../unittests/SemanticHighlightingTests.cpp | 2 +- .../cppcoreguidelines/owning-memory.cpp | 2 - .../modernize/use-equals-default-copy.cpp | 12 - clang/docs/ReleaseNotes.rst | 12 - clang/include/clang/Sema/Lookup.h | 4 +- clang/include/clang/Sema/Sema.h | 14 +- clang/lib/AST/Expr.cpp | 2 +- clang/lib/Parse/ParseDecl.cpp | 2 +- clang/lib/Sema/HLSLExternalSemaSource.cpp | 7 +- clang/lib/Sema/SemaAttr.cpp | 2 +- clang/lib/Sema/SemaDecl.cpp | 7 +- clang/lib/Sema/SemaDeclCXX.cpp | 6 +- clang/lib/Sema/SemaExpr.cpp | 20 +- clang/lib/Sema/SemaExprCXX.cpp | 2 +- clang/lib/Sema/SemaExprMember.cpp | 182 ++++--- clang/lib/Sema/SemaLookup.cpp | 114 +---- clang/lib/Sema/SemaOpenMP.cpp | 17 +- clang/lib/Sema/SemaTemplate.cpp | 32 +- clang/lib/Sema/TreeTransform.h | 20 - .../AST/HLSL/this-reference-template.hlsl | 2 +- clang/test/CXX/drs/dr2xx.cpp | 10 +- clang/test/CXX/drs/dr3xx.cpp | 16 +- .../temp.res/temp.dep/temp.dep.type/p4.cpp | 456 ------------------ .../test/CXX/temp/temp.res/temp.local/p3.cpp | 3 +- clang/test/CodeGenCXX/mangle.cpp | 8 + .../Index/annotate-nested-name-specifier.cpp | 4 +- clang/test/SemaCXX/member-expr.cpp | 4 +- .../SemaTemplate/instantiate-function-1.cpp | 14 +- .../ASTMatchers/ASTMatchersNarrowingTest.cpp | 5 +- 30 files changed, 224 insertions(+), 765 deletions(-) delete mode 100644 clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp index 94437857cecc..799a549ff081 100644 --- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp @@ -854,7 +854,7 @@ TEST_F(TargetDeclTest, DependentExprs) { } }; )cpp"; - EXPECT_DECLS("MemberExpr", "void foo()"); + EXPECT_DECLS("CXXDependentScopeMemberExpr", "void foo()"); // Similar to above but base expression involves a function call. Code = R"cpp( @@ -872,7 +872,7 @@ TEST_F(TargetDeclTest, DependentExprs) { } }; )cpp"; - EXPECT_DECLS("MemberExpr", "void foo()"); + EXPECT_DECLS("CXXDependentScopeMemberExpr", "void foo()"); // Similar to above but uses a function pointer. Code = R"cpp( @@ -891,7 +891,7 @@ TEST_F(TargetDeclTest, DependentExprs) { } }; )cpp"; - EXPECT_DECLS("MemberExpr", "void foo()"); + EXPECT_DECLS("CXXDependentScopeMemberExpr", "void foo()"); // Base expression involves a member access into this. Code = R"cpp( @@ -962,7 +962,7 @@ TEST_F(TargetDeclTest, DependentExprs) { void Foo() { this->[[find]](); } }; )cpp"; - EXPECT_DECLS("MemberExpr", "void find()"); + EXPECT_DECLS("CXXDependentScopeMemberExpr", "void find()"); } TEST_F(TargetDeclTest, DependentTypes) { diff --git a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp index 30b9b1902aa9..4156921d83ed 100644 --- a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp +++ b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp @@ -621,7 +621,7 @@ sizeof...($TemplateParameter[[Elements]]); struct $Class_def[[Foo]] { int $Field_decl[[Waldo]]; void $Method_def[[bar]]() { - $Class[[Foo]]().$Field[[Waldo]]; + $Class[[Foo]]().$Field_dependentName[[Waldo]]; } template $Bracket[[<]]typename $TemplateParameter_def[[U]]$Bracket[[>]] void $Method_def[[bar1]]() { diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/owning-memory.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/owning-memory.cpp index ae61b17ca14d..574efe7bd914 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/owning-memory.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/owning-memory.cpp @@ -309,8 +309,6 @@ struct HeapArray { // Ok, since destruc HeapArray(HeapArray &&other) : _data(other._data), size(other.size) { // Ok other._data = nullptr; // Ok - // CHECK-NOTES: [[@LINE-1]]:5: warning: expected assignment source to be of type 'gsl::owner<>'; got 'std::nullptr_t' - // FIXME: This warning is emitted because an ImplicitCastExpr for the NullToPointer conversion isn't created for dependent types. other.size = 0; } diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-equals-default-copy.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-equals-default-copy.cpp index 4abb9c855597..559031cf4d9b 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-equals-default-copy.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-equals-default-copy.cpp @@ -260,8 +260,6 @@ template struct Template { Template() = default; Template(const Template &Other) : Field(Other.Field) {} - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' - // CHECK-FIXES: Template(const Template &Other) = default; Template &operator=(const Template &Other); void foo(const T &t); int Field; @@ -271,12 +269,8 @@ Template &Template::operator=(const Template &Other) { Field = Other.Field; return *this; } -// CHECK-MESSAGES: :[[@LINE-4]]:27: warning: use '= default' -// CHECK-FIXES: Template &Template::operator=(const Template &Other) = default; - Template T1; - // Dependent types. template struct DT1 { @@ -290,9 +284,6 @@ DT1 &DT1::operator=(const DT1 &Other) { Field = Other.Field; return *this; } -// CHECK-MESSAGES: :[[@LINE-4]]:17: warning: use '= default' -// CHECK-FIXES: DT1 &DT1::operator=(const DT1 &Other) = default; - DT1 Dt1; template @@ -312,9 +303,6 @@ DT2 &DT2::operator=(const DT2 &Other) { struct T { typedef int TT; }; -// CHECK-MESSAGES: :[[@LINE-8]]:17: warning: use '= default' -// CHECK-FIXES: DT2 &DT2::operator=(const DT2 &Other) = default; - DT2 Dt2; // Default arguments. diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 00c684e773a2..f5e5d3a2e6ea 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -385,18 +385,6 @@ Improvements to Clang's diagnostics - Clang now diagnoses requires expressions with explicit object parameters. -- Clang now looks up members of the current instantiation in the template definition context - if the current instantiation has no dependent base classes. - - .. code-block:: c++ - - template - struct A { - int f() { - return this->x; // error: no member named 'x' in 'A' - } - }; - Improvements to Clang's time-trace ---------------------------------- diff --git a/clang/include/clang/Sema/Lookup.h b/clang/include/clang/Sema/Lookup.h index b0a08a05ac6a..0db5b847038f 100644 --- a/clang/include/clang/Sema/Lookup.h +++ b/clang/include/clang/Sema/Lookup.h @@ -499,9 +499,7 @@ public: /// Note that while no result was found in the current instantiation, /// there were dependent base classes that could not be searched. void setNotFoundInCurrentInstantiation() { - assert((ResultKind == NotFound || - ResultKind == NotFoundInCurrentInstantiation) && - Decls.empty()); + assert(ResultKind == NotFound && Decls.empty()); ResultKind = NotFoundInCurrentInstantiation; } diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index aa182b15e66e..1ca523ec88c2 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -7472,7 +7472,7 @@ public: bool LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx, CXXScopeSpec &SS); bool LookupParsedName(LookupResult &R, Scope *S, CXXScopeSpec *SS, - QualType ObjectType, bool AllowBuiltinCreation = false, + bool AllowBuiltinCreation = false, bool EnteringContext = false); ObjCProtocolDecl *LookupProtocol( IdentifierInfo *II, SourceLocation IdLoc, @@ -8881,13 +8881,11 @@ public: /// functions (but no function templates). FoundFunctions, }; - - bool - LookupTemplateName(LookupResult &R, Scope *S, CXXScopeSpec &SS, - QualType ObjectType, bool EnteringContext, - RequiredTemplateKind RequiredTemplate = SourceLocation(), - AssumedTemplateKind *ATK = nullptr, - bool AllowTypoCorrection = true); + bool LookupTemplateName( + LookupResult &R, Scope *S, CXXScopeSpec &SS, QualType ObjectType, + bool EnteringContext, bool &MemberOfUnknownSpecialization, + RequiredTemplateKind RequiredTemplate = SourceLocation(), + AssumedTemplateKind *ATK = nullptr, bool AllowTypoCorrection = true); TemplateNameKind isTemplateName(Scope *S, CXXScopeSpec &SS, bool hasTemplateKeyword, diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index d2e40be59d6f..63dcdb919c71 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -103,7 +103,7 @@ const Expr *Expr::skipRValueSubobjectAdjustments( } } else if (const auto *ME = dyn_cast(E)) { if (!ME->isArrow()) { - assert(ME->getBase()->getType()->getAsRecordDecl()); + assert(ME->getBase()->getType()->isRecordType()); if (const auto *Field = dyn_cast(ME->getMemberDecl())) { if (!Field->isBitField() && !Field->getType()->isReferenceType()) { E = ME->getBase(); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 53a33fa4add5..05ad5ecbfaa0 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2998,7 +2998,7 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, << TokenName << TagName << getLangOpts().CPlusPlus << FixItHint::CreateInsertion(Tok.getLocation(), FixitTagName); - if (Actions.LookupName(R, getCurScope())) { + if (Actions.LookupParsedName(R, getCurScope(), SS)) { for (LookupResult::iterator I = R.begin(), IEnd = R.end(); I != IEnd; ++I) Diag((*I)->getLocation(), diag::note_decl_hiding_tag_type) diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index bb283c54b3d2..1a1febf7a352 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -126,15 +126,12 @@ struct BuiltinTypeDeclBuilder { static DeclRefExpr *lookupBuiltinFunction(ASTContext &AST, Sema &S, StringRef Name) { + CXXScopeSpec SS; IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier); DeclarationNameInfo NameInfo = DeclarationNameInfo(DeclarationName(&II), SourceLocation()); LookupResult R(S, NameInfo, Sema::LookupOrdinaryName); - // AllowBuiltinCreation is false but LookupDirect will create - // the builtin when searching the global scope anyways... - S.LookupName(R, S.getCurScope()); - // FIXME: If the builtin function was user-declared in global scope, - // this assert *will* fail. Should this call LookupBuiltin instead? + S.LookupParsedName(R, S.getCurScope(), &SS, false); assert(R.isSingleResult() && "Since this is a builtin it should always resolve!"); auto *VD = cast(R.getFoundDecl()); diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp index a83b1e8afadb..a5dd158808f2 100644 --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -837,7 +837,7 @@ void Sema::ActOnPragmaUnused(const Token &IdTok, Scope *curScope, IdentifierInfo *Name = IdTok.getIdentifierInfo(); LookupResult Lookup(*this, Name, IdTok.getLocation(), LookupOrdinaryName); - LookupName(Lookup, curScope, /*AllowBuiltinCreation=*/true); + LookupParsedName(Lookup, curScope, nullptr, true); if (Lookup.empty()) { Diag(PragmaLoc, diag::warn_pragma_unused_undeclared_var) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 4e275dc15fbb..e0745fe9a453 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -832,7 +832,7 @@ static bool isTagTypeWithMissingTag(Sema &SemaRef, LookupResult &Result, IdentifierInfo *&Name, SourceLocation NameLoc) { LookupResult R(SemaRef, Name, NameLoc, Sema::LookupTagName); - SemaRef.LookupParsedName(R, S, &SS, /*ObjectType=*/QualType()); + SemaRef.LookupParsedName(R, S, &SS); if (TagDecl *Tag = R.getAsSingle()) { StringRef FixItTagName; switch (Tag->getTagKind()) { @@ -869,7 +869,7 @@ static bool isTagTypeWithMissingTag(Sema &SemaRef, LookupResult &Result, // Replace lookup results with just the tag decl. Result.clear(Sema::LookupTagName); - SemaRef.LookupParsedName(Result, S, &SS, /*ObjectType=*/QualType()); + SemaRef.LookupParsedName(Result, S, &SS); return true; } @@ -896,8 +896,7 @@ Sema::NameClassification Sema::ClassifyName(Scope *S, CXXScopeSpec &SS, } LookupResult Result(*this, Name, NameLoc, LookupOrdinaryName); - LookupParsedName(Result, S, &SS, /*ObjectType=*/QualType(), - /*AllowBuiltinCreation=*/!CurMethod); + LookupParsedName(Result, S, &SS, !CurMethod); if (SS.isInvalid()) return NameClassification::Error(); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 4d5836720a65..abdbc9d8830c 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -4517,7 +4517,7 @@ Sema::BuildMemInitializer(Decl *ConstructorD, DS.getBeginLoc(), DS.getEllipsisLoc()); } else { LookupResult R(*this, MemberOrBase, IdLoc, LookupOrdinaryName); - LookupParsedName(R, S, &SS, /*ObjectType=*/QualType()); + LookupParsedName(R, S, &SS); TypeDecl *TyD = R.getAsSingle(); if (!TyD) { @@ -12262,7 +12262,7 @@ Decl *Sema::ActOnUsingDirective(Scope *S, SourceLocation UsingLoc, // Lookup namespace name. LookupResult R(*this, NamespcName, IdentLoc, LookupNamespaceName); - LookupParsedName(R, S, &SS, /*ObjectType=*/QualType()); + LookupParsedName(R, S, &SS); if (R.isAmbiguous()) return nullptr; @@ -13721,7 +13721,7 @@ Decl *Sema::ActOnNamespaceAliasDef(Scope *S, SourceLocation NamespaceLoc, // Lookup the namespace name. LookupResult R(*this, Ident, IdentLoc, LookupNamespaceName); - LookupParsedName(R, S, &SS, /*ObjectType=*/QualType()); + LookupParsedName(R, S, &SS); if (R.isAmbiguous()) return nullptr; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 0c37f43f7540..50f92c496a53 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -673,9 +673,8 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E) { // expressions of certain types in C++. if (getLangOpts().CPlusPlus && (E->getType() == Context.OverloadTy || - // FIXME: This is a hack! We want the lvalue-to-rvalue conversion applied - // to pointer types even if the pointee type is dependent. - (T->isDependentType() && !T->isPointerType()) || T->isRecordType())) + T->isDependentType() || + T->isRecordType())) return E; // The C standard is actually really unclear on this point, and @@ -2752,8 +2751,8 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS, if (isBoundsAttrContext() && !getLangOpts().CPlusPlus && S->isClassScope()) { // See if this is reference to a field of struct. LookupResult R(*this, NameInfo, LookupMemberName); - // LookupName handles a name lookup from within anonymous struct. - if (LookupName(R, S)) { + // LookupParsedName handles a name lookup from within anonymous struct. + if (LookupParsedName(R, S, &SS)) { if (auto *VD = dyn_cast(R.getFoundDecl())) { QualType type = VD->getType().getNonReferenceType(); // This will eventually be translated into MemberExpr upon @@ -2774,19 +2773,20 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS, // lookup to determine that it was a template name in the first place. If // this becomes a performance hit, we can work harder to preserve those // results until we get here but it's likely not worth it. + bool MemberOfUnknownSpecialization; AssumedTemplateKind AssumedTemplate; - if (LookupTemplateName(R, S, SS, /*ObjectType=*/QualType(), - /*EnteringContext=*/false, TemplateKWLoc, + if (LookupTemplateName(R, S, SS, QualType(), /*EnteringContext=*/false, + MemberOfUnknownSpecialization, TemplateKWLoc, &AssumedTemplate)) return ExprError(); - if (R.wasNotFoundInCurrentInstantiation()) + if (MemberOfUnknownSpecialization || + (R.getResultKind() == LookupResult::NotFoundInCurrentInstantiation)) return ActOnDependentIdExpression(SS, TemplateKWLoc, NameInfo, IsAddressOfOperand, TemplateArgs); } else { bool IvarLookupFollowUp = II && !SS.isSet() && getCurMethodDecl(); - LookupParsedName(R, S, &SS, /*ObjectType=*/QualType(), - /*AllowBuiltinCreation=*/!IvarLookupFollowUp); + LookupParsedName(R, S, &SS, !IvarLookupFollowUp); // If the result might be in a dependent base class, this is a dependent // id-expression. diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index c1cb03e4ec7a..779a41620033 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -9157,7 +9157,7 @@ Sema::CheckMicrosoftIfExistsSymbol(Scope *S, // Do the redeclaration lookup in the current scope. LookupResult R(*this, TargetNameInfo, Sema::LookupAnyName, RedeclarationKind::NotForRedeclaration); - LookupParsedName(R, S, &SS, /*ObjectType=*/QualType()); + LookupParsedName(R, S, &SS); R.suppressDiagnostics(); switch (R.getResultKind()) { diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index 0eeb7b1faa0a..6e30716b9ae4 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -667,8 +667,8 @@ namespace { // classes, one of its base classes. class RecordMemberExprValidatorCCC final : public CorrectionCandidateCallback { public: - explicit RecordMemberExprValidatorCCC(QualType RTy) - : Record(RTy->getAsRecordDecl()) { + explicit RecordMemberExprValidatorCCC(const RecordType *RTy) + : Record(RTy->getDecl()) { // Don't add bare keywords to the consumer since they will always fail // validation by virtue of not being associated with any decls. WantTypeSpecifiers = false; @@ -713,36 +713,58 @@ private: } static bool LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R, - Expr *BaseExpr, QualType RTy, + Expr *BaseExpr, + const RecordType *RTy, SourceLocation OpLoc, bool IsArrow, CXXScopeSpec &SS, bool HasTemplateArgs, SourceLocation TemplateKWLoc, TypoExpr *&TE) { SourceRange BaseRange = BaseExpr ? BaseExpr->getSourceRange() : SourceRange(); - if (!RTy->isDependentType() && - !SemaRef.isThisOutsideMemberFunctionBody(RTy) && - SemaRef.RequireCompleteType( - OpLoc, RTy, diag::err_typecheck_incomplete_tag, BaseRange)) + RecordDecl *RDecl = RTy->getDecl(); + if (!SemaRef.isThisOutsideMemberFunctionBody(QualType(RTy, 0)) && + SemaRef.RequireCompleteType(OpLoc, QualType(RTy, 0), + diag::err_typecheck_incomplete_tag, + BaseRange)) return true; - // LookupTemplateName/LookupParsedName don't expect these both to exist - // simultaneously. - QualType ObjectType = SS.isSet() ? QualType() : RTy; - if (HasTemplateArgs || TemplateKWLoc.isValid()) - return SemaRef.LookupTemplateName(R, - /*S=*/nullptr, SS, ObjectType, - /*EnteringContext=*/false, TemplateKWLoc); + if (HasTemplateArgs || TemplateKWLoc.isValid()) { + // LookupTemplateName doesn't expect these both to exist simultaneously. + QualType ObjectType = SS.isSet() ? QualType() : QualType(RTy, 0); - SemaRef.LookupParsedName(R, /*S=*/nullptr, &SS, ObjectType); + bool MOUS; + return SemaRef.LookupTemplateName(R, nullptr, SS, ObjectType, false, MOUS, + TemplateKWLoc); + } + + DeclContext *DC = RDecl; + if (SS.isSet()) { + // If the member name was a qualified-id, look into the + // nested-name-specifier. + DC = SemaRef.computeDeclContext(SS, false); + + if (SemaRef.RequireCompleteDeclContext(SS, DC)) { + SemaRef.Diag(SS.getRange().getEnd(), diag::err_typecheck_incomplete_tag) + << SS.getRange() << DC; + return true; + } + + assert(DC && "Cannot handle non-computable dependent contexts in lookup"); + + if (!isa(DC)) { + SemaRef.Diag(R.getNameLoc(), diag::err_qualified_member_nonclass) + << DC << SS.getRange(); + return true; + } + } - if (!R.empty() || R.wasNotFoundInCurrentInstantiation()) + // The record definition is complete, now look up the member. + SemaRef.LookupQualifiedName(R, DC, SS); + + if (!R.empty()) return false; DeclarationName Typo = R.getLookupName(); SourceLocation TypoLoc = R.getNameLoc(); - // Recompute the lookup context. - DeclContext *DC = SS.isSet() ? SemaRef.computeDeclContext(SS) - : SemaRef.computeDeclContext(RTy); struct QueryState { Sema &SemaRef; @@ -766,8 +788,7 @@ static bool LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R, << Typo << DC << DroppedSpecifier << SS.getRange()); } else { - SemaRef.Diag(TypoLoc, diag::err_no_member) - << Typo << DC << (SS.isSet() ? SS.getRange() : BaseRange); + SemaRef.Diag(TypoLoc, diag::err_no_member) << Typo << DC << BaseRange; } }, [=](Sema &SemaRef, TypoExpr *TE, TypoCorrection TC) mutable { @@ -793,25 +814,34 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, Decl *ObjCImpDecl, bool HasTemplateArgs, SourceLocation TemplateKWLoc); -ExprResult Sema::BuildMemberReferenceExpr( - Expr *Base, QualType BaseType, SourceLocation OpLoc, bool IsArrow, - CXXScopeSpec &SS, SourceLocation TemplateKWLoc, - NamedDecl *FirstQualifierInScope, const DeclarationNameInfo &NameInfo, - const TemplateArgumentListInfo *TemplateArgs, const Scope *S, - ActOnMemberAccessExtraArgs *ExtraArgs) { - LookupResult R(*this, NameInfo, LookupMemberName); +ExprResult +Sema::BuildMemberReferenceExpr(Expr *Base, QualType BaseType, + SourceLocation OpLoc, bool IsArrow, + CXXScopeSpec &SS, + SourceLocation TemplateKWLoc, + NamedDecl *FirstQualifierInScope, + const DeclarationNameInfo &NameInfo, + const TemplateArgumentListInfo *TemplateArgs, + const Scope *S, + ActOnMemberAccessExtraArgs *ExtraArgs) { + if (BaseType->isDependentType() || + (SS.isSet() && isDependentScopeSpecifier(SS)) || + NameInfo.getName().isDependentName()) + return ActOnDependentMemberExpr(Base, BaseType, + IsArrow, OpLoc, + SS, TemplateKWLoc, FirstQualifierInScope, + NameInfo, TemplateArgs); - if (SS.isInvalid()) - return ExprError(); + LookupResult R(*this, NameInfo, LookupMemberName); // Implicit member accesses. if (!Base) { TypoExpr *TE = nullptr; QualType RecordTy = BaseType; if (IsArrow) RecordTy = RecordTy->castAs()->getPointeeType(); - if (LookupMemberExprInRecord(*this, R, nullptr, RecordTy, OpLoc, IsArrow, - SS, TemplateArgs != nullptr, TemplateKWLoc, - TE)) + if (LookupMemberExprInRecord( + *this, R, nullptr, RecordTy->castAs(), OpLoc, IsArrow, + SS, TemplateArgs != nullptr, TemplateKWLoc, TE)) return ExprError(); if (TE) return TE; @@ -1003,12 +1033,6 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, const Scope *S, bool SuppressQualifierCheck, ActOnMemberAccessExtraArgs *ExtraArgs) { - assert(!SS.isInvalid() && "nested-name-specifier cannot be invalid"); - if (R.wasNotFoundInCurrentInstantiation()) - return ActOnDependentMemberExpr(BaseExpr, BaseExprType, IsArrow, OpLoc, SS, - TemplateKWLoc, FirstQualifierInScope, - R.getLookupNameInfo(), TemplateArgs); - QualType BaseType = BaseExprType; if (IsArrow) { assert(BaseType->isPointerType()); @@ -1016,11 +1040,6 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, } R.setBaseObjectType(BaseType); - assert((SS.isEmpty() - ? !BaseType->isDependentType() || computeDeclContext(BaseType) - : !isDependentScopeSpecifier(SS) || computeDeclContext(SS)) && - "dependent lookup context that isn't the current instantiation?"); - // C++1z [expr.ref]p2: // For the first option (dot) the first expression shall be a glvalue [...] if (!IsArrow && BaseExpr && BaseExpr->isPRValue()) { @@ -1050,11 +1069,13 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, if (R.empty()) { // Rederive where we looked up. - DeclContext *DC = - (SS.isSet() ? computeDeclContext(SS) : computeDeclContext(BaseType)); + DeclContext *DC = (SS.isSet() + ? computeDeclContext(SS, false) + : BaseType->castAs()->getDecl()); + if (ExtraArgs) { ExprResult RetryExpr; - if (!IsArrow && BaseExpr && !BaseExpr->isTypeDependent()) { + if (!IsArrow && BaseExpr) { SFINAETrap Trap(*this, true); ParsedType ObjectType; bool MayBePseudoDestructor = false; @@ -1077,12 +1098,9 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, } } - assert(DC); Diag(R.getNameLoc(), diag::err_no_member) - << MemberName << DC - << (SS.isSet() - ? SS.getRange() - : (BaseExpr ? BaseExpr->getSourceRange() : SourceRange())); + << MemberName << DC + << (BaseExpr ? BaseExpr->getSourceRange() : SourceRange()); return ExprError(); } @@ -1312,6 +1330,7 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, return ExprError(); QualType BaseType = BaseExpr.get()->getType(); + assert(!BaseType->isDependentType()); DeclarationName MemberName = R.getLookupName(); SourceLocation MemberLoc = R.getNameLoc(); @@ -1323,31 +1342,29 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, if (IsArrow) { if (const PointerType *Ptr = BaseType->getAs()) BaseType = Ptr->getPointeeType(); - else if (!BaseType->isDependentType()) { - if (const ObjCObjectPointerType *Ptr = - BaseType->getAs()) - BaseType = Ptr->getPointeeType(); - else if (BaseType->isRecordType()) { - // Recover from arrow accesses to records, e.g.: - // struct MyRecord foo; - // foo->bar - // This is actually well-formed in C++ if MyRecord has an - // overloaded operator->, but that should have been dealt with - // by now--or a diagnostic message already issued if a problem - // was encountered while looking for the overloaded operator->. - if (!S.getLangOpts().CPlusPlus) { - S.Diag(OpLoc, diag::err_typecheck_member_reference_suggestion) - << BaseType << int(IsArrow) << BaseExpr.get()->getSourceRange() - << FixItHint::CreateReplacement(OpLoc, "."); - } - IsArrow = false; - } else if (BaseType->isFunctionType()) { - goto fail; - } else { - S.Diag(MemberLoc, diag::err_typecheck_member_reference_arrow) - << BaseType << BaseExpr.get()->getSourceRange(); - return ExprError(); + else if (const ObjCObjectPointerType *Ptr + = BaseType->getAs()) + BaseType = Ptr->getPointeeType(); + else if (BaseType->isRecordType()) { + // Recover from arrow accesses to records, e.g.: + // struct MyRecord foo; + // foo->bar + // This is actually well-formed in C++ if MyRecord has an + // overloaded operator->, but that should have been dealt with + // by now--or a diagnostic message already issued if a problem + // was encountered while looking for the overloaded operator->. + if (!S.getLangOpts().CPlusPlus) { + S.Diag(OpLoc, diag::err_typecheck_member_reference_suggestion) + << BaseType << int(IsArrow) << BaseExpr.get()->getSourceRange() + << FixItHint::CreateReplacement(OpLoc, "."); } + IsArrow = false; + } else if (BaseType->isFunctionType()) { + goto fail; + } else { + S.Diag(MemberLoc, diag::err_typecheck_member_reference_arrow) + << BaseType << BaseExpr.get()->getSourceRange(); + return ExprError(); } } @@ -1367,10 +1384,10 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, } // Handle field access to simple records. - if (BaseType->getAsRecordDecl() || BaseType->isDependentType()) { + if (const RecordType *RTy = BaseType->getAs()) { TypoExpr *TE = nullptr; - if (LookupMemberExprInRecord(S, R, BaseExpr.get(), BaseType, OpLoc, IsArrow, - SS, HasTemplateArgs, TemplateKWLoc, TE)) + if (LookupMemberExprInRecord(S, R, BaseExpr.get(), RTy, OpLoc, IsArrow, SS, + HasTemplateArgs, TemplateKWLoc, TE)) return ExprError(); // Returning valid-but-null is how we indicate to the caller that @@ -1807,6 +1824,13 @@ ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base, if (Result.isInvalid()) return ExprError(); Base = Result.get(); + if (Base->getType()->isDependentType() || Name.isDependentName() || + isDependentScopeSpecifier(SS)) { + return ActOnDependentMemberExpr(Base, Base->getType(), IsArrow, OpLoc, SS, + TemplateKWLoc, FirstQualifierInScope, + NameInfo, TemplateArgs); + } + ActOnMemberAccessExtraArgs ExtraArgs = {S, Id, ObjCImpDecl}; ExprResult Res = BuildMemberReferenceExpr( Base, Base->getType(), OpLoc, IsArrow, SS, TemplateKWLoc, diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index a537eccc2eba..55af414df39f 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -1282,31 +1282,6 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) { if (DeclContext *DC = PreS->getEntity()) DeclareImplicitMemberFunctionsWithName(*this, Name, R.getNameLoc(), DC); } - // C++23 [temp.dep.general]p2: - // The component name of an unqualified-id is dependent if - // - it is a conversion-function-id whose conversion-type-id - // is dependent, or - // - it is operator= and the current class is a templated entity, or - // - the unqualified-id is the postfix-expression in a dependent call. - if (Name.getNameKind() == DeclarationName::CXXConversionFunctionName && - Name.getCXXNameType()->isDependentType()) { - R.setNotFoundInCurrentInstantiation(); - return false; - } - - // If this is the name of an implicitly-declared special member function, - // go through the scope stack to implicitly declare - if (isImplicitlyDeclaredMemberFunctionName(Name)) { - for (Scope *PreS = S; PreS; PreS = PreS->getParent()) - if (DeclContext *DC = PreS->getEntity()) { - if (DC->isDependentContext() && isa(DC) && - Name.getCXXOverloadedOperator() == OO_Equal) { - R.setNotFoundInCurrentInstantiation(); - return false; - } - DeclareImplicitMemberFunctionsWithName(*this, Name, R.getNameLoc(), DC); - } - } // Implicitly declare member functions with the name we're looking for, if in // fact we are in a scope where it matters. @@ -2471,33 +2446,10 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx, } } QL(LookupCtx); - CXXRecordDecl *LookupRec = dyn_cast(LookupCtx); - // FIXME: Per [temp.dep.general]p2, an unqualified name is also dependent - // if it's a dependent conversion-function-id or operator= where the current - // class is a templated entity. This should be handled in LookupName. - if (!InUnqualifiedLookup && !R.isForRedeclaration()) { - // C++23 [temp.dep.type]p5: - // A qualified name is dependent if - // - it is a conversion-function-id whose conversion-type-id - // is dependent, or - // - [...] - // - its lookup context is the current instantiation and it - // is operator=, or - // - [...] - if (DeclarationName Name = R.getLookupName(); - (Name.getNameKind() == DeclarationName::CXXConversionFunctionName && - Name.getCXXNameType()->isDependentType()) || - (Name.getCXXOverloadedOperator() == OO_Equal && LookupRec && - LookupRec->isDependentContext())) { - R.setNotFoundInCurrentInstantiation(); - return false; - } - } - if (LookupDirect(*this, R, LookupCtx)) { R.resolveKind(); - if (LookupRec) - R.setNamingClass(LookupRec); + if (isa(LookupCtx)) + R.setNamingClass(cast(LookupCtx)); return true; } @@ -2519,6 +2471,7 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx, // If this isn't a C++ class, we aren't allowed to look into base // classes, we're done. + CXXRecordDecl *LookupRec = dyn_cast(LookupCtx); if (!LookupRec || !LookupRec->getDefinition()) return false; @@ -2765,54 +2718,38 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx, /// /// @returns True if any decls were found (but possibly ambiguous) bool Sema::LookupParsedName(LookupResult &R, Scope *S, CXXScopeSpec *SS, - QualType ObjectType, bool AllowBuiltinCreation, - bool EnteringContext) { - // When the scope specifier is invalid, don't even look for anything. - if (SS && SS->isInvalid()) + bool AllowBuiltinCreation, bool EnteringContext) { + if (SS && SS->isInvalid()) { + // When the scope specifier is invalid, don't even look for + // anything. return false; + } - // Determine where to perform name lookup - DeclContext *DC = nullptr; - bool IsDependent = false; - if (!ObjectType.isNull()) { - // This nested-name-specifier occurs in a member access expression, e.g., - // x->B::f, and we are looking into the type of the object. - assert((!SS || SS->isEmpty()) && - "ObjectType and scope specifier cannot coexist"); - DC = computeDeclContext(ObjectType); - IsDependent = !DC && ObjectType->isDependentType(); - assert(((!DC && ObjectType->isDependentType()) || - !ObjectType->isIncompleteType() || !ObjectType->getAs() || - ObjectType->castAs()->isBeingDefined()) && - "Caller should have completed object type"); - } else if (SS && SS->isNotEmpty()) { - if (NestedNameSpecifier *NNS = SS->getScopeRep(); - NNS->getKind() == NestedNameSpecifier::Super) + if (SS && SS->isSet()) { + NestedNameSpecifier *NNS = SS->getScopeRep(); + if (NNS->getKind() == NestedNameSpecifier::Super) return LookupInSuper(R, NNS->getAsRecordDecl()); - // This nested-name-specifier occurs after another nested-name-specifier, - // so long into the context associated with the prior nested-name-specifier. - if (DC = computeDeclContext(*SS, EnteringContext)) { - // The declaration context must be complete. + + if (DeclContext *DC = computeDeclContext(*SS, EnteringContext)) { + // We have resolved the scope specifier to a particular declaration + // contex, and will perform name lookup in that context. if (!DC->isDependentContext() && RequireCompleteDeclContext(*SS, DC)) return false; + R.setContextRange(SS->getRange()); + return LookupQualifiedName(R, DC); } - IsDependent = !DC && isDependentScopeSpecifier(*SS); - } else { - // Perform unqualified name lookup starting in the given scope. - return LookupName(R, S, AllowBuiltinCreation); - } - // If we were able to compute a declaration context, perform qualified name - // lookup in that context. - if (DC) - return LookupQualifiedName(R, DC); - else if (IsDependent) // We could not resolve the scope specified to a specific declaration // context, which means that SS refers to an unknown specialization. // Name lookup can't find anything in this case. R.setNotFoundInCurrentInstantiation(); - return false; + R.setContextRange(SS->getRange()); + return false; + } + + // Perform unqualified name lookup starting in the given scope. + return LookupName(R, S, AllowBuiltinCreation); } /// Perform qualified name lookup into all base classes of the given @@ -5081,9 +5018,8 @@ static void LookupPotentialTypoResult(Sema &SemaRef, return; } - SemaRef.LookupParsedName(Res, S, SS, - /*ObjectType=*/QualType(), - /*AllowBuiltinCreation=*/false, EnteringContext); + SemaRef.LookupParsedName(Res, S, SS, /*AllowBuiltinCreation=*/false, + EnteringContext); // Fake ivar lookup; this should really be part of // LookupParsedName. diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index cf5447f223d4..cee8da495c54 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -3061,9 +3061,7 @@ ExprResult SemaOpenMP::ActOnOpenMPIdExpression(Scope *CurScope, OpenMPDirectiveKind Kind) { ASTContext &Context = getASTContext(); LookupResult Lookup(SemaRef, Id, Sema::LookupOrdinaryName); - SemaRef.LookupParsedName(Lookup, CurScope, &ScopeSpec, - /*ObjectType=*/QualType(), - /*AllowBuiltinCreation=*/true); + SemaRef.LookupParsedName(Lookup, CurScope, &ScopeSpec, true); if (Lookup.isAmbiguous()) return ExprError(); @@ -7409,8 +7407,7 @@ void SemaOpenMP::ActOnStartOfFunctionDefinitionInOpenMPDeclareVariantScope( const IdentifierInfo *BaseII = D.getIdentifier(); LookupResult Lookup(SemaRef, DeclarationName(BaseII), D.getIdentifierLoc(), Sema::LookupOrdinaryName); - SemaRef.LookupParsedName(Lookup, S, &D.getCXXScopeSpec(), - /*ObjectType=*/QualType()); + SemaRef.LookupParsedName(Lookup, S, &D.getCXXScopeSpec()); TypeSourceInfo *TInfo = SemaRef.GetTypeForDeclarator(D); QualType FType = TInfo->getType(); @@ -19314,8 +19311,7 @@ buildDeclareReductionRef(Sema &SemaRef, SourceLocation Loc, SourceRange Range, if (S) { LookupResult Lookup(SemaRef, ReductionId, Sema::LookupOMPReductionName); Lookup.suppressDiagnostics(); - while (S && SemaRef.LookupParsedName(Lookup, S, &ReductionIdScopeSpec, - /*ObjectType=*/QualType())) { + while (S && SemaRef.LookupParsedName(Lookup, S, &ReductionIdScopeSpec)) { NamedDecl *D = Lookup.getRepresentativeDecl(); do { S = S->getParent(); @@ -22184,8 +22180,7 @@ static ExprResult buildUserDefinedMapperRef(Sema &SemaRef, Scope *S, LookupResult Lookup(SemaRef, MapperId, Sema::LookupOMPMapperName); Lookup.suppressDiagnostics(); if (S) { - while (S && SemaRef.LookupParsedName(Lookup, S, &MapperIdScopeSpec, - /*ObjectType=*/QualType())) { + while (S && SemaRef.LookupParsedName(Lookup, S, &MapperIdScopeSpec)) { NamedDecl *D = Lookup.getRepresentativeDecl(); while (S && !S->isDeclScope(D)) S = S->getParent(); @@ -23502,9 +23497,7 @@ void SemaOpenMP::DiagnoseUnterminatedOpenMPDeclareTarget() { NamedDecl *SemaOpenMP::lookupOpenMPDeclareTargetName( Scope *CurScope, CXXScopeSpec &ScopeSpec, const DeclarationNameInfo &Id) { LookupResult Lookup(SemaRef, Id, Sema::LookupOrdinaryName); - SemaRef.LookupParsedName(Lookup, CurScope, &ScopeSpec, - /*ObjectType=*/QualType(), - /*AllowBuiltinCreation=*/true); + SemaRef.LookupParsedName(Lookup, CurScope, &ScopeSpec, true); if (Lookup.isAmbiguous()) return nullptr; diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 72bf6370ca82..bbcb7c33a985 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -210,11 +210,10 @@ TemplateNameKind Sema::isTemplateName(Scope *S, AssumedTemplateKind AssumedTemplate; LookupResult R(*this, TName, Name.getBeginLoc(), LookupOrdinaryName); if (LookupTemplateName(R, S, SS, ObjectType, EnteringContext, - /*RequiredTemplate=*/SourceLocation(), + MemberOfUnknownSpecialization, SourceLocation(), &AssumedTemplate, /*AllowTypoCorrection=*/!Disambiguation)) return TNK_Non_template; - MemberOfUnknownSpecialization = R.wasNotFoundInCurrentInstantiation(); if (AssumedTemplate != AssumedTemplateKind::None) { TemplateResult = TemplateTy::make(Context.getAssumedTemplateName(TName)); @@ -321,12 +320,15 @@ TemplateNameKind Sema::isTemplateName(Scope *S, bool Sema::isDeductionGuideName(Scope *S, const IdentifierInfo &Name, SourceLocation NameLoc, CXXScopeSpec &SS, ParsedTemplateTy *Template /*=nullptr*/) { + bool MemberOfUnknownSpecialization = false; + // We could use redeclaration lookup here, but we don't need to: the // syntactic form of a deduction guide is enough to identify it even // if we can't look up the template name at all. LookupResult R(*this, DeclarationName(&Name), NameLoc, LookupOrdinaryName); if (LookupTemplateName(R, S, SS, /*ObjectType*/ QualType(), - /*EnteringContext*/ false)) + /*EnteringContext*/ false, + MemberOfUnknownSpecialization)) return false; if (R.empty()) return false; @@ -372,8 +374,11 @@ bool Sema::DiagnoseUnknownTemplateName(const IdentifierInfo &II, return true; } -bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS, - QualType ObjectType, bool EnteringContext, +bool Sema::LookupTemplateName(LookupResult &Found, + Scope *S, CXXScopeSpec &SS, + QualType ObjectType, + bool EnteringContext, + bool &MemberOfUnknownSpecialization, RequiredTemplateKind RequiredTemplate, AssumedTemplateKind *ATK, bool AllowTypoCorrection) { @@ -386,6 +391,7 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS, Found.setTemplateNameLookup(true); // Determine where to perform name lookup + MemberOfUnknownSpecialization = false; DeclContext *LookupCtx = nullptr; bool IsDependent = false; if (!ObjectType.isNull()) { @@ -542,7 +548,7 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS, FilterAcceptableTemplateNames(Found, AllowFunctionTemplatesInLookup); if (Found.empty()) { if (IsDependent) { - Found.setNotFoundInCurrentInstantiation(); + MemberOfUnknownSpecialization = true; return false; } @@ -5589,9 +5595,11 @@ Sema::BuildQualifiedTemplateIdExpr(CXXScopeSpec &SS, RequireCompleteDeclContext(SS, DC)) return BuildDependentDeclRefExpr(SS, TemplateKWLoc, NameInfo, TemplateArgs); + bool MemberOfUnknownSpecialization; LookupResult R(*this, NameInfo, LookupOrdinaryName); if (LookupTemplateName(R, (Scope *)nullptr, SS, QualType(), - /*Entering*/ false, TemplateKWLoc)) + /*Entering*/false, MemberOfUnknownSpecialization, + TemplateKWLoc)) return ExprError(); if (R.isAmbiguous()) @@ -5712,13 +5720,14 @@ TemplateNameKind Sema::ActOnTemplateName(Scope *S, DeclarationNameInfo DNI = GetNameFromUnqualifiedId(Name); LookupResult R(*this, DNI.getName(), Name.getBeginLoc(), LookupOrdinaryName); + bool MOUS; // Tell LookupTemplateName that we require a template so that it diagnoses // cases where it finds a non-template. RequiredTemplateKind RTK = TemplateKWLoc.isValid() ? RequiredTemplateKind(TemplateKWLoc) : TemplateNameIsRequired; - if (!LookupTemplateName(R, S, SS, ObjectType.get(), EnteringContext, RTK, - /*ATK=*/nullptr, /*AllowTypoCorrection=*/false) && + if (!LookupTemplateName(R, S, SS, ObjectType.get(), EnteringContext, MOUS, + RTK, nullptr, /*AllowTypoCorrection=*/false) && !R.isAmbiguous()) { if (LookupCtx) Diag(Name.getBeginLoc(), diag::err_no_member) @@ -5807,7 +5816,7 @@ bool Sema::CheckTemplateTypeArgument( if (auto *II = NameInfo.getName().getAsIdentifierInfo()) { LookupResult Result(*this, NameInfo, LookupOrdinaryName); - LookupParsedName(Result, CurScope, &SS, /*ObjectType=*/QualType()); + LookupParsedName(Result, CurScope, &SS); if (Result.getAsSingle() || Result.getResultKind() == @@ -11170,8 +11179,7 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, : TSK_ExplicitInstantiationDeclaration; LookupResult Previous(*this, NameInfo, LookupOrdinaryName); - LookupParsedName(Previous, S, &D.getCXXScopeSpec(), - /*ObjectType=*/QualType()); + LookupParsedName(Previous, S, &D.getCXXScopeSpec()); if (!R->isFunctionType()) { // C++ [temp.explicit]p1: diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 28d3d1b79a74..f47bc219e6fa 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -13217,26 +13217,6 @@ bool TreeTransform::TransformOverloadExprDecls(OverloadExpr *Old, // Resolve a kind, but don't do any further analysis. If it's // ambiguous, the callee needs to deal with it. R.resolveKind(); - - if (Old->hasTemplateKeyword() && !R.empty()) { - NamedDecl *FoundDecl = R.getRepresentativeDecl()->getUnderlyingDecl(); - getSema().FilterAcceptableTemplateNames(R, - /*AllowFunctionTemplates=*/true, - /*AllowDependent=*/true); - if (R.empty()) { - // If a 'template' keyword was used, a lookup that finds only non-template - // names is an error. - getSema().Diag(R.getNameLoc(), - diag::err_template_kw_refers_to_non_template) - << R.getLookupName() << Old->getQualifierLoc().getSourceRange() - << Old->hasTemplateKeyword() << Old->getTemplateKeywordLoc(); - getSema().Diag(FoundDecl->getLocation(), - diag::note_template_kw_refers_to_non_template) - << R.getLookupName(); - return true; - } - } - return false; } diff --git a/clang/test/AST/HLSL/this-reference-template.hlsl b/clang/test/AST/HLSL/this-reference-template.hlsl index d427e73044b7..60e057986ebf 100644 --- a/clang/test/AST/HLSL/this-reference-template.hlsl +++ b/clang/test/AST/HLSL/this-reference-template.hlsl @@ -24,7 +24,7 @@ void main() { // CHECK: -CXXMethodDecl 0x{{[0-9A-Fa-f]+}} line:8:5 getFirst 'K ()' implicit-inline // CHECK-NEXT:-CompoundStmt 0x{{[0-9A-Fa-f]+}} // CHECK-NEXT:-ReturnStmt 0x{{[0-9A-Fa-f]+}} -// CHECK-NEXT:-MemberExpr 0x{{[0-9A-Fa-f]+}} 'K' lvalue .First 0x{{[0-9A-Fa-f]+}} +// CHECK-NEXT:-CXXDependentScopeMemberExpr 0x{{[0-9A-Fa-f]+}} '' lvalue .First // CHECK-NEXT:-CXXThisExpr 0x{{[0-9A-Fa-f]+}} 'Pair' lvalue this // CHECK-NEXT:-CXXMethodDecl 0x{{[0-9A-Fa-f]+}} line:12:5 getSecond 'V ()' implicit-inline // CHECK-NEXT:-CompoundStmt 0x{{[0-9A-Fa-f]+}} diff --git a/clang/test/CXX/drs/dr2xx.cpp b/clang/test/CXX/drs/dr2xx.cpp index 2b3131be3305..5d3e8ce4bea3 100644 --- a/clang/test/CXX/drs/dr2xx.cpp +++ b/clang/test/CXX/drs/dr2xx.cpp @@ -561,9 +561,9 @@ namespace cwg244 { // cwg244: 11 B_ptr->B_alias::~B(); B_ptr->B_alias::~B_alias(); B_ptr->cwg244::~B(); - // expected-error@-1 {{no member named '~B' in namespace 'cwg244'}} + // expected-error@-1 {{qualified member access refers to a member in namespace 'cwg244'}} B_ptr->cwg244::~B_alias(); - // expected-error@-1 {{no member named '~B' in namespace 'cwg244'}} + // expected-error@-1 {{qualified member access refers to a member in namespace 'cwg244'}} } template @@ -836,7 +836,7 @@ namespace cwg258 { // cwg258: 2.8 namespace cwg259 { // cwg259: 4 template struct A {}; - template struct A; // #cwg259-A-int + template struct A; // #cwg259-A-int template struct A; // expected-error@-1 {{duplicate explicit instantiation of 'A'}} // expected-note@#cwg259-A-int {{previous explicit instantiation is here}} @@ -997,7 +997,7 @@ namespace cwg275 { // cwg275: no // expected-error@-1 {{no function template matches function template specialization 'f'}} } - template void g(T) {} // #cwg275-g + template void g(T) {} // #cwg275-g template <> void N::f(char) {} template <> void f(int) {} @@ -1164,7 +1164,7 @@ namespace cwg285 { // cwg285: yes namespace cwg286 { // cwg286: 2.8 template struct A { class C { - template struct B {}; // #cwg286-B + template struct B {}; // #cwg286-B }; }; diff --git a/clang/test/CXX/drs/dr3xx.cpp b/clang/test/CXX/drs/dr3xx.cpp index 94227dc031c6..3e9228fe21fb 100644 --- a/clang/test/CXX/drs/dr3xx.cpp +++ b/clang/test/CXX/drs/dr3xx.cpp @@ -34,7 +34,7 @@ namespace cwg301 { // cwg301: 3.5 bool b = (void(*)(S, S))operator- < (void(*)(S, S))operator-; // cxx98-17-warning@-1 {{ordered comparison of function pointers ('void (*)(S, S)' and 'void (*)(S, S)')}} // cxx20-23-error@-2 {{expected '>'}} - // cxx20-23-note@-3 {{to match this '<'}} + // cxx20-23-note@-3 {{to match this '<'}} bool c = (void(*)(S, S))operator+ < (void(*)(S, S))operator-; // expected-error@-1 {{expected '>'}} // expected-note@-2 {{to match this '<'}} @@ -642,7 +642,7 @@ namespace cwg339 { // cwg339: 2.8 char xxx(int); char (&xxx(float))[2]; - template A f(T) {} // #cwg339-f + template A f(T) {} // #cwg339-f void test() { A<1> a = f(0); @@ -828,7 +828,7 @@ namespace cwg352 { // cwg352: 2.8 void g(A::E e) { foo(e, &arg); // expected-error@-1 {{no matching function for call to 'foo'}} - // expected-note@#cwg352-foo {{candidate template ignored: couldn't infer template argument 'R'}} + // expected-note@#cwg352-foo {{candidate template ignored: couldn't infer template argument 'R'}} using A::foo; foo(e, &arg); // ok, uses non-template @@ -929,7 +929,7 @@ namespace cwg352 { // cwg352: 2.8 namespace example5 { template class A {}; - template void g(A); // #cwg352-g + template void g(A); // #cwg352-g template void f(A, A); void h(A<1> a1, A<2> a2) { g(a1); @@ -1256,7 +1256,7 @@ namespace cwg373 { // cwg373: 5 } }; - struct A { struct B {}; }; // #cwg373-A + struct A { struct B {}; }; // #cwg373-A namespace X = A::B; // expected-error@-1 {{expected namespace name}} // expected-note@#cwg373-A {{'A' declared here}} @@ -1608,7 +1608,7 @@ namespace cwg395 { // cwg395: 3.0 // expected-error@-2 {{conversion function cannot have any parameters}} // expected-error@-3 {{cannot specify any part of a return type in the declaration of a conversion function}} // expected-error@-4 {{conversion function cannot convert to a function type}} - + }; struct null1_t { @@ -1721,9 +1721,9 @@ namespace cwg399 { // cwg399: 11 B_ptr->B_alias::~B(); B_ptr->B_alias::~B_alias(); B_ptr->cwg399::~B(); - // expected-error@-1 {{no member named '~B' in namespace 'cwg399'}} + // expected-error@-1 {{qualified member access refers to a member in namespace 'cwg399'}} B_ptr->cwg399::~B_alias(); - // expected-error@-1 {{no member named '~B' in namespace 'cwg399'}} + // expected-error@-1 {{qualified member access refers to a member in namespace 'cwg399'}} } template diff --git a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp deleted file mode 100644 index b1d2859be863..000000000000 --- a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp +++ /dev/null @@ -1,456 +0,0 @@ -// RUN: %clang_cc1 -Wno-unused-value -verify %s - -namespace N0 { - struct A { - int x0; - static int y0; - int x1; - static int y1; - - void f0(); - static void g0(); - void f1(); - static void g1(); - - using M0 = int; - using M1 = int; - - struct C0 { }; - struct C1 { }; - }; - - template - struct B : A { - int x2; - static int y2; - - void f2(); - static void g2(); - - using M2 = int; - - struct C2 { }; - - using A::x1; - using A::y1; - using A::f1; - using A::g1; - using A::M1; - using A::C1; - - using T::x3; - using T::y3; - using T::f3; - using T::g3; - using typename T::M3; - using typename T::C3; - - void not_instantiated(B *a, B &b) { - // All of the following should be found in the current instantiation. - - new M0; - new B::M0; - new A::M0; - new B::A::M0; - new C0; - new B::C0; - new A::C0; - new B::A::C0; - new M1; - new B::M1; - new A::M1; - new B::A::M1; - new C1; - new B::C1; - new A::C1; - new B::A::C1; - new M2; - new B::M2; - new C2; - new B::C2; - new M3; - new B::M3; - new C3; - new B::C3; - - x0; - B::x0; - A::x0; - B::A::x0; - y0; - B::y0; - A::y0; - B::A::y0; - x1; - B::x1; - A::x1; - B::A::x1; - y1; - B::y1; - A::y1; - B::A::y1; - x2; - B::x2; - y2; - B::y2; - x3; - B::x3; - y3; - B::y3; - - f0(); - B::f0(); - A::f0(); - B::A::f0(); - g0(); - B::g0(); - A::g0(); - B::A::g0(); - f1(); - B::f1(); - A::f1(); - B::A::f1(); - g1(); - B::g1(); - A::g1(); - B::A::g1(); - f2(); - B::f2(); - g2(); - B::g2(); - f3(); - B::f3(); - g3(); - B::g3(); - - this->x0; - this->B::x0; - this->A::x0; - this->B::A::x0; - this->y0; - this->B::y0; - this->A::y0; - this->B::A::y0; - this->x1; - this->B::x1; - this->A::x1; - this->B::A::x1; - this->y1; - this->B::y1; - this->A::y1; - this->B::A::y1; - this->x2; - this->B::x2; - this->y2; - this->B::y2; - this->x3; - this->B::x3; - this->y3; - this->B::y3; - - this->f0(); - this->B::f0(); - this->A::f0(); - this->B::A::f0(); - this->g0(); - this->B::g0(); - this->A::g0(); - this->B::A::g0(); - this->f1(); - this->B::f1(); - this->A::f1(); - this->B::A::f1(); - this->g1(); - this->B::g1(); - this->A::g1(); - this->B::A::g1(); - this->f2(); - this->B::f2(); - this->g2(); - this->B::g2(); - this->f3(); - this->B::f3(); - this->g3(); - this->B::g3(); - - a->x0; - a->B::x0; - a->A::x0; - a->B::A::x0; - a->y0; - a->B::y0; - a->A::y0; - a->B::A::y0; - a->x1; - a->B::x1; - a->A::x1; - a->B::A::x1; - a->y1; - a->B::y1; - a->A::y1; - a->B::A::y1; - a->x2; - a->B::x2; - a->y2; - a->B::y2; - a->x3; - a->B::x3; - a->y3; - a->B::y3; - - a->f0(); - a->B::f0(); - a->A::f0(); - a->B::A::f0(); - a->g0(); - a->B::g0(); - a->A::g0(); - a->B::A::g0(); - a->f1(); - a->B::f1(); - a->A::f1(); - a->B::A::f1(); - a->g1(); - a->B::g1(); - a->A::g1(); - a->B::A::g1(); - a->f2(); - a->B::f2(); - a->g2(); - a->B::g2(); - a->f3(); - a->B::f3(); - a->g3(); - a->B::g3(); - - (*this).x0; - (*this).B::x0; - (*this).A::x0; - (*this).B::A::x0; - (*this).y0; - (*this).B::y0; - (*this).A::y0; - (*this).B::A::y0; - (*this).x1; - (*this).B::x1; - (*this).A::x1; - (*this).B::A::x1; - (*this).y1; - (*this).B::y1; - (*this).A::y1; - (*this).B::A::y1; - (*this).x2; - (*this).B::x2; - (*this).y2; - (*this).B::y2; - (*this).x3; - (*this).B::x3; - (*this).y3; - (*this).B::y3; - - (*this).f0(); - (*this).B::f0(); - (*this).A::f0(); - (*this).B::A::f0(); - (*this).g0(); - (*this).B::g0(); - (*this).A::g0(); - (*this).B::A::g0(); - (*this).f1(); - (*this).B::f1(); - (*this).A::f1(); - (*this).B::A::f1(); - (*this).g1(); - (*this).B::g1(); - (*this).A::g1(); - (*this).B::A::g1(); - (*this).f2(); - (*this).B::f2(); - (*this).g2(); - (*this).B::g2(); - (*this).f3(); - (*this).B::f3(); - (*this).g3(); - (*this).B::g3(); - - b.x0; - b.B::x0; - b.A::x0; - b.B::A::x0; - b.y0; - b.B::y0; - b.A::y0; - b.B::A::y0; - b.x1; - b.B::x1; - b.A::x1; - b.B::A::x1; - b.y1; - b.B::y1; - b.A::y1; - b.B::A::y1; - b.x2; - b.B::x2; - b.y2; - b.B::y2; - b.x3; - b.B::x3; - b.y3; - b.B::y3; - - b.f0(); - b.B::f0(); - b.A::f0(); - b.B::A::f0(); - b.g0(); - b.B::g0(); - b.A::g0(); - b.B::A::g0(); - b.f1(); - b.B::f1(); - b.A::f1(); - b.B::A::f1(); - b.g1(); - b.B::g1(); - b.A::g1(); - b.B::A::g1(); - b.f2(); - b.B::f2(); - b.g2(); - b.B::g2(); - b.f3(); - b.B::f3(); - b.g3(); - b.B::g3(); - - // None of the following should be found in the current instantiation. - - new M4; // expected-error{{unknown type name 'M4'}} - new B::M4; // expected-error{{no type named 'M4' in 'B'}} - new A::M4; // expected-error{{no type named 'M4' in 'N0::A'}} - new B::A::M4; // expected-error{{no type named 'M4' in 'N0::A'}} - - x4; // expected-error{{use of undeclared identifier 'x4'}} - B::x4; // expected-error{{no member named 'x4' in 'B'}} - A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} - B::A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} - f4(); // expected-error{{use of undeclared identifier 'f4'}} - B::f4(); // expected-error{{no member named 'f4' in 'B'}} - A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} - B::A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} - - this->x4; // expected-error{{no member named 'x4' in 'B'}} - this->B::x4; // expected-error{{no member named 'x4' in 'B'}} - this->A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} - this->B::A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} - this->f4(); // expected-error{{no member named 'f4' in 'B'}} - this->B::f4(); // expected-error{{no member named 'f4' in 'B'}} - this->A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} - this->B::A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} - - a->x4; // expected-error{{no member named 'x4' in 'B'}} - a->B::x4; // expected-error{{no member named 'x4' in 'B'}} - a->A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} - a->B::A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} - a->f4(); // expected-error{{no member named 'f4' in 'B'}} - a->B::f4(); // expected-error{{no member named 'f4' in 'B'}} - a->A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} - a->B::A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} - - // FIXME: An overloaded unary 'operator*' is built for these - // even though the operand is a pointer (to a dependent type). - // Type::isOverloadableType should return false for such cases. - (*this).x4; - (*this).B::x4; - (*this).A::x4; - (*this).B::A::x4; - (*this).f4(); - (*this).B::f4(); - (*this).A::f4(); - (*this).B::A::f4(); - - b.x4; // expected-error{{no member named 'x4' in 'B'}} - b.B::x4; // expected-error{{no member named 'x4' in 'B'}} - b.A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} - b.B::A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} - b.f4(); // expected-error{{no member named 'f4' in 'B'}} - b.B::f4(); // expected-error{{no member named 'f4' in 'B'}} - b.A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} - b.B::A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} - } - }; -} // namespace N0 - -namespace N1 { - struct A { - template - void f(); - }; - - template - struct B { - template - void f(); - - A x; - A g(); - - void not_instantiated(B *a, B &b) { - f<0>(); - this->f<0>(); - a->f<0>(); - // FIXME: This should not require 'template'! - (*this).f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}} - b.f<0>(); - - x.f<0>(); - this->x.f<0>(); - a->x.f<0>(); - // FIXME: This should not require 'template'! - (*this).x.f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}} - b.x.f<0>(); - - // FIXME: None of these should require 'template'! - g().f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}} - this->g().f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}} - a->g().f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}} - (*this).g().f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}} - b.g().f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}} - } - }; -} // namespace N1 - -namespace N2 { - template - struct A { - struct B { - using C = A; - - void not_instantiated(A *a, B *b) { - b->x; // expected-error{{no member named 'x' in 'N2::A::B'}} - b->B::x; // expected-error{{no member named 'x' in 'N2::A::B'}} - a->B::C::x; // expected-error{{no member named 'x' in 'A'}} - } - }; - - void not_instantiated(A *a, B *b) { - b->x; - b->B::x; - a->B::C::x; - } - }; -} - -namespace N3 { - struct A { }; - - template - struct B : A { - void not_instantiated() { - // Dependent, lookup context is the current instantiation. - this->operator=(*this); - // Not dependent, the lookup context is A (not the current instantiation). - this->A::operator=(*this); - } - }; -} diff --git a/clang/test/CXX/temp/temp.res/temp.local/p3.cpp b/clang/test/CXX/temp/temp.res/temp.local/p3.cpp index b9b29d22736e..87589e1e5bcd 100644 --- a/clang/test/CXX/temp/temp.res/temp.local/p3.cpp +++ b/clang/test/CXX/temp/temp.res/temp.local/p3.cpp @@ -16,7 +16,8 @@ template struct Derived: Base, Base { void g(X0 *t) { t->Derived::Base::f(); t->Base::f(); - t->Base::f(); // expected-error{{member 'Base' found in multiple base classes of different types}} + t->Base::f(); // expected-error{{member 'Base' found in multiple base classes of different types}} \ + // expected-error{{no member named 'f' in 'X0'}} } }; diff --git a/clang/test/CodeGenCXX/mangle.cpp b/clang/test/CodeGenCXX/mangle.cpp index d0800af55c87..31467d943840 100644 --- a/clang/test/CodeGenCXX/mangle.cpp +++ b/clang/test/CodeGenCXX/mangle.cpp @@ -1032,6 +1032,10 @@ namespace test51 { template decltype(S1().~S1(), S1().~S1()) fun4() {}; template + decltype(S1().~S1()) fun5(){}; + template