From 2155dc51d700c9fb5f29d79eaacf5e1470e4d8ca Mon Sep 17 00:00:00 2001 From: Arthur Eubanks Date: Sat, 1 May 2021 19:04:42 -0700 Subject: [PATCH] [IR] Introduce the opaque pointer type The opaque pointer type is essentially just a normal pointer type with a null pointee type. This also adds support for the opaque pointer type to the bitcode reader/writer, as well as to textual IR. To avoid confusion with existing pointer types, we disallow creating a pointer to an opaque pointer. Opaque pointer types should not be widely used at this point since many parts of LLVM still do not support them. The next steps are to add some very simple use cases of opaque pointers to make sure they work, then start pretending that all pointers are opaque pointers and see what breaks. https://lists.llvm.org/pipermail/llvm-dev/2021-May/150359.html Reviewed By: dblaikie, dexonsmith, pcc Differential Revision: https://reviews.llvm.org/D101704 --- llvm/docs/LangRef.rst | 12 ++++++++++- llvm/docs/ReleaseNotes.rst | 3 +++ llvm/include/llvm/Bitcode/LLVMBitCodes.h | 6 ++++-- llvm/include/llvm/IR/DerivedTypes.h | 19 +++++++++++++++-- llvm/lib/AsmParser/LLLexer.cpp | 1 + llvm/lib/AsmParser/LLParser.cpp | 7 ++++++ llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 7 ++++++ llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 25 +++++++++++++++++----- llvm/lib/IR/AsmWriter.cpp | 6 ++++++ llvm/lib/IR/LLVMContextImpl.h | 2 ++ llvm/lib/IR/Type.cpp | 22 ++++++++++++++++++- llvm/test/Assembler/invalid-opaque-ptr.ll | 7 ++++++ llvm/test/Assembler/opaque-ptr.ll | 26 +++++++++++++++++++++++ 13 files changed, 132 insertions(+), 11 deletions(-) create mode 100644 llvm/test/Assembler/invalid-opaque-ptr.ll create mode 100644 llvm/test/Assembler/opaque-ptr.ll diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index 5241ad2a9865..d85a5c3df392 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -3307,11 +3307,17 @@ are target-specific. Note that LLVM does not permit pointers to void (``void*``) nor does it permit pointers to labels (``label*``). Use ``i8*`` instead. +LLVM is in the process of transitioning to opaque pointers. Opaque pointers do +not have a pointee type. Rather, instructions interacting through pointers +specify the type of the underlying memory they are interacting with. Opaque +pointers are still in the process of being worked on and are not complete. + :Syntax: :: * + ptr :Examples: @@ -3320,7 +3326,11 @@ permit pointers to labels (``label*``). Use ``i8*`` instead. +-------------------------+--------------------------------------------------------------------------------------------------------------+ | ``i32 (i32*) *`` | A :ref:`pointer ` to a :ref:`function ` that takes an ``i32*``, returning an ``i32``. | +-------------------------+--------------------------------------------------------------------------------------------------------------+ -| ``i32 addrspace(5)*`` | A :ref:`pointer ` to an ``i32`` value that resides in address space #5. | +| ``i32 addrspace(5)*`` | A :ref:`pointer ` to an ``i32`` value that resides in address space 5. | ++-------------------------+--------------------------------------------------------------------------------------------------------------+ +| ``ptr`` | An opaque pointer type to a value that resides in address space 0. | ++-------------------------+--------------------------------------------------------------------------------------------------------------+ +| ``ptr addrspace(5)`` | An opaque pointer type to a value that resides in address space 5. | +-------------------------+--------------------------------------------------------------------------------------------------------------+ .. _t_vector: diff --git a/llvm/docs/ReleaseNotes.rst b/llvm/docs/ReleaseNotes.rst index e96f14411c6d..d96c2080ffa0 100644 --- a/llvm/docs/ReleaseNotes.rst +++ b/llvm/docs/ReleaseNotes.rst @@ -61,6 +61,9 @@ Changes to the LLVM IR * The ``inalloca`` attribute now has a mandatory type field, similar to ``byval`` and ``sret``. +* The opaque pointer type ``ptr`` has been introduced. It is still in the + process of being worked on and should not be used yet. + Changes to building LLVM ------------------------ diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h index 36c0354ae8c9..5589b777ad41 100644 --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -168,8 +168,10 @@ enum TypeCodes { TYPE_CODE_TOKEN = 22, // TOKEN - TYPE_CODE_BFLOAT = 23, // BRAIN FLOATING POINT - TYPE_CODE_X86_AMX = 24 // X86 AMX + TYPE_CODE_BFLOAT = 23, // BRAIN FLOATING POINT + TYPE_CODE_X86_AMX = 24, // X86 AMX + + TYPE_CODE_OPAQUE_POINTER = 25, // OPAQUE_POINTER: [addrspace] }; enum OperandBundleTagCode { diff --git a/llvm/include/llvm/IR/DerivedTypes.h b/llvm/include/llvm/IR/DerivedTypes.h index edc59a8be55a..1566b4264573 100644 --- a/llvm/include/llvm/IR/DerivedTypes.h +++ b/llvm/include/llvm/IR/DerivedTypes.h @@ -633,6 +633,7 @@ inline ElementCount VectorType::getElementCount() const { /// Class to represent pointers. class PointerType : public Type { explicit PointerType(Type *ElType, unsigned AddrSpace); + explicit PointerType(LLVMContext &C, unsigned AddrSpace); Type *PointeeTy; @@ -643,14 +644,28 @@ public: /// This constructs a pointer to an object of the specified type in a numbered /// address space. static PointerType *get(Type *ElementType, unsigned AddressSpace); + /// This constructs an opaque pointer to an object in a numbered address + /// space. + static PointerType *get(LLVMContext &C, unsigned AddressSpace); /// This constructs a pointer to an object of the specified type in the - /// generic address space (address space zero). + /// default address space (address space zero). static PointerType *getUnqual(Type *ElementType) { return PointerType::get(ElementType, 0); } - Type *getElementType() const { return PointeeTy; } + /// This constructs an opaque pointer to an object in the + /// default address space (address space zero). + static PointerType *getUnqual(LLVMContext &C) { + return PointerType::get(C, 0); + } + + Type *getElementType() const { + assert(!isOpaque() && "Attempting to get element type of opaque pointer"); + return PointeeTy; + } + + bool isOpaque() const { return !PointeeTy; } /// Return true if the specified type is valid as a element type. static bool isValidElementType(Type *ElemTy); diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp index b882291e935c..67227f05f360 100644 --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -845,6 +845,7 @@ lltok::Kind LLLexer::LexIdentifier() { TYPEKEYWORD("x86_mmx", Type::getX86_MMXTy(Context)); TYPEKEYWORD("x86_amx", Type::getX86_AMXTy(Context)); TYPEKEYWORD("token", Type::getTokenTy(Context)); + TYPEKEYWORD("ptr", PointerType::getUnqual(Context)); #undef TYPEKEYWORD diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index 55cbaaca4264..b8ea14a5efc9 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -2595,6 +2595,13 @@ bool LLParser::parseType(Type *&Result, const Twine &Msg, bool AllowVoid) { } } + if (Result->isPointerTy() && cast(Result)->isOpaque()) { + unsigned AddrSpace; + if (parseOptionalAddrSpace(AddrSpace)) + return true; + Result = PointerType::get(getContext(), AddrSpace); + } + // parse the type suffixes. while (true) { switch (Lex.getKind()) { diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index 91a65c7ea06e..3a56a67c4a88 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -1807,6 +1807,13 @@ Error BitcodeReader::parseTypeTableBody() { ResultTy = PointerType::get(ResultTy, AddressSpace); break; } + case bitc::TYPE_CODE_OPAQUE_POINTER: { // OPAQUE_POINTER: [addrspace] + if (Record.size() != 1) + return error("Invalid record"); + unsigned AddressSpace = Record[0]; + ResultTy = PointerType::get(Context, AddressSpace); + break; + } case bitc::TYPE_CODE_FUNCTION_OLD: { // Deprecated, but still needed to read old bitcode files. // FUNCTION: [vararg, attrid, retty, paramty x N] diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp index 7261e3085794..50fb3d4afac5 100644 --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -858,6 +858,12 @@ void ModuleBitcodeWriter::writeTypeTable() { Abbv->Add(BitCodeAbbrevOp(0)); // Addrspace = 0 unsigned PtrAbbrev = Stream.EmitAbbrev(std::move(Abbv)); + // Abbrev for TYPE_CODE_OPAQUE_POINTER. + Abbv = std::make_shared(); + Abbv->Add(BitCodeAbbrevOp(bitc::TYPE_CODE_OPAQUE_POINTER)); + Abbv->Add(BitCodeAbbrevOp(0)); // Addrspace = 0 + unsigned OpaquePtrAbbrev = Stream.EmitAbbrev(std::move(Abbv)); + // Abbrev for TYPE_CODE_FUNCTION. Abbv = std::make_shared(); Abbv->Add(BitCodeAbbrevOp(bitc::TYPE_CODE_FUNCTION)); @@ -928,12 +934,21 @@ void ModuleBitcodeWriter::writeTypeTable() { break; case Type::PointerTyID: { PointerType *PTy = cast(T); - // POINTER: [pointee type, address space] - Code = bitc::TYPE_CODE_POINTER; - TypeVals.push_back(VE.getTypeID(PTy->getElementType())); unsigned AddressSpace = PTy->getAddressSpace(); - TypeVals.push_back(AddressSpace); - if (AddressSpace == 0) AbbrevToUse = PtrAbbrev; + if (PTy->isOpaque()) { + // OPAQUE_POINTER: [address space] + Code = bitc::TYPE_CODE_OPAQUE_POINTER; + TypeVals.push_back(AddressSpace); + if (AddressSpace == 0) + AbbrevToUse = OpaquePtrAbbrev; + } else { + // POINTER: [pointee type, address space] + Code = bitc::TYPE_CODE_POINTER; + TypeVals.push_back(VE.getTypeID(PTy->getElementType())); + TypeVals.push_back(AddressSpace); + if (AddressSpace == 0) + AbbrevToUse = PtrAbbrev; + } break; } case Type::FunctionTyID: { diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp index e33ac67d3157..5b60b89e7a00 100644 --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -651,6 +651,12 @@ void TypePrinting::print(Type *Ty, raw_ostream &OS) { } case Type::PointerTyID: { PointerType *PTy = cast(Ty); + if (PTy->isOpaque()) { + OS << "ptr"; + if (unsigned AddressSpace = PTy->getAddressSpace()) + OS << " addrspace(" << AddressSpace << ')'; + return; + } print(PTy->getElementType(), OS); if (unsigned AddressSpace = PTy->getAddressSpace()) OS << " addrspace(" << AddressSpace << ')'; diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h index b221bcd29617..f257901c12c2 100644 --- a/llvm/lib/IR/LLVMContextImpl.h +++ b/llvm/lib/IR/LLVMContextImpl.h @@ -1445,6 +1445,8 @@ public: DenseMap, ArrayType*> ArrayTypes; DenseMap, VectorType*> VectorTypes; + // TODO: clean up the following after we no longer support non-opaque pointer + // types. DenseMap PointerTypes; // Pointers in AddrSpace = 0 DenseMap, PointerType*> ASPointerTypes; diff --git a/llvm/lib/IR/Type.cpp b/llvm/lib/IR/Type.cpp index 9b25e518c3c6..914ac1d10096 100644 --- a/llvm/lib/IR/Type.cpp +++ b/llvm/lib/IR/Type.cpp @@ -699,6 +699,20 @@ PointerType *PointerType::get(Type *EltTy, unsigned AddressSpace) { return Entry; } +PointerType *PointerType::get(LLVMContext &C, unsigned AddressSpace) { + LLVMContextImpl *CImpl = C.pImpl; + + // Since AddressSpace #0 is the common case, we special case it. + PointerType *&Entry = + AddressSpace == 0 + ? CImpl->PointerTypes[nullptr] + : CImpl->ASPointerTypes[std::make_pair(nullptr, AddressSpace)]; + + if (!Entry) + Entry = new (CImpl->Alloc) PointerType(C, AddressSpace); + return Entry; +} + PointerType::PointerType(Type *E, unsigned AddrSpace) : Type(E->getContext(), PointerTyID), PointeeTy(E) { ContainedTys = &PointeeTy; @@ -706,6 +720,11 @@ PointerType::PointerType(Type *E, unsigned AddrSpace) setSubclassData(AddrSpace); } +PointerType::PointerType(LLVMContext &C, unsigned AddrSpace) + : Type(C, PointerTyID), PointeeTy(nullptr) { + setSubclassData(AddrSpace); +} + PointerType *Type::getPointerTo(unsigned addrs) const { return PointerType::get(const_cast(this), addrs); } @@ -713,7 +732,8 @@ PointerType *Type::getPointerTo(unsigned addrs) const { bool PointerType::isValidElementType(Type *ElemTy) { return !ElemTy->isVoidTy() && !ElemTy->isLabelTy() && !ElemTy->isMetadataTy() && !ElemTy->isTokenTy() && - !ElemTy->isX86_AMXTy(); + !ElemTy->isX86_AMXTy() && + !(ElemTy->isPointerTy() && cast(ElemTy)->isOpaque()); } bool PointerType::isLoadableOrStorableType(Type *ElemTy) { diff --git a/llvm/test/Assembler/invalid-opaque-ptr.ll b/llvm/test/Assembler/invalid-opaque-ptr.ll new file mode 100644 index 000000000000..6acb78e9bda7 --- /dev/null +++ b/llvm/test/Assembler/invalid-opaque-ptr.ll @@ -0,0 +1,7 @@ +; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s + +; CHECK: pointer to this type is invalid +define void @f(ptr %a) { + %b = bitcast ptr %a to ptr* + ret void +} diff --git a/llvm/test/Assembler/opaque-ptr.ll b/llvm/test/Assembler/opaque-ptr.ll new file mode 100644 index 000000000000..82fcf9f21a6e --- /dev/null +++ b/llvm/test/Assembler/opaque-ptr.ll @@ -0,0 +1,26 @@ +; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis | FileCheck %s +; RUN: verify-uselistorder %s + +; CHECK: define ptr @f(ptr %a) { +; CHECK: %b = bitcast ptr %a to ptr +; CHECK: ret ptr %b +define ptr @f(ptr %a) { + %b = bitcast ptr %a to ptr + ret ptr %b +} + +; CHECK: define ptr @g(ptr addrspace(2) %a) { +; CHECK: %b = addrspacecast ptr addrspace(2) %a to ptr +; CHECK: ret ptr %b +define ptr @g(ptr addrspace(2) %a) { + %b = addrspacecast ptr addrspace(2) %a to ptr addrspace(0) + ret ptr addrspace(0) %b +} + +; CHECK: define ptr addrspace(2) @g2(ptr %a) { +; CHECK: %b = addrspacecast ptr %a to ptr addrspace(2) +; CHECK: ret ptr addrspace(2) %b +define ptr addrspace(2) @g2(ptr addrspace(0) %a) { + %b = addrspacecast ptr addrspace(0) %a to ptr addrspace(2) + ret ptr addrspace(2) %b +}