IceRNG.cpp \
IceTargetLowering.cpp \
IceTargetLoweringX8632.cpp \
+ IceThreading.cpp \
IceTimerTree.cpp \
IceTranslator.cpp \
IceTypes.cpp \
cmd += ['|']
cmd += [args.llvm2ice]
if args.insts:
- cmd += ['-verbose', 'inst', '-notranslate']
+ # If the tests are based on '-verbose inst' output, force
+ # single-threaded translation because dump output does not get
+ # reassembled into order.
+ cmd += ['-verbose', 'inst', '-notranslate', '-threads=0']
if not args.llvm_source:
cmd += ['--bitcode-format=pnacl']
if not args.no_local_syms:
return Cfg::getCurrentCfgAllocator();
}
-Cfg::Cfg(GlobalContext *Ctx)
- : Ctx(Ctx), VMask(Ctx->getVerbose()), FunctionName(""),
- ReturnType(IceType_void), IsInternalLinkage(false), HasError(false),
- FocusedTiming(false), ErrorMessage(""), Entry(nullptr),
+Cfg::Cfg(GlobalContext *Ctx, uint32_t SequenceNumber)
+ : Ctx(Ctx), SequenceNumber(SequenceNumber), 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)),
// ======================== Dump routines ======================== //
-void Cfg::emitTextHeader(const IceString &MangledName) {
+// emitTextHeader() is not target-specific (apart from what is
+// abstracted by the Assembler), so it is defined here rather than in
+// the target lowering class.
+void Cfg::emitTextHeader(const IceString &MangledName, GlobalContext *Ctx,
+ const Assembler *Asm) {
// Note: Still used by emit IAS.
Ostream &Str = Ctx->getStrEmit();
Str << "\t.text\n";
if (Ctx->getFlags().getFunctionSections())
Str << "\t.section\t.text." << MangledName << ",\"ax\",@progbits\n";
- if (!getInternal() || Ctx->getFlags().getDisableInternal()) {
+ if (!Asm->getInternal() || Ctx->getFlags().getDisableInternal()) {
Str << "\t.globl\t" << MangledName << "\n";
Str << "\t.type\t" << MangledName << ",@function\n";
}
- Assembler *Asm = getAssembler<Assembler>();
Str << "\t.p2align " << Asm->getBundleAlignLog2Bytes() << ",0x";
for (uint8_t I : Asm->getNonExecBundlePadding())
Str.write_hex(I);
OstreamLocker L(Ctx);
Ostream &Str = Ctx->getStrEmit();
IceString MangledName = getContext()->mangleName(getFunctionName());
- emitTextHeader(MangledName);
+ emitTextHeader(MangledName, Ctx, getAssembler<>());
for (CfgNode *Node : Nodes)
Node->emit(this);
Str << "\n";
void Cfg::emitIAS() {
TimerMarker T(TimerStack::TT_emit, this);
assert(!Ctx->getFlags().getDecorateAsm());
- IceString MangledName = getContext()->mangleName(getFunctionName());
// The emitIAS() routines emit into the internal assembler buffer,
- // so there's no need to lock the streams until we're ready to call
- // emitIASBytes().
+ // so there's no need to lock the streams.
for (CfgNode *Node : Nodes)
Node->emitIAS(this);
- // Now write the function to the file and track.
- if (Ctx->getFlags().getUseELFWriter()) {
- getAssembler<Assembler>()->alignFunction();
- Ctx->getObjectWriter()->writeFunctionCode(MangledName, getInternal(),
- getAssembler<Assembler>());
- } else {
- OstreamLocker L(Ctx);
- emitTextHeader(MangledName);
- getAssembler<Assembler>()->emitIASBytes(Ctx);
- }
}
// Dumps the IR with an optional introductory message.
public:
~Cfg();
- static std::unique_ptr<Cfg> create(GlobalContext *Ctx) {
- return std::unique_ptr<Cfg>(new Cfg(Ctx));
+ static std::unique_ptr<Cfg> create(GlobalContext *Ctx,
+ uint32_t SequenceNumber) {
+ return std::unique_ptr<Cfg>(new Cfg(Ctx, SequenceNumber));
}
// Gets a pointer to the current thread's Cfg.
static const Cfg *getCurrentCfg() { return ICE_TLS_GET_FIELD(CurrentCfg); }
}
GlobalContext *getContext() const { return Ctx; }
+ uint32_t getSequenceNumber() const { return SequenceNumber; }
// Returns true if any of the specified options in the verbose mask
// are set. If the argument is omitted, it checks if any verbose
TargetLowering *getTarget() const { return Target.get(); }
VariablesMetadata *getVMetadata() const { return VMetadata.get(); }
Liveness *getLiveness() const { return Live.get(); }
- template <typename T> T *getAssembler() const {
+ template <typename T = Assembler> T *getAssembler() const {
return static_cast<T *>(TargetAssembler.get());
}
+ Assembler *releaseAssembler() { return TargetAssembler.release(); }
bool hasComputedFrame() const;
bool getFocusedTiming() const { return FocusedTiming; }
void setFocusedTiming() { FocusedTiming = true; }
void emit();
void emitIAS();
- void emitTextHeader(const IceString &MangledName);
+ static void emitTextHeader(const IceString &MangledName, GlobalContext *Ctx,
+ const Assembler *Asm);
void dump(const IceString &Message = "");
// Allocate data of type T using the per-Cfg allocator.
}
private:
- Cfg(GlobalContext *Ctx);
+ Cfg(GlobalContext *Ctx, uint32_t SequenceNumber);
GlobalContext *Ctx;
+ uint32_t SequenceNumber; // output order for emission
VerboseMask VMask;
IceString FunctionName;
Type ReturnType;
void CfgNode::emitIAS(Cfg *Func) const {
Func->setCurrentNode(this);
- Assembler *Asm = Func->getAssembler<Assembler>();
+ Assembler *Asm = Func->getAssembler<>();
Asm->BindCfgNodeLabel(getIndex());
for (const Inst &I : Phis) {
if (I.isDeleted())
// size_t accessors.
size_t getNumTranslationThreads() const { return NumTranslationThreads; }
+ bool isSequential() const { return NumTranslationThreads == 0; }
void setNumTranslationThreads(size_t NewValue) {
NumTranslationThreads = NewValue;
}
: LLVM2ICEConverter(Converter), Func(nullptr) {}
void convertFunction(const Function *F) {
- Func = Ice::Cfg::create(Ctx);
+ if (Ctx->isIRGenerationDisabled())
+ return;
+ Func = Ice::Cfg::create(Ctx, Converter.getNextSequenceNumber());
Ice::Cfg::setCurrentCfg(Func.get());
VarMap.clear();
: LLVM2ICEConverter(Converter) {}
/// Converts global variables, and their initializers into ICE
- /// global variable declarations, for module Mod. Puts corresponding
- /// converted declarations into VariableDeclarations.
- void convertGlobalsToIce(Module *Mod,
- Ice::VariableDeclarationList &VariableDeclarations);
+ /// global variable declarations, for module Mod. Returns the set of
+ /// converted declarations.
+ std::unique_ptr<Ice::VariableDeclarationList>
+ convertGlobalsToIce(Module *Mod);
private:
// Adds the Initializer to the list of initializers for the Global
}
};
-void LLVM2ICEGlobalsConverter::convertGlobalsToIce(
- Module *Mod, Ice::VariableDeclarationList &VariableDeclarations) {
+std::unique_ptr<Ice::VariableDeclarationList>
+LLVM2ICEGlobalsConverter::convertGlobalsToIce(Module *Mod) {
+ std::unique_ptr<Ice::VariableDeclarationList> VariableDeclarations(
+ new Ice::VariableDeclarationList);
for (Module::const_global_iterator I = Mod->global_begin(),
E = Mod->global_end();
I != E; ++I) {
Ice::GlobalDeclaration *Var = getConverter().getGlobalDeclaration(GV);
Ice::VariableDeclaration *VarDecl = cast<Ice::VariableDeclaration>(Var);
- VariableDeclarations.push_back(VarDecl);
+ VariableDeclarations->push_back(VarDecl);
if (!GV->hasInternalLinkage() && GV->hasInitializer()) {
std::string Buffer;
addGlobalInitializer(*VarDecl, Initializer);
}
}
+ return std::move(VariableDeclarations);
}
void LLVM2ICEGlobalsConverter::addGlobalInitializer(
namespace Ice {
void Converter::nameUnnamedGlobalVariables(Module *Mod) {
- const IceString &GlobalPrefix = Flags.getDefaultGlobalPrefix();
+ const IceString &GlobalPrefix = Ctx->getFlags().getDefaultGlobalPrefix();
if (GlobalPrefix.empty())
return;
uint32_t NameIndex = 0;
}
void Converter::nameUnnamedFunctions(Module *Mod) {
- const IceString &FunctionPrefix = Flags.getDefaultFunctionPrefix();
+ const IceString &FunctionPrefix = Ctx->getFlags().getDefaultFunctionPrefix();
if (FunctionPrefix.empty())
return;
uint32_t NameIndex = 0;
}
void Converter::convertGlobals(Module *Mod) {
- LLVM2ICEGlobalsConverter GlobalsConverter(*this);
- VariableDeclarationList VariableDeclarations;
- GlobalsConverter.convertGlobalsToIce(Mod, VariableDeclarations);
- lowerGlobals(VariableDeclarations);
+ lowerGlobals(LLVM2ICEGlobalsConverter(*this).convertGlobalsToIce(Mod));
}
void Converter::convertFunctions() {
Converter &operator=(const Converter &) = delete;
public:
- Converter(llvm::Module *Mod, GlobalContext *Ctx, const Ice::ClFlags &Flags)
- : Translator(Ctx, Flags), Mod(Mod) {}
+ Converter(llvm::Module *Mod, GlobalContext *Ctx)
+ : Translator(Ctx), Mod(Mod) {}
~Converter() {}
: ConstPool(new ConstantPool()), ErrorStatus(), StrDump(OsDump),
StrEmit(OsEmit), VMask(Mask), Arch(Arch), Opt(Opt),
TestPrefix(TestPrefix), Flags(Flags), RNG(""), ObjectWriter(),
- CfgQ(/*MaxSize=*/Flags.getNumTranslationThreads(),
- /*Sequential=*/(Flags.getNumTranslationThreads() == 0)) {
+ OptQ(/*Sequential=*/Flags.isSequential(),
+ /*MaxSize=*/Flags.getNumTranslationThreads()),
+ // EmitQ is allowed unlimited size.
+ EmitQ(/*Sequential=*/Flags.isSequential()) {
// 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
}
void GlobalContext::translateFunctions() {
- while (std::unique_ptr<Cfg> Func = cfgQueueBlockingPop()) {
+ while (std::unique_ptr<Cfg> Func = optQueueBlockingPop()) {
// Install Func in TLS for Cfg-specific container allocators.
Cfg::setCurrentCfg(Func.get());
// Reset per-function stats being accumulated in TLS.
!matchSymbolName(Func->getFunctionName(),
getFlags().getTranslateOnly())) {
Func->dump();
+ Cfg::setCurrentCfg(nullptr);
+ continue; // Func goes out of scope and gets deleted
+ }
+ Func->translate();
+ EmitterWorkItem *Item = nullptr;
+ if (Func->hasError()) {
+ getErrorStatus()->assign(EC_Translation);
+ OstreamLocker L(this);
+ getStrDump() << "ICE translation error: " << Func->getError() << "\n";
+ Item = new EmitterWorkItem(Func->getSequenceNumber());
} else {
- Func->translate();
- if (Func->hasError()) {
- getErrorStatus()->assign(EC_Translation);
- OstreamLocker L(this);
- getStrDump() << "ICE translation error: " << Func->getError() << "\n";
+ if (getFlags().getUseIntegratedAssembler()) {
+ Func->emitIAS();
+ // The Cfg has already emitted into the assembly buffer, so
+ // stats have been fully collected into this thread's TLS.
+ // Dump them before TLS is reset for the next Cfg.
+ dumpStats(Func->getFunctionName());
+ Assembler *Asm = Func->releaseAssembler();
+ // Copy relevant fields into Asm before Func is deleted.
+ Asm->setFunctionName(Func->getFunctionName());
+ Asm->setInternal(Func->getInternal());
+ Item = new EmitterWorkItem(Func->getSequenceNumber(), Asm);
} else {
- if (getFlags().getUseIntegratedAssembler())
- Func->emitIAS();
- else
- Func->emit();
- // TODO(stichnot): actually add to emit queue
+ // The Cfg has not been emitted yet, so stats are not ready
+ // to be dumped.
+ Item = new EmitterWorkItem(Func->getSequenceNumber(), Func.release());
}
- dumpStats(Func->getFunctionName());
}
Cfg::setCurrentCfg(nullptr);
+ assert(Item);
+ emitQueueBlockingPush(Item);
// The Cfg now gets deleted as Func goes out of scope.
}
}
+namespace {
+
+void lowerGlobals(GlobalContext *Ctx,
+ std::unique_ptr<VariableDeclarationList> VariableDeclarations,
+ TargetDataLowering *DataLowering) {
+ TimerMarker T(TimerStack::TT_emitGlobalInitializers, Ctx);
+ const bool DumpGlobalVariables = ALLOW_DUMP && Ctx->getVerbose() &&
+ Ctx->getFlags().getVerboseFocusOn().empty();
+ if (DumpGlobalVariables) {
+ OstreamLocker L(Ctx);
+ Ostream &Stream = Ctx->getStrDump();
+ for (const Ice::VariableDeclaration *Global : *VariableDeclarations) {
+ Global->dump(Ctx, Stream);
+ }
+ }
+ if (Ctx->getFlags().getDisableTranslation())
+ return;
+ DataLowering->lowerGlobals(std::move(VariableDeclarations));
+}
+
+// Ensure Pending is large enough that Pending[Index] is valid.
+void resizePending(std::vector<EmitterWorkItem *> &Pending, uint32_t Index) {
+ if (Index >= Pending.size())
+ Pending.resize(Index + 1);
+}
+
+} // end of anonymous namespace
+
+void GlobalContext::emitItems() {
+ const bool Threaded = !getFlags().isSequential();
+ // Pending is a vector containing the reassembled, ordered list of
+ // work items. When we're ready for the next item, we first check
+ // whether it's in the Pending list. If not, we take an item from
+ // the work queue, and if it's not the item we're waiting for, we
+ // insert it into Pending and repeat. The work item is deleted
+ // after it is processed.
+ std::vector<EmitterWorkItem *> Pending;
+ uint32_t DesiredSequenceNumber = getFirstSequenceNumber();
+ while (true) {
+ resizePending(Pending, DesiredSequenceNumber);
+ // See if Pending contains DesiredSequenceNumber.
+ EmitterWorkItem *RawItem = Pending[DesiredSequenceNumber];
+ if (RawItem == nullptr)
+ RawItem = emitQueueBlockingPop();
+ if (RawItem == nullptr)
+ return;
+ uint32_t ItemSeq = RawItem->getSequenceNumber();
+ if (Threaded && ItemSeq != DesiredSequenceNumber) {
+ resizePending(Pending, ItemSeq);
+ Pending[ItemSeq] = RawItem;
+ continue;
+ }
+
+ std::unique_ptr<EmitterWorkItem> Item(RawItem);
+ ++DesiredSequenceNumber;
+ switch (Item->getKind()) {
+ case EmitterWorkItem::WI_Nop:
+ break;
+ case EmitterWorkItem::WI_GlobalInits: {
+ lowerGlobals(this, Item->getGlobalInits(),
+ TargetDataLowering::createLowering(this).get());
+ } break;
+ case EmitterWorkItem::WI_Asm: {
+ std::unique_ptr<Assembler> Asm = Item->getAsm();
+ Asm->alignFunction();
+ IceString MangledName = mangleName(Asm->getFunctionName());
+ if (getFlags().getUseELFWriter()) {
+ getObjectWriter()->writeFunctionCode(MangledName, Asm->getInternal(),
+ Asm.get());
+ } else {
+ OstreamLocker L(this);
+ Cfg::emitTextHeader(MangledName, this, Asm.get());
+ Asm->emitIASBytes(this);
+ }
+ } break;
+ case EmitterWorkItem::WI_Cfg: {
+ if (!ALLOW_DUMP)
+ llvm::report_fatal_error("WI_Cfg work item created inappropriately");
+ assert(!getFlags().getUseIntegratedAssembler());
+ std::unique_ptr<Cfg> Func = Item->getCfg();
+ // Unfortunately, we have to temporarily install the Cfg in TLS
+ // because Variable::asType() uses the allocator to create the
+ // differently-typed copy.
+ Cfg::setCurrentCfg(Func.get());
+ Func->emit();
+ Cfg::setCurrentCfg(nullptr);
+ dumpStats(Func->getFunctionName());
+ } break;
+ }
+ }
+}
+
// 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
Timers->at(StackID).setName(NewName);
}
-// Note: cfgQueueBlockingPush and cfgQueueBlockingPop use unique_ptr
+// Note: optQueueBlockingPush and optQueueBlockingPop use unique_ptr
// at the interface to take and transfer ownership, but they
// internally store the raw Cfg pointer in the work queue. This
// allows e.g. future queue optimizations such as the use of atomics
// to modify queue elements.
-void GlobalContext::cfgQueueBlockingPush(std::unique_ptr<Cfg> Func) {
- CfgQ.blockingPush(Func.release());
+void GlobalContext::optQueueBlockingPush(std::unique_ptr<Cfg> Func) {
+ assert(Func);
+ OptQ.blockingPush(Func.release());
+ if (getFlags().isSequential())
+ translateFunctions();
+}
+
+std::unique_ptr<Cfg> GlobalContext::optQueueBlockingPop() {
+ return std::unique_ptr<Cfg>(OptQ.blockingPop());
+}
+
+void GlobalContext::emitQueueBlockingPush(EmitterWorkItem *Item) {
+ assert(Item);
+ EmitQ.blockingPush(Item);
+ if (getFlags().isSequential())
+ emitItems();
}
-std::unique_ptr<Cfg> GlobalContext::cfgQueueBlockingPop() {
- return std::unique_ptr<Cfg>(CfgQ.blockingPop());
+EmitterWorkItem *GlobalContext::emitQueueBlockingPop() {
+ return EmitQ.blockingPop();
}
void GlobalContext::dumpStats(const IceString &Name, bool Final) {
#include "IceClFlags.h"
#include "IceIntrinsics.h"
#include "IceRNG.h"
+#include "IceThreading.h"
#include "IceTimerTree.h"
#include "IceTypes.h"
#include "IceUtils.h"
class ClFlags;
class ConstantPool;
+class EmitterWorkItem;
class FuncSigType;
// LockedPtr is a way to provide automatically locked access to some object.
void resetTimer(TimerStackIdT StackID);
void setTimerName(TimerStackIdT StackID, const IceString &NewName);
+ // This is the first work item sequence number that the parser
+ // produces, and correspondingly the first sequence number that the
+ // emitter thread will wait for. Start numbering at 1 to leave room
+ // for a sentinel, in case e.g. we wish to inject items with a
+ // special sequence number that may be executed out of order.
+ static uint32_t getFirstSequenceNumber() { return 1; }
// 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(std::unique_ptr<Cfg> Func);
+ void optQueueBlockingPush(std::unique_ptr<Cfg> 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.
- std::unique_ptr<Cfg> cfgQueueBlockingPop();
+ std::unique_ptr<Cfg> optQueueBlockingPop();
// Notifies that no more work will be added to the work queue.
- void cfgQueueNotifyEnd() { CfgQ.notifyEnd(); }
+ void optQueueNotifyEnd() { OptQ.notifyEnd(); }
+
+ void emitQueueBlockingPush(EmitterWorkItem *Item);
+ EmitterWorkItem *emitQueueBlockingPop();
+ void emitQueueNotifyEnd() { EmitQ.notifyEnd(); }
void startWorkerThreads() {
size_t NumWorkers = getFlags().getNumTranslationThreads();
&GlobalContext::translateFunctionsWrapper, this, WorkerTLS));
}
if (NumWorkers) {
- // TODO(stichnot): start a new thread for the emitter queue worker.
+ ThreadContext *WorkerTLS = new ThreadContext();
+ Timers->initInto(WorkerTLS->Timers);
+ AllThreadContexts.push_back(WorkerTLS);
+ EmitterThreads.push_back(
+ std::thread(&GlobalContext::emitterWrapper, this, WorkerTLS));
}
}
void waitForWorkerThreads() {
- cfgQueueNotifyEnd();
- // TODO(stichnot): call end() on the emitter work queue.
+ optQueueNotifyEnd();
for (std::thread &Worker : TranslationThreads) {
Worker.join();
}
TranslationThreads.clear();
- // TODO(stichnot): join the emitter thread.
+
+ // Only notify the emit queue to end after all the translation
+ // threads have ended.
+ emitQueueNotifyEnd();
+ for (std::thread &Worker : EmitterThreads) {
+ Worker.join();
+ }
+ EmitterThreads.clear();
+
if (ALLOW_DUMP) {
auto Timers = getTimers();
for (ThreadContext *TLS : AllThreadContexts)
// Translate functions from the Cfg queue until the queue is empty.
void translateFunctions();
+ // Emitter thread startup routine.
+ void emitterWrapper(ThreadContext *MyTLS) {
+ ICE_TLS_SET_FIELD(TLS, MyTLS);
+ emitItems();
+ }
+ // Emit functions and global initializers from the emitter queue
+ // until the queue is empty.
+ void emitItems();
+
// 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,
const ClFlags &Flags;
RandomNumberGenerator RNG; // TODO(stichnot): Move into Cfg.
std::unique_ptr<ELFObjectWriter> ObjectWriter;
- BoundedProducerConsumerQueue<Cfg> CfgQ;
+ BoundedProducerConsumerQueue<Cfg> OptQ;
+ BoundedProducerConsumerQueue<EmitterWorkItem> EmitQ;
LockedPtr<ArenaAllocator<>> getAllocator() {
return LockedPtr<ArenaAllocator<>>(&Allocator, &AllocLock);
return LockedPtr<TimerList>(&Timers, &TimerLock);
}
- std::vector<ThreadContext *> AllThreadContexts;
- std::vector<std::thread> TranslationThreads;
+ llvm::SmallVector<ThreadContext *, 128> AllThreadContexts;
+ llvm::SmallVector<std::thread, 128> TranslationThreads;
+ llvm::SmallVector<std::thread, 128> EmitterThreads;
// Each thread has its own TLS pointer which is also held in
// AllThreadContexts.
ICE_TLS_DECLARE_FIELD(ThreadContext *, TLS);
LinearScan.scan(RegMask, RandomizeRegisterAllocation);
}
-TargetDataLowering *TargetDataLowering::createLowering(GlobalContext *Ctx) {
+std::unique_ptr<TargetDataLowering>
+TargetDataLowering::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 TargetDataX8632::create(Ctx);
+ return std::unique_ptr<TargetDataLowering>(TargetDataX8632::create(Ctx));
#if 0
if (Target == Target_X8664)
- return TargetDataX8664::create(Ctx);
+ return std::unique_ptr<TargetDataLowering>(TargetDataX8664::create(Ctx));
if (Target == Target_ARM32)
- return TargetDataARM32::create(Ctx);
+ return std::unique_ptr<TargetDataLowering>(TargetDataARM32::create(Ctx));
if (Target == Target_ARM64)
- return TargetDataARM64::create(Ctx);
+ return std::unique_ptr<TargetDataLowering>(TargetDataARM64::create(Ctx));
#endif
llvm_unreachable("Unsupported target");
return nullptr;
//
//===----------------------------------------------------------------------===//
//
-// This file declares the TargetLowering and LoweringContext
-// classes. TargetLowering is an abstract class used to drive the
-// translation/lowering process. LoweringContext maintains a
-// context for lowering each instruction, offering conveniences such
-// as iterating over non-deleted instructions.
+// This file declares the TargetLowering, LoweringContext, and
+// TargetDataLowering classes. TargetLowering is an abstract class
+// used to drive the translation/lowering process. LoweringContext
+// maintains a context for lowering each instruction, offering
+// conveniences such as iterating over non-deleted instructions.
+// TargetDataLowering is an abstract class used to drive the
+// lowering/emission of global initializers, external global
+// declarations, and internal constant pools.
//
//===----------------------------------------------------------------------===//
TargetDataLowering &operator=(const TargetDataLowering &) = delete;
public:
- static TargetDataLowering *createLowering(GlobalContext *Ctx);
+ static std::unique_ptr<TargetDataLowering> createLowering(GlobalContext *Ctx);
virtual ~TargetDataLowering();
- virtual void lowerGlobal(const VariableDeclaration &Var) const = 0;
- virtual void lowerGlobalsELF(const VariableDeclarationList &Vars) const = 0;
- virtual void lowerConstants(GlobalContext *Ctx) const = 0;
+ virtual void
+ lowerGlobals(std::unique_ptr<VariableDeclarationList> Vars) const = 0;
+ virtual void lowerConstants() const = 0;
protected:
TargetDataLowering(GlobalContext *Ctx) : Ctx(Ctx) {}
Str << "\t.size\t" << MangledName << ", " << Size << "\n";
}
-void
-TargetDataX8632::lowerGlobalsELF(const VariableDeclarationList &Vars) const {
- ELFObjectWriter *Writer = Ctx->getObjectWriter();
- Writer->writeDataSection(Vars, llvm::ELF::R_386_32);
+void TargetDataX8632::lowerGlobals(
+ std::unique_ptr<VariableDeclarationList> Vars) const {
+ if (Ctx->getFlags().getUseELFWriter()) {
+ ELFObjectWriter *Writer = Ctx->getObjectWriter();
+ Writer->writeDataSection(*Vars, llvm::ELF::R_386_32);
+ } else {
+ const IceString &TranslateOnly = Ctx->getFlags().getTranslateOnly();
+ OstreamLocker L(Ctx);
+ for (const VariableDeclaration *Var : *Vars) {
+ if (GlobalContext::matchSymbolName(Var->getName(), TranslateOnly)) {
+ lowerGlobal(*Var);
+ }
+ }
+ }
}
template <typename T> struct PoolTypeConverter {};
}
}
-void TargetDataX8632::lowerConstants(GlobalContext *Ctx) const {
+void TargetDataX8632::lowerConstants() const {
if (Ctx->getFlags().getDisableTranslation())
return;
// No need to emit constants from the int pool since (for x86) they
return new TargetDataX8632(Ctx);
}
- void lowerGlobal(const VariableDeclaration &Var) const final;
- void lowerGlobalsELF(const VariableDeclarationList &Vars) const final;
- void lowerConstants(GlobalContext *Ctx) const final;
+ void lowerGlobals(std::unique_ptr<VariableDeclarationList> Vars) const final;
+ void lowerConstants() const final;
protected:
TargetDataX8632(GlobalContext *Ctx);
private:
+ void lowerGlobal(const VariableDeclaration &Var) const;
~TargetDataX8632() override {}
template <typename T> static void emitConstantPool(GlobalContext *Ctx);
};
--- /dev/null
+//===- subzero/src/IceThreading.cpp - Threading function definitions ------===//
+//
+// The Subzero Code Generator
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines threading-related functions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "IceCfg.h"
+#include "IceDefs.h"
+#include "IceThreading.h"
+
+namespace Ice {
+
+EmitterWorkItem::EmitterWorkItem(uint32_t Seq)
+ : Sequence(Seq), Kind(WI_Nop), GlobalInits(nullptr), Function(nullptr),
+ RawFunc(nullptr) {}
+EmitterWorkItem::EmitterWorkItem(uint32_t Seq, VariableDeclarationList *D)
+ : Sequence(Seq), Kind(WI_GlobalInits), GlobalInits(D), Function(nullptr),
+ RawFunc(nullptr) {}
+EmitterWorkItem::EmitterWorkItem(uint32_t Seq, Assembler *A)
+ : Sequence(Seq), Kind(WI_Asm), GlobalInits(nullptr), Function(A),
+ RawFunc(nullptr) {}
+EmitterWorkItem::EmitterWorkItem(uint32_t Seq, Cfg *F)
+ : Sequence(Seq), Kind(WI_Cfg), GlobalInits(nullptr), Function(nullptr),
+ RawFunc(F) {}
+
+std::unique_ptr<VariableDeclarationList> EmitterWorkItem::getGlobalInits() {
+ assert(getKind() == WI_GlobalInits);
+ return std::move(GlobalInits);
+}
+
+std::unique_ptr<Assembler> EmitterWorkItem::getAsm() {
+ assert(getKind() == WI_Asm);
+ return std::move(Function);
+}
+
+std::unique_ptr<Cfg> EmitterWorkItem::getCfg() {
+ assert(getKind() == WI_Cfg);
+ return std::move(RawFunc);
+}
+
+} // end of namespace Ice
--- /dev/null
+//===- subzero/src/IceThreading.h - Threading functions ---------*- C++ -*-===//
+//
+// The Subzero Code Generator
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file declares threading-related functions.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SUBZERO_SRC_ICETHREADING_H
+#define SUBZERO_SRC_ICETHREADING_H
+
+#include <condition_variable>
+#include <mutex>
+
+#include "IceDefs.h"
+
+namespace Ice {
+
+// 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(bool Sequential, size_t MaxSize = MaxStaticSize)
+ : 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];
+ }
+};
+
+// EmitterWorkItem is a simple wrapper around a pointer that
+// represents a work item to be emitted, i.e. a function or a set of
+// global declarations and initializers, and it includes a sequence
+// number so that work items can be emitted in a particular order for
+// deterministic output. It acts like an interface class, but instead
+// of making the classes of interest inherit from EmitterWorkItem, it
+// wraps pointers to these classes. Some space is wasted compared to
+// storing the pointers in a union, but not too much due to the work
+// granularity.
+class EmitterWorkItem {
+ EmitterWorkItem() = delete;
+ EmitterWorkItem(const EmitterWorkItem &) = delete;
+ EmitterWorkItem &operator=(const EmitterWorkItem &) = delete;
+
+public:
+ // ItemKind can be one of the following:
+ //
+ // WI_Nop: No actual work. This is a placeholder to maintain
+ // sequence numbers in case there is a translation error.
+ //
+ // WI_GlobalInits: A list of global declarations and initializers.
+ //
+ // WI_Asm: A function that has already had emitIAS() called on it.
+ // The work is transferred via the Assembler buffer, and the
+ // originating Cfg has been deleted (to recover lots of memory).
+ //
+ // WI_Cfg: A Cfg that has not yet had emit() or emitIAS() called on
+ // it. This is only used as a debugging configuration when we want
+ // to emit "readable" assembly code, possibly annotated with
+ // liveness and other information only available in the Cfg and not
+ // in the Assembler buffer.
+ enum ItemKind { WI_Nop, WI_GlobalInits, WI_Asm, WI_Cfg };
+ // Constructor for a WI_Nop work item.
+ explicit EmitterWorkItem(uint32_t Seq);
+ // Constructor for a WI_GlobalInits work item.
+ EmitterWorkItem(uint32_t Seq, VariableDeclarationList *D);
+ // Constructor for a WI_Asm work item.
+ EmitterWorkItem(uint32_t Seq, Assembler *A);
+ // Constructor for a WI_Cfg work item.
+ EmitterWorkItem(uint32_t Seq, Cfg *F);
+ uint32_t getSequenceNumber() const { return Sequence; }
+ ItemKind getKind() const { return Kind; }
+ std::unique_ptr<VariableDeclarationList> getGlobalInits();
+ std::unique_ptr<Assembler> getAsm();
+ std::unique_ptr<Cfg> getCfg();
+
+private:
+ const uint32_t Sequence;
+ const ItemKind Kind;
+ std::unique_ptr<VariableDeclarationList> GlobalInits;
+ std::unique_ptr<Assembler> Function;
+ std::unique_ptr<Cfg> RawFunc;
+};
+
+} // end of namespace Ice
+
+#endif // SUBZERO_SRC_ICETHREADING_H
using namespace Ice;
-Translator::Translator(GlobalContext *Ctx, const ClFlags &Flags)
- : Ctx(Ctx), Flags(Flags),
- DataLowering(TargetDataLowering::createLowering(Ctx)), ErrorStatus() {}
+Translator::Translator(GlobalContext *Ctx)
+ : Ctx(Ctx), NextSequenceNumber(GlobalContext::getFirstSequenceNumber()),
+ ErrorStatus() {}
Translator::~Translator() {}
}
void Translator::translateFcn(std::unique_ptr<Cfg> Func) {
- Ctx->cfgQueueBlockingPush(std::move(Func));
- if (Ctx->getFlags().getNumTranslationThreads() == 0) {
- Ctx->translateFunctions();
- }
+ Ctx->optQueueBlockingPush(std::move(Func));
}
void Translator::emitConstants() {
if (!getErrorStatus())
- DataLowering->lowerConstants(Ctx);
+ TargetDataLowering::createLowering(Ctx)->lowerConstants();
}
void Translator::transferErrorCode() const {
Ctx->getErrorStatus()->assign(getErrorStatus().value());
}
-void
-Translator::lowerGlobals(const VariableDeclarationList &VariableDeclarations) {
- TimerMarker T(TimerStack::TT_emitGlobalInitializers, Ctx);
- bool DisableTranslation = Ctx->getFlags().getDisableTranslation();
- const bool DumpGlobalVariables = ALLOW_DUMP && Ctx->getVerbose() &&
- Ctx->getFlags().getVerboseFocusOn().empty();
- if (Ctx->getFlags().getUseELFWriter()) {
- // Dump all globals if requested, but don't interleave w/ emission.
- if (DumpGlobalVariables) {
- OstreamLocker L(Ctx);
- Ostream &Stream = Ctx->getStrDump();
- for (const Ice::VariableDeclaration *Global : VariableDeclarations) {
- Global->dump(getContext(), Stream);
- }
- }
- DataLowering->lowerGlobalsELF(VariableDeclarations);
- } else {
- const IceString &TranslateOnly = Ctx->getFlags().getTranslateOnly();
- OstreamLocker L(Ctx);
- Ostream &Stream = Ctx->getStrDump();
- for (const Ice::VariableDeclaration *Global : VariableDeclarations) {
- // Interleave dump output w/ emit output.
- if (DumpGlobalVariables)
- Global->dump(getContext(), Stream);
- if (!DisableTranslation &&
- GlobalContext::matchSymbolName(Global->getName(), TranslateOnly))
- DataLowering->lowerGlobal(*Global);
- }
- }
+void Translator::lowerGlobals(
+ std::unique_ptr<VariableDeclarationList> VariableDeclarations) {
+ EmitterWorkItem *Item = new EmitterWorkItem(getNextSequenceNumber(),
+ VariableDeclarations.release());
+ Ctx->emitQueueBlockingPush(Item);
}
Translator &operator=(const Translator &) = delete;
public:
- Translator(GlobalContext *Ctx, const ClFlags &Flags);
+ Translator(GlobalContext *Ctx);
~Translator();
const ErrorCode &getErrorStatus() const { return ErrorStatus; }
GlobalContext *getContext() const { return Ctx; }
- const ClFlags &getFlags() const { return Flags; }
+ const ClFlags &getFlags() const { return Ctx->getFlags(); }
/// Translates the constructed ICE function Fcn to machine code.
/// Takes ownership of Func.
/// Lowers the given list of global addresses to target. Generates
/// list of corresponding variable declarations.
- void lowerGlobals(const VariableDeclarationList &VariableDeclarations);
+ void
+ lowerGlobals(std::unique_ptr<VariableDeclarationList> VariableDeclarations);
/// Creates a name using the given prefix and corresponding index.
std::string createUnnamedName(const IceString &Prefix, SizeT Index);
bool checkIfUnnamedNameSafe(const IceString &Name, const char *Kind,
const IceString &Prefix);
+ uint32_t getNextSequenceNumber() { return NextSequenceNumber++; }
+
protected:
GlobalContext *Ctx;
- const ClFlags &Flags;
- std::unique_ptr<TargetDataLowering> DataLowering;
+ uint32_t NextSequenceNumber;
// Exit status of the translation. False is successful. True otherwise.
ErrorCode ErrorStatus;
};
#ifndef SUBZERO_SRC_ICEUTILS_H
#define SUBZERO_SRC_ICEUTILS_H
-
#include <climits>
-#include <condition_variable>
namespace Ice {
}
};
-// 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
}
if (!isIRGenerationDisabled())
- Func = Ice::Cfg::create(getTranslator().getContext());
+ Func = Ice::Cfg::create(getTranslator().getContext(),
+ getTranslator().getNextSequenceNumber());
Ice::Cfg::setCurrentCfg(Func.get());
// TODO(kschimpf) Clean up API to add a function signature to
// translation of all remaining functions. This allows successive
// parsing errors to be reported, without adding extra checks to
// the translator for such parsing errors.
- if (Context->getNumErrors() == 0) {
+ if (Context->getNumErrors() == 0 && Func) {
getTranslator().translateFcn(std::move(Func));
// The translator now has ownership of Func.
} else {
if (!GlobalDeclarationNamesAndInitializersInstalled) {
Context->installGlobalNames();
Context->createValueIDs();
- std::unique_ptr<Ice::VariableDeclarationList> DeclsPtr =
- Context->getGlobalVariables();
- const Ice::VariableDeclarationList &Decls = *DeclsPtr;
- getTranslator().lowerGlobals(Decls);
+ getTranslator().lowerGlobals(Context->getGlobalVariables());
GlobalDeclarationNamesAndInitializersInstalled = true;
}
}
PNaClTranslator &operator=(const PNaClTranslator &) = delete;
public:
- PNaClTranslator(GlobalContext *Ctx, const ClFlags &Flags)
- : Translator(Ctx, Flags) {}
+ PNaClTranslator(GlobalContext *Ctx) : Translator(Ctx) {}
// Reads the PNaCl bitcode file and translates to ICE, which is then
// converted to machine code. Sets ErrorStatus to 1 if any errors
Assembler &operator=(const Assembler &) = delete;
public:
- Assembler() : buffer_(*this) {}
+ Assembler() : FunctionName(""), IsInternal(false), buffer_(*this) {}
virtual ~Assembler() {}
// Allocate a chunk of bytes using the per-Assembler allocator.
}
void emitIASBytes(GlobalContext *Ctx) const;
+ bool getInternal() const { return IsInternal; }
+ void setInternal(bool Internal) { IsInternal = Internal; }
+ const IceString &getFunctionName() { return FunctionName; }
+ void setFunctionName(const IceString &NewName) { FunctionName = NewName; }
private:
ArenaAllocator<32 * 1024> Allocator;
+ // FunctionName and IsInternal are transferred from the original Cfg
+ // object, since the Cfg object may be deleted by the time the
+ // assembler buffer is emitted.
+ IceString FunctionName;
+ bool IsInternal;
protected:
AssemblerBuffer buffer_;
std::unique_ptr<Ice::Translator> Translator;
if (BuildOnRead) {
std::unique_ptr<Ice::PNaClTranslator> PTranslator(
- new Ice::PNaClTranslator(&Ctx, Flags));
+ new Ice::PNaClTranslator(&Ctx));
PTranslator->translate(IRFilename);
Translator.reset(PTranslator.release());
} else if (ALLOW_LLVM_IR) {
return GetReturnValue(Ice::EC_Bitcode);
}
- std::unique_ptr<Ice::Converter> Converter(
- new Ice::Converter(Mod, &Ctx, Flags));
+ std::unique_ptr<Ice::Converter> Converter(new Ice::Converter(Mod, &Ctx));
Converter->convertToIce();
Translator.reset(Converter.release());
} else {
; REQUIRES: allow_dump
-; RUN: %p2i -i %s --args --verbose inst -ias=0 | FileCheck %s
+; RUN: %p2i -i %s --args --verbose inst -threads=0 -ias=0 | FileCheck %s
define i32 @Add(i32 %a, i32 %b) {
; CHECK: define i32 @Add
; REQUIRES: allow_dump
-; RUN: %p2i -i %s --args -O2 --verbose inst | FileCheck %s
-; RUN: %p2i -i %s --args -Om1 --verbose inst | FileCheck %s
+; RUN: %p2i -i %s --args -O2 --verbose inst -threads=0 | FileCheck %s
+; RUN: %p2i -i %s --args -Om1 --verbose inst -threads=0 | FileCheck %s
define i32 @simple_cond_branch(i32 %foo, i32 %bar) {
entry:
; REQUIRES: allow_dump
; Test that we handle it in the ICE converter.
-; RUN: %lc2i -i %s --args -verbose inst | %iflc FileCheck %s
+; RUN: %lc2i -i %s --args -verbose inst -threads=0 \
+; RUN: | %iflc FileCheck %s
+; RUN: %lc2i -i %s --args -verbose inst -threads=0 \
+; RUN: | %iflc FileCheck --check-prefix=DUMP %s
; Test that we handle it using Subzero's bitcode reader.
-; RUN: %p2i -i %s --args -verbose inst | FileCheck %s
+; RUN: %p2i -i %s --args -verbose inst -threads=0 \
+; RUN: | FileCheck %s
+; RUN: %p2i -i %s --args -verbose inst -threads=0 \
+; RUN: | FileCheck --check-prefix=DUMP %s
@bytes = internal global [7 x i8] c"abcdefg"
-; CHECK: @bytes = internal global [7 x i8] c"abcdefg"
+; DUMP: @bytes = internal global [7 x i8] c"abcdefg"
; CHECK: .type bytes,@object
; CHECK: .section .data,"aw",@progbits
; CHECK:bytes:
; CHECK: .size bytes, 7
@const_bytes = internal constant [7 x i8] c"abcdefg"
-; CHECK: @const_bytes = internal constant [7 x i8] c"abcdefg"
+; DUMP: @const_bytes = internal constant [7 x i8] c"abcdefg"
; CHECK: .type const_bytes,@object
; CHECK: .section .rodata,"a",@progbits
; CHECK:const_bytes:
; CHECK: .size const_bytes, 7
@ptr_to_ptr = internal global i32 ptrtoint (i32* @ptr to i32)
-; CHECK: @ptr_to_ptr = internal global i32 ptrtoint (i32* @ptr to i32)
+; DUMP: @ptr_to_ptr = internal global i32 ptrtoint (i32* @ptr to i32)
; CHECK: .type ptr_to_ptr,@object
; CHECK: .section .data,"aw",@progbits
; CHECK:ptr_to_ptr:
; CHECK: .size ptr_to_ptr, 4
@const_ptr_to_ptr = internal constant i32 ptrtoint (i32* @ptr to i32)
-; CHECK: @const_ptr_to_ptr = internal constant i32 ptrtoint (i32* @ptr to i32)
+; DUMP: @const_ptr_to_ptr = internal constant i32 ptrtoint (i32* @ptr to i32)
; CHECK: .type const_ptr_to_ptr,@object
; CHECK: .section .rodata,"a",@progbits
; CHECK:const_ptr_to_ptr:
; CHECK: .size const_ptr_to_ptr, 4
@ptr_to_func = internal global i32 ptrtoint (void ()* @func to i32)
-; CHECK: @ptr_to_func = internal global i32 ptrtoint (void ()* @func to i32)
+; DUMP: @ptr_to_func = internal global i32 ptrtoint (void ()* @func to i32)
; CHECK: .type ptr_to_func,@object
; CHECK: .section .data,"aw",@progbits
; CHECK:ptr_to_func:
; CHECK: .size ptr_to_func, 4
@const_ptr_to_func = internal constant i32 ptrtoint (void ()* @func to i32)
-; CHECK: @const_ptr_to_func = internal constant i32 ptrtoint (void ()* @func to i32)
+; DUMP: @const_ptr_to_func = internal constant i32 ptrtoint (void ()* @func to i32)
; CHECK: .type const_ptr_to_func,@object
; CHECK: .section .rodata,"a",@progbits
; CHECK:const_ptr_to_func:
; CHECK: .size const_ptr_to_func, 4
@compound = internal global <{ [3 x i8], i32 }> <{ [3 x i8] c"foo", i32 ptrtoint (void ()* @func to i32) }>
-; CHECK: @compound = internal global <{ [3 x i8], i32 }> <{ [3 x i8] c"foo", i32 ptrtoint (void ()* @func to i32) }>
+; DUMP: @compound = internal global <{ [3 x i8], i32 }> <{ [3 x i8] c"foo", i32 ptrtoint (void ()* @func to i32) }>
; CHECK: .type compound,@object
; CHECK: .section .data,"aw",@progbits
; CHECK:compound:
; CHECK: .size compound, 7
@const_compound = internal constant <{ [3 x i8], i32 }> <{ [3 x i8] c"foo", i32 ptrtoint (void ()* @func to i32) }>
-; CHECK: @const_compound = internal constant <{ [3 x i8], i32 }> <{ [3 x i8] c"foo", i32 ptrtoint (void ()* @func to i32) }>
+; DUMP: @const_compound = internal constant <{ [3 x i8], i32 }> <{ [3 x i8] c"foo", i32 ptrtoint (void ()* @func to i32) }>
; CHECK: .type const_compound,@object
; CHECK: .section .rodata,"a",@progbits
; CHECK:const_compound:
; CHECK: .size const_compound, 7
@ptr = internal global i32 ptrtoint ([7 x i8]* @bytes to i32)
-; CHECK: @ptr = internal global i32 ptrtoint ([7 x i8]* @bytes to i32)
+; DUMP: @ptr = internal global i32 ptrtoint ([7 x i8]* @bytes to i32)
; CHECK: .type ptr,@object
; CHECK: .section .data,"aw",@progbits
; CHECK:ptr:
; CHECK: .size ptr, 4
@const_ptr = internal constant i32 ptrtoint ([7 x i8]* @bytes to i32)
-; CHECK: @const_ptr = internal constant i32 ptrtoint ([7 x i8]* @bytes to i32)
+; DUMP: @const_ptr = internal constant i32 ptrtoint ([7 x i8]* @bytes to i32)
; CHECK: .type const_ptr,@object
; CHECK: .section .rodata,"a",@progbits
; CHECK:const_ptr:
; CHECK: .size const_ptr, 4
@addend_ptr = internal global i32 add (i32 ptrtoint (i32* @ptr to i32), i32 1)
-; CHECK: @addend_ptr = internal global i32 add (i32 ptrtoint (i32* @ptr to i32), i32 1)
+; DUMP: @addend_ptr = internal global i32 add (i32 ptrtoint (i32* @ptr to i32), i32 1)
; CHECK: .type addend_ptr,@object
; CHECK: .section .data,"aw",@progbits
; CHECK:addend_ptr:
; CHECK: .size addend_ptr, 4
@const_addend_ptr = internal constant i32 add (i32 ptrtoint (i32* @ptr to i32), i32 1)
-; CHECK: @const_addend_ptr = internal constant i32 add (i32 ptrtoint (i32* @ptr to i32), i32 1)
+; DUMP: @const_addend_ptr = internal constant i32 add (i32 ptrtoint (i32* @ptr to i32), i32 1)
; CHECK: .type const_addend_ptr,@object
; CHECK: .section .rodata,"a",@progbits
; CHECK:const_addend_ptr:
; CHECK: .size const_addend_ptr, 4
@addend_negative = internal global i32 add (i32 ptrtoint (i32* @ptr to i32), i32 -1)
-; CHECK: @addend_negative = internal global i32 add (i32 ptrtoint (i32* @ptr to i32), i32 -1)
+; DUMP: @addend_negative = internal global i32 add (i32 ptrtoint (i32* @ptr to i32), i32 -1)
; CHECK: .type addend_negative,@object
; CHECK: .section .data,"aw",@progbits
; CHECK:addend_negative:
; CHECK: .size addend_negative, 4
@const_addend_negative = internal constant i32 add (i32 ptrtoint (i32* @ptr to i32), i32 -1)
-; CHECK: @const_addend_negative = internal constant i32 add (i32 ptrtoint (i32* @ptr to i32), i32 -1)
+; DUMP: @const_addend_negative = internal constant i32 add (i32 ptrtoint (i32* @ptr to i32), i32 -1)
; CHECK: .type const_addend_negative,@object
; CHECK: .section .rodata,"a",@progbits
; CHECK:const_addend_negative:
; CHECK: .size const_addend_negative, 4
@addend_array1 = internal global i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 1)
-; CHECK: @addend_array1 = internal global i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 1)
+; DUMP: @addend_array1 = internal global i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 1)
; CHECK: .type addend_array1,@object
; CHECK: .section .data,"aw",@progbits
; CHECK:addend_array1:
; CHECK: .size addend_array1, 4
@const_addend_array1 = internal constant i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 1)
-; CHECK: @const_addend_array1 = internal constant i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 1)
+; DUMP: @const_addend_array1 = internal constant i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 1)
; CHECK: .type const_addend_array1,@object
; CHECK: .section .rodata,"a",@progbits
; CHECK:const_addend_array1:
; CHECK: .size const_addend_array1, 4
@addend_array2 = internal global i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 7)
-; CHECK: @addend_array2 = internal global i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 7)
+; DUMP: @addend_array2 = internal global i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 7)
; CHECK: .type addend_array2,@object
; CHECK: .section .data,"aw",@progbits
; CHECK:addend_array2:
; CHECK: .size addend_array2, 4
@const_addend_array2 = internal constant i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 7)
-; CHECK: @const_addend_array2 = internal constant i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 7)
+; DUMP: @const_addend_array2 = internal constant i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 7)
; CHECK: .type const_addend_array2,@object
; CHECK: .section .rodata,"a",@progbits
; CHECK:const_addend_array2:
; CHECK: .size const_addend_array2, 4
@addend_array3 = internal global i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 9)
-; CHECK: @addend_array3 = internal global i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 9)
+; DUMP: @addend_array3 = internal global i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 9)
; CHECK: .type addend_array3,@object
; CHECK: .section .data,"aw",@progbits
; CHECK:addend_array3:
; CHECK: .size addend_array3, 4
@const_addend_array3 = internal constant i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 9)
-; CHECK: @const_addend_array3 = internal constant i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 9)
+; DUMP: @const_addend_array3 = internal constant i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 9)
; CHECK: .type const_addend_array3,@object
; CHECK: .section .rodata,"a",@progbits
; CHECK:const_addend_array3:
; CHECK: .size const_addend_array3, 4
@addend_struct1 = internal global i32 add (i32 ptrtoint (<{ [3 x i8], i32 }>* @compound to i32), i32 1)
-; CHECK: @addend_struct1 = internal global i32 add (i32 ptrtoint (<{ [3 x i8], i32 }>* @compound to i32), i32 1)
+; DUMP: @addend_struct1 = internal global i32 add (i32 ptrtoint (<{ [3 x i8], i32 }>* @compound to i32), i32 1)
; CHECK: .type addend_struct1,@object
; CHECK: .section .data,"aw",@progbits
; CHECK:addend_struct1:
; CHECK: .size addend_struct1, 4
@const_addend_struct1 = internal constant i32 add (i32 ptrtoint (<{ [3 x i8], i32 }>* @compound to i32), i32 1)
-; CHECK: @const_addend_struct1 = internal constant i32 add (i32 ptrtoint (<{ [3 x i8], i32 }>* @compound to i32), i32 1)
+; DUMP: @const_addend_struct1 = internal constant i32 add (i32 ptrtoint (<{ [3 x i8], i32 }>* @compound to i32), i32 1)
; CHECK: .type const_addend_struct1,@object
; CHECK: .section .rodata,"a",@progbits
; CHECK:const_addend_struct1:
; CHECK: .size const_addend_struct1, 4
@addend_struct2 = internal global i32 add (i32 ptrtoint (<{ [3 x i8], i32 }>* @compound to i32), i32 4)
-; CHECK: @addend_struct2 = internal global i32 add (i32 ptrtoint (<{ [3 x i8], i32 }>* @compound to i32), i32 4)
+; DUMP: @addend_struct2 = internal global i32 add (i32 ptrtoint (<{ [3 x i8], i32 }>* @compound to i32), i32 4)
; CHECK: .type addend_struct2,@object
; CHECK: .section .data,"aw",@progbits
; CHECK:addend_struct2:
; CHECK: .size addend_struct2, 4
@const_addend_struct2 = internal constant i32 add (i32 ptrtoint (<{ [3 x i8], i32 }>* @compound to i32), i32 4)
-; CHECK: @const_addend_struct2 = internal constant i32 add (i32 ptrtoint (<{ [3 x i8], i32 }>* @compound to i32), i32 4)
+; DUMP: @const_addend_struct2 = internal constant i32 add (i32 ptrtoint (<{ [3 x i8], i32 }>* @compound to i32), i32 4)
; CHECK: .type const_addend_struct2,@object
; CHECK: .section .rodata,"a",@progbits
; CHECK:const_addend_struct2:
; CHECK: .size const_addend_struct2, 4
@ptr_to_func_align = internal global i32 ptrtoint (void ()* @func to i32), align 8
-; CHECK: @ptr_to_func_align = internal global i32 ptrtoint (void ()* @func to i32), align 8
+; DUMP: @ptr_to_func_align = internal global i32 ptrtoint (void ()* @func to i32), align 8
; CHECK: .type ptr_to_func_align,@object
; CHECK: .section .data,"aw",@progbits
; CHECK: .align 8
; CHECK: .size ptr_to_func_align, 4
@const_ptr_to_func_align = internal constant i32 ptrtoint (void ()* @func to i32), align 8
-; CHECK: @const_ptr_to_func_align = internal constant i32 ptrtoint (void ()* @func to i32), align 8
+; DUMP: @const_ptr_to_func_align = internal constant i32 ptrtoint (void ()* @func to i32), align 8
; CHECK: .type const_ptr_to_func_align,@object
; CHECK: .section .rodata,"a",@progbits
; CHECK: .align 8
; CHECK: .size const_ptr_to_func_align, 4
@char = internal constant [1 x i8] c"0"
-; CHECK: @char = internal constant [1 x i8] c"0"
+; DUMP: @char = internal constant [1 x i8] c"0"
; CHECK: .type char,@object
; CHECK: .section .rodata,"a",@progbits
; CHECK:char:
; CHECK: .size char, 1
@short = internal constant [2 x i8] zeroinitializer
-; CHECK: @short = internal constant [2 x i8] zeroinitializer
+; DUMP: @short = internal constant [2 x i8] zeroinitializer
; CHECK: .type short,@object
; CHECK: .section .rodata,"a",@progbits
; CHECK:short:
; REQUIRES: allow_dump
-; RUN: %p2i -i %s --args --verbose inst | FileCheck %s
+; RUN: %p2i -i %s --args --verbose inst -threads=0 | FileCheck %s
define void @load_i64(i32 %addr_arg) {
entry:
; Test that some errors trigger when the usage of NaCl atomic
; intrinsics does not match the required ABI.
-; RUN: %p2i -i %s --args --verbose none --exit-success 2>&1 | FileCheck %s
+; RUN: %p2i -i %s --args --verbose none --exit-success -threads=0 2>&1 \
+; RUN: | FileCheck %s
declare i8 @llvm.nacl.atomic.load.i8(i8*, i32)
declare i16 @llvm.nacl.atomic.load.i16(i16*, i32)
; REQUIRES: allow_dump
-; RUN: %p2i -i %s --args --verbose inst | FileCheck %s
+; RUN: %p2i -i %s --args --verbose inst -threads=0 | FileCheck %s
define void @store_i64(i32 %addr_arg) {
entry:
; TODO(kschimpf) Find out why lc2i is needed.
; REQUIRES: allow_llvm_ir_as_input
-; RUN: %lc2i -i %s --args --verbose inst | FileCheck %s
+; RUN: %lc2i -i %s --args --verbose inst -threads=0 | FileCheck %s
define internal i32 @compute_important_function(i32 %v1, i32 %v2) {
entry:
Ice::ClFlags Flags;
Flags.setAllowErrorRecovery(true);
Flags.setGenerateUnitTestMessages(true);
+ Flags.setUseIntegratedAssembler(true); // for the MINIMAL build
Ice::GlobalContext Ctx(DumpStream, DumpStream, nullptr,
Ice::IceV_Instructions, Ice::Target_X8632, Ice::Opt_m1,
"", Flags);
- Ice::PNaClTranslator Translator(&Ctx, Flags);
+ Ice::PNaClTranslator Translator(&Ctx);
Translator.translateBuffer(TestName, MungedInput.get());
cleanupTest();