diff --git a/llvm/docs/BitCodeFormat.rst b/llvm/docs/BitCodeFormat.rst --- a/llvm/docs/BitCodeFormat.rst +++ b/llvm/docs/BitCodeFormat.rst @@ -1338,6 +1338,21 @@ The ``X86_AMX`` record (code 24) adds an ``x86_amx`` type to the type table. +TYPE_CODE_OPAQUE_TYPE Record +^^^^^^^^^^^^^^^^^^^^^^^ + +``[OPAQUE_TYPE, num_tys, ...ty_params..., ...int_params... ]`` + +The ``OPAQUE_TYPE`` record (code 26) adds an opaque type to the type +table, with a name defined by a previously encountered ``STRUCT_NAME`` record. +The operand fields are + +* *num_tys*: The number of parameters that are types (as opposed to integers) + +* *ty_params*: Type indices that represent type parameters + +* *int_params*: Numbers that correspond to the integer parameters. + .. _CONSTANTS_BLOCK: CONSTANTS_BLOCK Contents diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -3604,6 +3604,46 @@ pointers" are still supported under non-default options. See the `opaque pointers document `__ for more information. +.. _t_opaque_type: + +Opaque Type +""""""""""" + +:Overview: + +Opaque types represent types that must be preserved through optimization, but +are otherwise generally opaque to the compiler. They may be used as function +parameters or arguments, and in :ref:`phi ` or :ref:`select ` +instructions. They may be also used in :ref:`alloca ` instructions or +as global values, and correspondingly it is legal to use :ref:`load ` +and :ref:`store ` instructions on them. + +The only constants that opaque types may have are ``zeroinitializer``, +``undef``, and ``poison``. Other possible values for opaque types may arise from +target-specific intrinsics and functions. + +While at first glance, these types may seem similar to pointer types, these +types cannot be converted to other types. As such, it is not legal to use them +in :ref:`bitcast ` instructions (as a source or target type), nor is +it legal to use them in :ref:`ptrtoint ` or +:ref:`inttoptr ` instructions. Similarly, they are not legal to use +in an :ref:`icmp ` instruction. + +Opaque types have a name and optional type or integer parameters. The meanings +of name and parameters are defined by the target. When being defined in LLVM IR, +all of the type parameters must precede all of the integer parameters. + +:Syntax: + +.. code-block:: llvm + + opaque("label") + opaque("label", void) + opaque("label", void, i32) + opaque("label", 0, 1, 2) + opaque("label", void, i32, 0, 1, 2) + + .. _t_vector: Vector Type diff --git a/llvm/include/llvm-c/Core.h b/llvm/include/llvm-c/Core.h --- a/llvm/include/llvm-c/Core.h +++ b/llvm/include/llvm-c/Core.h @@ -165,7 +165,8 @@ LLVMTokenTypeKind, /**< Tokens */ LLVMScalableVectorTypeKind, /**< Scalable SIMD vector type */ LLVMBFloatTypeKind, /**< 16 bit brain floating point type */ - LLVMX86_AMXTypeKind /**< X86 AMX */ + LLVMX86_AMXTypeKind, /**< X86 AMX */ + LLVMOpaqueTypeKind, /**< Sized opaque type */ } LLVMTypeKind; typedef enum { @@ -1561,6 +1562,16 @@ LLVMTypeRef LLVMX86MMXType(void); LLVMTypeRef LLVMX86AMXType(void); +/** + * Create an opaque type in LLVM context. + */ +LLVMTypeRef LLVMOpaqueTypeInContext(LLVMContextRef C, const char *Name, + LLVMTypeRef *TypeParams, + unsigned TypeParamCount, + unsigned *IntParams, + unsigned IntParamCount); + + /** * @} */ diff --git a/llvm/include/llvm/AsmParser/LLParser.h b/llvm/include/llvm/AsmParser/LLParser.h --- a/llvm/include/llvm/AsmParser/LLParser.h +++ b/llvm/include/llvm/AsmParser/LLParser.h @@ -421,6 +421,7 @@ bool parseArrayVectorType(Type *&Result, bool IsVector); bool parseFunctionType(Type *&Result); + bool parseOpaqueType(Type *&Result); // Function Semantic Analysis. class PerFunctionState { diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -175,6 +175,8 @@ TYPE_CODE_X86_AMX = 24, // X86 AMX TYPE_CODE_OPAQUE_POINTER = 25, // OPAQUE_POINTER: [addrspace] + + TYPE_CODE_OPAQUE_TYPE = 26, // OPAQUE_TYPE }; enum OperandBundleTagCode { diff --git a/llvm/include/llvm/IR/DataLayout.h b/llvm/include/llvm/IR/DataLayout.h --- a/llvm/include/llvm/IR/DataLayout.h +++ b/llvm/include/llvm/IR/DataLayout.h @@ -674,6 +674,7 @@ assert(Ty->isSized() && "Cannot getTypeInfo() on a type that is unsized!"); switch (Ty->getTypeID()) { case Type::LabelTyID: + case Type::OpaqueTyID: return TypeSize::Fixed(getPointerSizeInBits(0)); case Type::PointerTyID: return TypeSize::Fixed(getPointerSizeInBits(Ty->getPointerAddressSpace())); diff --git a/llvm/include/llvm/IR/DerivedTypes.h b/llvm/include/llvm/IR/DerivedTypes.h --- a/llvm/include/llvm/IR/DerivedTypes.h +++ b/llvm/include/llvm/IR/DerivedTypes.h @@ -730,6 +730,69 @@ return cast(getScalarType())->getAddressSpace(); } +/// Class to represent opaque types, which are generally unintrospectable from +/// target-independent optimizations. +/// +/// Opaque types have a string name, and optionally have type and/or integer +/// parameters. The exact meaning of any parameters is dependent on the target. +class OpaqueType : public Type { + OpaqueType(LLVMContext &C, StringRef Name, ArrayRef Types, + ArrayRef Ints); + + std::string Name; + unsigned *IntParams; + +public: + OpaqueType(const OpaqueType &) = delete; + OpaqueType &operator=(const OpaqueType &) = delete; + + /// Return an opaque type having the specified name and no type or integer + /// parameters. + static OpaqueType *get(LLVMContext &Context, StringRef Name) { + return get(Context, Name, {}, {}); + } + + /// Return an opaque type having the specified name, type, and integer + /// parameters. + static OpaqueType *get(LLVMContext &Context, StringRef Name, + ArrayRef Types, ArrayRef Ints); + + /// Return the name for this opaque type. Two distinct opaque types may have + /// the same name if their type or integer parameters differ. + StringRef getName() const { return Name; } + + /// Return the type parameters for this particular opaque type. If there are + /// no parameters, an empty array is returned. + ArrayRef type_params() const { + return makeArrayRef(type_param_begin(), type_param_end()); + } + + using type_param_iterator = Type::subtype_iterator; + type_param_iterator type_param_begin() const { return ContainedTys; } + type_param_iterator type_param_end() const { + return &ContainedTys[NumContainedTys]; + } + + Type *getTypeParameter(unsigned i) const { return getContainedType(i); } + unsigned getNumTypeParameters() const { return getNumContainedTypes(); } + + /// Return the integer parameters for this particular opaque type. If there + /// are no parameters, an empty array is returned. + ArrayRef int_params() const { + return makeArrayRef(IntParams, getNumIntParameters()); + } + + unsigned getIntParameter(unsigned i) const { return IntParams[i]; } + unsigned getNumIntParameters() const { return getSubclassData(); } + + /// Methods for support type inquiry through isa, cast, and dyn_cast. + static bool classof(const Type *T) { return T->getTypeID() == OpaqueTyID; } +}; + +StringRef Type::getOpaqueName() const { + return cast(this)->getName(); +} + } // end namespace llvm #endif // LLVM_IR_DERIVEDTYPES_H diff --git a/llvm/include/llvm/IR/Type.h b/llvm/include/llvm/IR/Type.h --- a/llvm/include/llvm/IR/Type.h +++ b/llvm/include/llvm/IR/Type.h @@ -76,6 +76,7 @@ FixedVectorTyID, ///< Fixed width SIMD vector type ScalableVectorTyID, ///< Scalable SIMD vector type TypedPointerTyID, ///< Typed pointer used by some GPU targets + OpaqueTyID, ///< Sized opaque types }; private: @@ -180,6 +181,9 @@ /// Return true if this is X86 AMX. bool isX86_AMXTy() const { return getTypeID() == X86_AMXTyID; } + /// Return true if this is a sized opaque type. + bool isOpaqueTy() const { return getTypeID() == OpaqueTyID; } + /// Return true if this is a FP type or a vector of FP. bool isFPOrFPVectorTy() const { return getScalarType()->isFloatingPointTy(); } @@ -253,7 +257,7 @@ /// includes all first-class types except struct and array types. bool isSingleValueType() const { return isFloatingPointTy() || isX86_MMXTy() || isIntegerTy() || - isPointerTy() || isVectorTy() || isX86_AMXTy(); + isPointerTy() || isVectorTy() || isX86_AMXTy() || isOpaqueTy(); } /// Return true if the type is an aggregate type. This means it is valid as @@ -270,7 +274,7 @@ // If it's a primitive, it is always sized. if (getTypeID() == IntegerTyID || isFloatingPointTy() || getTypeID() == PointerTyID || getTypeID() == X86_MMXTyID || - getTypeID() == X86_AMXTyID) + getTypeID() == X86_AMXTyID || getTypeID() == OpaqueTyID) return true; // If it is not something that can have a size (e.g. a function or label), // it doesn't have a size. @@ -372,6 +376,8 @@ return ContainedTys[0]; } + inline StringRef getOpaqueName() const; + /// This method is deprecated without replacement. Pointer element types are /// not available with opaque pointers. [[deprecated("Deprecated without replacement, see " diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -2442,6 +2442,12 @@ return false; } break; + case lltok::kw_opaque: { + // Type ::= OpaqueType + if (parseOpaqueType(Result)) + return true; + break; + } case lltok::lbrace: // Type ::= StructType if (parseAnonStructType(Result, false)) @@ -2949,6 +2955,60 @@ return false; } +/// parseOpaqueType - handle opaque type syntax +/// OpaqueType +/// ::= 'opaque' '(' STRINGCONSTANT OpaqueTypeParams OpaqueIntParams ')' +/// +/// OpaqueTypeParams +/// ::= /*empty*/ +/// ::= ',' Type OpaqueTypeParams +/// +/// OpaqueIntParams +/// ::= /*empty*/ +/// ::= ',' uint32 OpaqueIntParams +bool LLParser::parseOpaqueType(Type *&Result) { + Lex.Lex(); // Eat the 'opaque' keyword. + + // Get the mandatory opaque type name. + std::string OpaqueName; + if (parseToken(lltok::lparen, "expected '(' in opaque type") || + parseStringConstant(OpaqueName)) + return true; + + // Parse all of the integer and type parameters at the same time; the use of + // SeenInt will allow us to catch cases where type parameters follow integer + // parameters. + SmallVector TypeParams; + SmallVector IntParams; + bool SeenInt = false; + while (Lex.getKind() == lltok::comma) { + Lex.Lex(); // Eat the comma. + + if (Lex.getKind() == lltok::APSInt) { + SeenInt = true; + unsigned IntVal; + if (parseUInt32(IntVal)) + return true; + IntParams.push_back(IntVal); + } else if (SeenInt) { + // The only other kind of parameter we support is type parameters, which + // must precede the integer parameters. This is therefore an error. + return tokError("expected uint32 param"); + } else { + Type *TypeParam; + if (parseType(TypeParam, /*AllowVoid=*/true)) + return true; + TypeParams.push_back(TypeParam); + } + } + + if (parseToken(lltok::rparen, "expected ')' in opaque type")) + return true; + + Result = OpaqueType::get(Context, OpaqueName, TypeParams, IntParams); + return false; +} + //===----------------------------------------------------------------------===// // Function Semantic Analysis. //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -2409,6 +2409,35 @@ ResultTy = Res; break; } + case bitc::TYPE_CODE_OPAQUE_TYPE: { // OPAQUE_TYPE: [NumTy, Tys..., Ints...] + if (Record.size() < 1) + return error("Invalid opaque type record"); + + if (NumRecords >= TypeList.size()) + return error("Invalid TYPE table"); + + if (Record[0] >= Record.size()) + return error("Too many type parameters"); + + unsigned NumTys = Record[0]; + SmallVector TypeParams; + SmallVector IntParams; + for (unsigned i = 0; i < NumTys; i++) { + if (Type *T = getTypeByID(Record[i + 1])) + TypeParams.push_back(T); + else + return error("Invalid type"); + } + + for (unsigned i = NumTys + 1, e = Record.size(); i < e; i++) { + if (Record[i] > UINT_MAX) + return error("Integer parameter too large"); + IntParams.push_back(Record[i]); + } + ResultTy = OpaqueType::get(Context, TypeName, TypeParams, IntParams); + TypeName.clear(); + break; + } case bitc::TYPE_CODE_ARRAY: // ARRAY: [numelts, eltty] if (Record.size() < 2) return error("Invalid array type record"); diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -1031,6 +1031,18 @@ TypeVals.push_back(true); break; } + case Type::OpaqueTyID: { + OpaqueType *OT = cast(T); + Code = bitc::TYPE_CODE_OPAQUE_TYPE; + writeStringRecord(Stream, bitc::TYPE_CODE_STRUCT_NAME, OT->getName(), + StructNameAbbrev); + TypeVals.push_back(OT->getNumTypeParameters()); + for (Type *InnerTy : OT->type_params()) + TypeVals.push_back(VE.getTypeID(InnerTy)); + for (unsigned IntParam : OT->int_params()) + TypeVals.push_back(IntParam); + break; + } case Type::TypedPointerTyID: llvm_unreachable("Typed pointers cannot be added to IR modules"); } diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -623,6 +623,17 @@ << TPTy->getAddressSpace() << ")"; return; } + case Type::OpaqueTyID: + OpaqueType *OTy = cast(Ty); + OS << "opaque(\""; + printEscapedString(Ty->getOpaqueName(), OS); + OS << "\""; + for (Type *Inner : OTy->type_params()) + OS << ", " << *Inner; + for (unsigned IntParam : OTy->int_params()) + OS << ", " << IntParam; + OS << ")"; + return; } llvm_unreachable("Invalid TypeID"); } diff --git a/llvm/lib/IR/Constants.cpp b/llvm/lib/IR/Constants.cpp --- a/llvm/lib/IR/Constants.cpp +++ b/llvm/lib/IR/Constants.cpp @@ -366,6 +366,7 @@ case Type::ArrayTyID: case Type::FixedVectorTyID: case Type::ScalableVectorTyID: + case Type::OpaqueTyID: return ConstantAggregateZero::get(Ty); case Type::TokenTyID: return ConstantTokenNone::get(Ty->getContext()); @@ -1087,6 +1088,8 @@ return ElementCount::getFixed(AT->getNumElements()); if (auto *VT = dyn_cast(Ty)) return VT->getElementCount(); + if (isa(Ty)) + return ElementCount::getNull(); return ElementCount::getFixed(Ty->getStructNumElements()); } @@ -1585,7 +1588,8 @@ // Factory Function Implementation ConstantAggregateZero *ConstantAggregateZero::get(Type *Ty) { - assert((Ty->isStructTy() || Ty->isArrayTy() || Ty->isVectorTy()) && + assert((Ty->isStructTy() || Ty->isArrayTy() || Ty->isVectorTy() || + Ty->isOpaqueTy()) && "Cannot create an aggregate zero of non-aggregate type!"); std::unique_ptr &Entry = diff --git a/llvm/lib/IR/Core.cpp b/llvm/lib/IR/Core.cpp --- a/llvm/lib/IR/Core.cpp +++ b/llvm/lib/IR/Core.cpp @@ -541,6 +541,8 @@ return LLVMTokenTypeKind; case Type::ScalableVectorTyID: return LLVMScalableVectorTypeKind; + case Type::OpaqueTyID: + return LLVMOpaqueTypeKind; case Type::TypedPointerTyID: llvm_unreachable("Typed pointers are unsupported via the C API"); } @@ -859,6 +861,16 @@ return LLVMLabelTypeInContext(LLVMGetGlobalContext()); } +LLVMTypeRef LLVMOpaqueTypeInContext(LLVMContextRef C, const char *Name, + LLVMTypeRef *TypeParams, + unsigned TypeParamCount, + unsigned *IntParams, + unsigned IntParamCount) { + ArrayRef TypeParamArray(unwrap(TypeParams), TypeParamCount); + ArrayRef IntParamArray(IntParams, IntParamCount); + return wrap(OpaqueType::get(*unwrap(C), Name, TypeParamArray, IntParamArray)); +} + /*===-- Operations on values ----------------------------------------------===*/ /*--.. Operations on all values ............................................--*/ diff --git a/llvm/lib/IR/DataLayout.cpp b/llvm/lib/IR/DataLayout.cpp --- a/llvm/lib/IR/DataLayout.cpp +++ b/llvm/lib/IR/DataLayout.cpp @@ -749,6 +749,7 @@ switch (Ty->getTypeID()) { // Early escape for the non-numeric types. case Type::LabelTyID: + case Type::OpaqueTyID: return abi_or_pref ? getPointerABIAlignment(0) : getPointerPrefAlignment(0); case Type::PointerTyID: { unsigned AS = cast(Ty)->getAddressSpace(); diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp --- a/llvm/lib/IR/Function.cpp +++ b/llvm/lib/IR/Function.cpp @@ -852,6 +852,9 @@ Result += "nx"; Result += "v" + utostr(EC.getKnownMinValue()) + getMangledTypeStr(VTy->getElementType(), HasUnnamedType); + } else if (OpaqueType *OTy = dyn_cast(Ty)) { + Result += "o"; + Result += OTy->getName(); } else if (Ty) { switch (Ty->getTypeID()) { default: llvm_unreachable("Unhandled type"); diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h --- a/llvm/lib/IR/LLVMContextImpl.h +++ b/llvm/lib/IR/LLVMContextImpl.h @@ -191,6 +191,60 @@ } }; +struct OpaqueTypeKeyInfo { + struct KeyTy { + StringRef Name; + ArrayRef TypeParams; + ArrayRef IntParams; + + KeyTy(StringRef N, const ArrayRef &TP, const ArrayRef &IP) + : Name(N), TypeParams(TP), IntParams(IP) {} + KeyTy(const OpaqueType *OT) + : Name(OT->getName()), TypeParams(OT->type_params()), + IntParams(OT->int_params()) {} + + bool operator==(const KeyTy &that) const { + if (Name != that.Name) + return false; + if (TypeParams != that.TypeParams) + return false; + if (IntParams != that.IntParams) + return false; + return true; + } + bool operator!=(const KeyTy &that) const { return !this->operator==(that); } + }; + + static inline OpaqueType *getEmptyKey() { + return DenseMapInfo::getEmptyKey(); + } + + static inline OpaqueType *getTombstoneKey() { + return DenseMapInfo::getTombstoneKey(); + } + + static unsigned getHashValue(const KeyTy &Key) { + return hash_combine( + Key.Name, + hash_combine_range(Key.TypeParams.begin(), Key.TypeParams.end()), + hash_combine_range(Key.IntParams.begin(), Key.IntParams.end())); + } + + static unsigned getHashValue(const OpaqueType *FT) { + return getHashValue(KeyTy(FT)); + } + + static bool isEqual(const KeyTy &LHS, const OpaqueType *RHS) { + if (RHS == getEmptyKey() || RHS == getTombstoneKey()) + return false; + return LHS == KeyTy(RHS); + } + + static bool isEqual(const OpaqueType *LHS, const OpaqueType *RHS) { + return LHS == RHS; + } +}; + /// Structure for hashing arbitrary MDNode operands. class MDNodeOpsKey { ArrayRef RawOps; @@ -1481,6 +1535,9 @@ StringMap NamedStructTypes; unsigned NamedStructTypesUniqueID = 0; + using OpaqueTypeSet = DenseSet; + OpaqueTypeSet OpaqueTypes; + DenseMap, ArrayType *> ArrayTypes; DenseMap, VectorType *> VectorTypes; DenseMap PointerTypes; // Pointers in AddrSpace = 0 diff --git a/llvm/lib/IR/Type.cpp b/llvm/lib/IR/Type.cpp --- a/llvm/lib/IR/Type.cpp +++ b/llvm/lib/IR/Type.cpp @@ -784,3 +784,51 @@ bool PointerType::isLoadableOrStorableType(Type *ElemTy) { return isValidElementType(ElemTy) && !ElemTy->isFunctionTy(); } + +//===----------------------------------------------------------------------===// +// OpaqueType Implementation +//===----------------------------------------------------------------------===// + +OpaqueType::OpaqueType(LLVMContext &C, StringRef Name, ArrayRef Types, + ArrayRef Ints) + : Type(C, OpaqueTyID), Name(Name) { + NumContainedTys = Types.size(); + + // Parameter storage immediately follows the class in allocation. + Type **Params = reinterpret_cast(this + 1); + ContainedTys = Params; + for (Type *T : Types) + *Params++ = T; + + setSubclassData(Ints.size()); + unsigned *IntParamSpace = reinterpret_cast(Params); + IntParams = IntParamSpace; + for (unsigned IntParam : Ints) + *IntParamSpace++ = IntParam; +} + +OpaqueType *OpaqueType::get(LLVMContext &C, StringRef Name, + ArrayRef Types, ArrayRef Ints) { + const OpaqueTypeKeyInfo::KeyTy Key(Name, Types, Ints); + OpaqueType *OT; + // Since we only want to allocate a fresh opaque type in case none is found + // and we don't want to perform two lookups (one for checking if existent and + // one for inserting the newly allocated one), here we instead lookup based on + // Key and update the reference to the opaque type in-place to a newly + // allocated one if not found. + auto Insertion = C.pImpl->OpaqueTypes.insert_as(nullptr, Key); + if (Insertion.second) { + // The opaque type was not found. Allocate one and update OpaqueTypes + // in-place. + OT = (OpaqueType *)C.pImpl->Alloc.Allocate( + sizeof(OpaqueType) + sizeof(Type *) * Types.size() + + sizeof(unsigned) * Ints.size(), + alignof(OpaqueType)); + new (OT) OpaqueType(C, Name, Types, Ints); + *Insertion.first = OT; + } else { + // The opaque type was found. Just return it. + OT = *Insertion.first; + } + return OT; +} diff --git a/llvm/lib/Transforms/Scalar/SROA.cpp b/llvm/lib/Transforms/Scalar/SROA.cpp --- a/llvm/lib/Transforms/Scalar/SROA.cpp +++ b/llvm/lib/Transforms/Scalar/SROA.cpp @@ -1714,6 +1714,9 @@ return false; } + if (OldTy->isOpaqueTy() || NewTy->isOpaqueTy()) + return false; + return true; } diff --git a/llvm/lib/Transforms/Utils/VNCoercion.cpp b/llvm/lib/Transforms/Utils/VNCoercion.cpp --- a/llvm/lib/Transforms/Utils/VNCoercion.cpp +++ b/llvm/lib/Transforms/Utils/VNCoercion.cpp @@ -57,10 +57,13 @@ // The implementation below uses inttoptr for vectors of unequal size; we // can't allow this for non integral pointers. We could teach it to extract - // exact subvectors if desired. + // exact subvectors if desired. if (StoredNI && StoreSize != DL.getTypeSizeInBits(LoadTy).getFixedSize()) return false; + if (StoredTy->isOpaqueTy() || LoadTy->isOpaqueTy()) + return false; + return true; } diff --git a/llvm/test/Assembler/invalid-opaque-type-mixed.ll b/llvm/test/Assembler/invalid-opaque-type-mixed.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Assembler/invalid-opaque-type-mixed.ll @@ -0,0 +1,6 @@ +; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s + +; CHECK: expected uint32 param +define void @f(opaque("type", i32, 0, void) %a) { + ret void +} diff --git a/llvm/test/Assembler/opaque-type-params.ll b/llvm/test/Assembler/opaque-type-params.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Assembler/opaque-type-params.ll @@ -0,0 +1,16 @@ +; RUN: llvm-as < %s | llvm-dis | FileCheck %s +; Check support for basic opaque type properties + +@g1 = global opaque("atype", void) zeroinitializer +@g2 = global opaque("atype", i32) zeroinitializer +@g3 = global opaque("atype", i32, 0) zeroinitializer +@g4 = global opaque("atype", 0) zeroinitializer +@g5 = global opaque("atype", 0, 1, 2) zeroinitializer +@g6 = global opaque("atype", void, i32, float, {i32, bfloat}, 0, 1, 2) zeroinitializer + +;CHECK: @g1 = global opaque("atype", void) zeroinitializer +;CHECK: @g2 = global opaque("atype", i32) zeroinitializer +;CHECK: @g3 = global opaque("atype", i32, 0) zeroinitializer +;CHECK: @g4 = global opaque("atype", 0) zeroinitializer +;CHECK: @g5 = global opaque("atype", 0, 1, 2) zeroinitializer +;CHECK: @g6 = global opaque("atype", void, i32, float, { i32, bfloat }, 0, 1, 2) zeroinitializer diff --git a/llvm/test/Assembler/opaque-types.ll b/llvm/test/Assembler/opaque-types.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Assembler/opaque-types.ll @@ -0,0 +1,25 @@ +; RUN: llvm-as < %s | llvm-dis | FileCheck %s +; Check support for basic opaque type properties + +@global = global opaque("ctype") zeroinitializer + +define opaque("atype") @foo(opaque("atype") %a) { + ret opaque("atype") %a +} + +define opaque("btype") @func2() { + %mem = alloca opaque("btype") + %val = load opaque("btype"), ptr %mem + ret opaque("btype") poison +} + +; CHECK: @global = global opaque("ctype") zeroinitializer +; CHECK: define opaque("atype") @foo(opaque("atype") %a) { +; CHECK: ret opaque("atype") %a +; CHECK: } +; CHECK: define opaque("btype") @func2() { +; CHECK: %mem = alloca opaque("btype") +; CHECK: %val = load opaque("btype"), ptr %mem +; CHECK: ret opaque("btype") poison +; CHECK: } + diff --git a/llvm/test/Transforms/GVN/opaque-type.ll b/llvm/test/Transforms/GVN/opaque-type.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/GVN/opaque-type.ll @@ -0,0 +1,52 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -passes=gvn < %s | FileCheck %s + +; Check that GVN can work with opaque types correctly. + +target datalayout = "e-p:64:64:64-p1:16:16:16-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-n8:16:32:64" + +define opaque("type") @basic_alloc(opaque("type") %arg) { +; CHECK-LABEL: @basic_alloc( +; CHECK-NEXT: [[VAL:%.*]] = alloca opaque("type"), align 8 +; CHECK-NEXT: store opaque("type") [[ARG:%.*]], ptr [[VAL]], align 8 +; CHECK-NEXT: ret opaque("type") [[ARG]] +; + %val = alloca opaque("type") + store opaque("type") %arg, ptr %val + %ret = load opaque("type"), ptr %val + ret opaque("type") %ret +} + +define opaque("type") @nobitcast(ptr %arg) { +; CHECK-LABEL: @nobitcast( +; CHECK-NEXT: [[VAL:%.*]] = alloca opaque("type"), align 8 +; CHECK-NEXT: store ptr [[ARG:%.*]], ptr [[VAL]], align 8 +; CHECK-NEXT: [[RET:%.*]] = load opaque("type"), ptr [[VAL]], align 8 +; CHECK-NEXT: ret opaque("type") [[RET]] +; + %val = alloca opaque("type") + store ptr %arg, ptr %val + %ret = load opaque("type"), ptr %val + ret opaque("type") %ret +} + +define opaque("type") @viai64(opaque("type") %arg) { +; CHECK-LABEL: @viai64( +; CHECK-NEXT: [[VAL:%.*]] = alloca opaque("type"), align 8 +; CHECK-NEXT: [[BAR:%.*]] = alloca opaque("type"), align 8 +; CHECK-NEXT: store opaque("type") [[ARG:%.*]], ptr [[VAL]], align 8 +; CHECK-NEXT: [[IMEMCPY:%.*]] = load i64, ptr [[VAL]], align 4 +; CHECK-NEXT: store i64 [[IMEMCPY]], ptr [[BAR]], align 4 +; CHECK-NEXT: [[RET:%.*]] = load opaque("type"), ptr [[BAR]], align 8 +; CHECK-NEXT: ret opaque("type") [[RET]] +; + %val = alloca opaque("type") + %bar = alloca opaque("type") + store opaque("type") %arg, ptr %val + %imemcpy = load i64, ptr %val + store i64 %imemcpy, ptr %bar + %ret = load opaque("type"), ptr %bar + ret opaque("type") %ret +} + +declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1) diff --git a/llvm/test/Transforms/SROA/sroa-opaque.ll b/llvm/test/Transforms/SROA/sroa-opaque.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/SROA/sroa-opaque.ll @@ -0,0 +1,62 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -passes=sroa < %s | FileCheck %s + +; Check that SROA can work with opaque types correctly. + +target datalayout = "e-p:64:64:64-p1:16:16:16-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-n8:16:32:64" + +define opaque("type") @basic_alloc(opaque("type") %arg) { +; CHECK-LABEL: @basic_alloc( +; CHECK-NEXT: ret opaque("type") [[ARG:%.*]] +; + %val = alloca opaque("type") + store opaque("type") %arg, ptr %val + %ret = load opaque("type"), ptr %val + ret opaque("type") %ret +} + +define opaque("type") @via_memcpy(opaque("type") %arg) { +; CHECK-LABEL: @via_memcpy( +; CHECK-NEXT: ret opaque("type") [[ARG:%.*]] +; + %val = alloca opaque("type") + %bar = alloca opaque("type") + store opaque("type") %arg, ptr %val + call void @llvm.memcpy.p0.p0.i64(ptr %bar, ptr %val, i64 8, i1 false) + %ret = load opaque("type"), ptr %bar + ret opaque("type") %ret +} + +define opaque("type") @nobitcast(ptr %arg) { +; CHECK-LABEL: @nobitcast( +; CHECK-NEXT: [[VAL:%.*]] = alloca opaque("type"), align 8 +; CHECK-NEXT: store ptr [[ARG:%.*]], ptr [[VAL]], align 8 +; CHECK-NEXT: [[VAL_0_RET:%.*]] = load opaque("type"), ptr [[VAL]], align 8 +; CHECK-NEXT: ret opaque("type") [[VAL_0_RET]] +; + %val = alloca opaque("type") + store ptr %arg, ptr %val + %ret = load opaque("type"), ptr %val + ret opaque("type") %ret +} + +define opaque("type") @viai64(opaque("type") %arg) { +; CHECK-LABEL: @viai64( +; CHECK-NEXT: [[VAL:%.*]] = alloca opaque("type"), align 8 +; CHECK-NEXT: [[BAR:%.*]] = alloca opaque("type"), align 8 +; CHECK-NEXT: store opaque("type") [[ARG:%.*]], ptr [[VAL]], align 8 +; CHECK-NEXT: [[VAL_0_IMEMCPY:%.*]] = load i64, ptr [[VAL]], align 8 +; CHECK-NEXT: store i64 [[VAL_0_IMEMCPY]], ptr [[BAR]], align 8 +; CHECK-NEXT: [[BAR_0_RET:%.*]] = load opaque("type"), ptr [[BAR]], align 8 +; CHECK-NEXT: ret opaque("type") [[BAR_0_RET]] +; + %val = alloca opaque("type") + %bar = alloca opaque("type") + store opaque("type") %arg, ptr %val + %imemcpy = load i64, ptr %val + store i64 %imemcpy, ptr %bar + %ret = load opaque("type"), ptr %bar + ret opaque("type") %ret +} + +declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1) diff --git a/llvm/tools/llvm-c-test/echo.cpp b/llvm/tools/llvm-c-test/echo.cpp --- a/llvm/tools/llvm-c-test/echo.cpp +++ b/llvm/tools/llvm-c-test/echo.cpp @@ -159,6 +159,8 @@ return LLVMX86MMXTypeInContext(Ctx); case LLVMTokenTypeKind: return LLVMTokenTypeInContext(Ctx); + case LLVMOpaqueTypeKind: + assert(false && "Implement me"); } fprintf(stderr, "%d is not a supported typekind\n", Kind); diff --git a/llvm/unittests/IR/TypesTest.cpp b/llvm/unittests/IR/TypesTest.cpp --- a/llvm/unittests/IR/TypesTest.cpp +++ b/llvm/unittests/IR/TypesTest.cpp @@ -61,6 +61,17 @@ EXPECT_FALSE(P2C0->isOpaque()); } +TEST(TypesTest, OpaqueType) { + LLVMContext Context; + Type *A = OpaqueType::get(Context, "typea"); + Type *Aparam = OpaqueType::get(Context, "typea", {}, {0, 1}); + Type *Aparam2 = OpaqueType::get(Context, "typea", {}, {0, 1}); + // Opaque types with same parameters are identical... + EXPECT_EQ(Aparam, Aparam2); + // ... but just having the same name is not enough. + EXPECT_NE(A, Aparam); +} + TEST(TypedPointerType, PrintTest) { std::string Buffer; LLVMContext Context;