Subzero: Initial implementation of multithreaded translation.
Provides a single-producer, multiple-consumer translation queue where the number of translation threads is given by the -threads=N argument. The producer (i.e., bitcode parser) blocks if the queue size is >=N, in order to control the memory footprint. If N=0 (which is the default), execution is purely single-threaded. If N=1, there is a single translation thread running in parallel with the parser thread. "make check" succeeds with the default changed to N=1.
Currently emission is also done by the translation thread, which limits scalability since the emit stream has to be locked. Also, since the ELF writer stream is not locked, it won't be safe to use N>1 with the ELF writer. Furthermore, for N>1, emitted function ordering is nondeterministic and needs to be recombobulated. This will all be fixed in a follow-on CL.
The -timing option is broken for N>0. This will be fixed in a follow-on CL.
Verbose flags are now managed in the Cfg instead of (or in addition to) the GlobalContext, due to the -verbose-focus option which wants to temporarily change the verbose level for a particular function.
TargetLowering::emitConstants() and related methods are changed to be static, so that a valid TargetLowering object isn't required. This is because the TargetLowering object wants to hold a valid Cfg, and none really exists after all functions are translated and the constant pool is ready for emission.
The Makefile.standalone now has a TSAN=1 option to enable ThreadSanitizer.
BUG= none
R=jfb@chromium.org
Review URL: https://codereview.chromium.org/870653002
diff --git a/src/IceCfg.cpp b/src/IceCfg.cpp
index c7be511..f6deda0 100644
--- a/src/IceCfg.cpp
+++ b/src/IceCfg.cpp
@@ -32,10 +32,11 @@
}
Cfg::Cfg(GlobalContext *Ctx)
- : Ctx(Ctx), FunctionName(""), ReturnType(IceType_void),
- IsInternalLinkage(false), HasError(false), FocusedTiming(false),
- ErrorMessage(""), Entry(nullptr), NextInstNumber(Inst::NumberInitial),
- Allocator(new ArenaAllocator<>()), Live(nullptr),
+ : Ctx(Ctx), VMask(Ctx->getVerbose()), FunctionName(""),
+ ReturnType(IceType_void), IsInternalLinkage(false), HasError(false),
+ FocusedTiming(false), ErrorMessage(""), Entry(nullptr),
+ NextInstNumber(Inst::NumberInitial), Allocator(new ArenaAllocator<>()),
+ Live(nullptr),
Target(TargetLowering::createLowering(Ctx->getTargetArch(), this)),
VMetadata(new VariablesMetadata(this)),
TargetAssembler(
@@ -46,18 +47,14 @@
}
Cfg::~Cfg() {
- // TODO(stichnot,kschimpf): Set CurrentCfg=nullptr in the dtor for
- // safety. This can't be done currently because the translator
- // manages the Cfg by creating a new Cfg (which sets CurrentCfg to
- // the new value), then deleting the old Cfg (which would then reset
- // CurrentCfg to nullptr).
+ assert(ICE_TLS_GET_FIELD(CurrentCfg) == this);
+ // Reset the thread-local CurrentCfg pointer.
+ ICE_TLS_SET_FIELD(CurrentCfg, nullptr);
}
void Cfg::setError(const IceString &Message) {
HasError = true;
ErrorMessage = Message;
- OstreamLocker L(Ctx);
- Ctx->getStrDump() << "ICE translation error: " << ErrorMessage << "\n";
}
CfgNode *Cfg::makeNode() {
@@ -478,7 +475,7 @@
void Cfg::dump(const IceString &Message) {
if (!ALLOW_DUMP)
return;
- if (!Ctx->isVerbose())
+ if (!isVerbose())
return;
OstreamLocker L(Ctx);
Ostream &Str = Ctx->getStrDump();
@@ -486,7 +483,7 @@
Str << "================ " << Message << " ================\n";
setCurrentNode(getEntryNode());
// Print function name+args
- if (getContext()->isVerbose(IceV_Instructions)) {
+ if (isVerbose(IceV_Instructions)) {
Str << "define ";
if (getInternal() && !Ctx->getFlags().DisableInternal)
Str << "internal ";
@@ -500,7 +497,7 @@
Str << ") {\n";
}
resetCurrentNode();
- if (getContext()->isVerbose(IceV_Liveness)) {
+ if (isVerbose(IceV_Liveness)) {
// Print summary info about variables
for (Variable *Var : Variables) {
Str << "// multiblock=";
@@ -516,7 +513,7 @@
// Print each basic block
for (CfgNode *Node : Nodes)
Node->dump(this);
- if (getContext()->isVerbose(IceV_Instructions))
+ if (isVerbose(IceV_Instructions))
Str << "}\n";
}
diff --git a/src/IceCfg.h b/src/IceCfg.h
index 472ba79..5f827f5 100644
--- a/src/IceCfg.h
+++ b/src/IceCfg.h
@@ -15,8 +15,6 @@
#ifndef SUBZERO_SRC_ICECFG_H
#define SUBZERO_SRC_ICECFG_H
-#include <memory>
-
#include "assembler.h"
#include "IceClFlags.h"
#include "IceDefs.h"
@@ -42,6 +40,7 @@
}
// Gets a pointer to the current thread's Cfg.
static const Cfg *getCurrentCfg() { return ICE_TLS_GET_FIELD(CurrentCfg); }
+ void updateTLS() const { ICE_TLS_SET_FIELD(CurrentCfg, this); }
// Gets a pointer to the current thread's Cfg's allocator.
static ArenaAllocator<> *getCurrentCfgAllocator() {
assert(ICE_TLS_GET_FIELD(CurrentCfg));
@@ -50,6 +49,12 @@
GlobalContext *getContext() const { return Ctx; }
+ // Returns true if any of the specified options in the verbose mask
+ // are set. If the argument is omitted, it checks if any verbose
+ // options at all are set.
+ bool isVerbose(VerboseMask Mask = IceV_All) const { return VMask & Mask; }
+ void setVerbose(VerboseMask Mask) { VMask = Mask; }
+
// Manage the name and return type of the function being translated.
void setFunctionName(const IceString &Name) { FunctionName = Name; }
IceString getFunctionName() const { return FunctionName; }
@@ -184,6 +189,7 @@
Cfg(GlobalContext *Ctx);
GlobalContext *Ctx;
+ VerboseMask VMask;
IceString FunctionName;
Type ReturnType;
bool IsInternalLinkage;
diff --git a/src/IceCfgNode.cpp b/src/IceCfgNode.cpp
index 29eb9d0..7f96454 100644
--- a/src/IceCfgNode.cpp
+++ b/src/IceCfgNode.cpp
@@ -917,11 +917,11 @@
Func->setCurrentNode(this);
Ostream &Str = Func->getContext()->getStrDump();
Liveness *Liveness = Func->getLiveness();
- if (Func->getContext()->isVerbose(IceV_Instructions)) {
+ if (Func->isVerbose(IceV_Instructions)) {
Str << getName() << ":\n";
}
// Dump list of predecessor nodes.
- if (Func->getContext()->isVerbose(IceV_Preds) && !InEdges.empty()) {
+ if (Func->isVerbose(IceV_Preds) && !InEdges.empty()) {
Str << " // preds = ";
bool First = true;
for (CfgNode *I : InEdges) {
@@ -936,13 +936,13 @@
LivenessBV LiveIn;
if (Liveness)
LiveIn = Liveness->getLiveIn(this);
- if (Func->getContext()->isVerbose(IceV_Liveness) && !LiveIn.empty()) {
+ if (Func->isVerbose(IceV_Liveness) && !LiveIn.empty()) {
Str << " // LiveIn:";
for (SizeT i = 0; i < LiveIn.size(); ++i) {
if (LiveIn[i]) {
Variable *Var = Liveness->getVariable(i, this);
Str << " %" << Var->getName(Func);
- if (Func->getContext()->isVerbose(IceV_RegOrigins) && Var->hasReg()) {
+ if (Func->isVerbose(IceV_RegOrigins) && Var->hasReg()) {
Str << ":" << Func->getTarget()->getRegName(Var->getRegNum(),
Var->getType());
}
@@ -951,7 +951,7 @@
Str << "\n";
}
// Dump each instruction.
- if (Func->getContext()->isVerbose(IceV_Instructions)) {
+ if (Func->isVerbose(IceV_Instructions)) {
for (const Inst &I : Phis)
I.dumpDecorated(Func);
for (const Inst &I : Insts)
@@ -961,13 +961,13 @@
LivenessBV LiveOut;
if (Liveness)
LiveOut = Liveness->getLiveOut(this);
- if (Func->getContext()->isVerbose(IceV_Liveness) && !LiveOut.empty()) {
+ if (Func->isVerbose(IceV_Liveness) && !LiveOut.empty()) {
Str << " // LiveOut:";
for (SizeT i = 0; i < LiveOut.size(); ++i) {
if (LiveOut[i]) {
Variable *Var = Liveness->getVariable(i, this);
Str << " %" << Var->getName(Func);
- if (Func->getContext()->isVerbose(IceV_RegOrigins) && Var->hasReg()) {
+ if (Func->isVerbose(IceV_RegOrigins) && Var->hasReg()) {
Str << ":" << Func->getTarget()->getRegName(Var->getRegNum(),
Var->getType());
}
@@ -976,7 +976,7 @@
Str << "\n";
}
// Dump list of successor nodes.
- if (Func->getContext()->isVerbose(IceV_Succs)) {
+ if (Func->isVerbose(IceV_Succs)) {
Str << " // succs = ";
bool First = true;
for (CfgNode *I : OutEdges) {
diff --git a/src/IceClFlags.h b/src/IceClFlags.h
index b444dc0..d2c0243 100644
--- a/src/IceClFlags.h
+++ b/src/IceClFlags.h
@@ -28,8 +28,9 @@
DumpStats(false), AllowUninitializedGlobals(false),
TimeEachFunction(false), DisableIRGeneration(false),
AllowErrorRecovery(false), GenerateUnitTestMessages(false),
- DefaultGlobalPrefix(""), DefaultFunctionPrefix(""), TimingFocusOn(""),
- VerboseFocusOn(""), TranslateOnly("") {}
+ NumTranslationThreads(0), DefaultGlobalPrefix(""),
+ DefaultFunctionPrefix(""), TimingFocusOn(""), VerboseFocusOn(""),
+ TranslateOnly("") {}
bool DisableInternal;
bool SubzeroTimingEnabled;
bool DisableTranslation;
@@ -46,6 +47,7 @@
bool DisableIRGeneration;
bool AllowErrorRecovery;
bool GenerateUnitTestMessages;
+ size_t NumTranslationThreads; // 0 means completely sequential
IceString DefaultGlobalPrefix;
IceString DefaultFunctionPrefix;
IceString TimingFocusOn;
diff --git a/src/IceConverter.cpp b/src/IceConverter.cpp
index a972f73..0bbcfb7 100644
--- a/src/IceConverter.cpp
+++ b/src/IceConverter.cpp
@@ -891,8 +891,6 @@
if (ALLOW_DUMP && Ctx->getFlags().TimeEachFunction)
Ctx->popTimer(TimerID, StackID);
}
-
- emitConstants();
}
} // end of namespace Ice
diff --git a/src/IceDefs.h b/src/IceDefs.h
index 9add7ba..051edbb 100644
--- a/src/IceDefs.h
+++ b/src/IceDefs.h
@@ -23,7 +23,9 @@
#include <limits>
#include <list>
#include <map>
+#include <mutex>
#include <string>
+#include <system_error>
#include <vector>
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/BitVector.h"
@@ -58,6 +60,7 @@
class LiveRange;
class Liveness;
class Operand;
+class TargetGlobalLowering;
class TargetLowering;
class Variable;
class VariableDeclaration;
@@ -120,6 +123,17 @@
typedef uint32_t TimerStackIdT;
typedef uint32_t TimerIdT;
+// Use alignas(MaxCacheLineSize) to isolate variables/fields that
+// might be contended while multithreading. Assumes the maximum cache
+// line size is 64.
+enum {
+ MaxCacheLineSize = 64
+};
+// Use ICE_CACHELINE_BOUNDARY to force the next field in a declaration
+// list to be aligned to the next cache line.
+#define ICE_CACHELINE_BOUNDARY \
+ alignas(MaxCacheLineSize) struct {}
+
// PNaCl is ILP32, so theoretically we should only need 32-bit offsets.
typedef int32_t RelocOffsetT;
enum { RelocAddrSize = 4 };
@@ -163,6 +177,37 @@
typedef llvm::raw_ostream Ostream;
typedef llvm::raw_fd_ostream Fdstream;
+typedef std::mutex GlobalLockType;
+
+enum ErrorCodes {
+ EC_None = 0,
+ EC_Args,
+ EC_Bitcode,
+ EC_Translation
+};
+
+// Wrapper around std::error_code for allowing multiple errors to be
+// folded into one. The current implementation keeps track of the
+// first error, which is likely to be the most useful one, and this
+// could be extended to e.g. collect a vector of errors.
+class ErrorCode : public std::error_code {
+ ErrorCode(const ErrorCode &) = delete;
+ ErrorCode &operator=(const ErrorCode &) = delete;
+
+public:
+ ErrorCode() : HasError(false) {}
+ void assign(ErrorCodes Code) {
+ if (!HasError) {
+ HasError = true;
+ std::error_code::assign(Code, std::generic_category());
+ }
+ }
+ void assign(int Code) { assign(static_cast<ErrorCodes>(Code)); }
+
+private:
+ bool HasError;
+};
+
// Reverse range adaptors written in terms of llvm::make_range().
template <typename T>
llvm::iterator_range<typename T::const_reverse_iterator>
diff --git a/src/IceGlobalContext.cpp b/src/IceGlobalContext.cpp
index d5766d4..413278f 100644
--- a/src/IceGlobalContext.cpp
+++ b/src/IceGlobalContext.cpp
@@ -129,16 +129,17 @@
ELFStreamer *ELFStr, VerboseMask Mask,
TargetArch Arch, OptLevel Opt,
IceString TestPrefix, const ClFlags &Flags)
- : StrDump(OsDump), StrEmit(OsEmit), VMask(Mask),
- ConstPool(new ConstantPool()), Arch(Arch), Opt(Opt),
- TestPrefix(TestPrefix), Flags(Flags), RNG(""), ObjectWriter() {
+ : ConstPool(new ConstantPool()), ErrorStatus(), StrDump(OsDump),
+ StrEmit(OsEmit), VMask(Mask), Arch(Arch), Opt(Opt),
+ TestPrefix(TestPrefix), Flags(Flags), RNG(""), ObjectWriter(),
+ CfgQ(/*MaxSize=*/Flags.NumTranslationThreads,
+ /*Sequential=*/(Flags.NumTranslationThreads == 0)) {
// Make sure thread_local fields are properly initialized before any
// accesses are made. Do this here instead of at the start of
// main() so that all clients (e.g. unit tests) can benefit for
// free.
GlobalContext::TlsInit();
Cfg::TlsInit();
-
// Create a new ThreadContext for the current thread. No need to
// lock AllThreadContexts at this point since no other threads have
// access yet to this GlobalContext object.
@@ -156,6 +157,43 @@
}
}
+void GlobalContext::translateFunctions() {
+ while (Cfg *Func = cfgQueueBlockingPop()) {
+ // Reset per-function stats being accumulated in TLS.
+ resetStats();
+ // Install Func in TLS for Cfg-specific container allocators.
+ Func->updateTLS();
+ // Set verbose level to none if the current function does NOT
+ // match the -verbose-focus command-line option.
+ if (!matchSymbolName(Func->getFunctionName(), getFlags().VerboseFocusOn))
+ Func->setVerbose(IceV_None);
+ // Disable translation if -notranslate is specified, or if the
+ // current function matches the -translate-only option. If
+ // translation is disabled, just dump the high-level IR and
+ // continue.
+ if (getFlags().DisableTranslation ||
+ !matchSymbolName(Func->getFunctionName(), getFlags().TranslateOnly)) {
+ Func->dump();
+ } else {
+ Func->translate();
+ if (Func->hasError()) {
+ getErrorStatus()->assign(EC_Translation);
+ OstreamLocker L(this);
+ getStrDump() << "ICE translation error: " << Func->getError() << "\n";
+ } else {
+ if (getFlags().UseIntegratedAssembler)
+ Func->emitIAS();
+ else
+ Func->emit();
+ // TODO(stichnot): actually add to emit queue
+ }
+ // TODO(stichnot): fix multithreaded stats dumping.
+ dumpStats(Func->getFunctionName());
+ }
+ delete Func;
+ }
+}
+
// Scan a string for S[0-9A-Z]*_ patterns and replace them with
// S<num>_ where <num> is the next base-36 value. If a type name
// legitimately contains that pattern, then the substitution will be
@@ -471,12 +509,18 @@
}
void GlobalContext::pushTimer(TimerIdT ID, TimerStackIdT StackID) {
+ // TODO(stichnot): Timers are completely broken for multithreading; fix.
+ if (getFlags().NumTranslationThreads)
+ llvm::report_fatal_error("Timers and multithreading are currently broken");
auto Timers = getTimers();
assert(StackID < Timers->size());
Timers->at(StackID).push(ID);
}
void GlobalContext::popTimer(TimerIdT ID, TimerStackIdT StackID) {
+ // TODO(stichnot): Timers are completely broken for multithreading; fix.
+ if (getFlags().NumTranslationThreads)
+ llvm::report_fatal_error("Timers and multithreading are currently broken");
auto Timers = getTimers();
assert(StackID < Timers->size());
Timers->at(StackID).pop(ID);
diff --git a/src/IceGlobalContext.h b/src/IceGlobalContext.h
index 35d5493..dfa665b 100644
--- a/src/IceGlobalContext.h
+++ b/src/IceGlobalContext.h
@@ -15,8 +15,8 @@
#ifndef SUBZERO_SRC_ICEGLOBALCONTEXT_H
#define SUBZERO_SRC_ICEGLOBALCONTEXT_H
-#include <memory>
#include <mutex>
+#include <thread>
#include "IceDefs.h"
#include "IceClFlags.h"
@@ -24,6 +24,7 @@
#include "IceRNG.h"
#include "IceTimerTree.h"
#include "IceTypes.h"
+#include "IceUtils.h"
namespace Ice {
@@ -31,8 +32,6 @@
class ConstantPool;
class FuncSigType;
-typedef std::mutex GlobalLockType;
-
// LockedPtr is a way to provide automatically locked access to some object.
template <typename T> class LockedPtr {
LockedPtr() = delete;
@@ -102,14 +101,7 @@
IceString TestPrefix, const ClFlags &Flags);
~GlobalContext();
- // Returns true if any of the specified options in the verbose mask
- // are set. If the argument is omitted, it checks if any verbose
- // options at all are set.
VerboseMask getVerbose() const { return VMask; }
- bool isVerbose(VerboseMask Mask = IceV_All) const { return VMask & Mask; }
- void setVerbose(VerboseMask Mask) { VMask = Mask; }
- void addVerbose(VerboseMask Mask) { VMask |= Mask; }
- void subVerbose(VerboseMask Mask) { VMask &= ~Mask; }
// The dump and emit streams need to be used by only one thread at a
// time. This is done by exclusively reserving the streams via
@@ -129,6 +121,9 @@
TargetArch getTargetArch() const { return Arch; }
OptLevel getOptLevel() const { return Opt; }
+ LockedPtr<ErrorCode> getErrorStatus() {
+ return LockedPtr<ErrorCode>(&ErrorStatus, &ErrorStatusLock);
+ }
// When emitting assembly, we allow a string to be prepended to
// names of translated functions. This makes it easier to create an
@@ -229,34 +224,107 @@
void dumpTimers(TimerStackIdT StackID = TSK_Default,
bool DumpCumulative = true);
-private:
- // Try to make sure the mutexes are allocated on separate cache
- // lines, assuming the maximum cache line size is 64.
- const static size_t MaxCacheLineSize = 64;
- alignas(MaxCacheLineSize) GlobalLockType AllocLock;
- alignas(MaxCacheLineSize) GlobalLockType ConstPoolLock;
- alignas(MaxCacheLineSize) GlobalLockType StatsLock;
- alignas(MaxCacheLineSize) GlobalLockType TimerLock;
+ // Adds a newly parsed and constructed function to the Cfg work
+ // queue. Notifies any idle workers that a new function is
+ // available for translating. May block if the work queue is too
+ // large, in order to control memory footprint.
+ void cfgQueueBlockingPush(Cfg *Func) { CfgQ.blockingPush(Func); }
+ // Takes a Cfg from the work queue for translating. May block if
+ // the work queue is currently empty. Returns nullptr if there is
+ // no more work - the queue is empty and either end() has been
+ // called or the Sequential flag was set.
+ Cfg *cfgQueueBlockingPop() { return CfgQ.blockingPop(); }
+ // Notifies that no more work will be added to the work queue.
+ void cfgQueueNotifyEnd() { CfgQ.notifyEnd(); }
+ void startWorkerThreads() {
+ size_t NumWorkers = getFlags().NumTranslationThreads;
+ for (size_t i = 0; i < NumWorkers; ++i) {
+ ThreadContext *WorkerTLS = new ThreadContext();
+ AllThreadContexts.push_back(WorkerTLS);
+ TranslationThreads.push_back(std::thread(
+ &GlobalContext::translateFunctionsWrapper, this, WorkerTLS));
+ }
+ if (NumWorkers) {
+ // TODO(stichnot): start a new thread for the emitter queue worker.
+ }
+ }
+
+ void waitForWorkerThreads() {
+ cfgQueueNotifyEnd();
+ // TODO(stichnot): call end() on the emitter work queue.
+ for (std::thread &Worker : TranslationThreads) {
+ Worker.join();
+ }
+ TranslationThreads.clear();
+ // TODO(stichnot): join the emitter thread.
+ }
+
+ // Translation thread startup routine.
+ void translateFunctionsWrapper(ThreadContext *MyTLS) {
+ ICE_TLS_SET_FIELD(TLS, MyTLS);
+ translateFunctions();
+ }
+ // Translate functions from the Cfg queue until the queue is empty.
+ void translateFunctions();
+
+ // Utility function to match a symbol name against a match string.
+ // This is used in a few cases where we want to take some action on
+ // a particular function or symbol based on a command-line argument,
+ // such as changing the verbose level for a particular function. An
+ // empty Match argument means match everything. Returns true if
+ // there is a match.
+ static bool matchSymbolName(const IceString &SymbolName,
+ const IceString &Match) {
+ return Match.empty() || Match == SymbolName;
+ }
+
+private:
+ // Try to ensure mutexes are allocated on separate cache lines.
+
+ ICE_CACHELINE_BOUNDARY;
+ // Managed by getAllocator()
+ GlobalLockType AllocLock;
+ ArenaAllocator<> Allocator;
+
+ ICE_CACHELINE_BOUNDARY;
+ // Managed by getConstantPool()
+ GlobalLockType ConstPoolLock;
+ std::unique_ptr<ConstantPool> ConstPool;
+
+ ICE_CACHELINE_BOUNDARY;
+ // Managed by getErrorStatus()
+ GlobalLockType ErrorStatusLock;
+ ErrorCode ErrorStatus;
+
+ ICE_CACHELINE_BOUNDARY;
+ // Managed by getStatsCumulative()
+ GlobalLockType StatsLock;
+ CodeStats StatsCumulative;
+
+ ICE_CACHELINE_BOUNDARY;
+ // Managed by getTimers()
+ GlobalLockType TimerLock;
+ std::vector<TimerStack> Timers;
+
+ ICE_CACHELINE_BOUNDARY;
// StrLock is a global lock on the dump and emit output streams.
typedef std::mutex StrLockType;
StrLockType StrLock;
-
Ostream *StrDump; // Stream for dumping / diagnostics
Ostream *StrEmit; // Stream for code emission
- ArenaAllocator<> Allocator;
- VerboseMask VMask;
- std::unique_ptr<ConstantPool> ConstPool;
+ ICE_CACHELINE_BOUNDARY;
+
+ const VerboseMask VMask;
Intrinsics IntrinsicsInfo;
const TargetArch Arch;
const OptLevel Opt;
const IceString TestPrefix;
const ClFlags &Flags;
- RandomNumberGenerator RNG;
+ RandomNumberGenerator RNG; // TODO(stichnot): Move into Cfg.
std::unique_ptr<ELFObjectWriter> ObjectWriter;
- CodeStats StatsCumulative;
- std::vector<TimerStack> Timers;
+ BoundedProducerConsumerQueue<Cfg> CfgQ;
LockedPtr<ArenaAllocator<>> getAllocator() {
return LockedPtr<ArenaAllocator<>>(&Allocator, &AllocLock);
@@ -272,6 +340,7 @@
}
std::vector<ThreadContext *> AllThreadContexts;
+ std::vector<std::thread> TranslationThreads;
// Each thread has its own TLS pointer which is also held in
// AllThreadContexts.
ICE_TLS_DECLARE_FIELD(ThreadContext *, TLS);
diff --git a/src/IceInst.cpp b/src/IceInst.cpp
index 845a6db..86886a6 100644
--- a/src/IceInst.cpp
+++ b/src/IceInst.cpp
@@ -464,10 +464,9 @@
if (!ALLOW_DUMP)
return;
Ostream &Str = Func->getContext()->getStrDump();
- if (!Func->getContext()->isVerbose(IceV_Deleted) &&
- (isDeleted() || isRedundantAssign()))
+ if (!Func->isVerbose(IceV_Deleted) && (isDeleted() || isRedundantAssign()))
return;
- if (Func->getContext()->isVerbose(IceV_InstNumbers)) {
+ if (Func->isVerbose(IceV_InstNumbers)) {
char buf[30];
InstNumberT Number = getNumber();
if (Number == NumberDeleted)
@@ -500,7 +499,7 @@
bool First = true;
// Print "LIVEEND={a,b,c}" for all source operands whose live ranges
// are known to end at this instruction.
- if (Func->getContext()->isVerbose(IceV_Liveness)) {
+ if (Func->isVerbose(IceV_Liveness)) {
for (SizeT I = 0; I < getSrcSize(); ++I) {
Operand *Src = getSrc(I);
SizeT NumVars = Src->getNumVars();
diff --git a/src/IceOperand.cpp b/src/IceOperand.cpp
index a6a3c09..102fe7d 100644
--- a/src/IceOperand.cpp
+++ b/src/IceOperand.cpp
@@ -399,15 +399,15 @@
Str << "%" << getName(Func);
return;
}
- if (Func->getContext()->isVerbose(IceV_RegOrigins) ||
+ if (Func->isVerbose(IceV_RegOrigins) ||
(!hasReg() && !Func->getTarget()->hasComputedFrame()))
Str << "%" << getName(Func);
if (hasReg()) {
- if (Func->getContext()->isVerbose(IceV_RegOrigins))
+ if (Func->isVerbose(IceV_RegOrigins))
Str << ":";
Str << Func->getTarget()->getRegName(RegNum, getType());
} else if (Func->getTarget()->hasComputedFrame()) {
- if (Func->getContext()->isVerbose(IceV_RegOrigins))
+ if (Func->isVerbose(IceV_RegOrigins))
Str << ":";
Str << "[" << Func->getTarget()->getRegName(
Func->getTarget()->getFrameOrStackReg(), IceType_i32);
diff --git a/src/IceRegAlloc.cpp b/src/IceRegAlloc.cpp
index 5056bfd..d171005 100644
--- a/src/IceRegAlloc.cpp
+++ b/src/IceRegAlloc.cpp
@@ -52,7 +52,7 @@
const char *Reason) {
if (!ALLOW_DUMP)
return;
- if (Func->getContext()->isVerbose(IceV_LinearScan)) {
+ if (Func->isVerbose(IceV_LinearScan)) {
VariablesMetadata *VMetadata = Func->getVMetadata();
Ostream &Str = Func->getContext()->getStrDump();
Str << "Disabling Overlap due to " << Reason << " " << *Var
@@ -265,8 +265,7 @@
TimerMarker T(TimerStack::TT_linearScan, Func);
assert(RegMaskFull.any()); // Sanity check
GlobalContext *Ctx = Func->getContext();
- const bool Verbose =
- ALLOW_DUMP && Ctx->isVerbose(IceV_LinearScan);
+ const bool Verbose = ALLOW_DUMP && Func->isVerbose(IceV_LinearScan);
if (Verbose)
Ctx->lockStr();
Func->resetCurrentNode();
@@ -738,7 +737,7 @@
void LinearScan::dump(Cfg *Func) const {
if (!ALLOW_DUMP)
return;
- if (!Func->getContext()->isVerbose(IceV_LinearScan))
+ if (!Func->isVerbose(IceV_LinearScan))
return;
Ostream &Str = Func->getContext()->getStrDump();
Func->resetCurrentNode();
diff --git a/src/IceTargetLowering.cpp b/src/IceTargetLowering.cpp
index b04727d..4373c6c 100644
--- a/src/IceTargetLowering.cpp
+++ b/src/IceTargetLowering.cpp
@@ -252,25 +252,24 @@
LinearScan.scan(RegMask, RandomizeRegisterAllocation);
}
-TargetGlobalInitLowering *
-TargetGlobalInitLowering::createLowering(TargetArch Target,
- GlobalContext *Ctx) {
+TargetGlobalLowering *TargetGlobalLowering::createLowering(GlobalContext *Ctx) {
// These statements can be #ifdef'd to specialize the code generator
// to a subset of the available targets. TODO: use CRTP.
+ TargetArch Target = Ctx->getTargetArch();
if (Target == Target_X8632)
- return TargetGlobalInitX8632::create(Ctx);
+ return TargetGlobalX8632::create(Ctx);
#if 0
if (Target == Target_X8664)
- return IceTargetGlobalInitX8664::create(Ctx);
+ return TargetGlobalX8664::create(Ctx);
if (Target == Target_ARM32)
- return IceTargetGlobalInitARM32::create(Ctx);
+ return TargetGlobalARM32::create(Ctx);
if (Target == Target_ARM64)
- return IceTargetGlobalInitARM64::create(Ctx);
+ return TargetGlobalARM64::create(Ctx);
#endif
llvm_unreachable("Unsupported target");
return nullptr;
}
-TargetGlobalInitLowering::~TargetGlobalInitLowering() {}
+TargetGlobalLowering::~TargetGlobalLowering() {}
} // end of namespace Ice
diff --git a/src/IceTargetLowering.h b/src/IceTargetLowering.h
index 39fa589..49108fa 100644
--- a/src/IceTargetLowering.h
+++ b/src/IceTargetLowering.h
@@ -199,8 +199,6 @@
virtual void addProlog(CfgNode *Node) = 0;
virtual void addEpilog(CfgNode *Node) = 0;
- virtual void emitConstants() const = 0;
-
virtual ~TargetLowering() {}
protected:
@@ -242,23 +240,23 @@
LoweringContext Context;
};
-// TargetGlobalInitLowering is used for "lowering" global
-// initializers. It is separated out from TargetLowering because it
-// does not require a Cfg.
-class TargetGlobalInitLowering {
- TargetGlobalInitLowering(const TargetGlobalInitLowering &) = delete;
- TargetGlobalInitLowering &operator=(const TargetGlobalInitLowering &) =
- delete;
+// TargetGlobalLowering is used for "lowering" global initializers,
+// including the internal constant pool. It is separated out from
+// TargetLowering because it does not require a Cfg.
+class TargetGlobalLowering {
+ TargetGlobalLowering() = delete;
+ TargetGlobalLowering(const TargetGlobalLowering &) = delete;
+ TargetGlobalLowering &operator=(const TargetGlobalLowering &) = delete;
public:
- static TargetGlobalInitLowering *createLowering(TargetArch Target,
- GlobalContext *Ctx);
- virtual ~TargetGlobalInitLowering();
+ static TargetGlobalLowering *createLowering(GlobalContext *Ctx);
+ virtual ~TargetGlobalLowering();
- virtual void lower(const VariableDeclaration &Var) = 0;
+ virtual void lowerInit(const VariableDeclaration &Var) const = 0;
+ virtual void lowerConstants(GlobalContext *Ctx) const = 0;
protected:
- TargetGlobalInitLowering(GlobalContext *Ctx) : Ctx(Ctx) {}
+ TargetGlobalLowering(GlobalContext *Ctx) : Ctx(Ctx) {}
GlobalContext *Ctx;
};
diff --git a/src/IceTargetLoweringX8632.cpp b/src/IceTargetLoweringX8632.cpp
index b092f7f..79c79cc 100644
--- a/src/IceTargetLoweringX8632.cpp
+++ b/src/IceTargetLoweringX8632.cpp
@@ -897,7 +897,7 @@
Var->setStackOffset(Linked->getStackOffset());
}
- if (ALLOW_DUMP && Func->getContext()->isVerbose(IceV_Frame)) {
+ if (ALLOW_DUMP && Func->isVerbose(IceV_Frame)) {
OstreamLocker L(Func->getContext());
Ostream &Str = Func->getContext()->getStrDump();
@@ -966,75 +966,6 @@
}
}
-template <typename T> struct PoolTypeConverter {};
-
-template <> struct PoolTypeConverter<float> {
- typedef uint32_t PrimitiveIntType;
- typedef ConstantFloat IceType;
- static const Type Ty = IceType_f32;
- static const char *TypeName;
- static const char *AsmTag;
- static const char *PrintfString;
-};
-const char *PoolTypeConverter<float>::TypeName = "float";
-const char *PoolTypeConverter<float>::AsmTag = ".long";
-const char *PoolTypeConverter<float>::PrintfString = "0x%x";
-
-template <> struct PoolTypeConverter<double> {
- typedef uint64_t PrimitiveIntType;
- typedef ConstantDouble IceType;
- static const Type Ty = IceType_f64;
- static const char *TypeName;
- static const char *AsmTag;
- static const char *PrintfString;
-};
-const char *PoolTypeConverter<double>::TypeName = "double";
-const char *PoolTypeConverter<double>::AsmTag = ".quad";
-const char *PoolTypeConverter<double>::PrintfString = "0x%llx";
-
-template <typename T> void TargetX8632::emitConstantPool() const {
- // Note: Still used by emit IAS.
- Ostream &Str = Ctx->getStrEmit();
- Type Ty = T::Ty;
- SizeT Align = typeAlignInBytes(Ty);
- ConstantList Pool = Ctx->getConstantPool(Ty);
-
- Str << "\t.section\t.rodata.cst" << Align << ",\"aM\",@progbits," << Align
- << "\n";
- Str << "\t.align\t" << Align << "\n";
- for (Constant *C : Pool) {
- typename T::IceType *Const = llvm::cast<typename T::IceType>(C);
- typename T::IceType::PrimType Value = Const->getValue();
- // Use memcpy() to copy bits from Value into RawValue in a way
- // that avoids breaking strict-aliasing rules.
- typename T::PrimitiveIntType RawValue;
- memcpy(&RawValue, &Value, sizeof(Value));
- char buf[30];
- int CharsPrinted =
- snprintf(buf, llvm::array_lengthof(buf), T::PrintfString, RawValue);
- assert(CharsPrinted >= 0 &&
- (size_t)CharsPrinted < llvm::array_lengthof(buf));
- (void)CharsPrinted; // avoid warnings if asserts are disabled
- Const->emitPoolLabel(Str);
- Str << ":\n\t" << T::AsmTag << "\t" << buf << "\t# " << T::TypeName << " "
- << Value << "\n";
- }
-}
-
-void TargetX8632::emitConstants() const {
- // No need to emit constants from the int pool since (for x86) they
- // are embedded as immediates in the instructions, just emit float/double.
- if (Ctx->getFlags().UseELFWriter) {
- ELFObjectWriter *Writer = Ctx->getObjectWriter();
- Writer->writeConstantPool<ConstantFloat>(IceType_f32);
- Writer->writeConstantPool<ConstantDouble>(IceType_f64);
- } else {
- OstreamLocker L(Ctx);
- emitConstantPool<PoolTypeConverter<float>>();
- emitConstantPool<PoolTypeConverter<double>>();
- }
-}
-
void TargetX8632::split64(Variable *Var) {
switch (Var->getType()) {
default:
@@ -3567,7 +3498,7 @@
const Inst *Reason) {
if (!ALLOW_DUMP)
return;
- if (!Func->getContext()->isVerbose(IceV_AddrOpt))
+ if (!Func->isVerbose(IceV_AddrOpt))
return;
OstreamLocker L(Func->getContext());
Ostream &Str = Func->getContext()->getStrDump();
@@ -3740,7 +3671,7 @@
void computeAddressOpt(Cfg *Func, const Inst *Instr, Variable *&Base,
Variable *&Index, uint16_t &Shift, int32_t &Offset) {
Func->resetCurrentNode();
- if (Func->getContext()->isVerbose(IceV_AddrOpt)) {
+ if (Func->isVerbose(IceV_AddrOpt)) {
OstreamLocker L(Func->getContext());
Ostream &Str = Func->getContext()->getStrDump();
Str << "\nStarting computeAddressOpt for instruction:\n ";
@@ -4582,7 +4513,7 @@
assert(NumShuffled + NumPreserved == RegX8632::Reg_NUM);
- if (Func->getContext()->isVerbose(IceV_Random)) {
+ if (Func->isVerbose(IceV_Random)) {
OstreamLocker L(Func->getContext());
Ostream &Str = Func->getContext()->getStrDump();
Str << "Register equivalence classes:\n";
@@ -4630,10 +4561,10 @@
llvm_unreachable("undef value encountered by emitter.");
}
-TargetGlobalInitX8632::TargetGlobalInitX8632(GlobalContext *Ctx)
- : TargetGlobalInitLowering(Ctx) {}
+TargetGlobalX8632::TargetGlobalX8632(GlobalContext *Ctx)
+ : TargetGlobalLowering(Ctx) {}
-void TargetGlobalInitX8632::lower(const VariableDeclaration &Var) {
+void TargetGlobalX8632::lowerInit(const VariableDeclaration &Var) const {
// TODO(jvoung): handle this without text.
if (Ctx->getFlags().UseELFWriter)
return;
@@ -4714,4 +4645,76 @@
Str << "\t.size\t" << MangledName << ", " << Size << "\n";
}
+template <typename T> struct PoolTypeConverter {};
+
+template <> struct PoolTypeConverter<float> {
+ typedef uint32_t PrimitiveIntType;
+ typedef ConstantFloat IceType;
+ static const Type Ty = IceType_f32;
+ static const char *TypeName;
+ static const char *AsmTag;
+ static const char *PrintfString;
+};
+const char *PoolTypeConverter<float>::TypeName = "float";
+const char *PoolTypeConverter<float>::AsmTag = ".long";
+const char *PoolTypeConverter<float>::PrintfString = "0x%x";
+
+template <> struct PoolTypeConverter<double> {
+ typedef uint64_t PrimitiveIntType;
+ typedef ConstantDouble IceType;
+ static const Type Ty = IceType_f64;
+ static const char *TypeName;
+ static const char *AsmTag;
+ static const char *PrintfString;
+};
+const char *PoolTypeConverter<double>::TypeName = "double";
+const char *PoolTypeConverter<double>::AsmTag = ".quad";
+const char *PoolTypeConverter<double>::PrintfString = "0x%llx";
+
+template <typename T>
+void TargetGlobalX8632::emitConstantPool(GlobalContext *Ctx) {
+ // Note: Still used by emit IAS.
+ Ostream &Str = Ctx->getStrEmit();
+ Type Ty = T::Ty;
+ SizeT Align = typeAlignInBytes(Ty);
+ ConstantList Pool = Ctx->getConstantPool(Ty);
+
+ Str << "\t.section\t.rodata.cst" << Align << ",\"aM\",@progbits," << Align
+ << "\n";
+ Str << "\t.align\t" << Align << "\n";
+ for (Constant *C : Pool) {
+ typename T::IceType *Const = llvm::cast<typename T::IceType>(C);
+ typename T::IceType::PrimType Value = Const->getValue();
+ // Use memcpy() to copy bits from Value into RawValue in a way
+ // that avoids breaking strict-aliasing rules.
+ typename T::PrimitiveIntType RawValue;
+ memcpy(&RawValue, &Value, sizeof(Value));
+ char buf[30];
+ int CharsPrinted =
+ snprintf(buf, llvm::array_lengthof(buf), T::PrintfString, RawValue);
+ assert(CharsPrinted >= 0 &&
+ (size_t)CharsPrinted < llvm::array_lengthof(buf));
+ (void)CharsPrinted; // avoid warnings if asserts are disabled
+ Const->emitPoolLabel(Str);
+ Str << ":\n\t" << T::AsmTag << "\t" << buf << "\t# " << T::TypeName << " "
+ << Value << "\n";
+ }
+}
+
+void TargetGlobalX8632::lowerConstants(GlobalContext *Ctx) const {
+ if (Ctx->getFlags().DisableTranslation)
+ return;
+ // No need to emit constants from the int pool since (for x86) they
+ // are embedded as immediates in the instructions, just emit float/double.
+ if (Ctx->getFlags().UseELFWriter) {
+ ELFObjectWriter *Writer = Ctx->getObjectWriter();
+ Writer->writeConstantPool<ConstantFloat>(IceType_f32);
+ Writer->writeConstantPool<ConstantDouble>(IceType_f64);
+ } else {
+ OstreamLocker L(Ctx);
+ emitConstantPool<PoolTypeConverter<float>>(Ctx);
+ emitConstantPool<PoolTypeConverter<double>>(Ctx);
+ }
+}
+
} // end of namespace Ice
diff --git a/src/IceTargetLoweringX8632.h b/src/IceTargetLoweringX8632.h
index cd82d7a..1ee3c1d 100644
--- a/src/IceTargetLoweringX8632.h
+++ b/src/IceTargetLoweringX8632.h
@@ -56,7 +56,6 @@
void lowerArguments() override;
void addProlog(CfgNode *Node) override;
void addEpilog(CfgNode *Node) override;
- void emitConstants() const override;
SizeT makeNextLabelNumber() { return NextLabelNumber++; }
// Ensure that a 64-bit Variable has been split into 2 32-bit
// Variables, creating them if necessary. This is needed for all
@@ -487,25 +486,27 @@
private:
~TargetX8632() override {}
- template <typename T> void emitConstantPool() const;
};
-class TargetGlobalInitX8632 : public TargetGlobalInitLowering {
- TargetGlobalInitX8632(const TargetGlobalInitX8632 &) = delete;
- TargetGlobalInitX8632 &operator=(const TargetGlobalInitX8632 &) = delete;
+class TargetGlobalX8632 : public TargetGlobalLowering {
+ TargetGlobalX8632() = delete;
+ TargetGlobalX8632(const TargetGlobalX8632 &) = delete;
+ TargetGlobalX8632 &operator=(const TargetGlobalX8632 &) = delete;
public:
- static TargetGlobalInitLowering *create(GlobalContext *Ctx) {
- return new TargetGlobalInitX8632(Ctx);
+ static TargetGlobalLowering *create(GlobalContext *Ctx) {
+ return new TargetGlobalX8632(Ctx);
}
- virtual void lower(const VariableDeclaration &Var) final;
+ virtual void lowerInit(const VariableDeclaration &Var) const final;
+ virtual void lowerConstants(GlobalContext *Ctx) const final;
protected:
- TargetGlobalInitX8632(GlobalContext *Ctx);
+ TargetGlobalX8632(GlobalContext *Ctx);
private:
- ~TargetGlobalInitX8632() override {}
+ ~TargetGlobalX8632() override {}
+ template <typename T> static void emitConstantPool(GlobalContext *Ctx);
};
template <> void ConstantInteger32::emit(GlobalContext *Ctx) const;
diff --git a/src/IceTranslator.cpp b/src/IceTranslator.cpp
index c0331a8..4198442 100644
--- a/src/IceTranslator.cpp
+++ b/src/IceTranslator.cpp
@@ -12,13 +12,6 @@
//
//===----------------------------------------------------------------------===//
-#include <iostream>
-#include <memory>
-
-#include "llvm/IR/Constant.h"
-#include "llvm/IR/Constants.h"
-#include "llvm/IR/Module.h"
-
#include "IceCfg.h"
#include "IceClFlags.h"
#include "IceDefs.h"
@@ -28,16 +21,11 @@
using namespace Ice;
-namespace {
-
-// Match a symbol name against a match string. An empty match string
-// means match everything. Returns true if there is a match.
-bool matchSymbolName(const IceString &SymbolName, const IceString &Match) {
- return Match.empty() || Match == SymbolName;
+Translator::Translator(GlobalContext *Ctx, const ClFlags &Flags)
+ : Ctx(Ctx), Flags(Flags),
+ GlobalLowering(TargetGlobalLowering::createLowering(Ctx)), ErrorStatus() {
}
-} // end of anonymous namespace
-
Translator::~Translator() {}
IceString Translator::createUnnamedName(const IceString &Prefix, SizeT Index) {
@@ -66,49 +54,28 @@
return false;
}
-void Translator::translateFcn(Cfg *Fcn) {
- Ctx->resetStats();
- Func.reset(Fcn);
- VerboseMask OldVerboseMask = Ctx->getVerbose();
- if (!matchSymbolName(Func->getFunctionName(), Ctx->getFlags().VerboseFocusOn))
- Ctx->setVerbose(IceV_None);
-
- if (Ctx->getFlags().DisableTranslation ||
- !matchSymbolName(Func->getFunctionName(),
- Ctx->getFlags().TranslateOnly)) {
- Func->dump();
- } else {
- Func->translate();
- if (Func->hasError()) {
- std::cerr << "ICE translation error: " << Func->getError() << "\n";
- ErrorStatus = true;
- }
-
- if (!ErrorStatus) {
- if (Ctx->getFlags().UseIntegratedAssembler) {
- Func->emitIAS();
- } else {
- Func->emit();
- }
- }
- Ctx->dumpStats(Func->getFunctionName());
+void Translator::translateFcn(Cfg *Func) {
+ Ctx->cfgQueueBlockingPush(Func);
+ if (Ctx->getFlags().NumTranslationThreads == 0) {
+ Ctx->translateFunctions();
}
-
- Ctx->setVerbose(OldVerboseMask);
}
void Translator::emitConstants() {
- if (!Ctx->getFlags().DisableTranslation && Func)
- Func->getTarget()->emitConstants();
+ if (!getErrorStatus())
+ GlobalLowering->lowerConstants(Ctx);
+}
+
+void Translator::transferErrorCode() const {
+ if (getErrorStatus())
+ Ctx->getErrorStatus()->assign(getErrorStatus().value());
}
void Translator::lowerGlobals(
const VariableDeclarationListType &VariableDeclarations) {
- std::unique_ptr<TargetGlobalInitLowering> GlobalLowering(
- TargetGlobalInitLowering::createLowering(Ctx->getTargetArch(), Ctx));
bool DisableTranslation = Ctx->getFlags().DisableTranslation;
const bool DumpGlobalVariables =
- ALLOW_DUMP && Ctx->isVerbose() && Ctx->getFlags().VerboseFocusOn.empty();
+ ALLOW_DUMP && Ctx->getVerbose() && Ctx->getFlags().VerboseFocusOn.empty();
OstreamLocker L(Ctx);
Ostream &Stream = Ctx->getStrDump();
const IceString &TranslateOnly = Ctx->getFlags().TranslateOnly;
@@ -116,8 +83,7 @@
if (DumpGlobalVariables)
Global->dump(getContext(), Stream);
if (!DisableTranslation &&
- matchSymbolName(Global->getName(), TranslateOnly))
- GlobalLowering->lower(*Global);
+ GlobalContext::matchSymbolName(Global->getName(), TranslateOnly))
+ GlobalLowering->lowerInit(*Global);
}
- GlobalLowering.reset();
}
diff --git a/src/IceTranslator.h b/src/IceTranslator.h
index a0c0409..630997b 100644
--- a/src/IceTranslator.h
+++ b/src/IceTranslator.h
@@ -15,8 +15,6 @@
#ifndef SUBZERO_SRC_ICETRANSLATOR_H
#define SUBZERO_SRC_ICETRANSLATOR_H
-#include <memory>
-
namespace llvm {
class Module;
}
@@ -38,24 +36,25 @@
public:
typedef std::vector<VariableDeclaration *> VariableDeclarationListType;
- Translator(GlobalContext *Ctx, const ClFlags &Flags)
- : Ctx(Ctx), Flags(Flags), ErrorStatus(0) {}
-
+ Translator(GlobalContext *Ctx, const ClFlags &Flags);
~Translator();
- bool getErrorStatus() const { return ErrorStatus; }
+ const ErrorCode &getErrorStatus() const { return ErrorStatus; }
GlobalContext *getContext() const { return Ctx; }
const ClFlags &getFlags() const { return Flags; }
/// Translates the constructed ICE function Fcn to machine code.
- /// Takes ownership of Fcn. Note: As a side effect, Field Func is
- /// set to Fcn.
- void translateFcn(Cfg *Fcn);
+ /// Takes ownership of Func.
+ void translateFcn(Cfg *Func);
/// Emits the constant pool.
void emitConstants();
+ /// If there was an error during bitcode reading/parsing, copy the
+ /// error code into the GlobalContext.
+ void transferErrorCode() const;
+
/// Lowers the given list of global addresses to target. Generates
/// list of corresponding variable declarations.
void lowerGlobals(const VariableDeclarationListType &VariableDeclarations);
@@ -72,18 +71,9 @@
protected:
GlobalContext *Ctx;
const ClFlags &Flags;
- // The exit status of the translation. False is successful. True
- // otherwise.
- bool ErrorStatus;
- // Ideally, Func would be inside the methods that converts IR to
- // functions. However, emitting the constant pool requires a valid
- // Cfg object, so we need to defer deleting the last non-empty Cfg
- // object to emit the constant pool (via emitConstants). TODO:
- // Since all constants are globally pooled in the GlobalContext
- // object, change all Constant related functions to use
- // GlobalContext instead of Cfg, and then make emitConstantPool use
- // that.
- std::unique_ptr<Cfg> Func;
+ std::unique_ptr<TargetGlobalLowering> GlobalLowering;
+ // Exit status of the translation. False is successful. True otherwise.
+ ErrorCode ErrorStatus;
};
} // end of namespace Ice
diff --git a/src/IceUtils.h b/src/IceUtils.h
index ffeb792..493661c 100644
--- a/src/IceUtils.h
+++ b/src/IceUtils.h
@@ -54,6 +54,133 @@
}
};
+// BoundedProducerConsumerQueue is a work queue that allows multiple
+// producers and multiple consumers. A producer adds entries using
+// blockingPush(), and may block if the queue is "full". A producer
+// uses notifyEnd() to indicate that no more entries will be added. A
+// consumer removes an item using blockingPop(), which will return
+// nullptr if notifyEnd() has been called and the queue is empty (it
+// never returns nullptr if the queue contained any items).
+//
+// The MaxSize ctor arg controls the maximum size the queue can grow
+// to (subject to a hard limit of MaxStaticSize-1). The Sequential
+// arg indicates purely sequential execution in which the single
+// thread should never wait().
+//
+// Two condition variables are used in the implementation.
+// GrewOrEnded signals a waiting worker that a producer has changed
+// the state of the queue. Shrunk signals a blocked producer that a
+// consumer has changed the state of the queue.
+//
+// The methods begin with Sequential-specific code to be most clear.
+// The lock and condition variables are not used in the Sequential
+// case.
+//
+// Internally, the queue is implemented as a circular array of size
+// MaxStaticSize, where the queue boundaries are denoted by the Front
+// and Back fields. Front==Back indicates an empty queue.
+template <typename T, size_t MaxStaticSize = 128>
+class BoundedProducerConsumerQueue {
+ BoundedProducerConsumerQueue() = delete;
+ BoundedProducerConsumerQueue(const BoundedProducerConsumerQueue &) = delete;
+ BoundedProducerConsumerQueue &
+ operator=(const BoundedProducerConsumerQueue &) = delete;
+
+public:
+ BoundedProducerConsumerQueue(size_t MaxSize, bool Sequential)
+ : Back(0), Front(0), MaxSize(std::min(MaxSize, MaxStaticSize)),
+ Sequential(Sequential), IsEnded(false) {}
+ void blockingPush(T *Item) {
+ {
+ std::unique_lock<GlobalLockType> L(Lock);
+ // If the work queue is already "full", wait for a consumer to
+ // grab an element and shrink the queue.
+ Shrunk.wait(L, [this] { return size() < MaxSize || Sequential; });
+ push(Item);
+ }
+ GrewOrEnded.notify_one();
+ }
+ T *blockingPop() {
+ T *Item = nullptr;
+ bool ShouldNotifyProducer = false;
+ {
+ std::unique_lock<GlobalLockType> L(Lock);
+ GrewOrEnded.wait(L, [this] { return IsEnded || !empty() || Sequential; });
+ if (!empty()) {
+ Item = pop();
+ ShouldNotifyProducer = !IsEnded;
+ }
+ }
+ if (ShouldNotifyProducer)
+ Shrunk.notify_one();
+ return Item;
+ }
+ void notifyEnd() {
+ {
+ std::lock_guard<GlobalLockType> L(Lock);
+ IsEnded = true;
+ }
+ GrewOrEnded.notify_all();
+ }
+
+private:
+ const static size_t MaxStaticSizeMask = MaxStaticSize - 1;
+ static_assert(!(MaxStaticSize & (MaxStaticSize - 1)),
+ "MaxStaticSize must be a power of 2");
+
+ // WorkItems and Lock are read/written by all.
+ ICE_CACHELINE_BOUNDARY;
+ T *WorkItems[MaxStaticSize];
+ ICE_CACHELINE_BOUNDARY;
+ // Lock guards access to WorkItems, Front, Back, and IsEnded.
+ GlobalLockType Lock;
+
+ ICE_CACHELINE_BOUNDARY;
+ // GrewOrEnded is written by the producers and read by the
+ // consumers. It is notified (by the producer) when something is
+ // added to the queue, in case consumers are waiting for a non-empty
+ // queue.
+ std::condition_variable GrewOrEnded;
+ // Back is the index into WorkItems[] of where the next element will
+ // be pushed. (More precisely, Back&MaxStaticSize is the index.)
+ // It is written by the producers, and read by all via size() and
+ // empty().
+ size_t Back;
+
+ ICE_CACHELINE_BOUNDARY;
+ // Shrunk is notified (by the consumer) when something is removed
+ // from the queue, in case a producer is waiting for the queue to
+ // drop below maximum capacity. It is written by the consumers and
+ // read by the producers.
+ std::condition_variable Shrunk;
+ // Front is the index into WorkItems[] of the oldest element,
+ // i.e. the next to be popped. (More precisely Front&MaxStaticSize
+ // is the index.) It is written by the consumers, and read by all
+ // via size() and empty().
+ size_t Front;
+
+ ICE_CACHELINE_BOUNDARY;
+
+ // MaxSize and Sequential are read by all and written by none.
+ const size_t MaxSize;
+ const bool Sequential;
+ // IsEnded is read by the consumers, and only written once by the
+ // producer.
+ bool IsEnded;
+
+ // The lock must be held when the following methods are called.
+ bool empty() const { return Front == Back; }
+ size_t size() const { return Back - Front; }
+ void push(T *Item) {
+ WorkItems[Back++ & MaxStaticSizeMask] = Item;
+ assert(size() <= MaxStaticSize);
+ }
+ T *pop() {
+ assert(!empty());
+ return WorkItems[Front++ & MaxStaticSizeMask];
+ }
+};
+
} // end of namespace Ice
#endif // SUBZERO_SRC_ICEUTILS_H
diff --git a/src/PNaClTranslator.cpp b/src/PNaClTranslator.cpp
index dfe93ff..6a74dd2 100644
--- a/src/PNaClTranslator.cpp
+++ b/src/PNaClTranslator.cpp
@@ -32,8 +32,6 @@
#include "IceOperand.h"
#include "PNaClTranslator.h"
-#include <memory>
-
namespace {
using namespace llvm;
@@ -159,7 +157,7 @@
typedef std::vector<Ice::FunctionDeclaration *> FunctionDeclarationListType;
TopLevelParser(Ice::Translator &Translator, NaClBitcodeHeader &Header,
- NaClBitstreamCursor &Cursor, bool &ErrorStatus)
+ NaClBitstreamCursor &Cursor, Ice::ErrorCode &ErrorStatus)
: NaClBitcodeParser(Cursor), Translator(Translator), Header(Header),
ErrorStatus(ErrorStatus), NumErrors(0), NumFunctionIds(0),
NumFunctionBlocks(0), BlockParser(nullptr) {}
@@ -362,7 +360,7 @@
// The bitcode header.
NaClBitcodeHeader &Header;
// The exit status that should be set to true if an error occurs.
- bool &ErrorStatus;
+ Ice::ErrorCode &ErrorStatus;
// The number of errors reported.
unsigned NumErrors;
// The types associated with each type ID.
@@ -425,7 +423,7 @@
};
bool TopLevelParser::Error(const std::string &Message) {
- ErrorStatus = true;
+ ErrorStatus.assign(Ice::EC_Bitcode);
++NumErrors;
Ice::GlobalContext *Context = Translator.getContext();
Ice::OstreamLocker L(Context);
@@ -2812,7 +2810,6 @@
void ExitBlock() override {
InstallGlobalNamesAndGlobalVarInitializers();
- getTranslator().emitConstants();
}
void ProcessRecord() override;
@@ -2945,7 +2942,7 @@
MemoryBuffer::getFileOrSTDIN(IRFilename);
if (std::error_code EC = ErrOrFile.getError()) {
errs() << "Error reading '" << IRFilename << "': " << EC.message() << "\n";
- ErrorStatus = true;
+ ErrorStatus.assign(EC.value());
return;
}
@@ -2958,7 +2955,7 @@
if (MemBuf->getBufferSize() % 4 != 0) {
errs() << IRFilename
<< ": Bitcode stream should be a multiple of 4 bytes in length.\n";
- ErrorStatus = true;
+ ErrorStatus.assign(EC_Bitcode);
return;
}
@@ -2969,7 +2966,7 @@
NaClBitcodeHeader Header;
if (Header.Read(BufPtr, EndBufPtr) || !Header.IsSupported()) {
errs() << "Invalid PNaCl bitcode header.\n";
- ErrorStatus = true;
+ ErrorStatus.assign(EC_Bitcode);
return;
}
@@ -2981,7 +2978,7 @@
int TopLevelBlocks = 0;
while (!InputStream.AtEndOfStream()) {
if (Parser.Parse()) {
- ErrorStatus = true;
+ ErrorStatus.assign(EC_Bitcode);
return;
}
++TopLevelBlocks;
@@ -2991,7 +2988,7 @@
errs() << IRFilename
<< ": Contains more than one module. Found: " << TopLevelBlocks
<< "\n";
- ErrorStatus = true;
+ ErrorStatus.assign(EC_Bitcode);
}
}
diff --git a/src/PNaClTranslator.h b/src/PNaClTranslator.h
index 5738f11..e818aaf 100644
--- a/src/PNaClTranslator.h
+++ b/src/PNaClTranslator.h
@@ -34,8 +34,8 @@
: Translator(Ctx, Flags) {}
// Reads the PNaCl bitcode file and translates to ICE, which is then
- // converted to machine code. Sets ErrorStatus to true if any
- // errors occurred.
+ // converted to machine code. Sets ErrorStatus to 1 if any errors
+ // occurred.
void translate(const std::string &IRFilename);
// Reads MemBuf, assuming it is the PNaCl bitcode contents of IRFilename.
diff --git a/src/llvm2ice.cpp b/src/llvm2ice.cpp
index bd86c57..51757b8 100644
--- a/src/llvm2ice.cpp
+++ b/src/llvm2ice.cpp
@@ -203,6 +203,18 @@
"this executable."),
cl::init(false));
+// Number of translation threads (in addition to the parser thread and
+// the emitter thread). The special case of 0 means purely
+// sequential, i.e. parser, translator, and emitter all within the
+// same single thread. (This may need a slight rework if we expand to
+// multiple parser or emitter threads.)
+static cl::opt<uint32_t>
+NumThreads("threads",
+ cl::desc("Number of translation threads (0 for purely sequential)"),
+ // TODO(stichnot): Settle on a good default. Consider
+ // something related to std::thread::hardware_concurrency().
+ cl::init(0));
+
static int GetReturnValue(int Val) {
if (AlwaysExitSuccess)
return 0;
@@ -274,12 +286,12 @@
ValidateAndGenerateBuildAttributes(GenerateBuildAtts ? Ls.get() : nullptr);
if (GenerateBuildAtts)
- return GetReturnValue(0);
+ return GetReturnValue(Ice::EC_None);
if (!ALLOW_DISABLE_IR_GEN && DisableIRGeneration) {
*Ls << "Error: Build doesn't allow --no-ir-gen when not "
<< "ALLOW_DISABLE_IR_GEN!\n";
- return GetReturnValue(1);
+ return GetReturnValue(Ice::EC_Args);
}
Ice::ClFlags Flags;
@@ -288,14 +300,15 @@
Flags.DisableTranslation = DisableTranslation;
Flags.FunctionSections = FunctionSections;
Flags.DataSections = DataSections;
- Flags.UseIntegratedAssembler = UseIntegratedAssembler;
Flags.UseELFWriter = UseELFWriter;
+ Flags.UseIntegratedAssembler = UseIntegratedAssembler;
Flags.UseSandboxing = UseSandboxing;
Flags.PhiEdgeSplit = EnablePhiEdgeSplit;
Flags.DecorateAsm = DecorateAsm;
Flags.DumpStats = DumpStats;
Flags.AllowUninitializedGlobals = AllowUninitializedGlobals;
Flags.TimeEachFunction = TimeEachFunction;
+ Flags.NumTranslationThreads = NumThreads;
Flags.DefaultGlobalPrefix = DefaultGlobalPrefix;
Flags.DefaultFunctionPrefix = DefaultFunctionPrefix;
Flags.TimingFocusOn = TimingFocusOn;
@@ -319,7 +332,7 @@
if (UseELFWriter) {
if (OutputFilename == "-") {
*Ls << "Error: writing binary ELF to stdout is unsupported\n";
- return GetReturnValue(1);
+ return GetReturnValue(Ice::EC_Args);
}
std::string ErrorInfo;
raw_fd_ostream *FdOs =
@@ -328,7 +341,7 @@
if (!ErrorInfo.empty()) {
*Ls << "Failed to open output file: " << OutputFilename << ":\n"
<< ErrorInfo << "\n";
- return GetReturnValue(1);
+ return GetReturnValue(Ice::EC_Args);
}
ELFStr.reset(new Ice::ELFStreamer(*FdOs));
} else {
@@ -351,11 +364,14 @@
Ctx.getObjectWriter()->writeInitialELFHeader();
}
- int ErrorStatus = 0;
+ Ctx.startWorkerThreads();
+
+ std::unique_ptr<Ice::Translator> Translator;
if (BuildOnRead) {
- Ice::PNaClTranslator Translator(&Ctx, Flags);
- Translator.translate(IRFilename);
- ErrorStatus = Translator.getErrorStatus();
+ std::unique_ptr<Ice::PNaClTranslator> PTranslator(
+ new Ice::PNaClTranslator(&Ctx, Flags));
+ PTranslator->translate(IRFilename);
+ Translator.reset(PTranslator.release());
} else if (ALLOW_LLVM_IR) {
// Parse the input LLVM IR file into a module.
SMDiagnostic Err;
@@ -367,17 +383,23 @@
if (!Mod) {
Err.print(argv[0], errs());
- return GetReturnValue(1);
+ return GetReturnValue(Ice::EC_Bitcode);
}
- Ice::Converter Converter(Mod, &Ctx, Flags);
- Converter.convertToIce();
- ErrorStatus = Converter.getErrorStatus();
+ std::unique_ptr<Ice::Converter> Converter(
+ new Ice::Converter(Mod, &Ctx, Flags));
+ Converter->convertToIce();
+ Translator.reset(Converter.release());
} else {
*Ls << "Error: Build doesn't allow LLVM IR, "
<< "--build-on-read=0 not allowed\n";
- return GetReturnValue(1);
+ return GetReturnValue(Ice::EC_Args);
}
+
+ Ctx.waitForWorkerThreads();
+ Translator->transferErrorCode();
+ Translator->emitConstants();
+
if (UseELFWriter) {
Ice::TimerMarker T1(Ice::TimerStack::TT_emit, &Ctx);
Ctx.getObjectWriter()->writeNonUserSections();
@@ -390,5 +412,5 @@
}
const bool FinalStats = true;
Ctx.dumpStats("_FINAL_", FinalStats);
- return GetReturnValue(ErrorStatus);
+ return GetReturnValue(Ctx.getErrorStatus()->value());
}