//==-- CGFunctionInfo.h - Representation of function argument/return types -==// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Defines CGFunctionInfo and associated types used in representing the // LLVM source types and ABI-coerced types for function arguments and // return values. // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_CODEGEN_CGFUNCTIONINFO_H #define LLVM_CLANG_CODEGEN_CGFUNCTIONINFO_H #include "clang/AST/Attr.h" #include "clang/AST/CanonicalType.h" #include "clang/AST/CharUnits.h" #include "clang/AST/Decl.h" #include "clang/AST/Type.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/Support/TrailingObjects.h" #include namespace clang { namespace CodeGen { /// ABIArgInfo - Helper class to encapsulate information about how a /// specific C type should be passed to or returned from a function. class ABIArgInfo { public: enum Kind : uint8_t { /// Direct - Pass the argument directly using the normal converted LLVM /// type, or by coercing to another specified type stored in /// 'CoerceToType'). If an offset is specified (in UIntData), then the /// argument passed is offset by some number of bytes in the memory /// representation. A dummy argument is emitted before the real argument /// if the specified type stored in "PaddingType" is not zero. Direct, /// Extend - Valid only for integer argument types. Same as 'direct' /// but also emit a zero/sign extension attribute. Extend, /// Indirect - Pass the argument indirectly via a hidden pointer /// with the specified alignment (0 indicates default alignment). Indirect, /// Ignore - Ignore the argument (treat as void). Useful for void and /// empty structs. Ignore, /// Expand - Only valid for aggregate argument types. The structure should /// be expanded into consecutive arguments for its constituent fields. /// Currently expand is only allowed on structures whose fields /// are all scalar types or are themselves expandable types. Expand, /// CoerceAndExpand - Only valid for aggregate argument types. The /// structure should be expanded into consecutive arguments corresponding /// to the non-array elements of the type stored in CoerceToType. /// Array elements in the type are assumed to be padding and skipped. CoerceAndExpand, /// InAlloca - Pass the argument directly using the LLVM inalloca attribute. /// This is similar to indirect with byval, except it only applies to /// arguments stored in memory and forbids any implicit copies. When /// applied to a return type, it means the value is returned indirectly via /// an implicit sret parameter stored in the argument struct. InAlloca, KindFirst = Direct, KindLast = InAlloca }; private: llvm::Type *TypeData; // canHaveCoerceToType() union { llvm::Type *PaddingType; // canHavePaddingType() llvm::Type *UnpaddedCoerceAndExpandType; // isCoerceAndExpand() }; union { unsigned DirectOffset; // isDirect() || isExtend() unsigned IndirectAlign; // isIndirect() unsigned AllocaFieldIndex; // isInAlloca() }; Kind TheKind; bool PaddingInReg : 1; bool InAllocaSRet : 1; // isInAlloca() bool IndirectByVal : 1; // isIndirect() bool IndirectRealign : 1; // isIndirect() bool SRetAfterThis : 1; // isIndirect() bool InReg : 1; // isDirect() || isExtend() || isIndirect() bool CanBeFlattened: 1; // isDirect() bool canHavePaddingType() const { return isDirect() || isExtend() || isIndirect() || isExpand(); } void setPaddingType(llvm::Type *T) { assert(canHavePaddingType()); PaddingType = T; } void setUnpaddedCoerceToType(llvm::Type *T) { assert(isCoerceAndExpand()); UnpaddedCoerceAndExpandType = T; } ABIArgInfo(Kind K) : TheKind(K), PaddingInReg(false), InReg(false) { } public: ABIArgInfo() : TypeData(nullptr), PaddingType(nullptr), DirectOffset(0), TheKind(Direct), PaddingInReg(false), InReg(false) {} static ABIArgInfo getDirect(llvm::Type *T = nullptr, unsigned Offset = 0, llvm::Type *Padding = nullptr, bool CanBeFlattened = true) { auto AI = ABIArgInfo(Direct); AI.setCoerceToType(T); AI.setPaddingType(Padding); AI.setDirectOffset(Offset); AI.setCanBeFlattened(CanBeFlattened); return AI; } static ABIArgInfo getDirectInReg(llvm::Type *T = nullptr) { auto AI = getDirect(T); AI.setInReg(true); return AI; } static ABIArgInfo getExtend(llvm::Type *T = nullptr) { auto AI = ABIArgInfo(Extend); AI.setCoerceToType(T); AI.setPaddingType(nullptr); AI.setDirectOffset(0); return AI; } static ABIArgInfo getExtendInReg(llvm::Type *T = nullptr) { auto AI = getExtend(T); AI.setInReg(true); return AI; } static ABIArgInfo getIgnore() { return ABIArgInfo(Ignore); } static ABIArgInfo getIndirect(CharUnits Alignment, bool ByVal = true, bool Realign = false, llvm::Type *Padding = nullptr) { auto AI = ABIArgInfo(Indirect); AI.setIndirectAlign(Alignment); AI.setIndirectByVal(ByVal); AI.setIndirectRealign(Realign); AI.setSRetAfterThis(false); AI.setPaddingType(Padding); return AI; } static ABIArgInfo getIndirectInReg(CharUnits Alignment, bool ByVal = true, bool Realign = false) { auto AI = getIndirect(Alignment, ByVal, Realign); AI.setInReg(true); return AI; } static ABIArgInfo getInAlloca(unsigned FieldIndex) { auto AI = ABIArgInfo(InAlloca); AI.setInAllocaFieldIndex(FieldIndex); return AI; } static ABIArgInfo getExpand() { auto AI = ABIArgInfo(Expand); AI.setPaddingType(nullptr); return AI; } static ABIArgInfo getExpandWithPadding(bool PaddingInReg, llvm::Type *Padding) { auto AI = getExpand(); AI.setPaddingInReg(PaddingInReg); AI.setPaddingType(Padding); return AI; } /// \param unpaddedCoerceToType The coerce-to type with padding elements /// removed, canonicalized to a single element if it would otherwise /// have exactly one element. static ABIArgInfo getCoerceAndExpand(llvm::StructType *coerceToType, llvm::Type *unpaddedCoerceToType) { #ifndef NDEBUG // Sanity checks on unpaddedCoerceToType. // Assert that we only have a struct type if there are multiple elements. auto unpaddedStruct = dyn_cast(unpaddedCoerceToType); assert(!unpaddedStruct || unpaddedStruct->getNumElements() != 1); // Assert that all the non-padding elements have a corresponding element // in the unpadded type. unsigned unpaddedIndex = 0; for (auto eltType : coerceToType->elements()) { if (isPaddingForCoerceAndExpand(eltType)) continue; if (unpaddedStruct) { assert(unpaddedStruct->getElementType(unpaddedIndex) == eltType); } else { assert(unpaddedIndex == 0 && unpaddedCoerceToType == eltType); } unpaddedIndex++; } // Assert that there aren't extra elements in the unpadded type. if (unpaddedStruct) { assert(unpaddedStruct->getNumElements() == unpaddedIndex); } else { assert(unpaddedIndex == 1); } #endif auto AI = ABIArgInfo(CoerceAndExpand); AI.setCoerceToType(coerceToType); AI.setUnpaddedCoerceToType(unpaddedCoerceToType); return AI; } static bool isPaddingForCoerceAndExpand(llvm::Type *eltType) { if (eltType->isArrayTy()) { assert(eltType->getArrayElementType()->isIntegerTy(8)); return true; } else { return false; } } Kind getKind() const { return TheKind; } bool isDirect() const { return TheKind == Direct; } bool isInAlloca() const { return TheKind == InAlloca; } bool isExtend() const { return TheKind == Extend; } bool isIgnore() const { return TheKind == Ignore; } bool isIndirect() const { return TheKind == Indirect; } bool isExpand() const { return TheKind == Expand; } bool isCoerceAndExpand() const { return TheKind == CoerceAndExpand; } bool canHaveCoerceToType() const { return isDirect() || isExtend() || isCoerceAndExpand(); } // Direct/Extend accessors unsigned getDirectOffset() const { assert((isDirect() || isExtend()) && "Not a direct or extend kind"); return DirectOffset; } void setDirectOffset(unsigned Offset) { assert((isDirect() || isExtend()) && "Not a direct or extend kind"); DirectOffset = Offset; } llvm::Type *getPaddingType() const { return (canHavePaddingType() ? PaddingType : nullptr); } bool getPaddingInReg() const { return PaddingInReg; } void setPaddingInReg(bool PIR) { PaddingInReg = PIR; } llvm::Type *getCoerceToType() const { assert(canHaveCoerceToType() && "Invalid kind!"); return TypeData; } void setCoerceToType(llvm::Type *T) { assert(canHaveCoerceToType() && "Invalid kind!"); TypeData = T; } llvm::StructType *getCoerceAndExpandType() const { assert(isCoerceAndExpand()); return cast(TypeData); } llvm::Type *getUnpaddedCoerceAndExpandType() const { assert(isCoerceAndExpand()); return UnpaddedCoerceAndExpandType; } ArrayRefgetCoerceAndExpandTypeSequence() const { assert(isCoerceAndExpand()); if (auto structTy = dyn_cast(UnpaddedCoerceAndExpandType)) { return structTy->elements(); } else { return llvm::makeArrayRef(&UnpaddedCoerceAndExpandType, 1); } } bool getInReg() const { assert((isDirect() || isExtend() || isIndirect()) && "Invalid kind!"); return InReg; } void setInReg(bool IR) { assert((isDirect() || isExtend() || isIndirect()) && "Invalid kind!"); InReg = IR; } // Indirect accessors CharUnits getIndirectAlign() const { assert(isIndirect() && "Invalid kind!"); return CharUnits::fromQuantity(IndirectAlign); } void setIndirectAlign(CharUnits IA) { assert(isIndirect() && "Invalid kind!"); IndirectAlign = IA.getQuantity(); } bool getIndirectByVal() const { assert(isIndirect() && "Invalid kind!"); return IndirectByVal; } void setIndirectByVal(bool IBV) { assert(isIndirect() && "Invalid kind!"); IndirectByVal = IBV; } bool getIndirectRealign() const { assert(isIndirect() && "Invalid kind!"); return IndirectRealign; } void setIndirectRealign(bool IR) { assert(isIndirect() && "Invalid kind!"); IndirectRealign = IR; } bool isSRetAfterThis() const { assert(isIndirect() && "Invalid kind!"); return SRetAfterThis; } void setSRetAfterThis(bool AfterThis) { assert(isIndirect() && "Invalid kind!"); SRetAfterThis = AfterThis; } unsigned getInAllocaFieldIndex() const { assert(isInAlloca() && "Invalid kind!"); return AllocaFieldIndex; } void setInAllocaFieldIndex(unsigned FieldIndex) { assert(isInAlloca() && "Invalid kind!"); AllocaFieldIndex = FieldIndex; } /// \brief Return true if this field of an inalloca struct should be returned /// to implement a struct return calling convention. bool getInAllocaSRet() const { assert(isInAlloca() && "Invalid kind!"); return InAllocaSRet; } void setInAllocaSRet(bool SRet) { assert(isInAlloca() && "Invalid kind!"); InAllocaSRet = SRet; } bool getCanBeFlattened() const { assert(isDirect() && "Invalid kind!"); return CanBeFlattened; } void setCanBeFlattened(bool Flatten) { assert(isDirect() && "Invalid kind!"); CanBeFlattened = Flatten; } void dump() const; }; /// A class for recording the number of arguments that a function /// signature requires. class RequiredArgs { /// The number of required arguments, or ~0 if the signature does /// not permit optional arguments. unsigned NumRequired; public: enum All_t { All }; RequiredArgs(All_t _) : NumRequired(~0U) {} explicit RequiredArgs(unsigned n) : NumRequired(n) { assert(n != ~0U); } /// Compute the arguments required by the given formal prototype, /// given that there may be some additional, non-formal arguments /// in play. /// /// If FD is not null, this will consider pass_object_size params in FD. static RequiredArgs forPrototypePlus(const FunctionProtoType *prototype, unsigned additional, const FunctionDecl *FD) { if (!prototype->isVariadic()) return All; if (FD) additional += llvm::count_if(FD->parameters(), [](const ParmVarDecl *PVD) { return PVD->hasAttr(); }); return RequiredArgs(prototype->getNumParams() + additional); } static RequiredArgs forPrototype(const FunctionProtoType *prototype, const FunctionDecl *FD) { return forPrototypePlus(prototype, 0, FD); } static RequiredArgs forPrototype(CanQual prototype, const FunctionDecl *FD) { return forPrototype(prototype.getTypePtr(), FD); } static RequiredArgs forPrototypePlus(CanQual prototype, unsigned additional, const FunctionDecl *FD) { return forPrototypePlus(prototype.getTypePtr(), additional, FD); } bool allowsOptionalArgs() const { return NumRequired != ~0U; } unsigned getNumRequiredArgs() const { assert(allowsOptionalArgs()); return NumRequired; } unsigned getOpaqueData() const { return NumRequired; } static RequiredArgs getFromOpaqueData(unsigned value) { if (value == ~0U) return All; return RequiredArgs(value); } }; // Implementation detail of CGFunctionInfo, factored out so it can be named // in the TrailingObjects base class of CGFunctionInfo. struct CGFunctionInfoArgInfo { CanQualType type; ABIArgInfo info; }; /// CGFunctionInfo - Class to encapsulate the information about a /// function definition. class CGFunctionInfo final : public llvm::FoldingSetNode, private llvm::TrailingObjects { typedef CGFunctionInfoArgInfo ArgInfo; typedef FunctionProtoType::ExtParameterInfo ExtParameterInfo; /// The LLVM::CallingConv to use for this function (as specified by the /// user). unsigned CallingConvention : 8; /// The LLVM::CallingConv to actually use for this function, which may /// depend on the ABI. unsigned EffectiveCallingConvention : 8; /// The clang::CallingConv that this was originally created with. unsigned ASTCallingConvention : 8; /// Whether this is an instance method. unsigned InstanceMethod : 1; /// Whether this is a chain call. unsigned ChainCall : 1; /// Whether this function is noreturn. unsigned NoReturn : 1; /// Whether this function is returns-retained. unsigned ReturnsRetained : 1; /// How many arguments to pass inreg. unsigned HasRegParm : 1; unsigned RegParm : 3; RequiredArgs Required; /// The struct representing all arguments passed in memory. Only used when /// passing non-trivial types with inalloca. Not part of the profile. llvm::StructType *ArgStruct; unsigned ArgStructAlign : 31; unsigned HasExtParameterInfos : 1; unsigned NumArgs; ArgInfo *getArgsBuffer() { return getTrailingObjects(); } const ArgInfo *getArgsBuffer() const { return getTrailingObjects(); } ExtParameterInfo *getExtParameterInfosBuffer() { return getTrailingObjects(); } const ExtParameterInfo *getExtParameterInfosBuffer() const{ return getTrailingObjects(); } CGFunctionInfo() : Required(RequiredArgs::All) {} public: static CGFunctionInfo *create(unsigned llvmCC, bool instanceMethod, bool chainCall, const FunctionType::ExtInfo &extInfo, ArrayRef paramInfos, CanQualType resultType, ArrayRef argTypes, RequiredArgs required); void operator delete(void *p) { ::operator delete(p); } // Friending class TrailingObjects is apparently not good enough for MSVC, // so these have to be public. friend class TrailingObjects; size_t numTrailingObjects(OverloadToken) const { return NumArgs + 1; } size_t numTrailingObjects(OverloadToken) const { return (HasExtParameterInfos ? NumArgs : 0); } typedef const ArgInfo *const_arg_iterator; typedef ArgInfo *arg_iterator; typedef llvm::iterator_range arg_range; typedef llvm::iterator_range arg_const_range; arg_range arguments() { return arg_range(arg_begin(), arg_end()); } arg_const_range arguments() const { return arg_const_range(arg_begin(), arg_end()); } const_arg_iterator arg_begin() const { return getArgsBuffer() + 1; } const_arg_iterator arg_end() const { return getArgsBuffer() + 1 + NumArgs; } arg_iterator arg_begin() { return getArgsBuffer() + 1; } arg_iterator arg_end() { return getArgsBuffer() + 1 + NumArgs; } unsigned arg_size() const { return NumArgs; } bool isVariadic() const { return Required.allowsOptionalArgs(); } RequiredArgs getRequiredArgs() const { return Required; } unsigned getNumRequiredArgs() const { return isVariadic() ? getRequiredArgs().getNumRequiredArgs() : arg_size(); } bool isInstanceMethod() const { return InstanceMethod; } bool isChainCall() const { return ChainCall; } bool isNoReturn() const { return NoReturn; } /// In ARC, whether this function retains its return value. This /// is not always reliable for call sites. bool isReturnsRetained() const { return ReturnsRetained; } /// getASTCallingConvention() - Return the AST-specified calling /// convention. CallingConv getASTCallingConvention() const { return CallingConv(ASTCallingConvention); } /// getCallingConvention - Return the user specified calling /// convention, which has been translated into an LLVM CC. unsigned getCallingConvention() const { return CallingConvention; } /// getEffectiveCallingConvention - Return the actual calling convention to /// use, which may depend on the ABI. unsigned getEffectiveCallingConvention() const { return EffectiveCallingConvention; } void setEffectiveCallingConvention(unsigned Value) { EffectiveCallingConvention = Value; } bool getHasRegParm() const { return HasRegParm; } unsigned getRegParm() const { return RegParm; } FunctionType::ExtInfo getExtInfo() const { return FunctionType::ExtInfo(isNoReturn(), getHasRegParm(), getRegParm(), getASTCallingConvention(), isReturnsRetained()); } CanQualType getReturnType() const { return getArgsBuffer()[0].type; } ABIArgInfo &getReturnInfo() { return getArgsBuffer()[0].info; } const ABIArgInfo &getReturnInfo() const { return getArgsBuffer()[0].info; } ArrayRef getExtParameterInfos() const { if (!HasExtParameterInfos) return {}; return llvm::makeArrayRef(getExtParameterInfosBuffer(), NumArgs); } ExtParameterInfo getExtParameterInfo(unsigned argIndex) const { assert(argIndex <= NumArgs); if (!HasExtParameterInfos) return ExtParameterInfo(); return getExtParameterInfos()[argIndex]; } /// \brief Return true if this function uses inalloca arguments. bool usesInAlloca() const { return ArgStruct; } /// \brief Get the struct type used to represent all the arguments in memory. llvm::StructType *getArgStruct() const { return ArgStruct; } CharUnits getArgStructAlignment() const { return CharUnits::fromQuantity(ArgStructAlign); } void setArgStruct(llvm::StructType *Ty, CharUnits Align) { ArgStruct = Ty; ArgStructAlign = Align.getQuantity(); } void Profile(llvm::FoldingSetNodeID &ID) { ID.AddInteger(getASTCallingConvention()); ID.AddBoolean(InstanceMethod); ID.AddBoolean(ChainCall); ID.AddBoolean(NoReturn); ID.AddBoolean(ReturnsRetained); ID.AddBoolean(HasRegParm); ID.AddInteger(RegParm); ID.AddInteger(Required.getOpaqueData()); ID.AddBoolean(HasExtParameterInfos); if (HasExtParameterInfos) { for (auto paramInfo : getExtParameterInfos()) ID.AddInteger(paramInfo.getOpaqueValue()); } getReturnType().Profile(ID); for (const auto &I : arguments()) I.type.Profile(ID); } static void Profile(llvm::FoldingSetNodeID &ID, bool InstanceMethod, bool ChainCall, const FunctionType::ExtInfo &info, ArrayRef paramInfos, RequiredArgs required, CanQualType resultType, ArrayRef argTypes) { ID.AddInteger(info.getCC()); ID.AddBoolean(InstanceMethod); ID.AddBoolean(ChainCall); ID.AddBoolean(info.getNoReturn()); ID.AddBoolean(info.getProducesResult()); ID.AddBoolean(info.getHasRegParm()); ID.AddInteger(info.getRegParm()); ID.AddInteger(required.getOpaqueData()); ID.AddBoolean(!paramInfos.empty()); if (!paramInfos.empty()) { for (auto paramInfo : paramInfos) ID.AddInteger(paramInfo.getOpaqueValue()); } resultType.Profile(ID); for (ArrayRef::iterator i = argTypes.begin(), e = argTypes.end(); i != e; ++i) { i->Profile(ID); } } }; } // end namespace CodeGen } // end namespace clang #endif