Skip to content

Commit

Permalink
[OpaquePtr] Forbid mixing typed and opaque pointers
Browse files Browse the repository at this point in the history
Currently, opaque pointers are supported in two forms: The
-force-opaque-pointers mode, where all pointers are opaque and
typed pointers do not exist. And as a simple ptr type that can
coexist with typed pointers.

This patch removes support for the mixed mode. You either get
typed pointers, or you get opaque pointers, but not both. In the
(current) default mode, using ptr is forbidden. In -opaque-pointers
mode, all pointers are opaque.

The motivation here is that the mixed mode introduces additional
issues that don't exist in fully opaque mode. D105155 is an example
of a design problem. Looking at D109259, it would probably need
additional work to support mixed mode (e.g. to generate GEPs for
typed base but opaque result). Mixed mode will also end up
inserting many casts between i8* and ptr, which would require
significant additional work to consistently avoid.

I don't think the mixed mode is particularly valuable, as it
doesn't align with our end goal. The only thing I've found it to
be moderately useful for is adding some opaque pointer tests in
between typed pointer tests, but I think we can live without that.

Differential Revision: https://reviews.llvm.org/D109290
  • Loading branch information
nikic committed Sep 10, 2021
1 parent 745f82b commit 90ec6df
Show file tree
Hide file tree
Showing 53 changed files with 397 additions and 802 deletions.
13 changes: 6 additions & 7 deletions llvm/include/llvm/AsmParser/LLParser.h
Expand Up @@ -172,9 +172,8 @@ namespace llvm {
/// getGlobalVal - Get a value with the specified name or ID, creating a
/// forward reference record if needed. This can return null if the value
/// exists but does not have the right type.
GlobalValue *getGlobalVal(const std::string &N, Type *Ty, LocTy Loc,
bool IsCall);
GlobalValue *getGlobalVal(unsigned ID, Type *Ty, LocTy Loc, bool IsCall);
GlobalValue *getGlobalVal(const std::string &N, Type *Ty, LocTy Loc);
GlobalValue *getGlobalVal(unsigned ID, Type *Ty, LocTy Loc);

/// Get a Comdat with the specified name, creating a forward reference
/// record if needed.
Expand Down Expand Up @@ -423,8 +422,8 @@ namespace llvm {
/// GetVal - Get a value with the specified name or ID, creating a
/// forward reference record if needed. This can return null if the value
/// exists but does not have the right type.
Value *getVal(const std::string &Name, Type *Ty, LocTy Loc, bool IsCall);
Value *getVal(unsigned ID, Type *Ty, LocTy Loc, bool IsCall);
Value *getVal(const std::string &Name, Type *Ty, LocTy Loc);
Value *getVal(unsigned ID, Type *Ty, LocTy Loc);

/// setInstName - After an instruction is parsed and inserted into its
/// basic block, this installs its name.
Expand All @@ -446,10 +445,10 @@ namespace llvm {
};

bool convertValIDToValue(Type *Ty, ValID &ID, Value *&V,
PerFunctionState *PFS, bool IsCall);
PerFunctionState *PFS);

Value *checkValidVariableType(LocTy Loc, const Twine &Name, Type *Ty,
Value *Val, bool IsCall);
Value *Val);

bool parseConstantValue(Type *Ty, Constant *&C);
bool parseValue(Type *Ty, Value *&V, PerFunctionState *PFS);
Expand Down
4 changes: 4 additions & 0 deletions llvm/include/llvm/IR/LLVMContext.h
Expand Up @@ -305,6 +305,10 @@ class LLVMContext {
/// LLVMContext is used by compilation.
void setOptPassGate(OptPassGate&);

/// Enable opaque pointers. Can only be called before creating the first
/// pointer type.
void enableOpaquePointers() const;

/// Whether typed pointers are supported. If false, all pointers are opaque.
bool supportsTypedPointers() const;

Expand Down
10 changes: 9 additions & 1 deletion llvm/lib/AsmParser/LLLexer.cpp
Expand Up @@ -849,7 +849,15 @@ 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));

if (Keyword == "ptr") {
if (Context.supportsTypedPointers()) {
Warning("ptr type is only supported in -opaque-pointers mode");
return lltok::Error;
}
TyVal = PointerType::getUnqual(Context);
return lltok::Type;
}

#undef TYPEKEYWORD

Expand Down
55 changes: 24 additions & 31 deletions llvm/lib/AsmParser/LLParser.cpp
Expand Up @@ -1404,14 +1404,10 @@ static inline GlobalValue *createGlobalFwdRef(Module *M, PointerType *PTy) {
}

