Commit fa4efea5 by Jim Stichnoth

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
parent a5fe17a1
...@@ -47,6 +47,8 @@ endif ...@@ -47,6 +47,8 @@ endif
# The list of CXX defines that are dependent on build parameters. # The list of CXX defines that are dependent on build parameters.
CXX_DEFINES = CXX_DEFINES =
CXX_EXTRA =
LD_EXTRA =
ifdef MINIMAL ifdef MINIMAL
NOASSERT = 1 NOASSERT = 1
...@@ -65,6 +67,12 @@ else ...@@ -65,6 +67,12 @@ else
OBJDIR := $(OBJDIR)+Asserts OBJDIR := $(OBJDIR)+Asserts
endif endif
ifdef TSAN
OBJDIR := $(OBJDIR)+TSan
CXX_EXTRA += -fsanitize=thread
LD_EXTRA += -fsanitize=thread
endif
$(info -----------------------------------------------) $(info -----------------------------------------------)
$(info Using LLVM_SRC_PATH = $(LLVM_SRC_PATH)) $(info Using LLVM_SRC_PATH = $(LLVM_SRC_PATH))
$(info Using LLVM_BIN_PATH = $(LLVM_BIN_PATH)) $(info Using LLVM_BIN_PATH = $(LLVM_BIN_PATH))
...@@ -85,8 +93,9 @@ CXX := CCACHE_CPP2=yes $(CCACHE) $(CLANG_PATH)/clang++ ...@@ -85,8 +93,9 @@ CXX := CCACHE_CPP2=yes $(CCACHE) $(CLANG_PATH)/clang++
CXXFLAGS := $(LLVM_CXXFLAGS) -std=c++11 -Wall -Wextra -Werror -fno-rtti \ CXXFLAGS := $(LLVM_CXXFLAGS) -std=c++11 -Wall -Wextra -Werror -fno-rtti \
-fno-exceptions $(OPTLEVEL) $(ASSERTIONS) $(CXX_DEFINES) -g \ -fno-exceptions $(OPTLEVEL) $(ASSERTIONS) $(CXX_DEFINES) -g \
$(HOST_FLAGS) -Wno-error=unused-parameter \ $(HOST_FLAGS) -Wno-error=unused-parameter \
-I$(LIBCXX_INSTALL_PATH)/include/c++/v1 -I$(LIBCXX_INSTALL_PATH)/include/c++/v1 $(CXX_EXTRA)
LDFLAGS := $(HOST_FLAGS) -L$(LIBCXX_INSTALL_PATH)/lib -Wl,--gc-sections LDFLAGS := $(HOST_FLAGS) -L$(LIBCXX_INSTALL_PATH)/lib -Wl,--gc-sections \
$(LD_EXTRA)
SRCS = \ SRCS = \
assembler.cpp \ assembler.cpp \
......
...@@ -32,10 +32,11 @@ ArenaAllocator<> *getCurrentCfgAllocator() { ...@@ -32,10 +32,11 @@ ArenaAllocator<> *getCurrentCfgAllocator() {
} }
Cfg::Cfg(GlobalContext *Ctx) Cfg::Cfg(GlobalContext *Ctx)
: Ctx(Ctx), FunctionName(""), ReturnType(IceType_void), : Ctx(Ctx), VMask(Ctx->getVerbose()), FunctionName(""),
IsInternalLinkage(false), HasError(false), FocusedTiming(false), ReturnType(IceType_void), IsInternalLinkage(false), HasError(false),
ErrorMessage(""), Entry(nullptr), NextInstNumber(Inst::NumberInitial), FocusedTiming(false), ErrorMessage(""), Entry(nullptr),
Allocator(new ArenaAllocator<>()), Live(nullptr), NextInstNumber(Inst::NumberInitial), Allocator(new ArenaAllocator<>()),
Live(nullptr),
Target(TargetLowering::createLowering(Ctx->getTargetArch(), this)), Target(TargetLowering::createLowering(Ctx->getTargetArch(), this)),
VMetadata(new VariablesMetadata(this)), VMetadata(new VariablesMetadata(this)),
TargetAssembler( TargetAssembler(
...@@ -46,18 +47,14 @@ Cfg::Cfg(GlobalContext *Ctx) ...@@ -46,18 +47,14 @@ Cfg::Cfg(GlobalContext *Ctx)
} }
Cfg::~Cfg() { Cfg::~Cfg() {
// TODO(stichnot,kschimpf): Set CurrentCfg=nullptr in the dtor for assert(ICE_TLS_GET_FIELD(CurrentCfg) == this);
// safety. This can't be done currently because the translator // Reset the thread-local CurrentCfg pointer.
// manages the Cfg by creating a new Cfg (which sets CurrentCfg to ICE_TLS_SET_FIELD(CurrentCfg, nullptr);
// the new value), then deleting the old Cfg (which would then reset
// CurrentCfg to nullptr).
} }
void Cfg::setError(const IceString &Message) { void Cfg::setError(const IceString &Message) {
HasError = true; HasError = true;
ErrorMessage = Message; ErrorMessage = Message;
OstreamLocker L(Ctx);
Ctx->getStrDump() << "ICE translation error: " << ErrorMessage << "\n";
} }
CfgNode *Cfg::makeNode() { CfgNode *Cfg::makeNode() {
...@@ -478,7 +475,7 @@ void Cfg::emitIAS() { ...@@ -478,7 +475,7 @@ void Cfg::emitIAS() {
void Cfg::dump(const IceString &Message) { void Cfg::dump(const IceString &Message) {
if (!ALLOW_DUMP) if (!ALLOW_DUMP)
return; return;
if (!Ctx->isVerbose()) if (!isVerbose())
return; return;
OstreamLocker L(Ctx); OstreamLocker L(Ctx);
Ostream &Str = Ctx->getStrDump(); Ostream &Str = Ctx->getStrDump();
...@@ -486,7 +483,7 @@ void Cfg::dump(const IceString &Message) { ...@@ -486,7 +483,7 @@ void Cfg::dump(const IceString &Message) {
Str << "================ " << Message << " ================\n"; Str << "================ " << Message << " ================\n";
setCurrentNode(getEntryNode()); setCurrentNode(getEntryNode());
// Print function name+args // Print function name+args
if (getContext()->isVerbose(IceV_Instructions)) { if (isVerbose(IceV_Instructions)) {
Str << "define "; Str << "define ";
if (getInternal() && !Ctx->getFlags().DisableInternal) if (getInternal() && !Ctx->getFlags().DisableInternal)
Str << "internal "; Str << "internal ";
...@@ -500,7 +497,7 @@ void Cfg::dump(const IceString &Message) { ...@@ -500,7 +497,7 @@ void Cfg::dump(const IceString &Message) {
Str << ") {\n"; Str << ") {\n";
} }
resetCurrentNode(); resetCurrentNode();
if (getContext()->isVerbose(IceV_Liveness)) { if (isVerbose(IceV_Liveness)) {
// Print summary info about variables // Print summary info about variables
for (Variable *Var : Variables) { for (Variable *Var : Variables) {
Str << "// multiblock="; Str << "// multiblock=";
...@@ -516,7 +513,7 @@ void Cfg::dump(const IceString &Message) { ...@@ -516,7 +513,7 @@ void Cfg::dump(const IceString &Message) {
// Print each basic block // Print each basic block
for (CfgNode *Node : Nodes) for (CfgNode *Node : Nodes)
Node->dump(this); Node->dump(this);
if (getContext()->isVerbose(IceV_Instructions)) if (isVerbose(IceV_Instructions))
Str << "}\n"; Str << "}\n";
} }
......
...@@ -15,8 +15,6 @@ ...@@ -15,8 +15,6 @@
#ifndef SUBZERO_SRC_ICECFG_H #ifndef SUBZERO_SRC_ICECFG_H
#define SUBZERO_SRC_ICECFG_H #define SUBZERO_SRC_ICECFG_H
#include <memory>
#include "assembler.h" #include "assembler.h"
#include "IceClFlags.h" #include "IceClFlags.h"
#include "IceDefs.h" #include "IceDefs.h"
...@@ -42,6 +40,7 @@ public: ...@@ -42,6 +40,7 @@ public:
} }
// Gets a pointer to the current thread's Cfg. // Gets a pointer to the current thread's Cfg.
static const Cfg *getCurrentCfg() { return ICE_TLS_GET_FIELD(CurrentCfg); } 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. // Gets a pointer to the current thread's Cfg's allocator.
static ArenaAllocator<> *getCurrentCfgAllocator() { static ArenaAllocator<> *getCurrentCfgAllocator() {
assert(ICE_TLS_GET_FIELD(CurrentCfg)); assert(ICE_TLS_GET_FIELD(CurrentCfg));
...@@ -50,6 +49,12 @@ public: ...@@ -50,6 +49,12 @@ public:
GlobalContext *getContext() const { return Ctx; } 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. // Manage the name and return type of the function being translated.
void setFunctionName(const IceString &Name) { FunctionName = Name; } void setFunctionName(const IceString &Name) { FunctionName = Name; }
IceString getFunctionName() const { return FunctionName; } IceString getFunctionName() const { return FunctionName; }
...@@ -184,6 +189,7 @@ private: ...@@ -184,6 +189,7 @@ private:
Cfg(GlobalContext *Ctx); Cfg(GlobalContext *Ctx);
GlobalContext *Ctx; GlobalContext *Ctx;
VerboseMask VMask;
IceString FunctionName; IceString FunctionName;
Type ReturnType; Type ReturnType;
bool IsInternalLinkage; bool IsInternalLinkage;
......
...@@ -917,11 +917,11 @@ void CfgNode::dump(Cfg *Func) const { ...@@ -917,11 +917,11 @@ void CfgNode::dump(Cfg *Func) const {
Func->setCurrentNode(this); Func->setCurrentNode(this);
Ostream &Str = Func->getContext()->getStrDump(); Ostream &Str = Func->getContext()->getStrDump();
Liveness *Liveness = Func->getLiveness(); Liveness *Liveness = Func->getLiveness();
if (Func->getContext()->isVerbose(IceV_Instructions)) { if (Func->isVerbose(IceV_Instructions)) {
Str << getName() << ":\n"; Str << getName() << ":\n";
} }
// Dump list of predecessor nodes. // Dump list of predecessor nodes.
if (Func->getContext()->isVerbose(IceV_Preds) && !InEdges.empty()) { if (Func->isVerbose(IceV_Preds) && !InEdges.empty()) {
Str << " // preds = "; Str << " // preds = ";
bool First = true; bool First = true;
for (CfgNode *I : InEdges) { for (CfgNode *I : InEdges) {
...@@ -936,13 +936,13 @@ void CfgNode::dump(Cfg *Func) const { ...@@ -936,13 +936,13 @@ void CfgNode::dump(Cfg *Func) const {
LivenessBV LiveIn; LivenessBV LiveIn;
if (Liveness) if (Liveness)
LiveIn = Liveness->getLiveIn(this); LiveIn = Liveness->getLiveIn(this);
if (Func->getContext()->isVerbose(IceV_Liveness) && !LiveIn.empty()) { if (Func->isVerbose(IceV_Liveness) && !LiveIn.empty()) {
Str << " // LiveIn:"; Str << " // LiveIn:";
for (SizeT i = 0; i < LiveIn.size(); ++i) { for (SizeT i = 0; i < LiveIn.size(); ++i) {
if (LiveIn[i]) { if (LiveIn[i]) {
Variable *Var = Liveness->getVariable(i, this); Variable *Var = Liveness->getVariable(i, this);
Str << " %" << Var->getName(Func); 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(), Str << ":" << Func->getTarget()->getRegName(Var->getRegNum(),
Var->getType()); Var->getType());
} }
...@@ -951,7 +951,7 @@ void CfgNode::dump(Cfg *Func) const { ...@@ -951,7 +951,7 @@ void CfgNode::dump(Cfg *Func) const {
Str << "\n"; Str << "\n";
} }
// Dump each instruction. // Dump each instruction.
if (Func->getContext()->isVerbose(IceV_Instructions)) { if (Func->isVerbose(IceV_Instructions)) {
for (const Inst &I : Phis) for (const Inst &I : Phis)
I.dumpDecorated(Func); I.dumpDecorated(Func);
for (const Inst &I : Insts) for (const Inst &I : Insts)
...@@ -961,13 +961,13 @@ void CfgNode::dump(Cfg *Func) const { ...@@ -961,13 +961,13 @@ void CfgNode::dump(Cfg *Func) const {
LivenessBV LiveOut; LivenessBV LiveOut;
if (Liveness) if (Liveness)
LiveOut = Liveness->getLiveOut(this); LiveOut = Liveness->getLiveOut(this);
if (Func->getContext()->isVerbose(IceV_Liveness) && !LiveOut.empty()) { if (Func->isVerbose(IceV_Liveness) && !LiveOut.empty()) {
Str << " // LiveOut:"; Str << " // LiveOut:";
for (SizeT i = 0; i < LiveOut.size(); ++i) { for (SizeT i = 0; i < LiveOut.size(); ++i) {
if (LiveOut[i]) { if (LiveOut[i]) {
Variable *Var = Liveness->getVariable(i, this); Variable *Var = Liveness->getVariable(i, this);
Str << " %" << Var->getName(Func); 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(), Str << ":" << Func->getTarget()->getRegName(Var->getRegNum(),
Var->getType()); Var->getType());
} }
...@@ -976,7 +976,7 @@ void CfgNode::dump(Cfg *Func) const { ...@@ -976,7 +976,7 @@ void CfgNode::dump(Cfg *Func) const {
Str << "\n"; Str << "\n";
} }
// Dump list of successor nodes. // Dump list of successor nodes.
if (Func->getContext()->isVerbose(IceV_Succs)) { if (Func->isVerbose(IceV_Succs)) {
Str << " // succs = "; Str << " // succs = ";
bool First = true; bool First = true;
for (CfgNode *I : OutEdges) { for (CfgNode *I : OutEdges) {
......
...@@ -28,8 +28,9 @@ public: ...@@ -28,8 +28,9 @@ public:
DumpStats(false), AllowUninitializedGlobals(false), DumpStats(false), AllowUninitializedGlobals(false),
TimeEachFunction(false), DisableIRGeneration(false), TimeEachFunction(false), DisableIRGeneration(false),
AllowErrorRecovery(false), GenerateUnitTestMessages(false), AllowErrorRecovery(false), GenerateUnitTestMessages(false),
DefaultGlobalPrefix(""), DefaultFunctionPrefix(""), TimingFocusOn(""), NumTranslationThreads(0), DefaultGlobalPrefix(""),
VerboseFocusOn(""), TranslateOnly("") {} DefaultFunctionPrefix(""), TimingFocusOn(""), VerboseFocusOn(""),
TranslateOnly("") {}
bool DisableInternal; bool DisableInternal;
bool SubzeroTimingEnabled; bool SubzeroTimingEnabled;
bool DisableTranslation; bool DisableTranslation;
...@@ -46,6 +47,7 @@ public: ...@@ -46,6 +47,7 @@ public:
bool DisableIRGeneration; bool DisableIRGeneration;
bool AllowErrorRecovery; bool AllowErrorRecovery;
bool GenerateUnitTestMessages; bool GenerateUnitTestMessages;
size_t NumTranslationThreads; // 0 means completely sequential
IceString DefaultGlobalPrefix; IceString DefaultGlobalPrefix;
IceString DefaultFunctionPrefix; IceString DefaultFunctionPrefix;
IceString TimingFocusOn; IceString TimingFocusOn;
......
...@@ -891,8 +891,6 @@ void Converter::convertFunctions() { ...@@ -891,8 +891,6 @@ void Converter::convertFunctions() {
if (ALLOW_DUMP && Ctx->getFlags().TimeEachFunction) if (ALLOW_DUMP && Ctx->getFlags().TimeEachFunction)
Ctx->popTimer(TimerID, StackID); Ctx->popTimer(TimerID, StackID);
} }
emitConstants();
} }
} // end of namespace Ice } // end of namespace Ice
...@@ -23,7 +23,9 @@ ...@@ -23,7 +23,9 @@
#include <limits> #include <limits>
#include <list> #include <list>
#include <map> #include <map>
#include <mutex>
#include <string> #include <string>
#include <system_error>
#include <vector> #include <vector>
#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/BitVector.h" #include "llvm/ADT/BitVector.h"
...@@ -58,6 +60,7 @@ class InstTarget; ...@@ -58,6 +60,7 @@ class InstTarget;
class LiveRange; class LiveRange;
class Liveness; class Liveness;
class Operand; class Operand;
class TargetGlobalLowering;
class TargetLowering; class TargetLowering;
class Variable; class Variable;
class VariableDeclaration; class VariableDeclaration;
...@@ -120,6 +123,17 @@ typedef llvm::BitVector LivenessBV; ...@@ -120,6 +123,17 @@ typedef llvm::BitVector LivenessBV;
typedef uint32_t TimerStackIdT; typedef uint32_t TimerStackIdT;
typedef uint32_t TimerIdT; 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. // PNaCl is ILP32, so theoretically we should only need 32-bit offsets.
typedef int32_t RelocOffsetT; typedef int32_t RelocOffsetT;
enum { RelocAddrSize = 4 }; enum { RelocAddrSize = 4 };
...@@ -163,6 +177,37 @@ typedef uint32_t VerboseMask; ...@@ -163,6 +177,37 @@ typedef uint32_t VerboseMask;
typedef llvm::raw_ostream Ostream; typedef llvm::raw_ostream Ostream;
typedef llvm::raw_fd_ostream Fdstream; 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(). // Reverse range adaptors written in terms of llvm::make_range().
template <typename T> template <typename T>
llvm::iterator_range<typename T::const_reverse_iterator> llvm::iterator_range<typename T::const_reverse_iterator>
......
...@@ -129,16 +129,17 @@ GlobalContext::GlobalContext(Ostream *OsDump, Ostream *OsEmit, ...@@ -129,16 +129,17 @@ GlobalContext::GlobalContext(Ostream *OsDump, Ostream *OsEmit,
ELFStreamer *ELFStr, VerboseMask Mask, ELFStreamer *ELFStr, VerboseMask Mask,
TargetArch Arch, OptLevel Opt, TargetArch Arch, OptLevel Opt,
IceString TestPrefix, const ClFlags &Flags) IceString TestPrefix, const ClFlags &Flags)
: StrDump(OsDump), StrEmit(OsEmit), VMask(Mask), : ConstPool(new ConstantPool()), ErrorStatus(), StrDump(OsDump),
ConstPool(new ConstantPool()), Arch(Arch), Opt(Opt), StrEmit(OsEmit), VMask(Mask), Arch(Arch), Opt(Opt),
TestPrefix(TestPrefix), Flags(Flags), RNG(""), ObjectWriter() { TestPrefix(TestPrefix), Flags(Flags), RNG(""), ObjectWriter(),
CfgQ(/*MaxSize=*/Flags.NumTranslationThreads,
/*Sequential=*/(Flags.NumTranslationThreads == 0)) {
// Make sure thread_local fields are properly initialized before any // Make sure thread_local fields are properly initialized before any
// accesses are made. Do this here instead of at the start of // accesses are made. Do this here instead of at the start of
// main() so that all clients (e.g. unit tests) can benefit for // main() so that all clients (e.g. unit tests) can benefit for
// free. // free.
GlobalContext::TlsInit(); GlobalContext::TlsInit();
Cfg::TlsInit(); Cfg::TlsInit();
// Create a new ThreadContext for the current thread. No need to // Create a new ThreadContext for the current thread. No need to
// lock AllThreadContexts at this point since no other threads have // lock AllThreadContexts at this point since no other threads have
// access yet to this GlobalContext object. // access yet to this GlobalContext object.
...@@ -156,6 +157,43 @@ GlobalContext::GlobalContext(Ostream *OsDump, Ostream *OsEmit, ...@@ -156,6 +157,43 @@ GlobalContext::GlobalContext(Ostream *OsDump, Ostream *OsEmit,
} }
} }
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 // 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 // S<num>_ where <num> is the next base-36 value. If a type name
// legitimately contains that pattern, then the substitution will be // legitimately contains that pattern, then the substitution will be
...@@ -471,12 +509,18 @@ TimerIdT GlobalContext::getTimerID(TimerStackIdT StackID, ...@@ -471,12 +509,18 @@ TimerIdT GlobalContext::getTimerID(TimerStackIdT StackID,
} }
void GlobalContext::pushTimer(TimerIdT ID, TimerStackIdT StackID) { 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(); auto Timers = getTimers();
assert(StackID < Timers->size()); assert(StackID < Timers->size());
Timers->at(StackID).push(ID); Timers->at(StackID).push(ID);
} }
void GlobalContext::popTimer(TimerIdT ID, TimerStackIdT StackID) { 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(); auto Timers = getTimers();
assert(StackID < Timers->size()); assert(StackID < Timers->size());
Timers->at(StackID).pop(ID); Timers->at(StackID).pop(ID);
......
...@@ -15,8 +15,8 @@ ...@@ -15,8 +15,8 @@
#ifndef SUBZERO_SRC_ICEGLOBALCONTEXT_H #ifndef SUBZERO_SRC_ICEGLOBALCONTEXT_H
#define SUBZERO_SRC_ICEGLOBALCONTEXT_H #define SUBZERO_SRC_ICEGLOBALCONTEXT_H
#include <memory>
#include <mutex> #include <mutex>
#include <thread>
#include "IceDefs.h" #include "IceDefs.h"
#include "IceClFlags.h" #include "IceClFlags.h"
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include "IceRNG.h" #include "IceRNG.h"
#include "IceTimerTree.h" #include "IceTimerTree.h"
#include "IceTypes.h" #include "IceTypes.h"
#include "IceUtils.h"
namespace Ice { namespace Ice {
...@@ -31,8 +32,6 @@ class ClFlags; ...@@ -31,8 +32,6 @@ class ClFlags;
class ConstantPool; class ConstantPool;
class FuncSigType; class FuncSigType;
typedef std::mutex GlobalLockType;
// LockedPtr is a way to provide automatically locked access to some object. // LockedPtr is a way to provide automatically locked access to some object.
template <typename T> class LockedPtr { template <typename T> class LockedPtr {
LockedPtr() = delete; LockedPtr() = delete;
...@@ -102,14 +101,7 @@ public: ...@@ -102,14 +101,7 @@ public:
IceString TestPrefix, const ClFlags &Flags); IceString TestPrefix, const ClFlags &Flags);
~GlobalContext(); ~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; } 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 // 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 // time. This is done by exclusively reserving the streams via
...@@ -129,6 +121,9 @@ public: ...@@ -129,6 +121,9 @@ public:
TargetArch getTargetArch() const { return Arch; } TargetArch getTargetArch() const { return Arch; }
OptLevel getOptLevel() const { return Opt; } OptLevel getOptLevel() const { return Opt; }
LockedPtr<ErrorCode> getErrorStatus() {
return LockedPtr<ErrorCode>(&ErrorStatus, &ErrorStatusLock);
}
// When emitting assembly, we allow a string to be prepended to // When emitting assembly, we allow a string to be prepended to
// names of translated functions. This makes it easier to create an // names of translated functions. This makes it easier to create an
...@@ -229,34 +224,107 @@ public: ...@@ -229,34 +224,107 @@ public:
void dumpTimers(TimerStackIdT StackID = TSK_Default, void dumpTimers(TimerStackIdT StackID = TSK_Default,
bool DumpCumulative = true); bool DumpCumulative = true);
// 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: private:
// Try to make sure the mutexes are allocated on separate cache // Try to ensure mutexes are allocated on separate cache lines.
// lines, assuming the maximum cache line size is 64.
const static size_t MaxCacheLineSize = 64; ICE_CACHELINE_BOUNDARY;
alignas(MaxCacheLineSize) GlobalLockType AllocLock; // Managed by getAllocator()
alignas(MaxCacheLineSize) GlobalLockType ConstPoolLock; GlobalLockType AllocLock;
alignas(MaxCacheLineSize) GlobalLockType StatsLock; ArenaAllocator<> Allocator;
alignas(MaxCacheLineSize) GlobalLockType TimerLock;
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. // StrLock is a global lock on the dump and emit output streams.
typedef std::mutex StrLockType; typedef std::mutex StrLockType;
StrLockType StrLock; StrLockType StrLock;
Ostream *StrDump; // Stream for dumping / diagnostics Ostream *StrDump; // Stream for dumping / diagnostics
Ostream *StrEmit; // Stream for code emission Ostream *StrEmit; // Stream for code emission
ArenaAllocator<> Allocator; ICE_CACHELINE_BOUNDARY;
VerboseMask VMask;
std::unique_ptr<ConstantPool> ConstPool; const VerboseMask VMask;
Intrinsics IntrinsicsInfo; Intrinsics IntrinsicsInfo;
const TargetArch Arch; const TargetArch Arch;
const OptLevel Opt; const OptLevel Opt;
const IceString TestPrefix; const IceString TestPrefix;
const ClFlags &Flags; const ClFlags &Flags;
RandomNumberGenerator RNG; RandomNumberGenerator RNG; // TODO(stichnot): Move into Cfg.
std::unique_ptr<ELFObjectWriter> ObjectWriter; std::unique_ptr<ELFObjectWriter> ObjectWriter;
CodeStats StatsCumulative; BoundedProducerConsumerQueue<Cfg> CfgQ;
std::vector<TimerStack> Timers;
LockedPtr<ArenaAllocator<>> getAllocator() { LockedPtr<ArenaAllocator<>> getAllocator() {
return LockedPtr<ArenaAllocator<>>(&Allocator, &AllocLock); return LockedPtr<ArenaAllocator<>>(&Allocator, &AllocLock);
...@@ -272,6 +340,7 @@ private: ...@@ -272,6 +340,7 @@ private:
} }
std::vector<ThreadContext *> AllThreadContexts; std::vector<ThreadContext *> AllThreadContexts;
std::vector<std::thread> TranslationThreads;
// Each thread has its own TLS pointer which is also held in // Each thread has its own TLS pointer which is also held in
// AllThreadContexts. // AllThreadContexts.
ICE_TLS_DECLARE_FIELD(ThreadContext *, TLS); ICE_TLS_DECLARE_FIELD(ThreadContext *, TLS);
......
...@@ -464,10 +464,9 @@ void Inst::dumpDecorated(const Cfg *Func) const { ...@@ -464,10 +464,9 @@ void Inst::dumpDecorated(const Cfg *Func) const {
if (!ALLOW_DUMP) if (!ALLOW_DUMP)
return; return;
Ostream &Str = Func->getContext()->getStrDump(); Ostream &Str = Func->getContext()->getStrDump();
if (!Func->getContext()->isVerbose(IceV_Deleted) && if (!Func->isVerbose(IceV_Deleted) && (isDeleted() || isRedundantAssign()))
(isDeleted() || isRedundantAssign()))
return; return;
if (Func->getContext()->isVerbose(IceV_InstNumbers)) { if (Func->isVerbose(IceV_InstNumbers)) {
char buf[30]; char buf[30];
InstNumberT Number = getNumber(); InstNumberT Number = getNumber();
if (Number == NumberDeleted) if (Number == NumberDeleted)
...@@ -500,7 +499,7 @@ void Inst::dumpExtras(const Cfg *Func) const { ...@@ -500,7 +499,7 @@ void Inst::dumpExtras(const Cfg *Func) const {
bool First = true; bool First = true;
// Print "LIVEEND={a,b,c}" for all source operands whose live ranges // Print "LIVEEND={a,b,c}" for all source operands whose live ranges
// are known to end at this instruction. // 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) { for (SizeT I = 0; I < getSrcSize(); ++I) {
Operand *Src = getSrc(I); Operand *Src = getSrc(I);
SizeT NumVars = Src->getNumVars(); SizeT NumVars = Src->getNumVars();
......
...@@ -399,15 +399,15 @@ void Variable::dump(const Cfg *Func, Ostream &Str) const { ...@@ -399,15 +399,15 @@ void Variable::dump(const Cfg *Func, Ostream &Str) const {
Str << "%" << getName(Func); Str << "%" << getName(Func);
return; return;
} }
if (Func->getContext()->isVerbose(IceV_RegOrigins) || if (Func->isVerbose(IceV_RegOrigins) ||
(!hasReg() && !Func->getTarget()->hasComputedFrame())) (!hasReg() && !Func->getTarget()->hasComputedFrame()))
Str << "%" << getName(Func); Str << "%" << getName(Func);
if (hasReg()) { if (hasReg()) {
if (Func->getContext()->isVerbose(IceV_RegOrigins)) if (Func->isVerbose(IceV_RegOrigins))
Str << ":"; Str << ":";
Str << Func->getTarget()->getRegName(RegNum, getType()); Str << Func->getTarget()->getRegName(RegNum, getType());
} else if (Func->getTarget()->hasComputedFrame()) { } else if (Func->getTarget()->hasComputedFrame()) {
if (Func->getContext()->isVerbose(IceV_RegOrigins)) if (Func->isVerbose(IceV_RegOrigins))
Str << ":"; Str << ":";
Str << "[" << Func->getTarget()->getRegName( Str << "[" << Func->getTarget()->getRegName(
Func->getTarget()->getFrameOrStackReg(), IceType_i32); Func->getTarget()->getFrameOrStackReg(), IceType_i32);
......
...@@ -52,7 +52,7 @@ void dumpDisableOverlap(const Cfg *Func, const Variable *Var, ...@@ -52,7 +52,7 @@ void dumpDisableOverlap(const Cfg *Func, const Variable *Var,
const char *Reason) { const char *Reason) {
if (!ALLOW_DUMP) if (!ALLOW_DUMP)
return; return;
if (Func->getContext()->isVerbose(IceV_LinearScan)) { if (Func->isVerbose(IceV_LinearScan)) {
VariablesMetadata *VMetadata = Func->getVMetadata(); VariablesMetadata *VMetadata = Func->getVMetadata();
Ostream &Str = Func->getContext()->getStrDump(); Ostream &Str = Func->getContext()->getStrDump();
Str << "Disabling Overlap due to " << Reason << " " << *Var Str << "Disabling Overlap due to " << Reason << " " << *Var
...@@ -265,8 +265,7 @@ void LinearScan::scan(const llvm::SmallBitVector &RegMaskFull, ...@@ -265,8 +265,7 @@ void LinearScan::scan(const llvm::SmallBitVector &RegMaskFull,
TimerMarker T(TimerStack::TT_linearScan, Func); TimerMarker T(TimerStack::TT_linearScan, Func);
assert(RegMaskFull.any()); // Sanity check assert(RegMaskFull.any()); // Sanity check
GlobalContext *Ctx = Func->getContext(); GlobalContext *Ctx = Func->getContext();
const bool Verbose = const bool Verbose = ALLOW_DUMP && Func->isVerbose(IceV_LinearScan);
ALLOW_DUMP && Ctx->isVerbose(IceV_LinearScan);
if (Verbose) if (Verbose)
Ctx->lockStr(); Ctx->lockStr();
Func->resetCurrentNode(); Func->resetCurrentNode();
...@@ -738,7 +737,7 @@ void LinearScan::scan(const llvm::SmallBitVector &RegMaskFull, ...@@ -738,7 +737,7 @@ void LinearScan::scan(const llvm::SmallBitVector &RegMaskFull,
void LinearScan::dump(Cfg *Func) const { void LinearScan::dump(Cfg *Func) const {
if (!ALLOW_DUMP) if (!ALLOW_DUMP)
return; return;
if (!Func->getContext()->isVerbose(IceV_LinearScan)) if (!Func->isVerbose(IceV_LinearScan))
return; return;
Ostream &Str = Func->getContext()->getStrDump(); Ostream &Str = Func->getContext()->getStrDump();
Func->resetCurrentNode(); Func->resetCurrentNode();
......
...@@ -252,25 +252,24 @@ void TargetLowering::regAlloc(RegAllocKind Kind) { ...@@ -252,25 +252,24 @@ void TargetLowering::regAlloc(RegAllocKind Kind) {
LinearScan.scan(RegMask, RandomizeRegisterAllocation); LinearScan.scan(RegMask, RandomizeRegisterAllocation);
} }
TargetGlobalInitLowering * TargetGlobalLowering *TargetGlobalLowering::createLowering(GlobalContext *Ctx) {
TargetGlobalInitLowering::createLowering(TargetArch Target,
GlobalContext *Ctx) {
// These statements can be #ifdef'd to specialize the code generator // These statements can be #ifdef'd to specialize the code generator
// to a subset of the available targets. TODO: use CRTP. // to a subset of the available targets. TODO: use CRTP.
TargetArch Target = Ctx->getTargetArch();
if (Target == Target_X8632) if (Target == Target_X8632)
return TargetGlobalInitX8632::create(Ctx); return TargetGlobalX8632::create(Ctx);
#if 0 #if 0
if (Target == Target_X8664) if (Target == Target_X8664)
return IceTargetGlobalInitX8664::create(Ctx); return TargetGlobalX8664::create(Ctx);
if (Target == Target_ARM32) if (Target == Target_ARM32)
return IceTargetGlobalInitARM32::create(Ctx); return TargetGlobalARM32::create(Ctx);
if (Target == Target_ARM64) if (Target == Target_ARM64)
return IceTargetGlobalInitARM64::create(Ctx); return TargetGlobalARM64::create(Ctx);
#endif #endif
llvm_unreachable("Unsupported target"); llvm_unreachable("Unsupported target");
return nullptr; return nullptr;
} }
TargetGlobalInitLowering::~TargetGlobalInitLowering() {} TargetGlobalLowering::~TargetGlobalLowering() {}
} // end of namespace Ice } // end of namespace Ice
...@@ -199,8 +199,6 @@ public: ...@@ -199,8 +199,6 @@ public:
virtual void addProlog(CfgNode *Node) = 0; virtual void addProlog(CfgNode *Node) = 0;
virtual void addEpilog(CfgNode *Node) = 0; virtual void addEpilog(CfgNode *Node) = 0;
virtual void emitConstants() const = 0;
virtual ~TargetLowering() {} virtual ~TargetLowering() {}
protected: protected:
...@@ -242,23 +240,23 @@ protected: ...@@ -242,23 +240,23 @@ protected:
LoweringContext Context; LoweringContext Context;
}; };
// TargetGlobalInitLowering is used for "lowering" global // TargetGlobalLowering is used for "lowering" global initializers,
// initializers. It is separated out from TargetLowering because it // including the internal constant pool. It is separated out from
// does not require a Cfg. // TargetLowering because it does not require a Cfg.
class TargetGlobalInitLowering { class TargetGlobalLowering {
TargetGlobalInitLowering(const TargetGlobalInitLowering &) = delete; TargetGlobalLowering() = delete;
TargetGlobalInitLowering &operator=(const TargetGlobalInitLowering &) = TargetGlobalLowering(const TargetGlobalLowering &) = delete;
delete; TargetGlobalLowering &operator=(const TargetGlobalLowering &) = delete;
public: public:
static TargetGlobalInitLowering *createLowering(TargetArch Target, static TargetGlobalLowering *createLowering(GlobalContext *Ctx);
GlobalContext *Ctx); virtual ~TargetGlobalLowering();
virtual ~TargetGlobalInitLowering();
virtual void lower(const VariableDeclaration &Var) = 0; virtual void lowerInit(const VariableDeclaration &Var) const = 0;
virtual void lowerConstants(GlobalContext *Ctx) const = 0;
protected: protected:
TargetGlobalInitLowering(GlobalContext *Ctx) : Ctx(Ctx) {} TargetGlobalLowering(GlobalContext *Ctx) : Ctx(Ctx) {}
GlobalContext *Ctx; GlobalContext *Ctx;
}; };
......
...@@ -897,7 +897,7 @@ void TargetX8632::addProlog(CfgNode *Node) { ...@@ -897,7 +897,7 @@ void TargetX8632::addProlog(CfgNode *Node) {
Var->setStackOffset(Linked->getStackOffset()); Var->setStackOffset(Linked->getStackOffset());
} }
if (ALLOW_DUMP && Func->getContext()->isVerbose(IceV_Frame)) { if (ALLOW_DUMP && Func->isVerbose(IceV_Frame)) {
OstreamLocker L(Func->getContext()); OstreamLocker L(Func->getContext());
Ostream &Str = Func->getContext()->getStrDump(); Ostream &Str = Func->getContext()->getStrDump();
...@@ -966,75 +966,6 @@ void TargetX8632::addEpilog(CfgNode *Node) { ...@@ -966,75 +966,6 @@ void TargetX8632::addEpilog(CfgNode *Node) {
} }
} }
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) { void TargetX8632::split64(Variable *Var) {
switch (Var->getType()) { switch (Var->getType()) {
default: default:
...@@ -3567,7 +3498,7 @@ void dumpAddressOpt(const Cfg *Func, const Variable *Base, ...@@ -3567,7 +3498,7 @@ void dumpAddressOpt(const Cfg *Func, const Variable *Base,
const Inst *Reason) { const Inst *Reason) {
if (!ALLOW_DUMP) if (!ALLOW_DUMP)
return; return;
if (!Func->getContext()->isVerbose(IceV_AddrOpt)) if (!Func->isVerbose(IceV_AddrOpt))
return; return;
OstreamLocker L(Func->getContext()); OstreamLocker L(Func->getContext());
Ostream &Str = Func->getContext()->getStrDump(); Ostream &Str = Func->getContext()->getStrDump();
...@@ -3740,7 +3671,7 @@ bool matchOffsetBase(const VariablesMetadata *VMetadata, Variable *&Base, ...@@ -3740,7 +3671,7 @@ bool matchOffsetBase(const VariablesMetadata *VMetadata, Variable *&Base,
void computeAddressOpt(Cfg *Func, const Inst *Instr, Variable *&Base, void computeAddressOpt(Cfg *Func, const Inst *Instr, Variable *&Base,
Variable *&Index, uint16_t &Shift, int32_t &Offset) { Variable *&Index, uint16_t &Shift, int32_t &Offset) {
Func->resetCurrentNode(); Func->resetCurrentNode();
if (Func->getContext()->isVerbose(IceV_AddrOpt)) { if (Func->isVerbose(IceV_AddrOpt)) {
OstreamLocker L(Func->getContext()); OstreamLocker L(Func->getContext());
Ostream &Str = Func->getContext()->getStrDump(); Ostream &Str = Func->getContext()->getStrDump();
Str << "\nStarting computeAddressOpt for instruction:\n "; Str << "\nStarting computeAddressOpt for instruction:\n ";
...@@ -4582,7 +4513,7 @@ void TargetX8632::makeRandomRegisterPermutation( ...@@ -4582,7 +4513,7 @@ void TargetX8632::makeRandomRegisterPermutation(
assert(NumShuffled + NumPreserved == RegX8632::Reg_NUM); assert(NumShuffled + NumPreserved == RegX8632::Reg_NUM);
if (Func->getContext()->isVerbose(IceV_Random)) { if (Func->isVerbose(IceV_Random)) {
OstreamLocker L(Func->getContext()); OstreamLocker L(Func->getContext());
Ostream &Str = Func->getContext()->getStrDump(); Ostream &Str = Func->getContext()->getStrDump();
Str << "Register equivalence classes:\n"; Str << "Register equivalence classes:\n";
...@@ -4630,10 +4561,10 @@ void ConstantUndef::emit(GlobalContext *) const { ...@@ -4630,10 +4561,10 @@ void ConstantUndef::emit(GlobalContext *) const {
llvm_unreachable("undef value encountered by emitter."); llvm_unreachable("undef value encountered by emitter.");
} }
TargetGlobalInitX8632::TargetGlobalInitX8632(GlobalContext *Ctx) TargetGlobalX8632::TargetGlobalX8632(GlobalContext *Ctx)
: TargetGlobalInitLowering(Ctx) {} : TargetGlobalLowering(Ctx) {}
void TargetGlobalInitX8632::lower(const VariableDeclaration &Var) { void TargetGlobalX8632::lowerInit(const VariableDeclaration &Var) const {
// TODO(jvoung): handle this without text. // TODO(jvoung): handle this without text.
if (Ctx->getFlags().UseELFWriter) if (Ctx->getFlags().UseELFWriter)
return; return;
...@@ -4714,4 +4645,76 @@ void TargetGlobalInitX8632::lower(const VariableDeclaration &Var) { ...@@ -4714,4 +4645,76 @@ void TargetGlobalInitX8632::lower(const VariableDeclaration &Var) {
Str << "\t.size\t" << MangledName << ", " << Size << "\n"; 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 } // end of namespace Ice
...@@ -56,7 +56,6 @@ public: ...@@ -56,7 +56,6 @@ public:
void lowerArguments() override; void lowerArguments() override;
void addProlog(CfgNode *Node) override; void addProlog(CfgNode *Node) override;
void addEpilog(CfgNode *Node) override; void addEpilog(CfgNode *Node) override;
void emitConstants() const override;
SizeT makeNextLabelNumber() { return NextLabelNumber++; } SizeT makeNextLabelNumber() { return NextLabelNumber++; }
// Ensure that a 64-bit Variable has been split into 2 32-bit // Ensure that a 64-bit Variable has been split into 2 32-bit
// Variables, creating them if necessary. This is needed for all // Variables, creating them if necessary. This is needed for all
...@@ -487,25 +486,27 @@ protected: ...@@ -487,25 +486,27 @@ protected:
private: private:
~TargetX8632() override {} ~TargetX8632() override {}
template <typename T> void emitConstantPool() const;
}; };
class TargetGlobalInitX8632 : public TargetGlobalInitLowering { class TargetGlobalX8632 : public TargetGlobalLowering {
TargetGlobalInitX8632(const TargetGlobalInitX8632 &) = delete; TargetGlobalX8632() = delete;
TargetGlobalInitX8632 &operator=(const TargetGlobalInitX8632 &) = delete; TargetGlobalX8632(const TargetGlobalX8632 &) = delete;
TargetGlobalX8632 &operator=(const TargetGlobalX8632 &) = delete;
public: public:
static TargetGlobalInitLowering *create(GlobalContext *Ctx) { static TargetGlobalLowering *create(GlobalContext *Ctx) {
return new TargetGlobalInitX8632(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: protected:
TargetGlobalInitX8632(GlobalContext *Ctx); TargetGlobalX8632(GlobalContext *Ctx);
private: private:
~TargetGlobalInitX8632() override {} ~TargetGlobalX8632() override {}
template <typename T> static void emitConstantPool(GlobalContext *Ctx);
}; };
template <> void ConstantInteger32::emit(GlobalContext *Ctx) const; template <> void ConstantInteger32::emit(GlobalContext *Ctx) const;
......
...@@ -12,13 +12,6 @@ ...@@ -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 "IceCfg.h"
#include "IceClFlags.h" #include "IceClFlags.h"
#include "IceDefs.h" #include "IceDefs.h"
...@@ -28,16 +21,11 @@ ...@@ -28,16 +21,11 @@
using namespace Ice; using namespace Ice;
namespace { Translator::Translator(GlobalContext *Ctx, const ClFlags &Flags)
: Ctx(Ctx), Flags(Flags),
// Match a symbol name against a match string. An empty match string GlobalLowering(TargetGlobalLowering::createLowering(Ctx)), ErrorStatus() {
// means match everything. Returns true if there is a match.
bool matchSymbolName(const IceString &SymbolName, const IceString &Match) {
return Match.empty() || Match == SymbolName;
} }
} // end of anonymous namespace
Translator::~Translator() {} Translator::~Translator() {}
IceString Translator::createUnnamedName(const IceString &Prefix, SizeT Index) { IceString Translator::createUnnamedName(const IceString &Prefix, SizeT Index) {
...@@ -66,49 +54,28 @@ bool Translator::checkIfUnnamedNameSafe(const IceString &Name, const char *Kind, ...@@ -66,49 +54,28 @@ bool Translator::checkIfUnnamedNameSafe(const IceString &Name, const char *Kind,
return false; return false;
} }
void Translator::translateFcn(Cfg *Fcn) { void Translator::translateFcn(Cfg *Func) {
Ctx->resetStats(); Ctx->cfgQueueBlockingPush(Func);
Func.reset(Fcn); if (Ctx->getFlags().NumTranslationThreads == 0) {
VerboseMask OldVerboseMask = Ctx->getVerbose(); Ctx->translateFunctions();
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());
} }
Ctx->setVerbose(OldVerboseMask);
} }
void Translator::emitConstants() { void Translator::emitConstants() {
if (!Ctx->getFlags().DisableTranslation && Func) if (!getErrorStatus())
Func->getTarget()->emitConstants(); GlobalLowering->lowerConstants(Ctx);
}
void Translator::transferErrorCode() const {
if (getErrorStatus())
Ctx->getErrorStatus()->assign(getErrorStatus().value());
} }
void Translator::lowerGlobals( void Translator::lowerGlobals(
const VariableDeclarationListType &VariableDeclarations) { const VariableDeclarationListType &VariableDeclarations) {
std::unique_ptr<TargetGlobalInitLowering> GlobalLowering(
TargetGlobalInitLowering::createLowering(Ctx->getTargetArch(), Ctx));
bool DisableTranslation = Ctx->getFlags().DisableTranslation; bool DisableTranslation = Ctx->getFlags().DisableTranslation;
const bool DumpGlobalVariables = const bool DumpGlobalVariables =
ALLOW_DUMP && Ctx->isVerbose() && Ctx->getFlags().VerboseFocusOn.empty(); ALLOW_DUMP && Ctx->getVerbose() && Ctx->getFlags().VerboseFocusOn.empty();
OstreamLocker L(Ctx); OstreamLocker L(Ctx);
Ostream &Stream = Ctx->getStrDump(); Ostream &Stream = Ctx->getStrDump();
const IceString &TranslateOnly = Ctx->getFlags().TranslateOnly; const IceString &TranslateOnly = Ctx->getFlags().TranslateOnly;
...@@ -116,8 +83,7 @@ void Translator::lowerGlobals( ...@@ -116,8 +83,7 @@ void Translator::lowerGlobals(
if (DumpGlobalVariables) if (DumpGlobalVariables)
Global->dump(getContext(), Stream); Global->dump(getContext(), Stream);
if (!DisableTranslation && if (!DisableTranslation &&
matchSymbolName(Global->getName(), TranslateOnly)) GlobalContext::matchSymbolName(Global->getName(), TranslateOnly))
GlobalLowering->lower(*Global); GlobalLowering->lowerInit(*Global);
} }
GlobalLowering.reset();
} }
...@@ -15,8 +15,6 @@ ...@@ -15,8 +15,6 @@
#ifndef SUBZERO_SRC_ICETRANSLATOR_H #ifndef SUBZERO_SRC_ICETRANSLATOR_H
#define SUBZERO_SRC_ICETRANSLATOR_H #define SUBZERO_SRC_ICETRANSLATOR_H
#include <memory>
namespace llvm { namespace llvm {
class Module; class Module;
} }
...@@ -38,24 +36,25 @@ class Translator { ...@@ -38,24 +36,25 @@ class Translator {
public: public:
typedef std::vector<VariableDeclaration *> VariableDeclarationListType; typedef std::vector<VariableDeclaration *> VariableDeclarationListType;
Translator(GlobalContext *Ctx, const ClFlags &Flags) Translator(GlobalContext *Ctx, const ClFlags &Flags);
: Ctx(Ctx), Flags(Flags), ErrorStatus(0) {}
~Translator(); ~Translator();
bool getErrorStatus() const { return ErrorStatus; } const ErrorCode &getErrorStatus() const { return ErrorStatus; }
GlobalContext *getContext() const { return Ctx; } GlobalContext *getContext() const { return Ctx; }
const ClFlags &getFlags() const { return Flags; } const ClFlags &getFlags() const { return Flags; }
/// Translates the constructed ICE function Fcn to machine code. /// Translates the constructed ICE function Fcn to machine code.
/// Takes ownership of Fcn. Note: As a side effect, Field Func is /// Takes ownership of Func.
/// set to Fcn. void translateFcn(Cfg *Func);
void translateFcn(Cfg *Fcn);
/// Emits the constant pool. /// Emits the constant pool.
void emitConstants(); 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 /// Lowers the given list of global addresses to target. Generates
/// list of corresponding variable declarations. /// list of corresponding variable declarations.
void lowerGlobals(const VariableDeclarationListType &VariableDeclarations); void lowerGlobals(const VariableDeclarationListType &VariableDeclarations);
...@@ -72,18 +71,9 @@ public: ...@@ -72,18 +71,9 @@ public:
protected: protected:
GlobalContext *Ctx; GlobalContext *Ctx;
const ClFlags &Flags; const ClFlags &Flags;
// The exit status of the translation. False is successful. True std::unique_ptr<TargetGlobalLowering> GlobalLowering;
// otherwise. // Exit status of the translation. False is successful. True otherwise.
bool ErrorStatus; ErrorCode 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;
}; };
} // end of namespace Ice } // end of namespace Ice
......
...@@ -54,6 +54,133 @@ public: ...@@ -54,6 +54,133 @@ public:
} }
}; };
// 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 } // end of namespace Ice
#endif // SUBZERO_SRC_ICEUTILS_H #endif // SUBZERO_SRC_ICEUTILS_H
...@@ -32,8 +32,6 @@ ...@@ -32,8 +32,6 @@
#include "IceOperand.h" #include "IceOperand.h"
#include "PNaClTranslator.h" #include "PNaClTranslator.h"
#include <memory>
namespace { namespace {
using namespace llvm; using namespace llvm;
...@@ -159,7 +157,7 @@ public: ...@@ -159,7 +157,7 @@ public:
typedef std::vector<Ice::FunctionDeclaration *> FunctionDeclarationListType; typedef std::vector<Ice::FunctionDeclaration *> FunctionDeclarationListType;
TopLevelParser(Ice::Translator &Translator, NaClBitcodeHeader &Header, TopLevelParser(Ice::Translator &Translator, NaClBitcodeHeader &Header,
NaClBitstreamCursor &Cursor, bool &ErrorStatus) NaClBitstreamCursor &Cursor, Ice::ErrorCode &ErrorStatus)
: NaClBitcodeParser(Cursor), Translator(Translator), Header(Header), : NaClBitcodeParser(Cursor), Translator(Translator), Header(Header),
ErrorStatus(ErrorStatus), NumErrors(0), NumFunctionIds(0), ErrorStatus(ErrorStatus), NumErrors(0), NumFunctionIds(0),
NumFunctionBlocks(0), BlockParser(nullptr) {} NumFunctionBlocks(0), BlockParser(nullptr) {}
...@@ -362,7 +360,7 @@ private: ...@@ -362,7 +360,7 @@ private:
// The bitcode header. // The bitcode header.
NaClBitcodeHeader &Header; NaClBitcodeHeader &Header;
// The exit status that should be set to true if an error occurs. // The exit status that should be set to true if an error occurs.
bool &ErrorStatus; Ice::ErrorCode &ErrorStatus;
// The number of errors reported. // The number of errors reported.
unsigned NumErrors; unsigned NumErrors;
// The types associated with each type ID. // The types associated with each type ID.
...@@ -425,7 +423,7 @@ private: ...@@ -425,7 +423,7 @@ private:
}; };
bool TopLevelParser::Error(const std::string &Message) { bool TopLevelParser::Error(const std::string &Message) {
ErrorStatus = true; ErrorStatus.assign(Ice::EC_Bitcode);
++NumErrors; ++NumErrors;
Ice::GlobalContext *Context = Translator.getContext(); Ice::GlobalContext *Context = Translator.getContext();
Ice::OstreamLocker L(Context); Ice::OstreamLocker L(Context);
...@@ -2812,7 +2810,6 @@ private: ...@@ -2812,7 +2810,6 @@ private:
void ExitBlock() override { void ExitBlock() override {
InstallGlobalNamesAndGlobalVarInitializers(); InstallGlobalNamesAndGlobalVarInitializers();
getTranslator().emitConstants();
} }
void ProcessRecord() override; void ProcessRecord() override;
...@@ -2945,7 +2942,7 @@ void PNaClTranslator::translate(const std::string &IRFilename) { ...@@ -2945,7 +2942,7 @@ void PNaClTranslator::translate(const std::string &IRFilename) {
MemoryBuffer::getFileOrSTDIN(IRFilename); MemoryBuffer::getFileOrSTDIN(IRFilename);
if (std::error_code EC = ErrOrFile.getError()) { if (std::error_code EC = ErrOrFile.getError()) {
errs() << "Error reading '" << IRFilename << "': " << EC.message() << "\n"; errs() << "Error reading '" << IRFilename << "': " << EC.message() << "\n";
ErrorStatus = true; ErrorStatus.assign(EC.value());
return; return;
} }
...@@ -2958,7 +2955,7 @@ void PNaClTranslator::translateBuffer(const std::string &IRFilename, ...@@ -2958,7 +2955,7 @@ void PNaClTranslator::translateBuffer(const std::string &IRFilename,
if (MemBuf->getBufferSize() % 4 != 0) { if (MemBuf->getBufferSize() % 4 != 0) {
errs() << IRFilename errs() << IRFilename
<< ": Bitcode stream should be a multiple of 4 bytes in length.\n"; << ": Bitcode stream should be a multiple of 4 bytes in length.\n";
ErrorStatus = true; ErrorStatus.assign(EC_Bitcode);
return; return;
} }
...@@ -2969,7 +2966,7 @@ void PNaClTranslator::translateBuffer(const std::string &IRFilename, ...@@ -2969,7 +2966,7 @@ void PNaClTranslator::translateBuffer(const std::string &IRFilename,
NaClBitcodeHeader Header; NaClBitcodeHeader Header;
if (Header.Read(BufPtr, EndBufPtr) || !Header.IsSupported()) { if (Header.Read(BufPtr, EndBufPtr) || !Header.IsSupported()) {
errs() << "Invalid PNaCl bitcode header.\n"; errs() << "Invalid PNaCl bitcode header.\n";
ErrorStatus = true; ErrorStatus.assign(EC_Bitcode);
return; return;
} }
...@@ -2981,7 +2978,7 @@ void PNaClTranslator::translateBuffer(const std::string &IRFilename, ...@@ -2981,7 +2978,7 @@ void PNaClTranslator::translateBuffer(const std::string &IRFilename,
int TopLevelBlocks = 0; int TopLevelBlocks = 0;
while (!InputStream.AtEndOfStream()) { while (!InputStream.AtEndOfStream()) {
if (Parser.Parse()) { if (Parser.Parse()) {
ErrorStatus = true; ErrorStatus.assign(EC_Bitcode);
return; return;
} }
++TopLevelBlocks; ++TopLevelBlocks;
...@@ -2991,7 +2988,7 @@ void PNaClTranslator::translateBuffer(const std::string &IRFilename, ...@@ -2991,7 +2988,7 @@ void PNaClTranslator::translateBuffer(const std::string &IRFilename,
errs() << IRFilename errs() << IRFilename
<< ": Contains more than one module. Found: " << TopLevelBlocks << ": Contains more than one module. Found: " << TopLevelBlocks
<< "\n"; << "\n";
ErrorStatus = true; ErrorStatus.assign(EC_Bitcode);
} }
} }
......
...@@ -34,8 +34,8 @@ public: ...@@ -34,8 +34,8 @@ public:
: Translator(Ctx, Flags) {} : Translator(Ctx, Flags) {}
// Reads the PNaCl bitcode file and translates to ICE, which is then // Reads the PNaCl bitcode file and translates to ICE, which is then
// converted to machine code. Sets ErrorStatus to true if any // converted to machine code. Sets ErrorStatus to 1 if any errors
// errors occurred. // occurred.
void translate(const std::string &IRFilename); void translate(const std::string &IRFilename);
// Reads MemBuf, assuming it is the PNaCl bitcode contents of IRFilename. // Reads MemBuf, assuming it is the PNaCl bitcode contents of IRFilename.
......
...@@ -203,6 +203,18 @@ GenerateBuildAtts("build-atts", ...@@ -203,6 +203,18 @@ GenerateBuildAtts("build-atts",
"this executable."), "this executable."),
cl::init(false)); 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) { static int GetReturnValue(int Val) {
if (AlwaysExitSuccess) if (AlwaysExitSuccess)
return 0; return 0;
...@@ -274,12 +286,12 @@ int main(int argc, char **argv) { ...@@ -274,12 +286,12 @@ int main(int argc, char **argv) {
ValidateAndGenerateBuildAttributes(GenerateBuildAtts ? Ls.get() : nullptr); ValidateAndGenerateBuildAttributes(GenerateBuildAtts ? Ls.get() : nullptr);
if (GenerateBuildAtts) if (GenerateBuildAtts)
return GetReturnValue(0); return GetReturnValue(Ice::EC_None);
if (!ALLOW_DISABLE_IR_GEN && DisableIRGeneration) { if (!ALLOW_DISABLE_IR_GEN && DisableIRGeneration) {
*Ls << "Error: Build doesn't allow --no-ir-gen when not " *Ls << "Error: Build doesn't allow --no-ir-gen when not "
<< "ALLOW_DISABLE_IR_GEN!\n"; << "ALLOW_DISABLE_IR_GEN!\n";
return GetReturnValue(1); return GetReturnValue(Ice::EC_Args);
} }
Ice::ClFlags Flags; Ice::ClFlags Flags;
...@@ -288,14 +300,15 @@ int main(int argc, char **argv) { ...@@ -288,14 +300,15 @@ int main(int argc, char **argv) {
Flags.DisableTranslation = DisableTranslation; Flags.DisableTranslation = DisableTranslation;
Flags.FunctionSections = FunctionSections; Flags.FunctionSections = FunctionSections;
Flags.DataSections = DataSections; Flags.DataSections = DataSections;
Flags.UseIntegratedAssembler = UseIntegratedAssembler;
Flags.UseELFWriter = UseELFWriter; Flags.UseELFWriter = UseELFWriter;
Flags.UseIntegratedAssembler = UseIntegratedAssembler;
Flags.UseSandboxing = UseSandboxing; Flags.UseSandboxing = UseSandboxing;
Flags.PhiEdgeSplit = EnablePhiEdgeSplit; Flags.PhiEdgeSplit = EnablePhiEdgeSplit;
Flags.DecorateAsm = DecorateAsm; Flags.DecorateAsm = DecorateAsm;
Flags.DumpStats = DumpStats; Flags.DumpStats = DumpStats;
Flags.AllowUninitializedGlobals = AllowUninitializedGlobals; Flags.AllowUninitializedGlobals = AllowUninitializedGlobals;
Flags.TimeEachFunction = TimeEachFunction; Flags.TimeEachFunction = TimeEachFunction;
Flags.NumTranslationThreads = NumThreads;
Flags.DefaultGlobalPrefix = DefaultGlobalPrefix; Flags.DefaultGlobalPrefix = DefaultGlobalPrefix;
Flags.DefaultFunctionPrefix = DefaultFunctionPrefix; Flags.DefaultFunctionPrefix = DefaultFunctionPrefix;
Flags.TimingFocusOn = TimingFocusOn; Flags.TimingFocusOn = TimingFocusOn;
...@@ -319,7 +332,7 @@ int main(int argc, char **argv) { ...@@ -319,7 +332,7 @@ int main(int argc, char **argv) {
if (UseELFWriter) { if (UseELFWriter) {
if (OutputFilename == "-") { if (OutputFilename == "-") {
*Ls << "Error: writing binary ELF to stdout is unsupported\n"; *Ls << "Error: writing binary ELF to stdout is unsupported\n";
return GetReturnValue(1); return GetReturnValue(Ice::EC_Args);
} }
std::string ErrorInfo; std::string ErrorInfo;
raw_fd_ostream *FdOs = raw_fd_ostream *FdOs =
...@@ -328,7 +341,7 @@ int main(int argc, char **argv) { ...@@ -328,7 +341,7 @@ int main(int argc, char **argv) {
if (!ErrorInfo.empty()) { if (!ErrorInfo.empty()) {
*Ls << "Failed to open output file: " << OutputFilename << ":\n" *Ls << "Failed to open output file: " << OutputFilename << ":\n"
<< ErrorInfo << "\n"; << ErrorInfo << "\n";
return GetReturnValue(1); return GetReturnValue(Ice::EC_Args);
} }
ELFStr.reset(new Ice::ELFStreamer(*FdOs)); ELFStr.reset(new Ice::ELFStreamer(*FdOs));
} else { } else {
...@@ -351,11 +364,14 @@ int main(int argc, char **argv) { ...@@ -351,11 +364,14 @@ int main(int argc, char **argv) {
Ctx.getObjectWriter()->writeInitialELFHeader(); Ctx.getObjectWriter()->writeInitialELFHeader();
} }
int ErrorStatus = 0; Ctx.startWorkerThreads();
std::unique_ptr<Ice::Translator> Translator;
if (BuildOnRead) { if (BuildOnRead) {
Ice::PNaClTranslator Translator(&Ctx, Flags); std::unique_ptr<Ice::PNaClTranslator> PTranslator(
Translator.translate(IRFilename); new Ice::PNaClTranslator(&Ctx, Flags));
ErrorStatus = Translator.getErrorStatus(); PTranslator->translate(IRFilename);
Translator.reset(PTranslator.release());
} else if (ALLOW_LLVM_IR) { } else if (ALLOW_LLVM_IR) {
// Parse the input LLVM IR file into a module. // Parse the input LLVM IR file into a module.
SMDiagnostic Err; SMDiagnostic Err;
...@@ -367,17 +383,23 @@ int main(int argc, char **argv) { ...@@ -367,17 +383,23 @@ int main(int argc, char **argv) {
if (!Mod) { if (!Mod) {
Err.print(argv[0], errs()); Err.print(argv[0], errs());
return GetReturnValue(1); return GetReturnValue(Ice::EC_Bitcode);
} }
Ice::Converter Converter(Mod, &Ctx, Flags); std::unique_ptr<Ice::Converter> Converter(
Converter.convertToIce(); new Ice::Converter(Mod, &Ctx, Flags));
ErrorStatus = Converter.getErrorStatus(); Converter->convertToIce();
Translator.reset(Converter.release());
} else { } else {
*Ls << "Error: Build doesn't allow LLVM IR, " *Ls << "Error: Build doesn't allow LLVM IR, "
<< "--build-on-read=0 not allowed\n"; << "--build-on-read=0 not allowed\n";
return GetReturnValue(1); return GetReturnValue(Ice::EC_Args);
} }
Ctx.waitForWorkerThreads();
Translator->transferErrorCode();
Translator->emitConstants();
if (UseELFWriter) { if (UseELFWriter) {
Ice::TimerMarker T1(Ice::TimerStack::TT_emit, &Ctx); Ice::TimerMarker T1(Ice::TimerStack::TT_emit, &Ctx);
Ctx.getObjectWriter()->writeNonUserSections(); Ctx.getObjectWriter()->writeNonUserSections();
...@@ -390,5 +412,5 @@ int main(int argc, char **argv) { ...@@ -390,5 +412,5 @@ int main(int argc, char **argv) {
} }
const bool FinalStats = true; const bool FinalStats = true;
Ctx.dumpStats("_FINAL_", FinalStats); Ctx.dumpStats("_FINAL_", FinalStats);
return GetReturnValue(ErrorStatus); return GetReturnValue(Ctx.getErrorStatus()->value());
} }
; Test that some errors trigger when the usage of NaCl atomic ; Test that some errors trigger when the usage of NaCl atomic
; intrinsics does not match the required ABI. ; intrinsics does not match the required ABI.
; RUN: not %p2i -i %s --args --verbose none 2>&1 | FileCheck %s ; RUN: %p2i -i %s --args --verbose none --exit-success 2>&1 | FileCheck %s
declare i8 @llvm.nacl.atomic.load.i8(i8*, i32) declare i8 @llvm.nacl.atomic.load.i8(i8*, i32)
declare i16 @llvm.nacl.atomic.load.i16(i16*, i32) declare i16 @llvm.nacl.atomic.load.i16(i16*, i32)
......
...@@ -34,7 +34,7 @@ bool IceTest::SubzeroBitcodeMunger::runTest( ...@@ -34,7 +34,7 @@ bool IceTest::SubzeroBitcodeMunger::runTest(
Translator.translateBuffer(TestName, MungedInput.get()); Translator.translateBuffer(TestName, MungedInput.get());
cleanupTest(); cleanupTest();
return Translator.getErrorStatus() == 0; return Translator.getErrorStatus().value() == 0;
} }
} // end of namespace IceTest } // end of namespace IceTest
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment