[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
This commit is contained in:
Arthur Eubanks 2021-05-01 19:04:42 -07:00
parent 83ff0ff463
commit 2155dc51d7
13 changed files with 132 additions and 11 deletions

View File

@ -3307,11 +3307,17 @@ are target-specific.
Note that LLVM does not permit pointers to void (``void*``) nor does it Note that LLVM does not permit pointers to void (``void*``) nor does it
permit pointers to labels (``label*``). Use ``i8*`` instead. 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: :Syntax:
:: ::
<type> * <type> *
ptr
:Examples: :Examples:
@ -3320,7 +3326,11 @@ permit pointers to labels (``label*``). Use ``i8*`` instead.
+-------------------------+--------------------------------------------------------------------------------------------------------------+ +-------------------------+--------------------------------------------------------------------------------------------------------------+
| ``i32 (i32*) *`` | A :ref:`pointer <t_pointer>` to a :ref:`function <t_function>` that takes an ``i32*``, returning an ``i32``. | | ``i32 (i32*) *`` | A :ref:`pointer <t_pointer>` to a :ref:`function <t_function>` that takes an ``i32*``, returning an ``i32``. |
+-------------------------+--------------------------------------------------------------------------------------------------------------+ +-------------------------+--------------------------------------------------------------------------------------------------------------+
| ``i32 addrspace(5)*`` | A :ref:`pointer <t_pointer>` to an ``i32`` value that resides in address space #5. | | ``i32 addrspace(5)*`` | A :ref:`pointer <t_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: .. _t_vector:

View File

@ -61,6 +61,9 @@ Changes to the LLVM IR
* The ``inalloca`` attribute now has a mandatory type field, similar * The ``inalloca`` attribute now has a mandatory type field, similar
to ``byval`` and ``sret``. 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 Changes to building LLVM
------------------------ ------------------------

View File

@ -168,8 +168,10 @@ enum TypeCodes {
TYPE_CODE_TOKEN = 22, // TOKEN TYPE_CODE_TOKEN = 22, // TOKEN
TYPE_CODE_BFLOAT = 23, // BRAIN FLOATING POINT TYPE_CODE_BFLOAT = 23, // BRAIN FLOATING POINT
TYPE_CODE_X86_AMX = 24 // X86 AMX TYPE_CODE_X86_AMX = 24, // X86 AMX
TYPE_CODE_OPAQUE_POINTER = 25, // OPAQUE_POINTER: [addrspace]
}; };
enum OperandBundleTagCode { enum OperandBundleTagCode {

View File

@ -633,6 +633,7 @@ inline ElementCount VectorType::getElementCount() const {
/// Class to represent pointers. /// Class to represent pointers.
class PointerType : public Type { class PointerType : public Type {
explicit PointerType(Type *ElType, unsigned AddrSpace); explicit PointerType(Type *ElType, unsigned AddrSpace);
explicit PointerType(LLVMContext &C, unsigned AddrSpace);
Type *PointeeTy; Type *PointeeTy;
@ -643,14 +644,28 @@ public:
/// This constructs a pointer to an object of the specified type in a numbered /// This constructs a pointer to an object of the specified type in a numbered
/// address space. /// address space.
static PointerType *get(Type *ElementType, unsigned AddressSpace); 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 /// 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) { static PointerType *getUnqual(Type *ElementType) {
return PointerType::get(ElementType, 0); 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. /// Return true if the specified type is valid as a element type.
static bool isValidElementType(Type *ElemTy); static bool isValidElementType(Type *ElemTy);

View File

@ -845,6 +845,7 @@ lltok::Kind LLLexer::LexIdentifier() {
TYPEKEYWORD("x86_mmx", Type::getX86_MMXTy(Context)); TYPEKEYWORD("x86_mmx", Type::getX86_MMXTy(Context));
TYPEKEYWORD("x86_amx", Type::getX86_AMXTy(Context)); TYPEKEYWORD("x86_amx", Type::getX86_AMXTy(Context));
TYPEKEYWORD("token", Type::getTokenTy(Context)); TYPEKEYWORD("token", Type::getTokenTy(Context));
TYPEKEYWORD("ptr", PointerType::getUnqual(Context));
#undef TYPEKEYWORD #undef TYPEKEYWORD

View File

@ -2595,6 +2595,13 @@ bool LLParser::parseType(Type *&Result, const Twine &Msg, bool AllowVoid) {
} }
} }
if (Result->isPointerTy() && cast<PointerType>(Result)->isOpaque()) {
unsigned AddrSpace;
if (parseOptionalAddrSpace(AddrSpace))
return true;
Result = PointerType::get(getContext(), AddrSpace);
}
// parse the type suffixes. // parse the type suffixes.
while (true) { while (true) {
switch (Lex.getKind()) { switch (Lex.getKind()) {

View File

@ -1807,6 +1807,13 @@ Error BitcodeReader::parseTypeTableBody() {
ResultTy = PointerType::get(ResultTy, AddressSpace); ResultTy = PointerType::get(ResultTy, AddressSpace);
break; 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: { case bitc::TYPE_CODE_FUNCTION_OLD: {
// Deprecated, but still needed to read old bitcode files. // Deprecated, but still needed to read old bitcode files.
// FUNCTION: [vararg, attrid, retty, paramty x N] // FUNCTION: [vararg, attrid, retty, paramty x N]

View File

@ -858,6 +858,12 @@ void ModuleBitcodeWriter::writeTypeTable() {
Abbv->Add(BitCodeAbbrevOp(0)); // Addrspace = 0 Abbv->Add(BitCodeAbbrevOp(0)); // Addrspace = 0
unsigned PtrAbbrev = Stream.EmitAbbrev(std::move(Abbv)); unsigned PtrAbbrev = Stream.EmitAbbrev(std::move(Abbv));
// Abbrev for TYPE_CODE_OPAQUE_POINTER.
Abbv = std::make_shared<BitCodeAbbrev>();
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. // Abbrev for TYPE_CODE_FUNCTION.
Abbv = std::make_shared<BitCodeAbbrev>(); Abbv = std::make_shared<BitCodeAbbrev>();
Abbv->Add(BitCodeAbbrevOp(bitc::TYPE_CODE_FUNCTION)); Abbv->Add(BitCodeAbbrevOp(bitc::TYPE_CODE_FUNCTION));
@ -928,12 +934,21 @@ void ModuleBitcodeWriter::writeTypeTable() {
break; break;
case Type::PointerTyID: { case Type::PointerTyID: {
PointerType *PTy = cast<PointerType>(T); PointerType *PTy = cast<PointerType>(T);
// POINTER: [pointee type, address space]
Code = bitc::TYPE_CODE_POINTER;
TypeVals.push_back(VE.getTypeID(PTy->getElementType()));
unsigned AddressSpace = PTy->getAddressSpace(); unsigned AddressSpace = PTy->getAddressSpace();
TypeVals.push_back(AddressSpace); if (PTy->isOpaque()) {
if (AddressSpace == 0) AbbrevToUse = PtrAbbrev; // 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; break;
} }
case Type::FunctionTyID: { case Type::FunctionTyID: {

View File

@ -651,6 +651,12 @@ void TypePrinting::print(Type *Ty, raw_ostream &OS) {
} }
case Type::PointerTyID: { case Type::PointerTyID: {
PointerType *PTy = cast<PointerType>(Ty); PointerType *PTy = cast<PointerType>(Ty);
if (PTy->isOpaque()) {
OS << "ptr";
if (unsigned AddressSpace = PTy->getAddressSpace())
OS << " addrspace(" << AddressSpace << ')';
return;
}
print(PTy->getElementType(), OS); print(PTy->getElementType(), OS);
if (unsigned AddressSpace = PTy->getAddressSpace()) if (unsigned AddressSpace = PTy->getAddressSpace())
OS << " addrspace(" << AddressSpace << ')'; OS << " addrspace(" << AddressSpace << ')';

View File

@ -1445,6 +1445,8 @@ public:
DenseMap<std::pair<Type *, uint64_t>, ArrayType*> ArrayTypes; DenseMap<std::pair<Type *, uint64_t>, ArrayType*> ArrayTypes;
DenseMap<std::pair<Type *, ElementCount>, VectorType*> VectorTypes; DenseMap<std::pair<Type *, ElementCount>, VectorType*> VectorTypes;
// TODO: clean up the following after we no longer support non-opaque pointer
// types.
DenseMap<Type*, PointerType*> PointerTypes; // Pointers in AddrSpace = 0 DenseMap<Type*, PointerType*> PointerTypes; // Pointers in AddrSpace = 0
DenseMap<std::pair<Type*, unsigned>, PointerType*> ASPointerTypes; DenseMap<std::pair<Type*, unsigned>, PointerType*> ASPointerTypes;

View File

@ -699,6 +699,20 @@ PointerType *PointerType::get(Type *EltTy, unsigned AddressSpace) {
return Entry; 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) PointerType::PointerType(Type *E, unsigned AddrSpace)
: Type(E->getContext(), PointerTyID), PointeeTy(E) { : Type(E->getContext(), PointerTyID), PointeeTy(E) {
ContainedTys = &PointeeTy; ContainedTys = &PointeeTy;
@ -706,6 +720,11 @@ PointerType::PointerType(Type *E, unsigned AddrSpace)
setSubclassData(AddrSpace); setSubclassData(AddrSpace);
} }
PointerType::PointerType(LLVMContext &C, unsigned AddrSpace)
: Type(C, PointerTyID), PointeeTy(nullptr) {
setSubclassData(AddrSpace);
}
PointerType *Type::getPointerTo(unsigned addrs) const { PointerType *Type::getPointerTo(unsigned addrs) const {
return PointerType::get(const_cast<Type*>(this), addrs); return PointerType::get(const_cast<Type*>(this), addrs);
} }
@ -713,7 +732,8 @@ PointerType *Type::getPointerTo(unsigned addrs) const {
bool PointerType::isValidElementType(Type *ElemTy) { bool PointerType::isValidElementType(Type *ElemTy) {
return !ElemTy->isVoidTy() && !ElemTy->isLabelTy() && return !ElemTy->isVoidTy() && !ElemTy->isLabelTy() &&
!ElemTy->isMetadataTy() && !ElemTy->isTokenTy() && !ElemTy->isMetadataTy() && !ElemTy->isTokenTy() &&
!ElemTy->isX86_AMXTy(); !ElemTy->isX86_AMXTy() &&
!(ElemTy->isPointerTy() && cast<PointerType>(ElemTy)->isOpaque());
} }
bool PointerType::isLoadableOrStorableType(Type *ElemTy) { bool PointerType::isLoadableOrStorableType(Type *ElemTy) {

View File

@ -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
}

View File

@ -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
}