diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -472,7 +472,10 @@ return A == B || // Otherwise in OpenCLC v2.0 s6.5.5: every address space except // for __constant can be used as __generic. - (A == LangAS::opencl_generic && B != LangAS::opencl_constant); + (A == LangAS::opencl_generic && B != LangAS::opencl_constant) || + // Consider mixed sized pointers to be equivalent. + (isPtrSizeOrDefaultAddressSpace(A) && + isPtrSizeOrDefaultAddressSpace(B)); } /// Returns true if the address space in these qualifiers is equal to or @@ -2588,6 +2591,13 @@ otherQuals.isAddressSpaceSupersetOf(thisQuals); } + bool isAddressSpacePtrSize(const PointerType &other) const { + Qualifiers thisQuals = PointeeType.getQualifiers(); + Qualifiers otherQuals = other.getPointeeType().getQualifiers(); + return (isPtrSizeOrDefaultAddressSpace(thisQuals.getAddressSpace()) && + isPtrSizeOrDefaultAddressSpace(otherQuals.getAddressSpace())); + } + bool isSugared() const { return false; } QualType desugar() const { return QualType(this, 0); } diff --git a/clang/include/clang/Basic/AddressSpaces.h b/clang/include/clang/Basic/AddressSpaces.h --- a/clang/include/clang/Basic/AddressSpaces.h +++ b/clang/include/clang/Basic/AddressSpaces.h @@ -42,6 +42,11 @@ cuda_constant, cuda_shared, + // Pointer size and extension address spaces. + ptr32_sptr, + ptr32_uptr, + ptr64, + // This denotes the count of language-specific address spaces and also // the offset added to the target-specific address spaces, which are usually // specified by address space attributes __attribute__(address_space(n))). @@ -68,6 +73,11 @@ (unsigned)LangAS::FirstTargetAddressSpace); } +inline bool isPtrSizeOrDefaultAddressSpace(LangAS AS) { + return (AS == LangAS::Default || AS == LangAS::ptr32_sptr || + AS == LangAS::ptr32_uptr || AS == LangAS::ptr64); +} + } // namespace clang #endif // LLVM_CLANG_BASIC_ADDRESSSPACES_H diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -789,15 +789,18 @@ // The fake address space map must have a distinct entry for each // language-specific address space. static const unsigned FakeAddrSpaceMap[] = { - 0, // Default - 1, // opencl_global - 3, // opencl_local - 2, // opencl_constant - 0, // opencl_private - 4, // opencl_generic - 5, // cuda_device - 6, // cuda_constant - 7 // cuda_shared + 0, // Default + 1, // opencl_global + 3, // opencl_local + 2, // opencl_constant + 0, // opencl_private + 4, // opencl_generic + 5, // cuda_device + 6, // cuda_constant + 7, // cuda_shared + 8, // ptr32_sptr + 9, // ptr32_uptr + 10 // ptr64 }; return &FakeAddrSpaceMap; } else { diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -2266,6 +2266,10 @@ case LangAS::cuda_device: ASString = "CUdevice"; break; case LangAS::cuda_constant: ASString = "CUconstant"; break; case LangAS::cuda_shared: ASString = "CUshared"; break; + // ::= [ "ptr32_sptr" | "ptr32_uptr" | "ptr64" ] + case LangAS::ptr32_sptr: ASString = "ptr32_sptr"; break; + case LangAS::ptr32_uptr: ASString = "ptr32_uptr"; break; + case LangAS::ptr64: ASString = "ptr64"; break; } } if (!ASString.empty()) diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -1870,6 +1870,15 @@ case LangAS::cuda_shared: Extra.mangleSourceName("_ASCUshared"); break; + case LangAS::ptr32_sptr: + Extra.mangleSourceName("_ASPtr32_sptr"); + break; + case LangAS::ptr32_uptr: + Extra.mangleSourceName("_ASPtr32_uptr"); + break; + case LangAS::ptr64: + Extra.mangleSourceName("_ASPtr64"); + break; } } diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1819,6 +1819,15 @@ case LangAS::cuda_shared: OS << "__shared__"; break; + case LangAS::ptr32_sptr: + OS << "__ptr32_sptr"; + break; + case LangAS::ptr32_uptr: + OS << "__ptr32_uptr"; + break; + case LangAS::ptr64: + OS << "__ptr64"; + break; default: OS << "__attribute__((address_space("; OS << toTargetAddressSpace(addrspace); diff --git a/clang/lib/Basic/Targets/AMDGPU.cpp b/clang/lib/Basic/Targets/AMDGPU.cpp --- a/clang/lib/Basic/Targets/AMDGPU.cpp +++ b/clang/lib/Basic/Targets/AMDGPU.cpp @@ -46,7 +46,10 @@ Generic, // opencl_generic Global, // cuda_device Constant, // cuda_constant - Local // cuda_shared + Local, // cuda_shared + Generic, // ptr32_sptr + Generic, // ptr32_uptr + Generic // ptr64 }; const LangASMap AMDGPUTargetInfo::AMDGPUDefIsPrivMap = { @@ -58,7 +61,11 @@ Generic, // opencl_generic Global, // cuda_device Constant, // cuda_constant - Local // cuda_shared + Local, // cuda_shared + Generic, // ptr32_sptr + Generic, // ptr32_uptr + Generic // ptr64 + }; } // namespace targets } // namespace clang diff --git a/clang/lib/Basic/Targets/NVPTX.h b/clang/lib/Basic/Targets/NVPTX.h --- a/clang/lib/Basic/Targets/NVPTX.h +++ b/clang/lib/Basic/Targets/NVPTX.h @@ -33,6 +33,9 @@ 1, // cuda_device 4, // cuda_constant 3, // cuda_shared + 0, // ptr32_sptr + 0, // ptr32_uptr + 0 // ptr64 }; /// The DWARF address class. Taken from diff --git a/clang/lib/Basic/Targets/SPIR.h b/clang/lib/Basic/Targets/SPIR.h --- a/clang/lib/Basic/Targets/SPIR.h +++ b/clang/lib/Basic/Targets/SPIR.h @@ -30,7 +30,10 @@ 4, // opencl_generic 0, // cuda_device 0, // cuda_constant - 0 // cuda_shared + 0, // cuda_shared + 0, // ptr32_sptr + 0, // ptr32_uptr + 0 // ptr64 }; class LLVM_LIBRARY_VISIBILITY SPIRTargetInfo : public TargetInfo { diff --git a/clang/lib/Basic/Targets/TCE.h b/clang/lib/Basic/Targets/TCE.h --- a/clang/lib/Basic/Targets/TCE.h +++ b/clang/lib/Basic/Targets/TCE.h @@ -39,7 +39,10 @@ 0, // opencl_generic 0, // cuda_device 0, // cuda_constant - 0 // cuda_shared + 0, // cuda_shared + 0, // ptr32_sptr + 0, // ptr32_uptr + 0, // ptr64 }; class LLVM_LIBRARY_VISIBILITY TCETargetInfo : public TargetInfo { diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h --- a/clang/lib/Basic/Targets/X86.h +++ b/clang/lib/Basic/Targets/X86.h @@ -22,6 +22,21 @@ namespace clang { namespace targets { +static const unsigned X86AddrSpaceMap[] = { + 0, // Default + 0, // opencl_global + 0, // opencl_local + 0, // opencl_constant + 0, // opencl_private + 0, // opencl_generic + 0, // cuda_device + 0, // cuda_constant + 0, // cuda_shared + 270, // ptr32_sptr + 271, // ptr32_uptr + 272 // ptr64 +}; + // X86 target abstract base class; x86-32 and x86-64 are very close, so // most of the implementation can be shared. class LLVM_LIBRARY_VISIBILITY X86TargetInfo : public TargetInfo { @@ -45,6 +60,7 @@ AMD3DNowAthlon } MMX3DNowLevel = NoMMX3DNow; enum XOPEnum { NoXOP, SSE4A, FMA4, XOP } XOPLevel = NoXOP; + enum AddrSpace { ptr32_sptr = 270, ptr32_uptr = 271, ptr64 = 272 }; bool HasAES = false; bool HasVAES = false; @@ -131,6 +147,7 @@ X86TargetInfo(const llvm::Triple &Triple, const TargetOptions &) : TargetInfo(Triple) { LongDoubleFormat = &llvm::APFloat::x87DoubleExtended(); + AddrSpaceMap = &X86AddrSpaceMap; } const char *getLongDoubleMangling() const override { @@ -329,6 +346,18 @@ void setSupportedOpenCLOpts() override { getSupportedOpenCLOpts().supportAll(); } + + uint64_t getPointerWidthV(unsigned AddrSpace) const override { + if (AddrSpace == ptr32_sptr || AddrSpace == ptr32_uptr) + return 32; + if (AddrSpace == ptr64) + return 64; + return PointerWidth; + } + + uint64_t getPointerAlignV(unsigned AddrSpace) const override { + return getPointerWidthV(AddrSpace); + } }; // X86-32 generic target diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -2871,6 +2871,14 @@ O && (O != E); ++O, ++N) { if (!Context.hasSameType(O->getUnqualifiedType(), N->getUnqualifiedType())) { + const PointerType *OldTypePtr = + dyn_cast(O->getUnqualifiedType()); + const PointerType *NewTypePtr = + dyn_cast(N->getUnqualifiedType()); + if (OldTypePtr && NewTypePtr) + if (OldTypePtr->isAddressSpacePtrSize(*NewTypePtr)) + continue; + if (ArgPos) *ArgPos = O - OldType->param_type_begin(); return false; diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -6462,6 +6462,8 @@ attr::Kind NewAttrKind = A->getKind(); QualType Desugared = Type; const AttributedType *AT = dyn_cast(Type); + llvm::SmallSet Attrs; + Attrs.insert(NewAttrKind); while (AT) { attr::Kind CurAttrKind = AT->getAttrKind(); @@ -6489,6 +6491,7 @@ Desugared = AT->getEquivalentType(); AT = dyn_cast(Desugared); + Attrs.insert(CurAttrKind); } // Pointer type qualifiers can only operate on pointer types, but not @@ -6506,7 +6509,26 @@ return true; } - Type = State.getAttributedType(A, Type, Type); + // Add address space qualifier. + LangAS ASIdx = LangAS::Default; + uint64_t PtrWidth = S.Context.getTargetInfo().getPointerWidth(0); + if (PtrWidth == 32) { + if (Attrs.count(attr::Ptr64)) + ASIdx = LangAS::ptr64; + else if (Attrs.count(attr::UPtr)) + ASIdx = LangAS::ptr32_uptr; + } else if (PtrWidth == 64 && Attrs.count(attr::Ptr32)) { + if (Attrs.count(attr::UPtr)) + ASIdx = LangAS::ptr32_uptr; + else + ASIdx = LangAS::ptr32_sptr; + } + + QualType Pointee = Type->getPointeeType(); + if (ASIdx != LangAS::Default) + Pointee = S.Context.getAddrSpaceQualType( + S.Context.removeAddrSpaceQualType(Pointee), ASIdx); + Type = State.getAttributedType(A, Type, S.Context.getPointerType(Pointee)); return false; } diff --git a/llvm/lib/Target/X86/X86.h b/llvm/lib/Target/X86/X86.h --- a/llvm/lib/Target/X86/X86.h +++ b/llvm/lib/Target/X86/X86.h @@ -146,4 +146,15 @@ void initializeX86SpeculativeLoadHardeningPassPass(PassRegistry &); } // End llvm namespace +namespace X86AS { +enum : unsigned { + GS = 256, + FS = 257, + SS = 258, + PTR32_SPTR = 270, + PTR32_UPTR = 271, + PTR64 = 272 +}; +} + #endif diff --git a/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp b/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp --- a/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp +++ b/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp @@ -2218,12 +2218,11 @@ AM.Scale = cast(Mgs->getScale())->getZExtValue(); unsigned AddrSpace = cast(Parent)->getPointerInfo().getAddrSpace(); - // AddrSpace 256 -> GS, 257 -> FS, 258 -> SS. - if (AddrSpace == 256) + if (AddrSpace == X86AS::GS) AM.Segment = CurDAG->getRegister(X86::GS, MVT::i16); - if (AddrSpace == 257) + if (AddrSpace == X86AS::FS) AM.Segment = CurDAG->getRegister(X86::FS, MVT::i16); - if (AddrSpace == 258) + if (AddrSpace == X86AS::SS) AM.Segment = CurDAG->getRegister(X86::SS, MVT::i16); SDLoc DL(N); diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp --- a/llvm/lib/Target/X86/X86ISelLowering.cpp +++ b/llvm/lib/Target/X86/X86ISelLowering.cpp @@ -277,6 +277,10 @@ setOperationAction(ISD::FP_TO_UINT , MVT::i8 , Promote); setOperationAction(ISD::FP_TO_UINT , MVT::i16 , Promote); + // Handle address space casts between mixed sized pointers. + setOperationAction(ISD::ADDRSPACECAST, MVT::i32, Custom); + setOperationAction(ISD::ADDRSPACECAST, MVT::i64, Custom); + if (Subtarget.is64Bit()) { if (!Subtarget.useSoftFloat() && Subtarget.hasAVX512()) { // FP_TO_UINT-i32/i64 is legal for f32/f64, but custom for f80. @@ -2353,10 +2357,21 @@ return TargetLowering::getSafeStackPointerLocation(IRB); } +static bool isPtrSizeOrDefaultAddrSpace(unsigned AS) { + return (AS == 0 || AS == X86AS::PTR32_SPTR || AS == X86AS::PTR32_UPTR || + AS == X86AS::PTR64); +} + bool X86TargetLowering::isNoopAddrSpaceCast(unsigned SrcAS, unsigned DestAS) const { assert(SrcAS != DestAS && "Expected different address spaces!"); + const TargetMachine &TM = getTargetMachine(); + if ((isPtrSizeOrDefaultAddrSpace(SrcAS) && + isPtrSizeOrDefaultAddrSpace(DestAS)) && + TM.getPointerSize(SrcAS) != TM.getPointerSize(DestAS)) + return false; + return SrcAS < 256 && DestAS < 256; } @@ -27397,6 +27412,33 @@ return DAG.getMergeValues({Extract, NewGather.getValue(2)}, dl); } +static SDValue LowerADDRSPACECAST(SDValue Op, SelectionDAG &DAG) { + SDLoc dl(Op); + SDValue Src = Op.getOperand(0); + MVT DstVT = Op.getSimpleValueType(); + + AddrSpaceCastSDNode *N = cast(Op.getNode()); + unsigned SrcAS = N->getSrcAddressSpace(); + unsigned DstAS = N->getDestAddressSpace(); + + assert(SrcAS != DstAS && + "addrspacecast must be between different address spaces"); + + if (isPtrSizeOrDefaultAddrSpace(SrcAS) && + isPtrSizeOrDefaultAddrSpace(DstAS)) { + if (SrcAS == X86AS::PTR32_UPTR && DstVT == MVT::i64) { + Op = DAG.getNode(ISD::ZERO_EXTEND, dl, DstVT, Src); + } else if (DstVT == MVT::i64) { + Op = DAG.getNode(ISD::SIGN_EXTEND, dl, DstVT, Src); + } else if (DstVT == MVT::i32) { + Op = DAG.getNode(ISD::TRUNCATE, dl, DstVT, Src); + } + } else { + report_fatal_error("Bad address space in addrspacecast"); + } + return Op; +} + SDValue X86TargetLowering::LowerGC_TRANSITION_START(SDValue Op, SelectionDAG &DAG) const { // TODO: Eventually, the lowering of these nodes should be informed by or @@ -27559,6 +27601,8 @@ case ISD::GC_TRANSITION_START: return LowerGC_TRANSITION_START(Op, DAG); case ISD::GC_TRANSITION_END: return LowerGC_TRANSITION_END(Op, DAG); + case ISD::ADDRSPACECAST: + return LowerADDRSPACECAST(Op, DAG); } } @@ -28481,6 +28525,32 @@ Results.push_back(Res.getValue(1)); return; } + case ISD::ADDRSPACECAST: { + SDValue Src = N->getOperand(0); + EVT DstVT = N->getValueType(0); + AddrSpaceCastSDNode *CastN = cast(N); + unsigned SrcAS = CastN->getSrcAddressSpace(); + unsigned DstAS = CastN->getDestAddressSpace(); + + assert(SrcAS != DstAS && + "addrspacecast must be between different address spaces"); + + SDValue Res; + if (isPtrSizeOrDefaultAddrSpace(SrcAS) && + isPtrSizeOrDefaultAddrSpace(DstAS)) { + if (SrcAS == X86AS::PTR32_UPTR && DstVT == MVT::i64) + Res = DAG.getNode(ISD::ZERO_EXTEND, dl, DstVT, Src); + else if (DstVT == MVT::i64) + Res = DAG.getNode(ISD::SIGN_EXTEND, dl, DstVT, Src); + else + Res = DAG.getNode(ISD::TRUNCATE, dl, DstVT, Src); + } else { + report_fatal_error("Unrecognized addrspacecast type legalization"); + } + + Results.push_back(Res); + return; + } } } diff --git a/llvm/test/CodeGen/X86/mixed-ptr-sizes.ll b/llvm/test/CodeGen/X86/mixed-ptr-sizes.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/mixed-ptr-sizes.ll @@ -0,0 +1,108 @@ +; RUN: llc < %s | FileCheck --check-prefixes=CHECK %s + +; Source to regenerate: +; struct Foo { +; int * __ptr32 p32; +; int * __ptr64 p64; +; } +; void use_foo(Foo *f); +; void test_sign_ext(Foo *f, int * __ptr32 __sptr i) { +; f->p64 = i; +; use_foo(f); +; } +; void test_zero_ext(Foo *f, int * __ptr32 __uptr i) { +; f->p64 = i; +; use_foo(f); +; } +; void test_trunc(foo *f, int * __ptr64 i) { +; f->p32 = i; +; use_foo(f); +; } +; void test_noop1(foo *f, int * __ptr32 i) { +; f->p32 = i; +; use_foo(f); +; } +; void test_noop2(foo *f, int * __ptr64 i) { +; f->p64 = i; +; use_foo(f); +; } +; +; $ clang -cc1 -triple x86_64-windows-msvc -fms-extensions -O2 -S t.cpp + +; ModuleID = 't.cpp' +source_filename = "t.cpp" +target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-windows-msvc" + +%struct.Foo = type { i32 addrspace(270)*, i32* } +declare dso_local void @"?use_foo@@YAXPEAUFoo@@@Z"(%struct.Foo*) local_unnamed_addr #1 + +; Function Attrs: nounwind +define dso_local void @"?test_sign_ext@@YAXPEAUFoo@@PEAU?$_ASPtr32_sptr@$$CAH@__clang@@@Z"(%struct.Foo* %f, i32 addrspace(270)* %i) local_unnamed_addr #0 { +; CHECK-LABEL: {{.*}}test_sign_ext{{.*}} +; CHECK: movslq %edx, %rax +entry: + %0 = addrspacecast i32 addrspace(270)* %i to i32* + %p64 = getelementptr inbounds %struct.Foo, %struct.Foo* %f, i64 0, i32 1 + store i32* %0, i32** %p64, align 8, !tbaa !2 + tail call void @"?use_foo@@YAXPEAUFoo@@@Z"(%struct.Foo* %f) #2 + ret void +} + +; Function Attrs: nounwind +define dso_local void @"?test_zero_ext@@YAXPEAUFoo@@PEAU?$_ASPtr32_uptr@$$CAH@__clang@@@Z"(%struct.Foo* %f, i32 addrspace(271)* %i) local_unnamed_addr #0 { +; CHECK-LABEL: {{.*}}test_zero_ext{{.*}} +; CHECK: movl %edx, %eax +entry: + %0 = addrspacecast i32 addrspace(271)* %i to i32* + %p64 = getelementptr inbounds %struct.Foo, %struct.Foo* %f, i64 0, i32 1 + store i32* %0, i32** %p64, align 8, !tbaa !2 + tail call void @"?use_foo@@YAXPEAUFoo@@@Z"(%struct.Foo* %f) #2 + ret void +} + +; Function Attrs: nounwind +define dso_local void @"?test_trunc@@YAXPEAUFoo@@PEAH@Z"(%struct.Foo* %f, i32* %i) local_unnamed_addr #0 { +; CHECK-LABEL: {{.*}}test_trunc{{.*}} +; CHECK: movl %edx, (%rcx) +entry: + %0 = addrspacecast i32* %i to i32 addrspace(270)* + %p32 = getelementptr inbounds %struct.Foo, %struct.Foo* %f, i64 0, i32 0 + store i32 addrspace(270)* %0, i32 addrspace(270)** %p32, align 8, !tbaa !7 + tail call void @"?use_foo@@YAXPEAUFoo@@@Z"(%struct.Foo* %f) #2 + ret void +} + +; Function Attrs: nounwind +define dso_local void @"?test_noop1@@YAXPEAUFoo@@PEAU?$_ASPtr32_sptr@$$CAH@__clang@@@Z"(%struct.Foo* %f, i32 addrspace(270)* %i) local_unnamed_addr #0 { +; CHECK-LABEL: {{.*}}test_noop1{{.*}} +; CHECK: movl %edx, (%rcx) +entry: + %p32 = getelementptr inbounds %struct.Foo, %struct.Foo* %f, i64 0, i32 0 + store i32 addrspace(270)* %i, i32 addrspace(270)** %p32, align 8, !tbaa !7 + tail call void @"?use_foo@@YAXPEAUFoo@@@Z"(%struct.Foo* %f) #2 + ret void +} + +; Function Attrs: nounwind +define dso_local void @"?test_noop2@@YAXPEAUFoo@@PEAH@Z"(%struct.Foo* %f, i32* %i) local_unnamed_addr #0 { +; CHECK-LABEL: {{.*}}test_noop2{{.*}} +; CHECK: movq %rdx, 8(%rcx) +entry: + %p64 = getelementptr inbounds %struct.Foo, %struct.Foo* %f, i64 0, i32 1 + store i32* %i, i32** %p64, align 8, !tbaa !2 + tail call void @"?use_foo@@YAXPEAUFoo@@@Z"(%struct.Foo* %f) #2 + ret void +} + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"wchar_size", i32 2} +!1 = !{!"clang version 10.0.0 (https://github.com/llvm/llvm-project.git e7b4a1629519fc9910b49e59ea4b13040631b2b5)"} +!2 = !{!3, !4, i64 8} +!3 = !{!"?AUFoo@@", !4, i64 0, !4, i64 8} +!4 = !{!"any pointer", !5, i64 0} +!5 = !{!"omnipotent char", !6, i64 0} +!6 = !{!"Simple C++ TBAA"} +!7 = !{!3, !4, i64 0}