//===- 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