1 //===- subzero/src/IceGlobalContext.h - Global context defs -----*- 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 aspects of the compilation that persist across multiple 12 /// functions. 13 /// 14 //===----------------------------------------------------------------------===// 15 16 #ifndef SUBZERO_SRC_ICEGLOBALCONTEXT_H 17 #define SUBZERO_SRC_ICEGLOBALCONTEXT_H 18 19 #include "IceDefs.h" 20 #include "IceClFlags.h" 21 #include "IceInstrumentation.h" 22 #include "IceIntrinsics.h" 23 #include "IceRNG.h" 24 #include "IceStringPool.h" 25 #include "IceSwitchLowering.h" 26 #include "IceTargetLowering.def" 27 #include "IceThreading.h" 28 #include "IceTimerTree.h" 29 #include "IceTypes.h" 30 #include "IceUtils.h" 31 32 #include <array> 33 #include <atomic> 34 #include <cassert> 35 #include <functional> 36 #include <memory> 37 #include <mutex> 38 #include <thread> 39 #include <type_traits> 40 #include <utility> 41 #include <vector> 42 43 namespace Ice { 44 45 class ConstantPool; 46 class EmitterWorkItem; 47 class FuncSigType; 48 class Instrumentation; 49 50 // Runtime helper function IDs 51 52 enum class RuntimeHelper { 53 #define X(Tag, Name) H_##Tag, 54 RUNTIME_HELPER_FUNCTIONS_TABLE 55 #undef X 56 H_Num 57 }; 58 59 /// OptWorkItem is a simple wrapper used to pass parse information on a function 60 /// block, to a translator thread. 61 class OptWorkItem { 62 OptWorkItem(const OptWorkItem &) = delete; 63 OptWorkItem &operator=(const OptWorkItem &) = delete; 64 65 public: 66 // Get the Cfg for the funtion to translate. 67 virtual std::unique_ptr<Cfg> getParsedCfg() = 0; 68 virtual ~OptWorkItem() = default; 69 70 protected: 71 OptWorkItem() = default; 72 }; 73 74 class GlobalContext { 75 GlobalContext() = delete; 76 GlobalContext(const GlobalContext &) = delete; 77 GlobalContext &operator=(const GlobalContext &) = delete; 78 79 /// CodeStats collects rudimentary statistics during translation. 80 class CodeStats { 81 CodeStats(const CodeStats &) = delete; 82 CodeStats &operator=(const CodeStats &) = default; 83 #define CODESTATS_TABLE \ 84 /* dump string, enum value */ \ 85 X("Inst Count ", InstCount) \ 86 X("Regs Saved ", RegsSaved) \ 87 X("Frame Bytes ", FrameByte) \ 88 X("Spills ", NumSpills) \ 89 X("Fills ", NumFills) \ 90 X("R/P Imms ", NumRPImms) 91 //#define X(str, tag) 92 93 public: 94 enum CSTag { 95 #define X(str, tag) CS_##tag, 96 CODESTATS_TABLE 97 #undef X 98 CS_NUM 99 }; CodeStats()100 CodeStats() { reset(); } reset()101 void reset() { Stats.fill(0); } 102 void update(CSTag Tag, uint32_t Count = 1) { 103 assert(Tag < Stats.size()); 104 Stats[Tag] += Count; 105 } add(const CodeStats & Other)106 void add(const CodeStats &Other) { 107 for (uint32_t i = 0; i < Stats.size(); ++i) 108 Stats[i] += Other.Stats[i]; 109 } 110 /// Dumps the stats for the given Cfg. If Func==nullptr, it identifies it 111 /// as the "final" cumulative stats instead as a specific function's name. 112 void dump(const Cfg *Func, GlobalContext *Ctx); 113 114 private: 115 std::array<uint32_t, CS_NUM> Stats; 116 }; 117 118 /// TimerList is a vector of TimerStack objects, with extra methods 119 /// to initialize and merge these vectors. 120 class TimerList : public std::vector<TimerStack> { 121 TimerList(const TimerList &) = delete; 122 TimerList &operator=(const TimerList &) = delete; 123 124 public: 125 TimerList() = default; 126 /// initInto() initializes a target list of timers based on the 127 /// current list. In particular, it creates the same number of 128 /// timers, in the same order, with the same names, but initially 129 /// empty of timing data. initInto(TimerList & Dest)130 void initInto(TimerList &Dest) const { 131 if (!BuildDefs::timers()) 132 return; 133 Dest.clear(); 134 for (const TimerStack &Stack : *this) { 135 Dest.push_back(TimerStack(Stack.getName())); 136 } 137 } mergeFrom(TimerList & Src)138 void mergeFrom(TimerList &Src) { 139 if (!BuildDefs::timers()) 140 return; 141 assert(size() == Src.size()); 142 size_type i = 0; 143 for (TimerStack &Stack : *this) { 144 assert(Stack.getName() == Src[i].getName()); 145 Stack.mergeFrom(Src[i]); 146 ++i; 147 } 148 } 149 }; 150 151 /// ThreadContext contains thread-local data. This data can be 152 /// combined/reduced as needed after all threads complete. 153 class ThreadContext { 154 ThreadContext(const ThreadContext &) = delete; 155 ThreadContext &operator=(const ThreadContext &) = delete; 156 157 public: 158 ThreadContext() = default; 159 CodeStats StatsFunction; 160 CodeStats StatsCumulative; 161 TimerList Timers; 162 }; 163 164 public: 165 /// The dump stream is a log stream while emit is the stream code 166 /// is emitted to. The error stream is strictly for logging errors. 167 GlobalContext(Ostream *OsDump, Ostream *OsEmit, Ostream *OsError, 168 ELFStreamer *ELFStreamer); 169 ~GlobalContext(); 170 171 void dumpStrings(); 172 /// 173 /// The dump, error, and emit streams need to be used by only one 174 /// thread at a time. This is done by exclusively reserving the 175 /// streams via lockStr() and unlockStr(). The OstreamLocker class 176 /// can be used to conveniently manage this. 177 /// 178 /// The model is that a thread grabs the stream lock, then does an 179 /// arbitrary amount of work during which far-away callees may grab 180 /// the stream and do something with it, and finally the thread 181 /// releases the stream lock. This allows large chunks of output to 182 /// be dumped or emitted without risking interleaving from multiple 183 /// threads. lockStr()184 void lockStr() { StrLock.lock(); } unlockStr()185 void unlockStr() { StrLock.unlock(); } getStrDump()186 Ostream &getStrDump() { return *StrDump; } getStrError()187 Ostream &getStrError() { return *StrError; } getStrEmit()188 Ostream &getStrEmit() { return *StrEmit; } setStrEmit(Ostream & NewStrEmit)189 void setStrEmit(Ostream &NewStrEmit) { StrEmit = &NewStrEmit; } 190 getErrorStatus()191 LockedPtr<ErrorCode> getErrorStatus() { 192 return LockedPtr<ErrorCode>(&ErrorStatus, &ErrorStatusLock); 193 } 194 195 /// \name Manage Constants. 196 /// @{ 197 // getConstant*() functions are not const because they might add something to 198 // the constant pool. 199 Constant *getConstantInt(Type Ty, int64_t Value); getConstantInt1(int8_t ConstantInt1)200 Constant *getConstantInt1(int8_t ConstantInt1) { 201 ConstantInt1 &= INT8_C(1); 202 switch (ConstantInt1) { 203 case 0: 204 return getConstantZero(IceType_i1); 205 case 1: 206 return ConstantTrue; 207 default: 208 assert(false && "getConstantInt1 not on true/false"); 209 return getConstantInt1Internal(ConstantInt1); 210 } 211 } getConstantInt8(int8_t ConstantInt8)212 Constant *getConstantInt8(int8_t ConstantInt8) { 213 switch (ConstantInt8) { 214 case 0: 215 return getConstantZero(IceType_i8); 216 default: 217 return getConstantInt8Internal(ConstantInt8); 218 } 219 } getConstantInt16(int16_t ConstantInt16)220 Constant *getConstantInt16(int16_t ConstantInt16) { 221 switch (ConstantInt16) { 222 case 0: 223 return getConstantZero(IceType_i16); 224 default: 225 return getConstantInt16Internal(ConstantInt16); 226 } 227 } getConstantInt32(int32_t ConstantInt32)228 Constant *getConstantInt32(int32_t ConstantInt32) { 229 switch (ConstantInt32) { 230 case 0: 231 return getConstantZero(IceType_i32); 232 default: 233 return getConstantInt32Internal(ConstantInt32); 234 } 235 } getConstantInt64(int64_t ConstantInt64)236 Constant *getConstantInt64(int64_t ConstantInt64) { 237 switch (ConstantInt64) { 238 case 0: 239 return getConstantZero(IceType_i64); 240 default: 241 return getConstantInt64Internal(ConstantInt64); 242 } 243 } 244 Constant *getConstantFloat(float Value); 245 Constant *getConstantDouble(double Value); 246 /// Returns a symbolic constant. 247 Constant *getConstantSymWithEmitString(const RelocOffsetT Offset, 248 const RelocOffsetArray &OffsetExpr, 249 GlobalString Name, 250 const std::string &EmitString); 251 Constant *getConstantSym(RelocOffsetT Offset, GlobalString Name); 252 Constant *getConstantExternSym(GlobalString Name); 253 /// Returns an undef. 254 Constant *getConstantUndef(Type Ty); 255 /// Returns a zero value. 256 Constant *getConstantZero(Type Ty); 257 /// getConstantPool() returns a copy of the constant pool for constants of a 258 /// given type. 259 ConstantList getConstantPool(Type Ty); 260 /// Returns a copy of the list of external symbols. 261 ConstantList getConstantExternSyms(); 262 /// @} getRuntimeHelperFunc(RuntimeHelper FuncID)263 Constant *getRuntimeHelperFunc(RuntimeHelper FuncID) const { 264 assert(FuncID < RuntimeHelper::H_Num); 265 Constant *Result = RuntimeHelperFunc[static_cast<size_t>(FuncID)]; 266 assert(Result != nullptr && "No such runtime helper function"); 267 return Result; 268 } 269 GlobalString getGlobalString(const std::string &Name); 270 271 /// Return a locked pointer to the registered jump tables. 272 JumpTableDataList getJumpTables(); 273 /// Adds JumpTable to the list of know jump tables, for a posteriori emission. 274 void addJumpTableData(JumpTableData JumpTable); 275 276 /// Allocate data of type T using the global allocator. We allow entities 277 /// allocated from this global allocator to be either trivially or 278 /// non-trivially destructible. We optimize the case when T is trivially 279 /// destructible by not registering a destructor. Destructors will be invoked 280 /// during GlobalContext destruction in the reverse object creation order. 281 template <typename T> 282 typename std::enable_if<std::is_trivially_destructible<T>::value, T>::type * allocate()283 allocate() { 284 return getAllocator()->Allocate<T>(); 285 } 286 287 template <typename T> 288 typename std::enable_if<!std::is_trivially_destructible<T>::value, T>::type * allocate()289 allocate() { 290 T *Ret = getAllocator()->Allocate<T>(); 291 getDestructors()->emplace_back([Ret]() { Ret->~T(); }); 292 return Ret; 293 } 294 getIntrinsicsInfo()295 const Intrinsics &getIntrinsicsInfo() const { return IntrinsicsInfo; } 296 getObjectWriter()297 ELFObjectWriter *getObjectWriter() const { return ObjectWriter.get(); } 298 299 /// Reset stats at the beginning of a function. resetStats()300 void resetStats() { 301 if (BuildDefs::dump()) 302 ICE_TLS_GET_FIELD(TLS)->StatsFunction.reset(); 303 } 304 void dumpStats(const Cfg *Func = nullptr); statsUpdateEmitted(uint32_t InstCount)305 void statsUpdateEmitted(uint32_t InstCount) { 306 if (!getFlags().getDumpStats()) 307 return; 308 ThreadContext *Tls = ICE_TLS_GET_FIELD(TLS); 309 Tls->StatsFunction.update(CodeStats::CS_InstCount, InstCount); 310 Tls->StatsCumulative.update(CodeStats::CS_InstCount, InstCount); 311 } statsUpdateRegistersSaved(uint32_t Num)312 void statsUpdateRegistersSaved(uint32_t Num) { 313 if (!getFlags().getDumpStats()) 314 return; 315 ThreadContext *Tls = ICE_TLS_GET_FIELD(TLS); 316 Tls->StatsFunction.update(CodeStats::CS_RegsSaved, Num); 317 Tls->StatsCumulative.update(CodeStats::CS_RegsSaved, Num); 318 } statsUpdateFrameBytes(uint32_t Bytes)319 void statsUpdateFrameBytes(uint32_t Bytes) { 320 if (!getFlags().getDumpStats()) 321 return; 322 ThreadContext *Tls = ICE_TLS_GET_FIELD(TLS); 323 Tls->StatsFunction.update(CodeStats::CS_FrameByte, Bytes); 324 Tls->StatsCumulative.update(CodeStats::CS_FrameByte, Bytes); 325 } statsUpdateSpills()326 void statsUpdateSpills() { 327 if (!getFlags().getDumpStats()) 328 return; 329 ThreadContext *Tls = ICE_TLS_GET_FIELD(TLS); 330 Tls->StatsFunction.update(CodeStats::CS_NumSpills); 331 Tls->StatsCumulative.update(CodeStats::CS_NumSpills); 332 } statsUpdateFills()333 void statsUpdateFills() { 334 if (!getFlags().getDumpStats()) 335 return; 336 ThreadContext *Tls = ICE_TLS_GET_FIELD(TLS); 337 Tls->StatsFunction.update(CodeStats::CS_NumFills); 338 Tls->StatsCumulative.update(CodeStats::CS_NumFills); 339 } 340 341 /// Number of Randomized or Pooled Immediates statsUpdateRPImms()342 void statsUpdateRPImms() { 343 if (!getFlags().getDumpStats()) 344 return; 345 ThreadContext *Tls = ICE_TLS_GET_FIELD(TLS); 346 Tls->StatsFunction.update(CodeStats::CS_NumRPImms); 347 Tls->StatsCumulative.update(CodeStats::CS_NumRPImms); 348 } 349 350 /// These are predefined TimerStackIdT values. 351 enum TimerStackKind { TSK_Default = 0, TSK_Funcs, TSK_Num }; 352 353 /// newTimerStackID() creates a new TimerStack in the global space. It does 354 /// not affect any TimerStack objects in TLS. 355 TimerStackIdT newTimerStackID(const std::string &Name); 356 /// dumpTimers() dumps the global timer data. This assumes all the 357 /// thread-local copies of timer data have been merged into the global timer 358 /// data. 359 void dumpTimers(TimerStackIdT StackID = TSK_Default, 360 bool DumpCumulative = true); 361 void dumpLocalTimers(const std::string &TimerNameOverride, 362 TimerStackIdT StackID = TSK_Default, 363 bool DumpCumulative = true); 364 /// The following methods affect only the calling thread's TLS timer data. 365 TimerIdT getTimerID(TimerStackIdT StackID, const std::string &Name); 366 void pushTimer(TimerIdT ID, TimerStackIdT StackID); 367 void popTimer(TimerIdT ID, TimerStackIdT StackID); 368 void resetTimer(TimerStackIdT StackID); 369 std::string getTimerName(TimerStackIdT StackID); 370 void setTimerName(TimerStackIdT StackID, const std::string &NewName); 371 372 /// This is the first work item sequence number that the parser produces, and 373 /// correspondingly the first sequence number that the emitter thread will 374 /// wait for. Start numbering at 1 to leave room for a sentinel, in case e.g. 375 /// we wish to inject items with a special sequence number that may be 376 /// executed out of order. getFirstSequenceNumber()377 static constexpr uint32_t getFirstSequenceNumber() { return 1; } 378 /// Adds a newly parsed and constructed function to the Cfg work queue. 379 /// Notifies any idle workers that a new function is available for 380 /// translating. May block if the work queue is too large, in order to control 381 /// memory footprint. 382 void optQueueBlockingPush(std::unique_ptr<OptWorkItem> Item); 383 /// Takes a Cfg from the work queue for translating. May block if the work 384 /// queue is currently empty. Returns nullptr if there is no more work - the 385 /// queue is empty and either end() has been called or the Sequential flag was 386 /// set. 387 std::unique_ptr<OptWorkItem> optQueueBlockingPop(); 388 /// Notifies that no more work will be added to the work queue. optQueueNotifyEnd()389 void optQueueNotifyEnd() { OptQ.notifyEnd(); } 390 391 /// Emit file header for output file. 392 void emitFileHeader(); 393 394 void lowerConstants(); 395 396 void lowerJumpTables(); 397 398 /// Emit target specific read-only data sections if any. E.g., for MIPS this 399 /// generates a .MIPS.abiflags section. 400 void emitTargetRODataSections(); 401 402 void emitQueueBlockingPush(std::unique_ptr<EmitterWorkItem> Item); 403 std::unique_ptr<EmitterWorkItem> emitQueueBlockingPop(); emitQueueNotifyEnd()404 void emitQueueNotifyEnd() { EmitQ.notifyEnd(); } 405 initParserThread()406 void initParserThread() { 407 ThreadContext *Tls = new ThreadContext(); 408 auto Timers = getTimers(); 409 Timers->initInto(Tls->Timers); 410 AllThreadContexts.push_back(Tls); 411 ICE_TLS_SET_FIELD(TLS, Tls); 412 } 413 startWorkerThreads()414 void startWorkerThreads() { 415 size_t NumWorkers = getFlags().getNumTranslationThreads(); 416 auto Timers = getTimers(); 417 for (size_t i = 0; i < NumWorkers; ++i) { 418 ThreadContext *WorkerTLS = new ThreadContext(); 419 Timers->initInto(WorkerTLS->Timers); 420 AllThreadContexts.push_back(WorkerTLS); 421 TranslationThreads.push_back(std::thread( 422 &GlobalContext::translateFunctionsWrapper, this, WorkerTLS)); 423 } 424 if (NumWorkers) { 425 ThreadContext *WorkerTLS = new ThreadContext(); 426 Timers->initInto(WorkerTLS->Timers); 427 AllThreadContexts.push_back(WorkerTLS); 428 EmitterThreads.push_back( 429 std::thread(&GlobalContext::emitterWrapper, this, WorkerTLS)); 430 } 431 } 432 433 void waitForWorkerThreads(); 434 435 /// sets the instrumentation object to use. setInstrumentation(std::unique_ptr<Instrumentation> Instr)436 void setInstrumentation(std::unique_ptr<Instrumentation> Instr) { 437 if (!BuildDefs::minimal()) 438 Instrumentor = std::move(Instr); 439 } 440 instrumentFunc(Cfg * Func)441 void instrumentFunc(Cfg *Func) { 442 if (!BuildDefs::minimal() && Instrumentor) 443 Instrumentor->instrumentFunc(Func); 444 } 445 446 /// Translation thread startup routine. translateFunctionsWrapper(ThreadContext * MyTLS)447 void translateFunctionsWrapper(ThreadContext *MyTLS) { 448 ICE_TLS_SET_FIELD(TLS, MyTLS); 449 translateFunctions(); 450 } 451 /// Translate functions from the Cfg queue until the queue is empty. 452 void translateFunctions(); 453 454 /// Emitter thread startup routine. emitterWrapper(ThreadContext * MyTLS)455 void emitterWrapper(ThreadContext *MyTLS) { 456 ICE_TLS_SET_FIELD(TLS, MyTLS); 457 emitItems(); 458 } 459 /// Emit functions and global initializers from the emitter queue until the 460 /// queue is empty. 461 void emitItems(); 462 463 /// Uses DataLowering to lower Globals. Side effects: 464 /// - discards the initializer list for the global variable in Globals. 465 /// - clears the Globals array. 466 void lowerGlobals(const std::string &SectionSuffix); 467 468 /// Lowers the profile information. 469 void lowerProfileData(); 470 471 void dumpConstantLookupCounts(); 472 473 /// DisposeGlobalVariablesAfterLowering controls whether the memory used by 474 /// GlobaleVariables can be reclaimed right after they have been lowered. 475 /// @{ getDisposeGlobalVariablesAfterLowering()476 bool getDisposeGlobalVariablesAfterLowering() const { 477 return DisposeGlobalVariablesAfterLowering; 478 } 479 setDisposeGlobalVariablesAfterLowering(bool Value)480 void setDisposeGlobalVariablesAfterLowering(bool Value) { 481 DisposeGlobalVariablesAfterLowering = Value; 482 } 483 /// @} 484 getStrings()485 LockedPtr<StringPool> getStrings() const { 486 return LockedPtr<StringPool>(Strings.get(), &StringsLock); 487 } 488 getGlobals()489 LockedPtr<VariableDeclarationList> getGlobals() { 490 return LockedPtr<VariableDeclarationList>(&Globals, &InitAllocLock); 491 } 492 493 /// Number of function blocks that can be queued before waiting for 494 /// translation 495 /// threads to consume. 496 static constexpr size_t MaxOptQSize = 1 << 16; 497 498 private: 499 // Try to ensure mutexes are allocated on separate cache lines. 500 501 // Destructors collaborate with Allocator 502 ICE_CACHELINE_BOUNDARY; 503 // Managed by getAllocator() 504 mutable GlobalLockType AllocLock; 505 ArenaAllocator Allocator; 506 507 ICE_CACHELINE_BOUNDARY; 508 // Managed by getInitializerAllocator() 509 mutable GlobalLockType InitAllocLock; 510 VariableDeclarationList Globals; 511 512 ICE_CACHELINE_BOUNDARY; 513 // Managed by getDestructors() 514 using DestructorArray = std::vector<std::function<void()>>; 515 mutable GlobalLockType DestructorsLock; 516 DestructorArray Destructors; 517 518 ICE_CACHELINE_BOUNDARY; 519 // Managed by getStrings() 520 mutable GlobalLockType StringsLock; 521 std::unique_ptr<StringPool> Strings; 522 523 ICE_CACHELINE_BOUNDARY; 524 // Managed by getConstPool() 525 mutable GlobalLockType ConstPoolLock; 526 std::unique_ptr<ConstantPool> ConstPool; 527 528 ICE_CACHELINE_BOUNDARY; 529 // Managed by getJumpTableList() 530 mutable GlobalLockType JumpTablesLock; 531 JumpTableDataList JumpTableList; 532 533 ICE_CACHELINE_BOUNDARY; 534 // Managed by getErrorStatus() 535 mutable GlobalLockType ErrorStatusLock; 536 ErrorCode ErrorStatus; 537 538 ICE_CACHELINE_BOUNDARY; 539 // Managed by getStatsCumulative() 540 mutable GlobalLockType StatsLock; 541 CodeStats StatsCumulative; 542 543 ICE_CACHELINE_BOUNDARY; 544 // Managed by getTimers() 545 mutable GlobalLockType TimerLock; 546 TimerList Timers; 547 548 ICE_CACHELINE_BOUNDARY; 549 /// StrLock is a global lock on the dump and emit output streams. 550 using StrLockType = std::mutex; 551 StrLockType StrLock; 552 Ostream *StrDump; /// Stream for dumping / diagnostics 553 Ostream *StrEmit; /// Stream for code emission 554 Ostream *StrError; /// Stream for logging errors. 555 556 // True if waitForWorkerThreads() has been called. 557 std::atomic_bool WaitForWorkerThreadsCalled; 558 559 ICE_CACHELINE_BOUNDARY; 560 561 Intrinsics IntrinsicsInfo; 562 // TODO(jpp): move to EmitterContext. 563 std::unique_ptr<ELFObjectWriter> ObjectWriter; 564 // Value defining when to wake up the main parse thread. 565 const size_t OptQWakeupSize; 566 BoundedProducerConsumerQueue<OptWorkItem, MaxOptQSize> OptQ; 567 BoundedProducerConsumerQueue<EmitterWorkItem> EmitQ; 568 // DataLowering is only ever used by a single thread at a time (either in 569 // emitItems(), or in IceCompiler::run before the compilation is over.) 570 // TODO(jpp): move to EmitterContext. 571 std::unique_ptr<TargetDataLowering> DataLowering; 572 /// If !HasEmittedCode, SubZero will accumulate all Globals (which are "true" 573 /// program global variables) until the first code WorkItem is seen. 574 // TODO(jpp): move to EmitterContext. 575 bool HasSeenCode = false; 576 // If Instrumentor is not empty then it will be used to instrument globals and 577 // CFGs. 578 std::unique_ptr<Instrumentation> Instrumentor = nullptr; 579 // TODO(jpp): move to EmitterContext. 580 VariableDeclaration *ProfileBlockInfoVarDecl = nullptr; 581 std::vector<VariableDeclaration *> ProfileBlockInfos; 582 /// Indicates if global variable declarations can be disposed of right after 583 /// lowering. 584 bool DisposeGlobalVariablesAfterLowering = true; 585 Constant *ConstZeroForType[IceType_NUM]; 586 Constant *ConstantTrue; 587 // Holds the constants representing each runtime helper function. 588 Constant *RuntimeHelperFunc[static_cast<size_t>(RuntimeHelper::H_Num)]; 589 590 Constant *getConstantZeroInternal(Type Ty); 591 Constant *getConstantIntInternal(Type Ty, int64_t Value); 592 Constant *getConstantInt1Internal(int8_t ConstantInt1); 593 Constant *getConstantInt8Internal(int8_t ConstantInt8); 594 Constant *getConstantInt16Internal(int16_t ConstantInt16); 595 Constant *getConstantInt32Internal(int32_t ConstantInt32); 596 Constant *getConstantInt64Internal(int64_t ConstantInt64); getAllocator()597 LockedPtr<ArenaAllocator> getAllocator() { 598 return LockedPtr<ArenaAllocator>(&Allocator, &AllocLock); 599 } getInitializerAllocator()600 LockedPtr<VariableDeclarationList> getInitializerAllocator() { 601 return LockedPtr<VariableDeclarationList>(&Globals, &InitAllocLock); 602 } getConstPool()603 LockedPtr<ConstantPool> getConstPool() { 604 return LockedPtr<ConstantPool>(ConstPool.get(), &ConstPoolLock); 605 } getJumpTableList()606 LockedPtr<JumpTableDataList> getJumpTableList() { 607 return LockedPtr<JumpTableDataList>(&JumpTableList, &JumpTablesLock); 608 } getStatsCumulative()609 LockedPtr<CodeStats> getStatsCumulative() { 610 return LockedPtr<CodeStats>(&StatsCumulative, &StatsLock); 611 } getTimers()612 LockedPtr<TimerList> getTimers() { 613 return LockedPtr<TimerList>(&Timers, &TimerLock); 614 } getDestructors()615 LockedPtr<DestructorArray> getDestructors() { 616 return LockedPtr<DestructorArray>(&Destructors, &DestructorsLock); 617 } 618 accumulateGlobals(std::unique_ptr<VariableDeclarationList> Globls)619 void accumulateGlobals(std::unique_ptr<VariableDeclarationList> Globls) { 620 LockedPtr<VariableDeclarationList> _(&Globals, &InitAllocLock); 621 if (Globls != nullptr) { 622 Globals.merge(Globls.get()); 623 if (!BuildDefs::minimal() && Instrumentor != nullptr) 624 Instrumentor->setHasSeenGlobals(); 625 } 626 } 627 lowerGlobalsIfNoCodeHasBeenSeen()628 void lowerGlobalsIfNoCodeHasBeenSeen() { 629 if (HasSeenCode) 630 return; 631 constexpr char NoSuffix[] = ""; 632 lowerGlobals(NoSuffix); 633 HasSeenCode = true; 634 } 635 636 void saveBlockInfoPtrs(); 637 638 llvm::SmallVector<ThreadContext *, 128> AllThreadContexts; 639 llvm::SmallVector<std::thread, 128> TranslationThreads; 640 llvm::SmallVector<std::thread, 128> EmitterThreads; 641 // Each thread has its own TLS pointer which is also held in 642 // AllThreadContexts. 643 ICE_TLS_DECLARE_FIELD(ThreadContext *, TLS); 644 645 public: TlsInit()646 static void TlsInit() { ICE_TLS_INIT_FIELD(TLS); } 647 }; 648 649 /// Helper class to push and pop a timer marker. The constructor pushes a 650 /// marker, and the destructor pops it. This is for convenient timing of regions 651 /// of code. 652 class TimerMarker { 653 TimerMarker() = delete; 654 TimerMarker(const TimerMarker &) = delete; 655 TimerMarker &operator=(const TimerMarker &) = delete; 656 657 public: 658 TimerMarker(TimerIdT ID, GlobalContext *Ctx, 659 TimerStackIdT StackID = GlobalContext::TSK_Default) ID(ID)660 : ID(ID), Ctx(Ctx), StackID(StackID) { 661 if (BuildDefs::timers()) 662 push(); 663 } 664 TimerMarker(TimerIdT ID, const Cfg *Func, 665 TimerStackIdT StackID = GlobalContext::TSK_Default) ID(ID)666 : ID(ID), Ctx(nullptr), StackID(StackID) { 667 // Ctx gets set at the beginning of pushCfg(). 668 if (BuildDefs::timers()) 669 pushCfg(Func); 670 } TimerMarker(GlobalContext * Ctx,const std::string & FuncName)671 TimerMarker(GlobalContext *Ctx, const std::string &FuncName) 672 : ID(getTimerIdFromFuncName(Ctx, FuncName)), Ctx(Ctx), 673 StackID(GlobalContext::TSK_Funcs) { 674 if (BuildDefs::timers()) 675 push(); 676 } 677 ~TimerMarker()678 ~TimerMarker() { 679 if (BuildDefs::timers() && Active) 680 Ctx->popTimer(ID, StackID); 681 } 682 683 private: 684 void push(); 685 void pushCfg(const Cfg *Func); 686 static TimerIdT getTimerIdFromFuncName(GlobalContext *Ctx, 687 const std::string &FuncName); 688 const TimerIdT ID; 689 GlobalContext *Ctx; 690 const TimerStackIdT StackID; 691 bool Active = false; 692 }; 693 694 /// Helper class for locking the streams and then automatically unlocking them. 695 class OstreamLocker { 696 private: 697 OstreamLocker() = delete; 698 OstreamLocker(const OstreamLocker &) = delete; 699 OstreamLocker &operator=(const OstreamLocker &) = delete; 700 701 public: OstreamLocker(GlobalContext * Ctx)702 explicit OstreamLocker(GlobalContext *Ctx) : Ctx(Ctx) { Ctx->lockStr(); } ~OstreamLocker()703 ~OstreamLocker() { Ctx->unlockStr(); } 704 705 private: 706 GlobalContext *const Ctx; 707 }; 708 709 } // end of namespace Ice 710 711 #endif // SUBZERO_SRC_ICEGLOBALCONTEXT_H 712