Commit e8457a26 by Karl Schimpf

Allow Subzero to parse function blocks in parallel.

This CL modifies the code so that we can do sequential and parallel parsing of function blocks in bitcode files, based on a command line argument. The command line argument was added because during testing, I had one compilation failure (transient), and do not know the cause. Hence, I was reluctant to install this CL without a command-line flag. To test the new parallel parser, the easiest solution is to edit IceClFlags.def and set the default value of ParseParallel to true. This code also fixes up unit parsing tests, as well as one parsing test. The cause of these problems was the implicit assumption that function blocks are parsed sequentially, which no longer applies when function blocks are parsed in parallel. To fix this, the "threads=0" command line argument was added. It also added the starting up of worker threads, since parsing of function blocks will happen in the translation thread if parallel parsing is turned on. The OptQ queue was modified to contain OptWorkerItem instances with a single virtual to get the parsed code. This allows the IceConverter to continue to work, by simply passing the generated Cfg as a work item. BUG= https://bugs.chromium.org/p/nativeclient/issues/detail?id=4363 R=jpp@chromium.org, stichnot@chromium.org Review URL: https://codereview.chromium.org/1834473002 .
parent bbd449d2
...@@ -94,6 +94,9 @@ def main(): ...@@ -94,6 +94,9 @@ def main():
help='Input is textual bitcode (not .ll)') help='Input is textual bitcode (not .ll)')
argparser.add_argument('--expect-fail', required=False, action='store_true', argparser.add_argument('--expect-fail', required=False, action='store_true',
help='Negate success of run by using LLVM not') help='Negate success of run by using LLVM not')
argparser.add_argument('--allow-pnacl-reader-error-recovery',
action='store_true',
help='Continue parsing after first error')
argparser.add_argument('--args', '-a', nargs=argparse.REMAINDER, argparser.add_argument('--args', '-a', nargs=argparse.REMAINDER,
default=[], default=[],
help='Remaining arguments are passed to pnacl-sz') help='Remaining arguments are passed to pnacl-sz')
...@@ -118,7 +121,9 @@ def main(): ...@@ -118,7 +121,9 @@ def main():
raise RuntimeError("Can't specify both '--tbc' and '--llvm'") raise RuntimeError("Can't specify both '--tbc' and '--llvm'")
if args.forceasm: if args.forceasm:
if args.filetype == 'asm': if args.expect_fail:
args.forceasm = False
elif args.filetype == 'asm':
pass pass
elif args.filetype == 'iasm': elif args.filetype == 'iasm':
# TODO(sehr) implement forceasm for iasm. # TODO(sehr) implement forceasm for iasm.
...@@ -148,6 +153,8 @@ def main(): ...@@ -148,6 +153,8 @@ def main():
# single-threaded translation because dump output does not get # single-threaded translation because dump output does not get
# reassembled into order. # reassembled into order.
cmd += ['-verbose', 'inst,global_init', '-notranslate', '-threads=0'] cmd += ['-verbose', 'inst,global_init', '-notranslate', '-threads=0']
elif args.allow_pnacl_reader_error_recovery:
cmd += ['-allow-pnacl-reader-error-recovery', '-threads=0']
if not args.llvm_source: if not args.llvm_source:
cmd += ['--bitcode-format=pnacl'] cmd += ['--bitcode-format=pnacl']
if not args.no_local_syms: if not args.no_local_syms:
......
...@@ -196,6 +196,9 @@ struct dev_list_flag {}; ...@@ -196,6 +196,9 @@ struct dev_list_flag {};
"Low-level integrated assembly ('.s') file"), \ "Low-level integrated assembly ('.s') file"), \
clEnumValEnd)) \ clEnumValEnd)) \
\ \
X(ParseParallel, bool, dev_opt_flag, "parse-parallel", \
cl::desc("Parse function blocks in parallel"), cl::init(true)) \
\
X(RandomizeAndPoolImmediatesOption, Ice::RandomizeAndPoolImmediatesEnum, \ X(RandomizeAndPoolImmediatesOption, Ice::RandomizeAndPoolImmediatesEnum, \
dev_opt_flag, "randomize-pool-immediates", \ dev_opt_flag, "randomize-pool-immediates", \
cl::desc("Randomize or pooling the representation of immediates"), \ cl::desc("Randomize or pooling the representation of immediates"), \
......
...@@ -62,8 +62,6 @@ void Compiler::run(const Ice::ClFlags &Flags, GlobalContext &Ctx, ...@@ -62,8 +62,6 @@ void Compiler::run(const Ice::ClFlags &Flags, GlobalContext &Ctx,
// allows only --filetype=obj. Check here to avoid cryptic error messages // allows only --filetype=obj. Check here to avoid cryptic error messages
// downstream. // downstream.
if (!BuildDefs::dump() && Ctx.getFlags().getOutFileType() != FT_Elf) { if (!BuildDefs::dump() && Ctx.getFlags().getOutFileType() != FT_Elf) {
// TODO(stichnot): Access the actual command-line argument via
// llvm::Option.ArgStr and .ValueStr .
Ctx.getStrError() Ctx.getStrError()
<< "Error: only --filetype=obj is supported in this build.\n"; << "Error: only --filetype=obj is supported in this build.\n";
Ctx.getErrorStatus()->assign(EC_Args); Ctx.getErrorStatus()->assign(EC_Args);
...@@ -89,6 +87,7 @@ void Compiler::run(const Ice::ClFlags &Flags, GlobalContext &Ctx, ...@@ -89,6 +87,7 @@ void Compiler::run(const Ice::ClFlags &Flags, GlobalContext &Ctx,
Ctx.getStrError() Ctx.getStrError()
<< "non BuildOnRead is not supported w/ PNACL_BROWSER_TRANSLATOR\n"; << "non BuildOnRead is not supported w/ PNACL_BROWSER_TRANSLATOR\n";
Ctx.getErrorStatus()->assign(EC_Args); Ctx.getErrorStatus()->assign(EC_Args);
Ctx.waitForWorkerThreads();
return; return;
} }
// Globals must be kept alive after lowering when converting from LLVM to // Globals must be kept alive after lowering when converting from LLVM to
...@@ -107,6 +106,7 @@ void Compiler::run(const Ice::ClFlags &Flags, GlobalContext &Ctx, ...@@ -107,6 +106,7 @@ void Compiler::run(const Ice::ClFlags &Flags, GlobalContext &Ctx,
if (!Mod) { if (!Mod) {
Err.print(Flags.getAppName().c_str(), llvm::errs()); Err.print(Flags.getAppName().c_str(), llvm::errs());
Ctx.getErrorStatus()->assign(EC_Bitcode); Ctx.getErrorStatus()->assign(EC_Bitcode);
Ctx.waitForWorkerThreads();
return; return;
} }
...@@ -117,6 +117,7 @@ void Compiler::run(const Ice::ClFlags &Flags, GlobalContext &Ctx, ...@@ -117,6 +117,7 @@ void Compiler::run(const Ice::ClFlags &Flags, GlobalContext &Ctx,
Ctx.getStrError() << "Error: Build doesn't allow LLVM IR, " Ctx.getStrError() << "Error: Build doesn't allow LLVM IR, "
<< "--build-on-read=0 not allowed\n"; << "--build-on-read=0 not allowed\n";
Ctx.getErrorStatus()->assign(EC_Args); Ctx.getErrorStatus()->assign(EC_Args);
Ctx.waitForWorkerThreads();
return; return;
} }
......
...@@ -213,6 +213,37 @@ public: ...@@ -213,6 +213,37 @@ public:
UndefPool Undefs; UndefPool Undefs;
}; };
void GlobalContext::waitForWorkerThreads() {
if (WaitForWorkerThreadsCalled.exchange(true))
return;
optQueueNotifyEnd();
for (std::thread &Worker : TranslationThreads) {
Worker.join();
}
TranslationThreads.clear();
// 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 (BuildDefs::timers()) {
auto Timers = getTimers();
for (ThreadContext *TLS : AllThreadContexts)
Timers->mergeFrom(TLS->Timers);
}
if (BuildDefs::dump()) {
// Do a separate loop over AllThreadContexts to avoid holding two locks at
// once.
auto Stats = getStatsCumulative();
for (ThreadContext *TLS : AllThreadContexts)
Stats->add(TLS->StatsCumulative);
}
}
void GlobalContext::CodeStats::dump(const std::string &Name, void GlobalContext::CodeStats::dump(const std::string &Name,
GlobalContext *Ctx) { GlobalContext *Ctx) {
if (!BuildDefs::dump()) if (!BuildDefs::dump())
...@@ -252,7 +283,10 @@ GlobalContext::GlobalContext(Ostream *OsDump, Ostream *OsEmit, Ostream *OsError, ...@@ -252,7 +283,10 @@ GlobalContext::GlobalContext(Ostream *OsDump, Ostream *OsEmit, Ostream *OsError,
: Strings(new StringPool()), ConstPool(new ConstantPool()), ErrorStatus(), : Strings(new StringPool()), ConstPool(new ConstantPool()), ErrorStatus(),
StrDump(OsDump), StrEmit(OsEmit), StrError(OsError), IntrinsicsInfo(this), StrDump(OsDump), StrEmit(OsEmit), StrError(OsError), IntrinsicsInfo(this),
ObjectWriter(), OptQ(/*Sequential=*/Flags.isSequential(), ObjectWriter(), OptQ(/*Sequential=*/Flags.isSequential(),
/*MaxSize=*/Flags.getNumTranslationThreads()), /*MaxSize=*/
(Flags.getParseParallel() && Flags.getBuildOnRead())
? MaxOptQSize
: Flags.getNumTranslationThreads()),
// EmitQ is allowed unlimited size. // EmitQ is allowed unlimited size.
EmitQ(/*Sequential=*/Flags.isSequential()), EmitQ(/*Sequential=*/Flags.isSequential()),
DataLowering(TargetDataLowering::createLowering(this)) { DataLowering(TargetDataLowering::createLowering(this)) {
...@@ -305,7 +339,8 @@ GlobalContext::GlobalContext(Ostream *OsDump, Ostream *OsEmit, Ostream *OsError, ...@@ -305,7 +339,8 @@ GlobalContext::GlobalContext(Ostream *OsDump, Ostream *OsEmit, Ostream *OsError,
void GlobalContext::translateFunctions() { void GlobalContext::translateFunctions() {
TimerMarker Timer(TimerStack::TT_translateFunctions, this); TimerMarker Timer(TimerStack::TT_translateFunctions, this);
while (std::unique_ptr<Cfg> Func = optQueueBlockingPop()) { while (std::unique_ptr<OptWorkItem> OptItem = optQueueBlockingPop()) {
auto Func = OptItem->getParsedCfg();
// Install Func in TLS for Cfg-specific container allocators. // Install Func in TLS for Cfg-specific container allocators.
CfgLocalAllocatorScope _(Func.get()); CfgLocalAllocatorScope _(Func.get());
// Reset per-function stats being accumulated in TLS. // Reset per-function stats being accumulated in TLS.
...@@ -878,19 +913,19 @@ void GlobalContext::setTimerName(TimerStackIdT StackID, ...@@ -878,19 +913,19 @@ void GlobalContext::setTimerName(TimerStackIdT StackID,
// interface to take and transfer ownership, but they internally store the raw // 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 // Cfg pointer in the work queue. This allows e.g. future queue optimizations
// such as the use of atomics to modify queue elements. // such as the use of atomics to modify queue elements.
void GlobalContext::optQueueBlockingPush(std::unique_ptr<Cfg> Func) { void GlobalContext::optQueueBlockingPush(std::unique_ptr<OptWorkItem> Item) {
assert(Func); assert(Item);
{ {
TimerMarker _(TimerStack::TT_qTransPush, this); TimerMarker _(TimerStack::TT_qTransPush, this);
OptQ.blockingPush(std::move(Func)); OptQ.blockingPush(std::move(Item));
} }
if (getFlags().isSequential()) if (getFlags().isSequential())
translateFunctions(); translateFunctions();
} }
std::unique_ptr<Cfg> GlobalContext::optQueueBlockingPop() { std::unique_ptr<OptWorkItem> GlobalContext::optQueueBlockingPop() {
TimerMarker _(TimerStack::TT_qTransPop, this); TimerMarker _(TimerStack::TT_qTransPop, this);
return std::unique_ptr<Cfg>(OptQ.blockingPop()); return std::unique_ptr<OptWorkItem>(OptQ.blockingPop());
} }
void GlobalContext::emitQueueBlockingPush( void GlobalContext::emitQueueBlockingPush(
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "IceUtils.h" #include "IceUtils.h"
#include <array> #include <array>
#include <atomic>
#include <cassert> #include <cassert>
#include <functional> #include <functional>
#include <memory> #include <memory>
...@@ -54,6 +55,21 @@ enum class RuntimeHelper { ...@@ -54,6 +55,21 @@ enum class RuntimeHelper {
H_Num H_Num
}; };
/// OptWorkItem is a simple wrapper used to pass parse information on a function
/// block, to a translator thread.
class OptWorkItem {
OptWorkItem(const OptWorkItem &) = delete;
OptWorkItem &operator=(const OptWorkItem &) = delete;
public:
// Get the Cfg for the funtion to translate.
virtual std::unique_ptr<Cfg> getParsedCfg() = 0;
virtual ~OptWorkItem() = default;
protected:
OptWorkItem() = default;
};
class GlobalContext { class GlobalContext {
GlobalContext() = delete; GlobalContext() = delete;
GlobalContext(const GlobalContext &) = delete; GlobalContext(const GlobalContext &) = delete;
...@@ -358,12 +374,12 @@ public: ...@@ -358,12 +374,12 @@ public:
/// Notifies any idle workers that a new function is available for /// 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 /// translating. May block if the work queue is too large, in order to control
/// memory footprint. /// memory footprint.
void optQueueBlockingPush(std::unique_ptr<Cfg> Func); void optQueueBlockingPush(std::unique_ptr<OptWorkItem> Item);
/// Takes a Cfg from the work queue for translating. May block if the work /// 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 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 /// queue is empty and either end() has been called or the Sequential flag was
/// set. /// set.
std::unique_ptr<Cfg> optQueueBlockingPop(); std::unique_ptr<OptWorkItem> optQueueBlockingPop();
/// Notifies that no more work will be added to the work queue. /// Notifies that no more work will be added to the work queue.
void optQueueNotifyEnd() { OptQ.notifyEnd(); } void optQueueNotifyEnd() { OptQ.notifyEnd(); }
...@@ -405,34 +421,7 @@ public: ...@@ -405,34 +421,7 @@ public:
} }
} }
void waitForWorkerThreads() { void waitForWorkerThreads();
optQueueNotifyEnd();
for (std::thread &Worker : TranslationThreads) {
Worker.join();
}
TranslationThreads.clear();
// 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 (BuildDefs::timers()) {
auto Timers = getTimers();
for (ThreadContext *TLS : AllThreadContexts)
Timers->mergeFrom(TLS->Timers);
}
if (BuildDefs::dump()) {
// Do a separate loop over AllThreadContexts to avoid holding two locks
// at once.
auto Stats = getStatsCumulative();
for (ThreadContext *TLS : AllThreadContexts)
Stats->add(TLS->StatsCumulative);
}
}
/// Translation thread startup routine. /// Translation thread startup routine.
void translateFunctionsWrapper(ThreadContext *MyTLS) { void translateFunctionsWrapper(ThreadContext *MyTLS) {
...@@ -545,12 +534,16 @@ private: ...@@ -545,12 +534,16 @@ private:
Ostream *StrEmit; /// Stream for code emission Ostream *StrEmit; /// Stream for code emission
Ostream *StrError; /// Stream for logging errors. Ostream *StrError; /// Stream for logging errors.
// True if waitForWorkerThreads() has been called.
std::atomic_bool WaitForWorkerThreadsCalled;
ICE_CACHELINE_BOUNDARY; ICE_CACHELINE_BOUNDARY;
Intrinsics IntrinsicsInfo; Intrinsics IntrinsicsInfo;
// TODO(jpp): move to EmitterContext. // TODO(jpp): move to EmitterContext.
std::unique_ptr<ELFObjectWriter> ObjectWriter; std::unique_ptr<ELFObjectWriter> ObjectWriter;
BoundedProducerConsumerQueue<Cfg> OptQ; static constexpr size_t MaxOptQSize = 1 << 16;
BoundedProducerConsumerQueue<OptWorkItem, MaxOptQSize> OptQ;
BoundedProducerConsumerQueue<EmitterWorkItem> EmitQ; BoundedProducerConsumerQueue<EmitterWorkItem> EmitQ;
// DataLowering is only ever used by a single thread at a time (either in // DataLowering is only ever used by a single thread at a time (either in
// emitItems(), or in IceCompiler::run before the compilation is over.) // emitItems(), or in IceCompiler::run before the compilation is over.)
......
...@@ -24,6 +24,20 @@ ...@@ -24,6 +24,20 @@
namespace Ice { namespace Ice {
class CfgOptWorkItem final : public OptWorkItem {
CfgOptWorkItem() = delete;
CfgOptWorkItem(const CfgOptWorkItem &) = delete;
CfgOptWorkItem &operator=(const CfgOptWorkItem &) = delete;
public:
CfgOptWorkItem(std::unique_ptr<Cfg> Func) : Func(std::move(Func)) {}
std::unique_ptr<Cfg> getParsedCfg() override { return std::move(Func); }
~CfgOptWorkItem() override = default;
private:
std::unique_ptr<Ice::Cfg> Func;
};
Translator::Translator(GlobalContext *Ctx) Translator::Translator(GlobalContext *Ctx)
: Ctx(Ctx), NextSequenceNumber(GlobalContext::getFirstSequenceNumber()), : Ctx(Ctx), NextSequenceNumber(GlobalContext::getFirstSequenceNumber()),
ErrorStatus() {} ErrorStatus() {}
...@@ -57,7 +71,7 @@ bool Translator::checkIfUnnamedNameSafe(const std::string &Name, ...@@ -57,7 +71,7 @@ bool Translator::checkIfUnnamedNameSafe(const std::string &Name,
} }
void Translator::translateFcn(std::unique_ptr<Cfg> Func) { void Translator::translateFcn(std::unique_ptr<Cfg> Func) {
Ctx->optQueueBlockingPush(std::move(Func)); Ctx->optQueueBlockingPush(makeUnique<CfgOptWorkItem>(std::move(Func)));
} }
void Translator::lowerGlobals( void Translator::lowerGlobals(
......
...@@ -221,7 +221,7 @@ private: ...@@ -221,7 +221,7 @@ private:
class BlockParserBaseClass; class BlockParserBaseClass;
// Top-level class to read PNaCl bitcode files, and translate to ICE. // Top-level class to read PNaCl bitcode files, and translate to ICE.
class TopLevelParser : public NaClBitcodeParser { class TopLevelParser final : public NaClBitcodeParser {
TopLevelParser() = delete; TopLevelParser() = delete;
TopLevelParser(const TopLevelParser &) = delete; TopLevelParser(const TopLevelParser &) = delete;
TopLevelParser &operator=(const TopLevelParser &) = delete; TopLevelParser &operator=(const TopLevelParser &) = delete;
...@@ -237,21 +237,14 @@ public: ...@@ -237,21 +237,14 @@ public:
Ice::Translator &getTranslator() const { return Translator; } Ice::Translator &getTranslator() const { return Translator; }
void setBlockParser(BlockParserBaseClass *NewBlockParser) {
BlockParser = NewBlockParser;
}
/// Generates error with given Message, occurring at BitPosition within the /// Generates error with given Message, occurring at BitPosition within the
/// bitcode file. Always returns true. /// bitcode file. Always returns true.
bool ErrorAt(naclbitc::ErrorLevel Level, uint64_t BitPosition, bool ErrorAt(naclbitc::ErrorLevel Level, uint64_t BitPosition,
const std::string &Message) final; const std::string &Message) override;
/// Generates error message with respect to the current block parser. /// Generates error message with respect to the current block parser.
bool blockError(const std::string &Message); bool blockError(const std::string &Message);
/// Returns the number of errors found while parsing the bitcode file.
unsigned getNumErrors() const { return NumErrors; }
/// Changes the size of the type list to the given size. /// Changes the size of the type list to the given size.
void resizeTypeIDValues(size_t NewSize) { TypeIDValues.resize(NewSize); } void resizeTypeIDValues(size_t NewSize) { TypeIDValues.resize(NewSize); }
...@@ -428,10 +421,12 @@ public: ...@@ -428,10 +421,12 @@ public:
private: private:
// The translator associated with the parser. // The translator associated with the parser.
Ice::Translator &Translator; Ice::Translator &Translator;
// ErrorStatus should only be updated while this lock is locked.
Ice::GlobalLockType ErrorReportingLock;
// 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.
Ice::ErrorCode &ErrorStatus; Ice::ErrorCode &ErrorStatus;
// The number of errors reported.
unsigned NumErrors = 0;
// The types associated with each type ID. // The types associated with each type ID.
std::vector<ExtendedType> TypeIDValues; std::vector<ExtendedType> TypeIDValues;
// The set of functions (prototype and defined). // The set of functions (prototype and defined).
...@@ -448,8 +443,6 @@ private: ...@@ -448,8 +443,6 @@ private:
Ice::ConstantList ValueIDConstants; Ice::ConstantList ValueIDConstants;
// Error recovery value to use when getFuncSigTypeByID fails. // Error recovery value to use when getFuncSigTypeByID fails.
Ice::FuncSigType UndefinedFuncSigType; Ice::FuncSigType UndefinedFuncSigType;
// The block parser currently being applied. Used for error reporting.
BlockParserBaseClass *BlockParser = nullptr;
// Defines if a module block has already been parsed. // Defines if a module block has already been parsed.
bool ParsedModuleBlock = false; bool ParsedModuleBlock = false;
...@@ -584,9 +577,11 @@ private: ...@@ -584,9 +577,11 @@ private:
bool TopLevelParser::ErrorAt(naclbitc::ErrorLevel Level, uint64_t Bit, bool TopLevelParser::ErrorAt(naclbitc::ErrorLevel Level, uint64_t Bit,
const std::string &Message) { const std::string &Message) {
ErrorStatus.assign(Ice::EC_Bitcode);
++NumErrors;
Ice::GlobalContext *Context = Translator.getContext(); Ice::GlobalContext *Context = Translator.getContext();
{
std::unique_lock<Ice::GlobalLockType> _(ErrorReportingLock);
ErrorStatus.assign(Ice::EC_Bitcode);
}
{ // Lock while printing out error message. { // Lock while printing out error message.
Ice::OstreamLocker L(Context); Ice::OstreamLocker L(Context);
raw_ostream &OldErrStream = setErrStream(Context->getStrError()); raw_ostream &OldErrStream = setErrStream(Context->getStrError());
...@@ -665,11 +660,14 @@ class BlockParserBaseClass : public NaClBitcodeParser { ...@@ -665,11 +660,14 @@ class BlockParserBaseClass : public NaClBitcodeParser {
public: public:
// Constructor for the top-level module block parser. // Constructor for the top-level module block parser.
BlockParserBaseClass(unsigned BlockID, TopLevelParser *Context) BlockParserBaseClass(unsigned BlockID, TopLevelParser *Context)
: NaClBitcodeParser(BlockID, Context), Context(Context) { : NaClBitcodeParser(BlockID, Context), Context(Context) {}
Context->setBlockParser(this);
}
~BlockParserBaseClass() override { Context->setBlockParser(nullptr); } BlockParserBaseClass(unsigned BlockID, BlockParserBaseClass *EnclosingParser,
NaClBitstreamCursor &Cursor)
: NaClBitcodeParser(BlockID, EnclosingParser, Cursor),
Context(EnclosingParser->Context) {}
~BlockParserBaseClass() override {}
// Returns the printable name of the type of block being parsed. // Returns the printable name of the type of block being parsed.
virtual const char *getBlockName() const { virtual const char *getBlockName() const {
...@@ -679,11 +677,13 @@ public: ...@@ -679,11 +677,13 @@ public:
// Generates an error Message with the Bit address prefixed to it. // Generates an error Message with the Bit address prefixed to it.
bool ErrorAt(naclbitc::ErrorLevel Level, uint64_t Bit, bool ErrorAt(naclbitc::ErrorLevel Level, uint64_t Bit,
const std::string &Message) final; const std::string &Message) override;
protected: protected:
// The context parser that contains the decoded state. // The context parser that contains the decoded state.
TopLevelParser *Context; TopLevelParser *Context;
// True if ErrorAt has been called in this block.
bool BlockHasError = false;
// Constructor for nested block parsers. // Constructor for nested block parsers.
BlockParserBaseClass(unsigned BlockID, BlockParserBaseClass *EnclosingParser) BlockParserBaseClass(unsigned BlockID, BlockParserBaseClass *EnclosingParser)
...@@ -752,15 +752,19 @@ private: ...@@ -752,15 +752,19 @@ private:
}; };
bool TopLevelParser::blockError(const std::string &Message) { bool TopLevelParser::blockError(const std::string &Message) {
if (BlockParser) // TODO(kschimpf): Remove this method. This method used to redirect
return BlockParser->Error(Message); // block-level errors to the block we are in, rather than the top-level
else // block. This gave better bit location for error messages. However, with
// parallel parsing, we can't keep a field to redirect (there could be many
// and we don't know which block parser applies). Hence, This redirect can't
// be applied anymore.
return Error(Message); return Error(Message);
} }
// Generates an error Message with the bit address prefixed to it. // Generates an error Message with the bit address prefixed to it.
bool BlockParserBaseClass::ErrorAt(naclbitc::ErrorLevel Level, uint64_t Bit, bool BlockParserBaseClass::ErrorAt(naclbitc::ErrorLevel Level, uint64_t Bit,
const std::string &Message) { const std::string &Message) {
BlockHasError = true;
std::string Buffer; std::string Buffer;
raw_string_ostream StrBuf(Buffer); raw_string_ostream StrBuf(Buffer);
// Note: If dump routines have been turned off, the error messages will not // Note: If dump routines have been turned off, the error messages will not
...@@ -817,7 +821,7 @@ void BlockParserBaseClass::ProcessRecord() { ...@@ -817,7 +821,7 @@ void BlockParserBaseClass::ProcessRecord() {
} }
// Class to parse a types block. // Class to parse a types block.
class TypesParser : public BlockParserBaseClass { class TypesParser final : public BlockParserBaseClass {
TypesParser() = delete; TypesParser() = delete;
TypesParser(const TypesParser &) = delete; TypesParser(const TypesParser &) = delete;
TypesParser &operator=(const TypesParser &) = delete; TypesParser &operator=(const TypesParser &) = delete;
...@@ -1021,7 +1025,7 @@ void TypesParser::ProcessRecord() { ...@@ -1021,7 +1025,7 @@ void TypesParser::ProcessRecord() {
/// Parses the globals block (i.e. global variable declarations and /// Parses the globals block (i.e. global variable declarations and
/// corresponding initializers). /// corresponding initializers).
class GlobalsParser : public BlockParserBaseClass { class GlobalsParser final : public BlockParserBaseClass {
GlobalsParser() = delete; GlobalsParser() = delete;
GlobalsParser(const GlobalsParser &) = delete; GlobalsParser(const GlobalsParser &) = delete;
GlobalsParser &operator=(const GlobalsParser &) = delete; GlobalsParser &operator=(const GlobalsParser &) = delete;
...@@ -1037,7 +1041,7 @@ public: ...@@ -1037,7 +1041,7 @@ public:
Context->getGlobalVariablesPool()->willNotBeEmitted(DummyGlobalVar); Context->getGlobalVariablesPool()->willNotBeEmitted(DummyGlobalVar);
} }
~GlobalsParser() final = default; ~GlobalsParser() override = default;
const char *getBlockName() const override { return "globals"; } const char *getBlockName() const override { return "globals"; }
...@@ -1341,29 +1345,37 @@ void ValuesymtabParser::ProcessRecord() { ...@@ -1341,29 +1345,37 @@ void ValuesymtabParser::ProcessRecord() {
} }
/// Parses function blocks in the bitcode file. /// Parses function blocks in the bitcode file.
class FunctionParser : public BlockParserBaseClass { class FunctionParser final : public BlockParserBaseClass {
FunctionParser() = delete; FunctionParser() = delete;
FunctionParser(const FunctionParser &) = delete; FunctionParser(const FunctionParser &) = delete;
FunctionParser &operator=(const FunctionParser &) = delete; FunctionParser &operator=(const FunctionParser &) = delete;
public: public:
FunctionParser(unsigned BlockID, BlockParserBaseClass *EnclosingParser) FunctionParser(unsigned BlockID, BlockParserBaseClass *EnclosingParser,
NaClBcIndexSize_t FcnId)
: BlockParserBaseClass(BlockID, EnclosingParser), : BlockParserBaseClass(BlockID, EnclosingParser),
Timer(Ice::TimerStack::TT_parseFunctions, getTranslator().getContext()), Timer(Ice::TimerStack::TT_parseFunctions, getTranslator().getContext()),
Func(nullptr), FcnId(Context->getNextFunctionBlockValueID()), Func(nullptr), FuncDecl(Context->getFunctionByID(FcnId)),
FuncDecl(Context->getFunctionByID(FcnId)), CachedNumGlobalValueIDs(Context->getNumGlobalIDs()),
NextLocalInstIndex(Context->getNumGlobalIDs()) {}
FunctionParser(unsigned BlockID, BlockParserBaseClass *EnclosingParser,
NaClBcIndexSize_t FcnId, NaClBitstreamCursor &Cursor)
: BlockParserBaseClass(BlockID, EnclosingParser, Cursor),
Timer(Ice::TimerStack::TT_parseFunctions, getTranslator().getContext()),
Func(nullptr), FuncDecl(Context->getFunctionByID(FcnId)),
CachedNumGlobalValueIDs(Context->getNumGlobalIDs()), CachedNumGlobalValueIDs(Context->getNumGlobalIDs()),
NextLocalInstIndex(Context->getNumGlobalIDs()) {} NextLocalInstIndex(Context->getNumGlobalIDs()) {}
bool convertFunction() { std::unique_ptr<Ice::Cfg> parseFunction(uint32_t SeqNumber) {
bool ParserResult; bool ParserResult;
Ice::GlobalContext *Ctx = getTranslator().getContext();
{ {
Ice::TimerMarker T(getTranslator().getContext(), Ice::TimerMarker T(Ctx, FuncDecl->getName().toStringOrEmpty());
FuncDecl->getName().toStringOrEmpty());
// Note: The Cfg is created, even when IR generation is disabled. This is // Note: The Cfg is created, even when IR generation is disabled. This is
// done to install a CfgLocalAllocator for various internal containers. // done to install a CfgLocalAllocator for various internal containers.
Ice::GlobalContext *Ctx = getTranslator().getContext(); Ice::GlobalContext *Ctx = getTranslator().getContext();
Func = Ice::Cfg::create(Ctx, getTranslator().getNextSequenceNumber()); Func = Ice::Cfg::create(Ctx, SeqNumber);
Ice::CfgLocalAllocatorScope _(Func.get()); Ice::CfgLocalAllocatorScope _(Func.get());
...@@ -1380,23 +1392,15 @@ public: ...@@ -1380,23 +1392,15 @@ public:
} }
ParserResult = ParseThisBlock(); ParserResult = ParseThisBlock();
// Note: Once any errors have been found, we turn off all 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 && Func) {
getTranslator().translateFcn(std::move(Func));
// The translator now has ownership of Func.
} else {
Func.reset();
} }
return ParserResult; if (ParserResult || BlockHasError)
Func->setError("Unable to parse function");
return std::move(Func);
} }
~FunctionParser() final = default; ~FunctionParser() override = default;
const char *getBlockName() const override { return "function"; } const char *getBlockName() const override { return "function"; }
...@@ -1440,8 +1444,6 @@ private: ...@@ -1440,8 +1444,6 @@ private:
NaClBcIndexSize_t DeclaredNumberBbs = 0; NaClBcIndexSize_t DeclaredNumberBbs = 0;
// The basic block being built. // The basic block being built.
Ice::CfgNode *CurrentNode = nullptr; Ice::CfgNode *CurrentNode = nullptr;
// The ID for the function.
NaClBcIndexSize_t FcnId;
// The corresponding function declaration. // The corresponding function declaration.
Ice::FunctionDeclaration *FuncDecl; Ice::FunctionDeclaration *FuncDecl;
// Holds the dividing point between local and global absolute value indices. // Holds the dividing point between local and global absolute value indices.
...@@ -1459,7 +1461,7 @@ private: ...@@ -1459,7 +1461,7 @@ private:
void ProcessRecord() override; void ProcessRecord() override;
void EnterBlock(unsigned NumWords) final { void EnterBlock(unsigned NumWords) override {
// Note: Bitstream defines words as 32-bit values. // Note: Bitstream defines words as 32-bit values.
NumBytesDefiningFunction = NumWords * sizeof(uint32_t); NumBytesDefiningFunction = NumWords * sizeof(uint32_t);
// We know that all records are minimally defined by a two-bit abreviation. // We know that all records are minimally defined by a two-bit abreviation.
...@@ -2780,7 +2782,7 @@ void FunctionParser::ProcessRecord() { ...@@ -2780,7 +2782,7 @@ void FunctionParser::ProcessRecord() {
} }
/// Parses constants within a function block. /// Parses constants within a function block.
class ConstantsParser : public BlockParserBaseClass { class ConstantsParser final : public BlockParserBaseClass {
ConstantsParser() = delete; ConstantsParser() = delete;
ConstantsParser(const ConstantsParser &) = delete; ConstantsParser(const ConstantsParser &) = delete;
ConstantsParser &operator=(const ConstantsParser &) = delete; ConstantsParser &operator=(const ConstantsParser &) = delete;
...@@ -2897,7 +2899,7 @@ void ConstantsParser::ProcessRecord() { ...@@ -2897,7 +2899,7 @@ void ConstantsParser::ProcessRecord() {
} }
// Parses valuesymtab blocks appearing in a function block. // Parses valuesymtab blocks appearing in a function block.
class FunctionValuesymtabParser : public ValuesymtabParser { class FunctionValuesymtabParser final : public ValuesymtabParser {
FunctionValuesymtabParser() = delete; FunctionValuesymtabParser() = delete;
FunctionValuesymtabParser(const FunctionValuesymtabParser &) = delete; FunctionValuesymtabParser(const FunctionValuesymtabParser &) = delete;
void operator=(const FunctionValuesymtabParser &) = delete; void operator=(const FunctionValuesymtabParser &) = delete;
...@@ -2915,10 +2917,10 @@ private: ...@@ -2915,10 +2917,10 @@ private:
return reinterpret_cast<FunctionParser *>(GetEnclosingParser()); return reinterpret_cast<FunctionParser *>(GetEnclosingParser());
} }
const char *getTableKind() const final { return "Function"; } const char *getTableKind() const override { return "Function"; }
void setValueName(NaClBcIndexSize_t Index, StringType &Name) final; void setValueName(NaClBcIndexSize_t Index, StringType &Name) override;
void setBbName(NaClBcIndexSize_t Index, StringType &Name) final; void setBbName(NaClBcIndexSize_t Index, StringType &Name) override;
// Reports that the assignment of Name to the value associated with index is // Reports that the assignment of Name to the value associated with index is
// not possible, for the given Context. // not possible, for the given Context.
...@@ -2984,7 +2986,7 @@ bool FunctionParser::ParseBlock(unsigned BlockID) { ...@@ -2984,7 +2986,7 @@ bool FunctionParser::ParseBlock(unsigned BlockID) {
} }
/// Parses the module block in the bitcode file. /// Parses the module block in the bitcode file.
class ModuleParser : public BlockParserBaseClass { class ModuleParser final : public BlockParserBaseClass {
ModuleParser() = delete; ModuleParser() = delete;
ModuleParser(const ModuleParser &) = delete; ModuleParser(const ModuleParser &) = delete;
ModuleParser &operator=(const ModuleParser &) = delete; ModuleParser &operator=(const ModuleParser &) = delete;
...@@ -2994,10 +2996,9 @@ public: ...@@ -2994,10 +2996,9 @@ public:
: BlockParserBaseClass(BlockID, Context), : BlockParserBaseClass(BlockID, Context),
Timer(Ice::TimerStack::TT_parseModule, Timer(Ice::TimerStack::TT_parseModule,
Context->getTranslator().getContext()) {} Context->getTranslator().getContext()) {}
~ModuleParser() override = default; ~ModuleParser() override = default;
const char *getBlockName() const override { return "module"; } const char *getBlockName() const override { return "module"; }
NaClBitstreamCursor &getCursor() const { return Record.GetCursor(); }
private: private:
Ice::TimerMarker Timer; Ice::TimerMarker Timer;
...@@ -3025,7 +3026,10 @@ private: ...@@ -3025,7 +3026,10 @@ private:
} }
bool ParseBlock(unsigned BlockID) override; bool ParseBlock(unsigned BlockID) override;
void ExitBlock() override { installGlobalNamesAndGlobalVarInitializers(); } void ExitBlock() override {
installGlobalNamesAndGlobalVarInitializers();
Context->getTranslator().getContext()->waitForWorkerThreads();
}
void ProcessRecord() override; void ProcessRecord() override;
}; };
...@@ -3045,9 +3049,9 @@ public: ...@@ -3045,9 +3049,9 @@ public:
private: private:
Ice::TimerMarker Timer; Ice::TimerMarker Timer;
const char *getTableKind() const final { return "Module"; } const char *getTableKind() const override { return "Module"; }
void setValueName(NaClBcIndexSize_t Index, StringType &Name) final; void setValueName(NaClBcIndexSize_t Index, StringType &Name) override;
void setBbName(NaClBcIndexSize_t Index, StringType &Name) final; void setBbName(NaClBcIndexSize_t Index, StringType &Name) override;
}; };
void ModuleValuesymtabParser::setValueName(NaClBcIndexSize_t Index, void ModuleValuesymtabParser::setValueName(NaClBcIndexSize_t Index,
...@@ -3075,6 +3079,44 @@ void ModuleValuesymtabParser::setBbName(NaClBcIndexSize_t Index, ...@@ -3075,6 +3079,44 @@ void ModuleValuesymtabParser::setBbName(NaClBcIndexSize_t Index,
reportUnableToAssign("Basic block", Index, Name); reportUnableToAssign("Basic block", Index, Name);
} }
class CfgParserWorkItem final : public Ice::OptWorkItem {
CfgParserWorkItem() = delete;
CfgParserWorkItem(const CfgParserWorkItem &) = delete;
CfgParserWorkItem &operator=(const CfgParserWorkItem &) = delete;
public:
CfgParserWorkItem(unsigned BlockID, NaClBcIndexSize_t FcnId,
ModuleParser *ModParser, std::unique_ptr<uint8_t[]> Buffer,
uintptr_t BufferSize, uint64_t StartBit, uint32_t SeqNumber)
: BlockID(BlockID), FcnId(FcnId), ModParser(ModParser),
Buffer(std::move(Buffer)), BufferSize(BufferSize), StartBit(StartBit),
SeqNumber(SeqNumber) {}
std::unique_ptr<Ice::Cfg> getParsedCfg() override;
~CfgParserWorkItem() override = default;
private:
const unsigned BlockID;
const NaClBcIndexSize_t FcnId;
// Note: ModParser can't be const because the function parser needs to
// access non-const member functions (of ModuleParser and TopLevelParser).
// TODO(kschimpf): Fix this issue.
ModuleParser *ModParser;
const std::unique_ptr<uint8_t[]> Buffer;
const uintptr_t BufferSize;
const uint64_t StartBit;
const uint32_t SeqNumber;
};
std::unique_ptr<Ice::Cfg> CfgParserWorkItem::getParsedCfg() {
NaClBitstreamCursor &OldCursor(ModParser->getCursor());
llvm::NaClBitstreamReader Reader(Buffer.get(), Buffer.get() + BufferSize,
OldCursor.getBitStreamReader());
NaClBitstreamCursor NewCursor(Reader);
NewCursor.JumpToBit(NewCursor.getWordBitNo(StartBit));
FunctionParser Parser(BlockID, ModParser, FcnId, NewCursor);
return Parser.parseFunction(SeqNumber);
}
bool ModuleParser::ParseBlock(unsigned BlockID) { bool ModuleParser::ParseBlock(unsigned BlockID) {
switch (BlockID) { switch (BlockID) {
case naclbitc::BLOCKINFO_BLOCK_ID: case naclbitc::BLOCKINFO_BLOCK_ID:
...@@ -3103,8 +3145,37 @@ bool ModuleParser::ParseBlock(unsigned BlockID) { ...@@ -3103,8 +3145,37 @@ bool ModuleParser::ParseBlock(unsigned BlockID) {
} }
case naclbitc::FUNCTION_BLOCK_ID: { case naclbitc::FUNCTION_BLOCK_ID: {
installGlobalNamesAndGlobalVarInitializers(); installGlobalNamesAndGlobalVarInitializers();
FunctionParser Parser(BlockID, this); Ice::GlobalContext *Ctx = Context->getTranslator().getContext();
return Parser.convertFunction(); uint32_t SeqNumber = Context->getTranslator().getNextSequenceNumber();
NaClBcIndexSize_t FcnId = Context->getNextFunctionBlockValueID();
if (Ctx->getFlags().getParseParallel()) {
// Skip the block and copy into a buffer. Note: We copy into a buffer
// using the top-level parser to make sure that the underlying
// buffer reading from the data streamer is not thread safe.
NaClBitstreamCursor &Cursor = Record.GetCursor();
uint64_t StartBit = Cursor.GetCurrentBitNo();
if (SkipBlock())
return true;
const uint64_t EndBit = Cursor.GetCurrentBitNo();
const uintptr_t StartByte = Cursor.getStartWordByteForBit(StartBit);
const uintptr_t EndByte = Cursor.getEndWordByteForBit(EndBit);
const uintptr_t BufferSize = EndByte - StartByte;
std::unique_ptr<uint8_t[]> Buffer((uint8_t *)(new uint8_t[BufferSize]));
for (size_t i = Cursor.fillBuffer(Buffer.get(), BufferSize, StartByte);
i < BufferSize; ++i) {
Buffer[i] = 0;
}
Ctx->optQueueBlockingPush(Ice::makeUnique<CfgParserWorkItem>(
BlockID, FcnId, this, std::move(Buffer), BufferSize, StartBit,
SeqNumber));
return false;
} else {
FunctionParser Parser(BlockID, this, FcnId);
std::unique_ptr<Ice::Cfg> Func = Parser.parseFunction(SeqNumber);
bool Failed = Func->hasError();
getTranslator().translateFcn(std::move(Func));
return Failed && !getTranslator().getFlags().getAllowErrorRecovery();
}
} }
default: default:
return BlockParserBaseClass::ParseBlock(BlockID); return BlockParserBaseClass::ParseBlock(BlockID);
......
; Tests malformed insertelement and extractelement vector instructions. ; Tests malformed insertelement and extractelement vector instructions.
; RUN: %if --need=allow_dump --command llvm-as < %s \ ; RUN: %if --need=allow_dump --command \
; RUN: | %if --need=allow_dump --command pnacl-freeze \ ; RUN: %p2i --expect-fail -i %s --allow-pnacl-reader-error-recovery \
; RUN: | %if --need=allow_dump --command not %pnacl_sz -notranslate \ ; RUN: --filetype=obj -o /dev/null --args -notranslate \
; RUN: -build-on-read -allow-pnacl-reader-error-recovery \
; RUN: -filetype=obj -o /dev/null \
; RUN: | %if --need=allow_dump --command FileCheck %s ; RUN: | %if --need=allow_dump --command FileCheck %s
; RUN: %if --need=no_dump --command llvm-as < %s \ ; RUN: %if --need=no_dump --command \
; RUN: | %if --need=no_dump --command pnacl-freeze \ ; RUN: %p2i --expect-fail -i %s --allow-pnacl-reader-error-recovery \
; RUN: | %if --need=no_dump --command not %pnacl_sz -notranslate \ ; RUN: --filetype=obj -o /dev/null --args -notranslate \
; RUN: -build-on-read -allow-pnacl-reader-error-recovery \
; RUN: -filetype=obj -o /dev/null \
; RUN: | %if --need=no_dump --command FileCheck %s --check-prefix=MIN ; RUN: | %if --need=no_dump --command FileCheck %s --check-prefix=MIN
define void @ExtractV4xi1(<4 x i1> %v, i32 %i) { define void @ExtractV4xi1(<4 x i1> %v, i32 %i) {
......
...@@ -25,7 +25,8 @@ void IceTest::SubzeroBitcodeMunger::resetMungeFlags() { ...@@ -25,7 +25,8 @@ void IceTest::SubzeroBitcodeMunger::resetMungeFlags() {
Flags.setOptLevel(Ice::Opt_m1); Flags.setOptLevel(Ice::Opt_m1);
Flags.setOutFileType(Ice::FT_Iasm); Flags.setOutFileType(Ice::FT_Iasm);
Flags.setTargetArch(Ice::Target_X8632); Flags.setTargetArch(Ice::Target_X8632);
Flags.setVerbose(Ice::IceV_Instructions); Flags.setNumTranslationThreads(0);
Flags.setParseParallel(false);
} }
bool IceTest::SubzeroBitcodeMunger::runTest(const uint64_t Munges[], bool IceTest::SubzeroBitcodeMunger::runTest(const uint64_t Munges[],
...@@ -34,10 +35,12 @@ bool IceTest::SubzeroBitcodeMunger::runTest(const uint64_t Munges[], ...@@ -34,10 +35,12 @@ bool IceTest::SubzeroBitcodeMunger::runTest(const uint64_t Munges[],
const bool AddHeader = true; const bool AddHeader = true;
setupTest(Munges, MungeSize, AddHeader); setupTest(Munges, MungeSize, AddHeader);
Ice::GlobalContext Ctx(DumpStream, DumpStream, DumpStream, nullptr); Ice::GlobalContext Ctx(DumpStream, DumpStream, DumpStream, nullptr);
Ctx.startWorkerThreads();
Ice::PNaClTranslator Translator(&Ctx); Ice::PNaClTranslator Translator(&Ctx);
const char *BufferName = "Test"; const char *BufferName = "Test";
Flags.setDisableTranslation(DisableTranslation); Flags.setDisableTranslation(DisableTranslation);
Translator.translateBuffer(BufferName, MungedInput.get()); Translator.translateBuffer(BufferName, MungedInput.get());
Ctx.waitForWorkerThreads();
cleanupTest(); cleanupTest();
return Translator.getErrorStatus().value() == 0; return Translator.getErrorStatus().value() == 0;
......
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