Value *LLParser::checkValidVariableType(LocTy Loc, const Twine &Name, Type *Ty,
Value *Val, bool IsCall) {
Value *Val) {
Type *ValTy = Val->getType();
if (ValTy == Ty)
return Val;
// For calls, we also allow opaque pointers.
if (IsCall && ValTy == PointerType::get(Ty->getContext(),
Ty->getPointerAddressSpace()))
return Val;
if (Ty->isLabelTy())
error(Loc, "'" + Name + "' is not a basic block");
else
Expand All @@ -1425,7 +1421,7 @@ Value *LLParser::checkValidVariableType(LocTy Loc, const Twine &Name, Type *Ty,
/// forward reference record if needed. This can return null if the value
/// exists but does not have the right type.
GlobalValue *LLParser::getGlobalVal(const std::string &Name, Type *Ty,
LocTy Loc, bool IsCall) {
LocTy Loc) {
PointerType *PTy = dyn_cast<PointerType>(Ty);
if (!PTy) {
error(Loc, "global variable reference must have pointer type");
Expand All @@ -1447,16 +1443,15 @@ GlobalValue *LLParser::getGlobalVal(const std::string &Name, Type *Ty,
// If we have the value in the symbol table or fwd-ref table, return it.
if (Val)
return cast_or_null<GlobalValue>(
checkValidVariableType(Loc, "@" + Name, Ty, Val, IsCall));
checkValidVariableType(Loc, "@" + Name, Ty, Val));

// Otherwise, create a new forward reference for this value and remember it.
GlobalValue *FwdVal = createGlobalFwdRef(M, PTy);
ForwardRefVals[Name] = std::make_pair(FwdVal, Loc);
return FwdVal;
}

GlobalValue *LLParser::getGlobalVal(unsigned ID, Type *Ty, LocTy Loc,
bool IsCall) {
GlobalValue *LLParser::getGlobalVal(unsigned ID, Type *Ty, LocTy Loc) {
PointerType *PTy = dyn_cast<PointerType>(Ty);
if (!PTy) {
error(Loc, "global variable reference must have pointer type");
Expand All @@ -1476,7 +1471,7 @@ GlobalValue *LLParser::getGlobalVal(unsigned ID, Type *Ty, LocTy Loc,
// If we have the value in the symbol table or fwd-ref table, return it.
if (Val)
return cast_or_null<GlobalValue>(
checkValidVariableType(Loc, "@" + Twine(ID), Ty, Val, IsCall));
checkValidVariableType(Loc, "@" + Twine(ID), Ty, Val));

// Otherwise, create a new forward reference for this value and remember it.
GlobalValue *FwdVal = createGlobalFwdRef(M, PTy);
Expand Down Expand Up @@ -2218,7 +2213,7 @@ bool LLParser::parseType(Type *&Result, const Twine &Msg, bool AllowVoid) {
Result = Lex.getTyVal();
Lex.Lex();

// Handle (explicit) opaque pointer types (not --force-opaque-pointers).
// Handle "ptr" opaque pointer type.
//
// Type ::= ptr ('addrspace' '(' uint32 ')')?
if (Result->isOpaquePointerTy()) {
Expand Down Expand Up @@ -2794,7 +2789,7 @@ bool LLParser::PerFunctionState::finishFunction() {
/// forward reference record if needed. This can return null if the value
/// exists but does not have the right type.
Value *LLParser::PerFunctionState::getVal(const std::string &Name, Type *Ty,
LocTy Loc, bool IsCall) {
LocTy Loc) {
// Look this name up in the normal function symbol table.
Value *Val = F.getValueSymbolTable()->lookup(Name);

Expand All @@ -2808,7 +2803,7 @@ Value *LLParser::PerFunctionState::getVal(const std::string &Name, Type *Ty,

// If we have the value in the symbol table or fwd-ref table, return it.
if (Val)
return P.checkValidVariableType(Loc, "%" + Name, Ty, Val, IsCall);
return P.checkValidVariableType(Loc, "%" + Name, Ty, Val);

// Don't make placeholders with invalid type.
if (!Ty->isFirstClassType()) {
Expand All @@ -2828,8 +2823,7 @@ Value *LLParser::PerFunctionState::getVal(const std::string &Name, Type *Ty,
return FwdVal;
}

Value *LLParser::PerFunctionState::getVal(unsigned ID, Type *Ty, LocTy Loc,
bool IsCall) {
Value *LLParser::PerFunctionState::getVal(unsigned ID, Type *Ty, LocTy Loc) {
// Look this name up in the normal function symbol table.
Value *Val = ID < NumberedVals.size() ? NumberedVals[ID] : nullptr;

Expand All @@ -2843,7 +2837,7 @@ Value *LLParser::PerFunctionState::getVal(unsigned ID, Type *Ty, LocTy Loc,

// If we have the value in the symbol table or fwd-ref table, return it.
if (Val)
return P.checkValidVariableType(Loc, "%" + Twine(ID), Ty, Val, IsCall);
return P.checkValidVariableType(Loc, "%" + Twine(ID), Ty, Val);

if (!Ty->isFirstClassType()) {
P.error(Loc, "invalid use of a non-first-class type");
Expand Down Expand Up @@ -2930,12 +2924,12 @@ bool LLParser::PerFunctionState::setInstName(int NameID,
BasicBlock *LLParser::PerFunctionState::getBB(const std::string &Name,
LocTy Loc) {
return dyn_cast_or_null<BasicBlock>(
getVal(Name, Type::getLabelTy(F.getContext()), Loc, /*IsCall=*/false));
getVal(Name, Type::getLabelTy(F.getContext()), Loc));
}

BasicBlock *LLParser::PerFunctionState::getBB(unsigned ID, LocTy Loc) {
return dyn_cast_or_null<BasicBlock>(
getVal(ID, Type::getLabelTy(F.getContext()), Loc, /*IsCall=*/false));
getVal(ID, Type::getLabelTy(F.getContext()), Loc));
}

/// defineBB - Define the specified basic block, which is either named or
Expand Down Expand Up @@ -3648,7 +3642,7 @@ bool LLParser::parseGlobalValue(Type *Ty, Constant *&C) {
ValID ID;
Value *V = nullptr;
bool Parsed = parseValID(ID, /*PFS=*/nullptr, Ty) ||
convertValIDToValue(Ty, ID, V, nullptr, /*IsCall=*/false);
convertValIDToValue(Ty, ID, V, nullptr);
if (V && !(C = dyn_cast<Constant>(V)))
return error(ID.Loc, "global values must be constants");
return Parsed;
Expand Down Expand Up @@ -5238,20 +5232,20 @@ bool LLParser::parseMetadata(Metadata *&MD, PerFunctionState *PFS) {
//===----------------------------------------------------------------------===//

bool LLParser::convertValIDToValue(Type *Ty, ValID &ID, Value *&V,
PerFunctionState *PFS, bool IsCall) {
PerFunctionState *PFS) {
if (Ty->isFunctionTy())
return error(ID.Loc, "functions are not values, refer to them as pointers");

switch (ID.Kind) {
case ValID::t_LocalID:
if (!PFS)
return error(ID.Loc, "invalid use of function-local name");
V = PFS->getVal(ID.UIntVal, Ty, ID.Loc, IsCall);
V = PFS->getVal(ID.UIntVal, Ty, ID.Loc);
return V == nullptr;
case ValID::t_LocalName:
if (!PFS)
return error(ID.Loc, "invalid use of function-local name");
V = PFS->getVal(ID.StrVal, Ty, ID.Loc, IsCall);
V = PFS->getVal(ID.StrVal, Ty, ID.Loc);
return V == nullptr;
case ValID::t_InlineAsm: {
if (!ID.FTy || !InlineAsm::Verify(ID.FTy, ID.StrVal2))
Expand All @@ -5262,10 +5256,10 @@ bool LLParser::convertValIDToValue(Type *Ty, ValID &ID, Value *&V,
return false;
}
case ValID::t_GlobalName:
V = getGlobalVal(ID.StrVal, Ty, ID.Loc, IsCall);
V = getGlobalVal(ID.StrVal, Ty, ID.Loc);
return V == nullptr;
case ValID::t_GlobalID:
V = getGlobalVal(ID.UIntVal, Ty, ID.Loc, IsCall);
V = getGlobalVal(ID.UIntVal, Ty, ID.Loc);
return V == nullptr;
case ValID::t_APSInt:
if (!Ty->isIntegerTy())
Expand Down Expand Up @@ -5389,7 +5383,7 @@ bool LLParser::parseConstantValue(Type *Ty, Constant *&C) {
case ValID::t_ConstantStruct:
case ValID::t_PackedConstantStruct: {
Value *V;
if (convertValIDToValue(Ty, ID, V, /*PFS=*/nullptr, /*IsCall=*/false))
if (convertValIDToValue(Ty, ID, V, /*PFS=*/nullptr))
return true;
assert(isa<Constant>(V) && "Expected a constant value");
C = cast<Constant>(V);
Expand All @@ -5407,7 +5401,7 @@ bool LLParser::parseValue(Type *Ty, Value *&V, PerFunctionState *PFS) {
V = nullptr;
ValID ID;
return parseValID(ID, PFS, Ty) ||
convertValIDToValue(Ty, ID, V, PFS, /*IsCall=*/false);
convertValIDToValue(Ty, ID, V, PFS);
}

bool LLParser::parseTypeAndValue(Value *&V, PerFunctionState *PFS) {
Expand Down Expand Up @@ -5702,7 +5696,7 @@ bool LLParser::PerFunctionState::resolveForwardRefBlockAddresses() {

Value *ResolvedVal = BlockAddress::get(&F, BB);
ResolvedVal = P.checkValidVariableType(BBID.Loc, BBID.StrVal, GV->getType(),
ResolvedVal, false);
ResolvedVal);
if (!ResolvedVal)
return true;
GV->replaceAllUsesWith(ResolvedVal);
Expand Down Expand Up @@ -6271,7 +6265,7 @@ bool LLParser::parseInvoke(Instruction *&Inst, PerFunctionState &PFS) {
// Look up the callee.
Value *Callee;
if (convertValIDToValue(PointerType::get(Ty, InvokeAddrSpace), CalleeID,
Callee, &PFS, /*IsCall=*/true))
Callee, &PFS))
return true;

// Set up the Attribute for the function.
Expand Down Expand Up @@ -6596,8 +6590,7 @@ bool LLParser::parseCallBr(Instruction *&Inst, PerFunctionState &PFS) {

// Look up the callee.
Value *Callee;
if (convertValIDToValue(PointerType::getUnqual(Ty), CalleeID, Callee, &PFS,
/*IsCall=*/true))
if (convertValIDToValue(PointerType::getUnqual(Ty), CalleeID, Callee, &PFS))
return true;

// Set up the Attribute for the function.
Expand Down Expand Up @@ -7003,7 +6996,7 @@ bool LLParser::parseCall(Instruction *&Inst, PerFunctionState &PFS,
// Look up the callee.
Value *Callee;
if (convertValIDToValue(PointerType::get(Ty, CallAddrSpace), CalleeID, Callee,
&PFS, /*IsCall=*/true))
&PFS))
return true;

// Set up the Attribute for the function.
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Bitcode/Reader/BitcodeReader.cpp
Expand Up @@ -1792,6 +1792,9 @@ Error BitcodeReader::parseTypeTableBody() {
case bitc::TYPE_CODE_OPAQUE_POINTER: { // OPAQUE_POINTER: [addrspace]
if (Record.size() != 1)
return error("Invalid record");
if (Context.supportsTypedPointers())
return error(
"Opaque pointers are only supported in -opaque-pointers mode");
unsigned AddressSpace = Record[0];
ResultTy = PointerType::get(Context, AddressSpace);
break;
Expand Down
12 changes: 2 additions & 10 deletions llvm/lib/IR/Function.cpp
Expand Up @@ -1446,11 +1446,6 @@ static bool matchIntrinsicType(
if (!PT->isOpaque())
return matchIntrinsicType(PT->getElementType(), Infos, ArgTys,
DeferredChecks, IsDeferredCheck);
// If typed pointers are supported, do not allow using opaque pointer in
// place of fixed pointer type. This would make the intrinsic signature
// non-unique.
if (Ty->getContext().supportsTypedPointers())
return true;
// Consume IIT descriptors relating to the pointer element type.
while (Infos.front().Kind == IITDescriptor::Pointer)
Infos = Infos.slice(1);
Expand Down Expand Up @@ -1568,11 +1563,8 @@ static bool matchIntrinsicType(

if (!ThisArgType || !ReferenceType)
return true;
if (!ThisArgType->isOpaque())
return ThisArgType->getElementType() != ReferenceType->getElementType();
// If typed pointers are supported, do not allow opaque pointer to ensure
// uniqueness.
return Ty->getContext().supportsTypedPointers();
return !ThisArgType->isOpaqueOrPointeeTypeMatches(
ReferenceType->getElementType());
}
case IITDescriptor::VecOfAnyPtrsToElt: {
unsigned RefArgNumber = D.getRefArgNumber();
Expand Down
8 changes: 7 additions & 1 deletion llvm/lib/IR/LLVMContext.cpp
Expand Up @@ -348,6 +348,12 @@ std::unique_ptr<DiagnosticHandler> LLVMContext::getDiagnosticHandler() {
return std::move(pImpl->DiagHandler);
}

void LLVMContext::enableOpaquePointers() const {
assert(pImpl->PointerTypes.empty() && pImpl->ASPointerTypes.empty() &&
"Must be called before creating any pointer types");
pImpl->OpaquePointers = true;
}

bool LLVMContext::supportsTypedPointers() const {
return !pImpl->ForceOpaquePointers;
return !pImpl->OpaquePointers;
}
7 changes: 3 additions & 4 deletions llvm/lib/IR/LLVMContextImpl.cpp
Expand Up @@ -23,9 +23,8 @@
using namespace llvm;

static cl::opt<bool>
ForceOpaquePointersCL("force-opaque-pointers",
cl::desc("Force all pointers to be opaque pointers"),
cl::init(false));
OpaquePointersCL("opaque-pointers", cl::desc("Use opaque pointers"),
cl::init(false));

LLVMContextImpl::LLVMContextImpl(LLVMContext &C)
: DiagHandler(std::make_unique<DiagnosticHandler>()),
Expand All @@ -37,7 +36,7 @@ LLVMContextImpl::LLVMContextImpl(LLVMContext &C)
PPC_FP128Ty(C, Type::PPC_FP128TyID), X86_MMXTy(C, Type::X86_MMXTyID),
X86_AMXTy(C, Type::X86_AMXTyID), Int1Ty(C, 1), Int8Ty(C, 8),
Int16Ty(C, 16), Int32Ty(C, 32), Int64Ty(C, 64), Int128Ty(C, 128),
ForceOpaquePointers(ForceOpaquePointersCL) {}
OpaquePointers(OpaquePointersCL) {}

LLVMContextImpl::~LLVMContextImpl() {
// NOTE: We need to delete the contents of OwnedModules, but Module's dtor
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/IR/LLVMContextImpl.h
Expand Up @@ -1461,7 +1461,7 @@ class LLVMContextImpl {
DenseMap<std::pair<Type *, ElementCount>, VectorType*> VectorTypes;
// TODO: clean up the following after we no longer support non-opaque pointer
// types.
bool ForceOpaquePointers;
bool OpaquePointers;
DenseMap<Type*, PointerType*> PointerTypes; // Pointers in AddrSpace = 0
DenseMap<std::pair<Type*, unsigned>, PointerType*> ASPointerTypes;

Expand Down
6 changes: 4 additions & 2 deletions llvm/lib/IR/Type.cpp
Expand Up @@ -694,8 +694,8 @@ PointerType *PointerType::get(Type *EltTy, unsigned AddressSpace) {

LLVMContextImpl *CImpl = EltTy->getContext().pImpl;

// Create opaque pointer for pointer to opaque pointer.
if (CImpl->ForceOpaquePointers || EltTy->isOpaquePointerTy())
// Automatically convert typed pointers to opaque pointers.
if (CImpl->OpaquePointers)
return get(EltTy->getContext(), AddressSpace);

// Since AddressSpace #0 is the common case, we special case it.
Expand All @@ -709,6 +709,8 @@ PointerType *PointerType::get(Type *EltTy, unsigned AddressSpace) {

PointerType *PointerType::get(LLVMContext &C, unsigned AddressSpace) {
LLVMContextImpl *CImpl = C.pImpl;
assert(CImpl->OpaquePointers &&
"Can only create opaque pointers in opaque pointer mode");

// Since AddressSpace #0 is the common case, we special case it.
PointerType *&Entry =
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/Assembler/invalid-opaque-ptr-addrspace.ll
@@ -1,4 +1,4 @@
; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s
; RUN: not llvm-as < %s -opaque-pointers -disable-output 2>&1 | FileCheck %s

; CHECK: ptr* is invalid - use ptr instead
define void @f(ptr addrspace(3) %a) {
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/Assembler/invalid-opaque-ptr-double-addrspace.ll
@@ -1,4 +1,4 @@
; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s
; RUN: not llvm-as < %s -opaque-pointers -disable-output 2>&1 | FileCheck %s

; CHECK: expected top-level entity
@g1 = external global ptr addrspace(3) addrspace(4)
2 changes: 1 addition & 1 deletion llvm/test/Assembler/invalid-opaque-ptr.ll
@@ -1,4 +1,4 @@
; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s
; RUN: not llvm-as < %s -opaque-pointers -disable-output 2>&1 | FileCheck %s

; CHECK: ptr* is invalid - use ptr instead
define void @f(ptr %a) {
Expand Down

0 comments on commit 90ec6df

Please sign in to comment.