| //===- subzero/src/IceGlobalInits.h - Global declarations -------*- C++ -*-===// | |
| // | |
| // The Subzero Code Generator | |
| // | |
| // This file is distributed under the University of Illinois Open Source | |
| // License. See LICENSE.TXT for details. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| /// | |
| /// \file | |
| /// \brief Declares the representation of function declarations, global variable | |
| /// declarations, and the corresponding variable initializers in Subzero. | |
| /// | |
| /// Global variable initializers are represented as a sequence of simple | |
| /// initializers. | |
| /// | |
| //===----------------------------------------------------------------------===// | |
| #ifndef SUBZERO_SRC_ICEGLOBALINITS_H | |
| #define SUBZERO_SRC_ICEGLOBALINITS_H | |
| #include "IceDefs.h" | |
| #include "IceFixups.h" | |
| #include "IceGlobalContext.h" | |
| #include "IceIntrinsics.h" | |
| #include "IceMangling.h" | |
| #include "IceOperand.h" | |
| #include "IceTypes.h" | |
| #ifdef __clang__ | |
| #pragma clang diagnostic push | |
| #pragma clang diagnostic ignored "-Wunused-parameter" | |
| #endif // __clang__ | |
| #include "llvm/Bitcode/NaCl/NaClBitcodeParser.h" // for NaClBitcodeRecord. | |
| #include "llvm/IR/CallingConv.h" | |
| #include "llvm/IR/GlobalValue.h" // for GlobalValue::LinkageTypes. | |
| #ifdef __clang__ | |
| #pragma clang diagnostic pop | |
| #endif // __clang__ | |
| #include <memory> | |
| #include <utility> | |
| // TODO(kschimpf): Remove ourselves from using LLVM representation for calling | |
| // conventions and linkage types. | |
| namespace Ice { | |
| /// Base class for global variable and function declarations. | |
| class GlobalDeclaration { | |
| GlobalDeclaration() = delete; | |
| GlobalDeclaration(const GlobalDeclaration &) = delete; | |
| GlobalDeclaration &operator=(const GlobalDeclaration &) = delete; | |
| public: | |
| /// Discriminator for LLVM-style RTTI. | |
| enum GlobalDeclarationKind { | |
| FunctionDeclarationKind, | |
| VariableDeclarationKind | |
| }; | |
| GlobalDeclarationKind getKind() const { return Kind; } | |
| GlobalString getName() const { return Name; } | |
| void setName(GlobalContext *Ctx, const std::string &NewName) { | |
| Name = Ctx->getGlobalString(getSuppressMangling() ? NewName | |
| : mangleName(NewName)); | |
| } | |
| void setName(GlobalString NewName) { Name = NewName; } | |
| void setName(GlobalContext *Ctx) { | |
| Name = GlobalString::createWithoutString(Ctx); | |
| } | |
| bool hasName() const { return Name.hasStdString(); } | |
| bool isInternal() const { | |
| return Linkage == llvm::GlobalValue::InternalLinkage; | |
| } | |
| llvm::GlobalValue::LinkageTypes getLinkage() const { return Linkage; } | |
| void setLinkage(llvm::GlobalValue::LinkageTypes L) { | |
| assert(!hasName()); | |
| Linkage = L; | |
| } | |
| bool isExternal() const { | |
| return Linkage == llvm::GlobalValue::ExternalLinkage; | |
| } | |
| virtual ~GlobalDeclaration() = default; | |
| /// Prints out type of the global declaration. | |
| virtual void dumpType(Ostream &Stream) const = 0; | |
| /// Prints out the global declaration. | |
| virtual void dump(Ostream &Stream) const = 0; | |
| /// Returns true if when emitting names, we should suppress mangling. | |
| virtual bool getSuppressMangling() const = 0; | |
| /// Returns textual name of linkage. | |
| const char *getLinkageName() const { | |
| return isInternal() ? "internal" : "external"; | |
| } | |
| /// Returns true if the name of this GlobalDeclaration indicates that it | |
| /// should have ExternalLinkage (as a special case). | |
| virtual bool isPNaClABIExternalName(const std::string &Name) const = 0; | |
| protected: | |
| GlobalDeclaration(GlobalDeclarationKind Kind, | |
| llvm::GlobalValue::LinkageTypes Linkage) | |
| : Kind(Kind), Linkage(Linkage) {} | |
| /// Returns true if linkage is defined correctly for the global declaration, | |
| /// based on default rules. | |
| bool verifyLinkageDefault() const { | |
| switch (Linkage) { | |
| default: | |
| return false; | |
| case llvm::GlobalValue::InternalLinkage: | |
| return true; | |
| case llvm::GlobalValue::ExternalLinkage: | |
| return getFlags().getAllowExternDefinedSymbols(); | |
| } | |
| } | |
| const GlobalDeclarationKind Kind; | |
| llvm::GlobalValue::LinkageTypes Linkage; | |
| GlobalString Name; | |
| }; | |
| /// Models a function declaration. This includes the type signature of the | |
| /// function, its calling conventions, and its linkage. | |
| class FunctionDeclaration : public GlobalDeclaration { | |
| FunctionDeclaration() = delete; | |
| FunctionDeclaration(const FunctionDeclaration &) = delete; | |
| FunctionDeclaration &operator=(const FunctionDeclaration &) = delete; | |
| public: | |
| static FunctionDeclaration *create(GlobalContext *Context, | |
| const FuncSigType &Signature, | |
| llvm::CallingConv::ID CallingConv, | |
| llvm::GlobalValue::LinkageTypes Linkage, | |
| bool IsProto) { | |
| return new (Context->allocate<FunctionDeclaration>()) | |
| FunctionDeclaration(Signature, CallingConv, Linkage, IsProto); | |
| } | |
| const FuncSigType &getSignature() const { return Signature; } | |
| llvm::CallingConv::ID getCallingConv() const { return CallingConv; } | |
| /// isProto implies that there isn't a (local) definition for the function. | |
| bool isProto() const { return IsProto; } | |
| static bool classof(const GlobalDeclaration *Addr) { | |
| return Addr->getKind() == FunctionDeclarationKind; | |
| } | |
| void dumpType(Ostream &Stream) const final; | |
| void dump(Ostream &Stream) const final; | |
| bool getSuppressMangling() const final { return isExternal() && IsProto; } | |
| /// Returns true if linkage is correct for the function declaration. | |
| bool verifyLinkageCorrect(const GlobalContext *Ctx) const { | |
| if (getName().hasStdString()) { | |
| if (isPNaClABIExternalName(getName().toString()) || | |
| isIntrinsicName(Ctx)) { | |
| return Linkage == llvm::GlobalValue::ExternalLinkage; | |
| } | |
| } | |
| return verifyLinkageDefault(); | |
| } | |
| /// Validates that the type signature of the function is correct. Returns true | |
| /// if valid. | |
| bool validateTypeSignature(const GlobalContext *Ctx) const { | |
| bool IsIntrinsic; | |
| if (const Intrinsics::FullIntrinsicInfo *Info = | |
| getIntrinsicInfo(Ctx, &IsIntrinsic)) | |
| return validateIntrinsicTypeSignature(Info); | |
| return !IsIntrinsic && validateRegularTypeSignature(); | |
| } | |
| /// Generates an error message describing why validateTypeSignature returns | |
| /// false. | |
| std::string getTypeSignatureError(const GlobalContext *Ctx); | |
| /// Returns corresponding PNaCl intrisic information. | |
| const Intrinsics::FullIntrinsicInfo * | |
| getIntrinsicInfo(const GlobalContext *Ctx) const { | |
| bool BadIntrinsic; | |
| return getIntrinsicInfo(Ctx, &BadIntrinsic); | |
| } | |
| /// Same as above, except IsIntrinsic is true if the function is intrinsic | |
| /// (even if not a PNaCl intrinsic). | |
| const Intrinsics::FullIntrinsicInfo * | |
| getIntrinsicInfo(const GlobalContext *Ctx, bool *IsIntrinsic) const; | |
| private: | |
| const Ice::FuncSigType Signature; | |
| llvm::CallingConv::ID CallingConv; | |
| const bool IsProto; | |
| FunctionDeclaration(const FuncSigType &Signature, | |
| llvm::CallingConv::ID CallingConv, | |
| llvm::GlobalValue::LinkageTypes Linkage, bool IsProto) | |
| : GlobalDeclaration(FunctionDeclarationKind, Linkage), | |
| Signature(Signature), CallingConv(CallingConv), IsProto(IsProto) {} | |
| bool isPNaClABIExternalName(const std::string &Name) const override { | |
| return Name == "_start"; | |
| } | |
| bool isIntrinsicName(const GlobalContext *Ctx) const { | |
| bool IsIntrinsic; | |
| getIntrinsicInfo(Ctx, &IsIntrinsic); | |
| return IsIntrinsic; | |
| } | |
| bool validateRegularTypeSignature() const; | |
| bool validateIntrinsicTypeSignature( | |
| const Intrinsics::FullIntrinsicInfo *Info) const; | |
| }; | |
| /// Models a global variable declaration, and its initializers. | |
| class VariableDeclaration : public GlobalDeclaration { | |
| VariableDeclaration(const VariableDeclaration &) = delete; | |
| VariableDeclaration &operator=(const VariableDeclaration &) = delete; | |
| public: | |
| /// Base class for a global variable initializer. | |
| class Initializer { | |
| Initializer(const Initializer &) = delete; | |
| Initializer &operator=(const Initializer &) = delete; | |
| public: | |
| /// Discriminator for LLVM-style RTTI. | |
| enum InitializerKind { | |
| DataInitializerKind, | |
| ZeroInitializerKind, | |
| RelocInitializerKind | |
| }; | |
| InitializerKind getKind() const { return Kind; } | |
| virtual SizeT getNumBytes() const = 0; | |
| virtual void dump(Ostream &Stream) const = 0; | |
| virtual void dumpType(Ostream &Stream) const; | |
| protected: | |
| explicit Initializer(InitializerKind Kind) : Kind(Kind) {} | |
| private: | |
| const InitializerKind Kind; | |
| }; | |
| static_assert(std::is_trivially_destructible<Initializer>::value, | |
| "Initializer must be trivially destructible."); | |
| /// Models the data in a data initializer. | |
| using DataVecType = char *; | |
| /// Defines a sequence of byte values as a data initializer. | |
| class DataInitializer : public Initializer { | |
| DataInitializer(const DataInitializer &) = delete; | |
| DataInitializer &operator=(const DataInitializer &) = delete; | |
| public: | |
| template <class... Args> | |
| static DataInitializer *create(VariableDeclarationList *VDL, | |
| Args &&... TheArgs) { | |
| return new (VDL->allocate_initializer<DataInitializer>()) | |
| DataInitializer(VDL, std::forward<Args>(TheArgs)...); | |
| } | |
| const llvm::StringRef getContents() const { | |
| return llvm::StringRef(Contents, ContentsSize); | |
| } | |
| SizeT getNumBytes() const final { return ContentsSize; } | |
| void dump(Ostream &Stream) const final; | |
| static bool classof(const Initializer *D) { | |
| return D->getKind() == DataInitializerKind; | |
| } | |
| private: | |
| DataInitializer(VariableDeclarationList *VDL, | |
| const llvm::NaClBitcodeRecord::RecordVector &Values) | |
| : Initializer(DataInitializerKind), ContentsSize(Values.size()), | |
| // ugh, we should actually do new char[], but this may involve | |
| // implementation-specific details. Given that Contents is arena | |
| // allocated, and never delete[]d, just use char -- | |
| // AllocOwner->allocate_array will allocate a buffer with the right | |
| // size. | |
| Contents(new (VDL->allocate_initializer<char>(ContentsSize)) char) { | |
| for (SizeT I = 0; I < Values.size(); ++I) | |
| Contents[I] = static_cast<int8_t>(Values[I]); | |
| } | |
| DataInitializer(VariableDeclarationList *VDL, const char *Str, | |
| size_t StrLen) | |
| : Initializer(DataInitializerKind), ContentsSize(StrLen), | |
| Contents(new (VDL->allocate_initializer<char>(ContentsSize)) char) { | |
| for (size_t i = 0; i < StrLen; ++i) | |
| Contents[i] = Str[i]; | |
| } | |
| /// The byte contents of the data initializer. | |
| const SizeT ContentsSize; | |
| DataVecType Contents; | |
| }; | |
| static_assert(std::is_trivially_destructible<DataInitializer>::value, | |
| "DataInitializer must be trivially destructible."); | |
| /// Defines a sequence of bytes initialized to zero. | |
| class ZeroInitializer : public Initializer { | |
| ZeroInitializer(const ZeroInitializer &) = delete; | |
| ZeroInitializer &operator=(const ZeroInitializer &) = delete; | |
| public: | |
| static ZeroInitializer *create(VariableDeclarationList *VDL, SizeT Size) { | |
| return new (VDL->allocate_initializer<ZeroInitializer>()) | |
| ZeroInitializer(Size); | |
| } | |
| SizeT getNumBytes() const final { return Size; } | |
| void dump(Ostream &Stream) const final; | |
| static bool classof(const Initializer *Z) { | |
| return Z->getKind() == ZeroInitializerKind; | |
| } | |
| private: | |
| explicit ZeroInitializer(SizeT Size) | |
| : Initializer(ZeroInitializerKind), Size(Size) {} | |
| /// The number of bytes to be zero initialized. | |
| SizeT Size; | |
| }; | |
| static_assert(std::is_trivially_destructible<ZeroInitializer>::value, | |
| "ZeroInitializer must be trivially destructible."); | |
| /// Defines the relocation value of another global declaration. | |
| class RelocInitializer : public Initializer { | |
| RelocInitializer(const RelocInitializer &) = delete; | |
| RelocInitializer &operator=(const RelocInitializer &) = delete; | |
| public: | |
| static RelocInitializer *create(VariableDeclarationList *VDL, | |
| const GlobalDeclaration *Declaration, | |
| const RelocOffsetArray &OffsetExpr) { | |
| constexpr bool NoFixup = false; | |
| return new (VDL->allocate_initializer<RelocInitializer>()) | |
| RelocInitializer(VDL, Declaration, OffsetExpr, NoFixup); | |
| } | |
| static RelocInitializer *create(VariableDeclarationList *VDL, | |
| const GlobalDeclaration *Declaration, | |
| const RelocOffsetArray &OffsetExpr, | |
| FixupKind Fixup) { | |
| constexpr bool HasFixup = true; | |
| return new (VDL->allocate_initializer<RelocInitializer>()) | |
| RelocInitializer(VDL, Declaration, OffsetExpr, HasFixup, Fixup); | |
| } | |
| RelocOffsetT getOffset() const { | |
| RelocOffsetT Offset = 0; | |
| for (SizeT i = 0; i < OffsetExprSize; ++i) { | |
| Offset += OffsetExpr[i]->getOffset(); | |
| } | |
| return Offset; | |
| } | |
| bool hasFixup() const { return HasFixup; } | |
| FixupKind getFixup() const { | |
| assert(HasFixup); | |
| return Fixup; | |
| } | |
| const GlobalDeclaration *getDeclaration() const { return Declaration; } | |
| SizeT getNumBytes() const final { return RelocAddrSize; } | |
| void dump(Ostream &Stream) const final; | |
| void dumpType(Ostream &Stream) const final; | |
| static bool classof(const Initializer *R) { | |
| return R->getKind() == RelocInitializerKind; | |
| } | |
| private: | |
| RelocInitializer(VariableDeclarationList *VDL, | |
| const GlobalDeclaration *Declaration, | |
| const RelocOffsetArray &OffsetExpr, bool HasFixup, | |
| FixupKind Fixup = 0) | |
| : Initializer(RelocInitializerKind), | |
| Declaration(Declaration), // The global declaration used in the reloc. | |
| OffsetExprSize(OffsetExpr.size()), | |
| OffsetExpr(new (VDL->allocate_initializer<RelocOffset *>( | |
| OffsetExprSize)) RelocOffset *), | |
| HasFixup(HasFixup), Fixup(Fixup) { | |
| for (SizeT i = 0; i < OffsetExprSize; ++i) { | |
| this->OffsetExpr[i] = OffsetExpr[i]; | |
| } | |
| } | |
| const GlobalDeclaration *Declaration; | |
| /// The offset to add to the relocation. | |
| const SizeT OffsetExprSize; | |
| RelocOffset **OffsetExpr; | |
| const bool HasFixup = false; | |
| const FixupKind Fixup = 0; | |
| }; | |
| static_assert(std::is_trivially_destructible<RelocInitializer>::value, | |
| "RelocInitializer must be trivially destructible."); | |
| /// Models the list of initializers. | |
| // TODO(jpp): missing allocator. | |
| using InitializerListType = std::vector<Initializer *>; | |
| static VariableDeclaration *create(VariableDeclarationList *VDL, | |
| bool SuppressMangling = false, | |
| llvm::GlobalValue::LinkageTypes Linkage = | |
| llvm::GlobalValue::InternalLinkage) { | |
| return new (VDL->allocate_variable_declaration<VariableDeclaration>()) | |
| VariableDeclaration(Linkage, SuppressMangling); | |
| } | |
| static VariableDeclaration *createExternal(VariableDeclarationList *VDL) { | |
| constexpr bool SuppressMangling = true; | |
| constexpr llvm::GlobalValue::LinkageTypes Linkage = | |
| llvm::GlobalValue::ExternalLinkage; | |
| return create(VDL, SuppressMangling, Linkage); | |
| } | |
| const InitializerListType &getInitializers() const { return Initializers; } | |
| bool getIsConstant() const { return IsConstant; } | |
| void setIsConstant(bool NewValue) { IsConstant = NewValue; } | |
| uint32_t getAlignment() const { return Alignment; } | |
| void setAlignment(uint32_t NewAlignment) { Alignment = NewAlignment; } | |
| bool hasInitializer() const { return HasInitializer; } | |
| bool hasNonzeroInitializer() const { | |
| return !(Initializers.size() == 1 && | |
| llvm::isa<ZeroInitializer>(Initializers[0])); | |
| } | |
| /// Returns the number of bytes for the initializer of the global address. | |
| SizeT getNumBytes() const { | |
| SizeT Count = 0; | |
| for (const auto *Init : Initializers) { | |
| Count += Init->getNumBytes(); | |
| } | |
| return Count; | |
| } | |
| /// Adds Initializer to the list of initializers. Takes ownership of the | |
| /// initializer. | |
| void addInitializer(Initializer *Initializer) { | |
| const bool OldSuppressMangling = getSuppressMangling(); | |
| Initializers.emplace_back(Initializer); | |
| HasInitializer = true; | |
| // The getSuppressMangling() logic depends on whether the global variable | |
| // has initializers. If its value changed as a result of adding an | |
| // initializer, then make sure we haven't previously set the name based on | |
| // faulty SuppressMangling logic. | |
| const bool SameMangling = (OldSuppressMangling == getSuppressMangling()); | |
| (void)SameMangling; | |
| assert(Name.hasStdString() || SameMangling); | |
| } | |
| /// Prints out type for initializer associated with the declaration to Stream. | |
| void dumpType(Ostream &Stream) const final; | |
| /// Prints out the definition of the global variable declaration (including | |
| /// initialization). | |
| virtual void dump(Ostream &Stream) const override; | |
| /// Returns true if linkage is correct for the variable declaration. | |
| bool verifyLinkageCorrect() const { | |
| if (getName().hasStdString()) { | |
| if (isPNaClABIExternalName(getName().toString())) { | |
| return Linkage == llvm::GlobalValue::ExternalLinkage; | |
| } | |
| } | |
| return verifyLinkageDefault(); | |
| } | |
| static bool classof(const GlobalDeclaration *Addr) { | |
| return Addr->getKind() == VariableDeclarationKind; | |
| } | |
| bool getSuppressMangling() const final { | |
| if (ForceSuppressMangling) | |
| return true; | |
| return isExternal() && !hasInitializer(); | |
| } | |
| void discardInitializers() { Initializers.clear(); } | |
| bool isPNaClABIExternalName(const std::string &Name) const override { | |
| return Name == "__pnacl_pso_root"; | |
| } | |
| private: | |
| /// List of initializers for the declared variable. | |
| InitializerListType Initializers; | |
| bool HasInitializer = false; | |
| /// The alignment of the declared variable. | |
| uint32_t Alignment = 0; | |
| /// True if a declared (global) constant. | |
| bool IsConstant = false; | |
| /// If set to true, force getSuppressMangling() to return true. | |
| const bool ForceSuppressMangling; | |
| VariableDeclaration(llvm::GlobalValue::LinkageTypes Linkage, | |
| bool SuppressMangling) | |
| : GlobalDeclaration(VariableDeclarationKind, Linkage), | |
| ForceSuppressMangling(SuppressMangling) {} | |
| }; | |
| template <class StreamType> | |
| inline StreamType &operator<<(StreamType &Stream, | |
| const VariableDeclaration::Initializer &Init) { | |
| Init.dump(Stream); | |
| return Stream; | |
| } | |
| template <class StreamType> | |
| inline StreamType &operator<<(StreamType &Stream, | |
| const GlobalDeclaration &Addr) { | |
| Addr.dump(Stream); | |
| return Stream; | |
| } | |
| } // end of namespace Ice | |
| #endif // SUBZERO_SRC_ICEGLOBALINITS_H |