1 //===- subzero/src/IceGlobalInits.h - Global declarations -------*- C++ -*-===//
2 //
3 //                        The Subzero Code Generator
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 ///
10 /// \file
11 /// \brief Declares the representation of function declarations, global variable
12 /// declarations, and the corresponding variable initializers in Subzero.
13 ///
14 /// Global variable initializers are represented as a sequence of simple
15 /// initializers.
16 ///
17 //===----------------------------------------------------------------------===//
18 
19 #ifndef SUBZERO_SRC_ICEGLOBALINITS_H
20 #define SUBZERO_SRC_ICEGLOBALINITS_H
21 
22 #include "IceDefs.h"
23 #include "IceFixups.h"
24 #include "IceGlobalContext.h"
25 #include "IceIntrinsics.h"
26 #include "IceMangling.h"
27 #include "IceOperand.h"
28 #include "IceTypes.h"
29 
30 #ifdef __clang__
31 #pragma clang diagnostic push
32 #pragma clang diagnostic ignored "-Wunused-parameter"
33 #endif // __clang__
34 
35 #include "llvm/Bitcode/NaCl/NaClBitcodeParser.h" // for NaClBitcodeRecord.
36 #include "llvm/IR/CallingConv.h"
37 #include "llvm/IR/GlobalValue.h" // for GlobalValue::LinkageTypes.
38 
39 #ifdef __clang__
40 #pragma clang diagnostic pop
41 #endif // __clang__
42 
43 #include <memory>
44 #include <utility>
45 
46 // TODO(kschimpf): Remove ourselves from using LLVM representation for calling
47 // conventions and linkage types.
48 
49 namespace Ice {
50 
51 /// Base class for global variable and function declarations.
52 class GlobalDeclaration {
53   GlobalDeclaration() = delete;
54   GlobalDeclaration(const GlobalDeclaration &) = delete;
55   GlobalDeclaration &operator=(const GlobalDeclaration &) = delete;
56 
57 public:
58   /// Discriminator for LLVM-style RTTI.
59   enum GlobalDeclarationKind {
60     FunctionDeclarationKind,
61     VariableDeclarationKind
62   };
getKind()63   GlobalDeclarationKind getKind() const { return Kind; }
getName()64   GlobalString getName() const { return Name; }
setName(GlobalContext * Ctx,const std::string & NewName)65   void setName(GlobalContext *Ctx, const std::string &NewName) {
66     Name = Ctx->getGlobalString(getSuppressMangling() ? NewName
67                                                       : mangleName(NewName));
68   }
setName(GlobalString NewName)69   void setName(GlobalString NewName) { Name = NewName; }
setName(GlobalContext * Ctx)70   void setName(GlobalContext *Ctx) {
71     Name = GlobalString::createWithoutString(Ctx);
72   }
hasName()73   bool hasName() const { return Name.hasStdString(); }
isInternal()74   bool isInternal() const {
75     return Linkage == llvm::GlobalValue::InternalLinkage;
76   }
getLinkage()77   llvm::GlobalValue::LinkageTypes getLinkage() const { return Linkage; }
setLinkage(llvm::GlobalValue::LinkageTypes L)78   void setLinkage(llvm::GlobalValue::LinkageTypes L) {
79     assert(!hasName());
80     Linkage = L;
81   }
isExternal()82   bool isExternal() const {
83     return Linkage == llvm::GlobalValue::ExternalLinkage;
84   }
85   virtual ~GlobalDeclaration() = default;
86 
87   /// Prints out type of the global declaration.
88   virtual void dumpType(Ostream &Stream) const = 0;
89 
90   /// Prints out the global declaration.
91   virtual void dump(Ostream &Stream) const = 0;
92 
93   /// Returns true if when emitting names, we should suppress mangling.
94   virtual bool getSuppressMangling() const = 0;
95 
96   /// Returns textual name of linkage.
getLinkageName()97   const char *getLinkageName() const {
98     return isInternal() ? "internal" : "external";
99   }
100 
101 protected:
GlobalDeclaration(GlobalDeclarationKind Kind,llvm::GlobalValue::LinkageTypes Linkage)102   GlobalDeclaration(GlobalDeclarationKind Kind,
103                     llvm::GlobalValue::LinkageTypes Linkage)
104       : Kind(Kind), Linkage(Linkage) {}
105 
106   /// Returns true if linkage is defined correctly for the global declaration,
107   /// based on default rules.
verifyLinkageDefault()108   bool verifyLinkageDefault() const {
109     switch (Linkage) {
110     default:
111       return false;
112     case llvm::GlobalValue::InternalLinkage:
113       return true;
114     case llvm::GlobalValue::ExternalLinkage:
115       return getFlags().getAllowExternDefinedSymbols();
116     }
117   }
118 
119   const GlobalDeclarationKind Kind;
120   llvm::GlobalValue::LinkageTypes Linkage;
121   GlobalString Name;
122 };
123 
124 /// Models a function declaration. This includes the type signature of the
125 /// function, its calling conventions, and its linkage.
126 class FunctionDeclaration : public GlobalDeclaration {
127   FunctionDeclaration() = delete;
128   FunctionDeclaration(const FunctionDeclaration &) = delete;
129   FunctionDeclaration &operator=(const FunctionDeclaration &) = delete;
130 
131 public:
create(GlobalContext * Context,const FuncSigType & Signature,llvm::CallingConv::ID CallingConv,llvm::GlobalValue::LinkageTypes Linkage,bool IsProto)132   static FunctionDeclaration *create(GlobalContext *Context,
133                                      const FuncSigType &Signature,
134                                      llvm::CallingConv::ID CallingConv,
135                                      llvm::GlobalValue::LinkageTypes Linkage,
136                                      bool IsProto) {
137     return new (Context->allocate<FunctionDeclaration>())
138         FunctionDeclaration(Signature, CallingConv, Linkage, IsProto);
139   }
getSignature()140   const FuncSigType &getSignature() const { return Signature; }
getCallingConv()141   llvm::CallingConv::ID getCallingConv() const { return CallingConv; }
142   /// isProto implies that there isn't a (local) definition for the function.
isProto()143   bool isProto() const { return IsProto; }
classof(const GlobalDeclaration * Addr)144   static bool classof(const GlobalDeclaration *Addr) {
145     return Addr->getKind() == FunctionDeclarationKind;
146   }
147   void dumpType(Ostream &Stream) const final;
148   void dump(Ostream &Stream) const final;
getSuppressMangling()149   bool getSuppressMangling() const final { return isExternal() && IsProto; }
150 
151   /// Returns true if linkage is correct for the function declaration.
verifyLinkageCorrect(const GlobalContext * Ctx)152   bool verifyLinkageCorrect(const GlobalContext *Ctx) const {
153     return verifyLinkageDefault();
154   }
155 
156   /// Validates that the type signature of the function is correct. Returns true
157   /// if valid.
158   bool validateTypeSignature() const;
159 
160   /// Generates an error message describing why validateTypeSignature returns
161   /// false.
162   std::string getTypeSignatureError(const GlobalContext *Ctx);
163 
164 private:
165   const Ice::FuncSigType Signature;
166   llvm::CallingConv::ID CallingConv;
167   const bool IsProto;
168 
FunctionDeclaration(const FuncSigType & Signature,llvm::CallingConv::ID CallingConv,llvm::GlobalValue::LinkageTypes Linkage,bool IsProto)169   FunctionDeclaration(const FuncSigType &Signature,
170                       llvm::CallingConv::ID CallingConv,
171                       llvm::GlobalValue::LinkageTypes Linkage, bool IsProto)
172       : GlobalDeclaration(FunctionDeclarationKind, Linkage),
173         Signature(Signature), CallingConv(CallingConv), IsProto(IsProto) {}
174 };
175 
176 /// Models a global variable declaration, and its initializers.
177 class VariableDeclaration : public GlobalDeclaration {
178   VariableDeclaration(const VariableDeclaration &) = delete;
179   VariableDeclaration &operator=(const VariableDeclaration &) = delete;
180 
181 public:
182   /// Base class for a global variable initializer.
183   class Initializer {
184     Initializer(const Initializer &) = delete;
185     Initializer &operator=(const Initializer &) = delete;
186 
187   public:
188     /// Discriminator for LLVM-style RTTI.
189     enum InitializerKind {
190       DataInitializerKind,
191       ZeroInitializerKind,
192       RelocInitializerKind
193     };
getKind()194     InitializerKind getKind() const { return Kind; }
195     virtual SizeT getNumBytes() const = 0;
196     virtual void dump(Ostream &Stream) const = 0;
197     virtual void dumpType(Ostream &Stream) const;
198 
199   protected:
Initializer(InitializerKind Kind)200     explicit Initializer(InitializerKind Kind) : Kind(Kind) {}
201 
202   private:
203     const InitializerKind Kind;
204   };
205   static_assert(std::is_trivially_destructible<Initializer>::value,
206                 "Initializer must be trivially destructible.");
207 
208   /// Models the data in a data initializer.
209   using DataVecType = char *;
210 
211   /// Defines a sequence of byte values as a data initializer.
212   class DataInitializer : public Initializer {
213     DataInitializer(const DataInitializer &) = delete;
214     DataInitializer &operator=(const DataInitializer &) = delete;
215 
216   public:
217     template <class... Args>
create(VariableDeclarationList * VDL,Args &&...TheArgs)218     static DataInitializer *create(VariableDeclarationList *VDL,
219                                    Args &&... TheArgs) {
220       return new (VDL->allocate_initializer<DataInitializer>())
221           DataInitializer(VDL, std::forward<Args>(TheArgs)...);
222     }
223 
getContents()224     const llvm::StringRef getContents() const {
225       return llvm::StringRef(Contents, ContentsSize);
226     }
getNumBytes()227     SizeT getNumBytes() const final { return ContentsSize; }
228     void dump(Ostream &Stream) const final;
classof(const Initializer * D)229     static bool classof(const Initializer *D) {
230       return D->getKind() == DataInitializerKind;
231     }
232 
233   private:
DataInitializer(VariableDeclarationList * VDL,const llvm::NaClBitcodeRecord::RecordVector & Values)234     DataInitializer(VariableDeclarationList *VDL,
235                     const llvm::NaClBitcodeRecord::RecordVector &Values)
236         : Initializer(DataInitializerKind), ContentsSize(Values.size()),
237           // ugh, we should actually do new char[], but this may involve
238           // implementation-specific details. Given that Contents is arena
239           // allocated, and never delete[]d, just use char --
240           // AllocOwner->allocate_array will allocate a buffer with the right
241           // size.
242           Contents(new (VDL->allocate_initializer<char>(ContentsSize)) char) {
243       for (SizeT I = 0; I < Values.size(); ++I)
244         Contents[I] = static_cast<int8_t>(Values[I]);
245     }
246 
DataInitializer(VariableDeclarationList * VDL,const char * Str,size_t StrLen)247     DataInitializer(VariableDeclarationList *VDL, const char *Str,
248                     size_t StrLen)
249         : Initializer(DataInitializerKind), ContentsSize(StrLen),
250           Contents(new (VDL->allocate_initializer<char>(ContentsSize)) char) {
251       for (size_t i = 0; i < StrLen; ++i)
252         Contents[i] = Str[i];
253     }
254 
255     /// The byte contents of the data initializer.
256     const SizeT ContentsSize;
257     DataVecType Contents;
258   };
259   static_assert(std::is_trivially_destructible<DataInitializer>::value,
260                 "DataInitializer must be trivially destructible.");
261 
262   /// Defines a sequence of bytes initialized to zero.
263   class ZeroInitializer : public Initializer {
264     ZeroInitializer(const ZeroInitializer &) = delete;
265     ZeroInitializer &operator=(const ZeroInitializer &) = delete;
266 
267   public:
create(VariableDeclarationList * VDL,SizeT Size)268     static ZeroInitializer *create(VariableDeclarationList *VDL, SizeT Size) {
269       return new (VDL->allocate_initializer<ZeroInitializer>())
270           ZeroInitializer(Size);
271     }
getNumBytes()272     SizeT getNumBytes() const final { return Size; }
273     void dump(Ostream &Stream) const final;
classof(const Initializer * Z)274     static bool classof(const Initializer *Z) {
275       return Z->getKind() == ZeroInitializerKind;
276     }
277 
278   private:
ZeroInitializer(SizeT Size)279     explicit ZeroInitializer(SizeT Size)
280         : Initializer(ZeroInitializerKind), Size(Size) {}
281 
282     /// The number of bytes to be zero initialized.
283     SizeT Size;
284   };
285   static_assert(std::is_trivially_destructible<ZeroInitializer>::value,
286                 "ZeroInitializer must be trivially destructible.");
287 
288   /// Defines the relocation value of another global declaration.
289   class RelocInitializer : public Initializer {
290     RelocInitializer(const RelocInitializer &) = delete;
291     RelocInitializer &operator=(const RelocInitializer &) = delete;
292 
293   public:
create(VariableDeclarationList * VDL,const GlobalDeclaration * Declaration,const RelocOffsetArray & OffsetExpr)294     static RelocInitializer *create(VariableDeclarationList *VDL,
295                                     const GlobalDeclaration *Declaration,
296                                     const RelocOffsetArray &OffsetExpr) {
297       constexpr bool NoFixup = false;
298       return new (VDL->allocate_initializer<RelocInitializer>())
299           RelocInitializer(VDL, Declaration, OffsetExpr, NoFixup);
300     }
301 
create(VariableDeclarationList * VDL,const GlobalDeclaration * Declaration,const RelocOffsetArray & OffsetExpr,FixupKind Fixup)302     static RelocInitializer *create(VariableDeclarationList *VDL,
303                                     const GlobalDeclaration *Declaration,
304                                     const RelocOffsetArray &OffsetExpr,
305                                     FixupKind Fixup) {
306       constexpr bool HasFixup = true;
307       return new (VDL->allocate_initializer<RelocInitializer>())
308           RelocInitializer(VDL, Declaration, OffsetExpr, HasFixup, Fixup);
309     }
310 
getOffset()311     RelocOffsetT getOffset() const {
312       RelocOffsetT Offset = 0;
313       for (SizeT i = 0; i < OffsetExprSize; ++i) {
314         Offset += OffsetExpr[i]->getOffset();
315       }
316       return Offset;
317     }
318 
hasFixup()319     bool hasFixup() const { return HasFixup; }
getFixup()320     FixupKind getFixup() const {
321       assert(HasFixup);
322       return Fixup;
323     }
324 
getDeclaration()325     const GlobalDeclaration *getDeclaration() const { return Declaration; }
getNumBytes()326     SizeT getNumBytes() const final { return RelocAddrSize; }
327     void dump(Ostream &Stream) const final;
328     void dumpType(Ostream &Stream) const final;
classof(const Initializer * R)329     static bool classof(const Initializer *R) {
330       return R->getKind() == RelocInitializerKind;
331     }
332 
333   private:
334     RelocInitializer(VariableDeclarationList *VDL,
335                      const GlobalDeclaration *Declaration,
336                      const RelocOffsetArray &OffsetExpr, bool HasFixup,
337                      FixupKind Fixup = 0)
Initializer(RelocInitializerKind)338         : Initializer(RelocInitializerKind),
339           Declaration(Declaration), // The global declaration used in the reloc.
340           OffsetExprSize(OffsetExpr.size()),
341           OffsetExpr(new (VDL->allocate_initializer<RelocOffset *>(
342               OffsetExprSize)) RelocOffset *),
343           HasFixup(HasFixup), Fixup(Fixup) {
344       for (SizeT i = 0; i < OffsetExprSize; ++i) {
345         this->OffsetExpr[i] = OffsetExpr[i];
346       }
347     }
348 
349     const GlobalDeclaration *Declaration;
350     /// The offset to add to the relocation.
351     const SizeT OffsetExprSize;
352     RelocOffset **OffsetExpr;
353     const bool HasFixup = false;
354     const FixupKind Fixup = 0;
355   };
356   static_assert(std::is_trivially_destructible<RelocInitializer>::value,
357                 "RelocInitializer must be trivially destructible.");
358 
359   /// Models the list of initializers.
360   // TODO(jpp): missing allocator.
361   using InitializerListType = std::vector<Initializer *>;
362 
363   static VariableDeclaration *create(VariableDeclarationList *VDL,
364                                      bool SuppressMangling = false,
365                                      llvm::GlobalValue::LinkageTypes Linkage =
366                                          llvm::GlobalValue::InternalLinkage) {
367     return new (VDL->allocate_variable_declaration<VariableDeclaration>())
368         VariableDeclaration(Linkage, SuppressMangling);
369   }
370 
createExternal(VariableDeclarationList * VDL)371   static VariableDeclaration *createExternal(VariableDeclarationList *VDL) {
372     constexpr bool SuppressMangling = true;
373     constexpr llvm::GlobalValue::LinkageTypes Linkage =
374         llvm::GlobalValue::ExternalLinkage;
375     return create(VDL, SuppressMangling, Linkage);
376   }
377 
getInitializers()378   const InitializerListType &getInitializers() const { return Initializers; }
getIsConstant()379   bool getIsConstant() const { return IsConstant; }
setIsConstant(bool NewValue)380   void setIsConstant(bool NewValue) { IsConstant = NewValue; }
getAlignment()381   uint32_t getAlignment() const { return Alignment; }
setAlignment(uint32_t NewAlignment)382   void setAlignment(uint32_t NewAlignment) { Alignment = NewAlignment; }
hasInitializer()383   bool hasInitializer() const { return HasInitializer; }
hasNonzeroInitializer()384   bool hasNonzeroInitializer() const {
385     return !(Initializers.size() == 1 &&
386              llvm::isa<ZeroInitializer>(Initializers[0]));
387   }
388 
389   /// Returns the number of bytes for the initializer of the global address.
getNumBytes()390   SizeT getNumBytes() const {
391     SizeT Count = 0;
392     for (const auto *Init : Initializers) {
393       Count += Init->getNumBytes();
394     }
395     return Count;
396   }
397 
398   /// Adds Initializer to the list of initializers. Takes ownership of the
399   /// initializer.
addInitializer(Initializer * Initializer)400   void addInitializer(Initializer *Initializer) {
401     const bool OldSuppressMangling = getSuppressMangling();
402     Initializers.emplace_back(Initializer);
403     HasInitializer = true;
404     // The getSuppressMangling() logic depends on whether the global variable
405     // has initializers.  If its value changed as a result of adding an
406     // initializer, then make sure we haven't previously set the name based on
407     // faulty SuppressMangling logic.
408     const bool SameMangling = (OldSuppressMangling == getSuppressMangling());
409     (void)SameMangling;
410     assert(Name.hasStdString() || SameMangling);
411   }
412 
413   /// Prints out type for initializer associated with the declaration to Stream.
414   void dumpType(Ostream &Stream) const final;
415 
416   /// Prints out the definition of the global variable declaration (including
417   /// initialization).
418   virtual void dump(Ostream &Stream) const override;
419 
420   /// Returns true if linkage is correct for the variable declaration.
verifyLinkageCorrect()421   bool verifyLinkageCorrect() const { return verifyLinkageDefault(); }
422 
classof(const GlobalDeclaration * Addr)423   static bool classof(const GlobalDeclaration *Addr) {
424     return Addr->getKind() == VariableDeclarationKind;
425   }
426 
getSuppressMangling()427   bool getSuppressMangling() const final {
428     if (ForceSuppressMangling)
429       return true;
430     return isExternal() && !hasInitializer();
431   }
432 
discardInitializers()433   void discardInitializers() { Initializers.clear(); }
434 
435 private:
436   /// List of initializers for the declared variable.
437   InitializerListType Initializers;
438   bool HasInitializer = false;
439   /// The alignment of the declared variable.
440   uint32_t Alignment = 0;
441   /// True if a declared (global) constant.
442   bool IsConstant = false;
443   /// If set to true, force getSuppressMangling() to return true.
444   const bool ForceSuppressMangling;
445 
VariableDeclaration(llvm::GlobalValue::LinkageTypes Linkage,bool SuppressMangling)446   VariableDeclaration(llvm::GlobalValue::LinkageTypes Linkage,
447                       bool SuppressMangling)
448       : GlobalDeclaration(VariableDeclarationKind, Linkage),
449         ForceSuppressMangling(SuppressMangling) {}
450 };
451 
452 template <class StreamType>
453 inline StreamType &operator<<(StreamType &Stream,
454                               const VariableDeclaration::Initializer &Init) {
455   Init.dump(Stream);
456   return Stream;
457 }
458 
459 template <class StreamType>
460 inline StreamType &operator<<(StreamType &Stream,
461                               const GlobalDeclaration &Addr) {
462   Addr.dump(Stream);
463   return Stream;
464 }
465 
466 } // end of namespace Ice
467 
468 #endif // SUBZERO_SRC_ICEGLOBALINITS_H
469