Skip to content

Commit 8a02b70

Browse files
[llvm-exegesis] Refactor ExecutableFunction to use a named constructor (#72837)
This patch refactors ExecutableFunction to use a named constructor pattern, namely adding the create function, so that errors occurring during the creation of an ExecutableFunction can be propogated back up rather than having to deal with them in report_fatal_error.
1 parent b3dd14c commit 8a02b70

File tree

4 files changed

+81
-29
lines changed

4 files changed

+81
-29
lines changed

llvm/tools/llvm-exegesis/lib/Assembler.cpp

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -347,38 +347,44 @@ class TrackingSectionMemoryManager : public SectionMemoryManager {
347347

348348
} // namespace
349349

350-
ExecutableFunction::ExecutableFunction(
350+
Expected<ExecutableFunction> ExecutableFunction::create(
351351
std::unique_ptr<LLVMTargetMachine> TM,
352-
object::OwningBinary<object::ObjectFile> &&ObjectFileHolder)
353-
: Context(std::make_unique<LLVMContext>()) {
352+
object::OwningBinary<object::ObjectFile> &&ObjectFileHolder) {
354353
assert(ObjectFileHolder.getBinary() && "cannot create object file");
354+
std::unique_ptr<LLVMContext> Ctx = std::make_unique<LLVMContext>();
355355
// Initializing the execution engine.
356356
// We need to use the JIT EngineKind to be able to add an object file.
357357
LLVMLinkInMCJIT();
358358
uintptr_t CodeSize = 0;
359359
std::string Error;
360-
ExecEngine.reset(
361-
EngineBuilder(createModule(Context, TM->createDataLayout()))
360+
std::unique_ptr<ExecutionEngine> EE(
361+
EngineBuilder(createModule(Ctx, TM->createDataLayout()))
362362
.setErrorStr(&Error)
363363
.setMCPU(TM->getTargetCPU())
364364
.setEngineKind(EngineKind::JIT)
365365
.setMCJITMemoryManager(
366366
std::make_unique<TrackingSectionMemoryManager>(&CodeSize))
367367
.create(TM.release()));
368-
if (!ExecEngine)
369-
report_fatal_error(Twine(Error));
368+
if (!EE)
369+
return make_error<StringError>(Twine(Error), inconvertibleErrorCode());
370370
// Adding the generated object file containing the assembled function.
371371
// The ExecutionEngine makes sure the object file is copied into an
372372
// executable page.
373-
ExecEngine->addObjectFile(std::move(ObjectFileHolder));
373+
EE->addObjectFile(std::move(ObjectFileHolder));
374374
// Fetching function bytes.
375-
const uint64_t FunctionAddress = ExecEngine->getFunctionAddress(FunctionID);
375+
const uint64_t FunctionAddress = EE->getFunctionAddress(FunctionID);
376376
assert(isAligned(kFunctionAlignment, FunctionAddress) &&
377377
"function is not properly aligned");
378-
FunctionBytes =
378+
StringRef FBytes =
379379
StringRef(reinterpret_cast<const char *>(FunctionAddress), CodeSize);
380+
return ExecutableFunction(std::move(Ctx), std::move(EE), FBytes);
380381
}
381382

383+
ExecutableFunction::ExecutableFunction(std::unique_ptr<LLVMContext> Ctx,
384+
std::unique_ptr<ExecutionEngine> EE,
385+
StringRef FB)
386+
: FunctionBytes(FB), Context(std::move(Ctx)), ExecEngine(std::move(EE)) {}
387+
382388
Error getBenchmarkFunctionBytes(const StringRef InputData,
383389
std::vector<uint8_t> &Bytes) {
384390
const auto Holder = getObjectFromBuffer(InputData);

llvm/tools/llvm-exegesis/lib/Assembler.h

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,11 @@ object::OwningBinary<object::ObjectFile> getObjectFromFile(StringRef Filename);
106106

107107
// Consumes an ObjectFile containing a `void foo(char*)` function and make it
108108
// executable.
109-
struct ExecutableFunction {
110-
explicit ExecutableFunction(
111-
std::unique_ptr<LLVMTargetMachine> TM,
112-
object::OwningBinary<object::ObjectFile> &&ObjectFileHolder);
109+
class ExecutableFunction {
110+
public:
111+
static Expected<ExecutableFunction>
112+
create(std::unique_ptr<LLVMTargetMachine> TM,
113+
object::OwningBinary<object::ObjectFile> &&ObjectFileHolder);
113114

114115
// Retrieves the function as an array of bytes.
115116
StringRef getFunctionBytes() const { return FunctionBytes; }
@@ -119,9 +120,15 @@ struct ExecutableFunction {
119120
((void (*)(char *))(intptr_t)FunctionBytes.data())(Memory);
120121
}
121122

123+
StringRef FunctionBytes;
124+
125+
private:
126+
ExecutableFunction(std::unique_ptr<LLVMContext> Ctx,
127+
std::unique_ptr<ExecutionEngine> EE,
128+
StringRef FunctionBytes);
129+
122130
std::unique_ptr<LLVMContext> Context;
123131
std::unique_ptr<ExecutionEngine> ExecEngine;
124-
StringRef FunctionBytes;
125132
};
126133

127134
// Copies benchmark function's bytes from benchmark object.

llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,25 @@ BenchmarkRunner::FunctionExecutor::runAndSample(const char *Counters) const {
8989
namespace {
9090
class InProcessFunctionExecutorImpl : public BenchmarkRunner::FunctionExecutor {
9191
public:
92+
static Expected<std::unique_ptr<InProcessFunctionExecutorImpl>>
93+
create(const LLVMState &State, object::OwningBinary<object::ObjectFile> Obj,
94+
BenchmarkRunner::ScratchSpace *Scratch) {
95+
Expected<ExecutableFunction> EF =
96+
ExecutableFunction::create(State.createTargetMachine(), std::move(Obj));
97+
98+
if (!EF)
99+
return EF.takeError();
100+
101+
return std::unique_ptr<InProcessFunctionExecutorImpl>(
102+
new InProcessFunctionExecutorImpl(State, std::move(*EF), Scratch));
103+
}
104+
105+
private:
92106
InProcessFunctionExecutorImpl(const LLVMState &State,
93-
object::OwningBinary<object::ObjectFile> Obj,
107+
ExecutableFunction Function,
94108
BenchmarkRunner::ScratchSpace *Scratch)
95-
: State(State), Function(State.createTargetMachine(), std::move(Obj)),
96-
Scratch(Scratch) {}
109+
: State(State), Function(std::move(Function)), Scratch(Scratch) {}
97110

98-
private:
99111
static void
100112
accumulateCounterValues(const llvm::SmallVector<int64_t, 4> &NewValues,
101113
llvm::SmallVector<int64_t, 4> *Result) {
@@ -161,13 +173,24 @@ class InProcessFunctionExecutorImpl : public BenchmarkRunner::FunctionExecutor {
161173
class SubProcessFunctionExecutorImpl
162174
: public BenchmarkRunner::FunctionExecutor {
163175
public:
176+
static Expected<std::unique_ptr<SubProcessFunctionExecutorImpl>>
177+
create(const LLVMState &State, object::OwningBinary<object::ObjectFile> Obj,
178+
const BenchmarkKey &Key) {
179+
Expected<ExecutableFunction> EF =
180+
ExecutableFunction::create(State.createTargetMachine(), std::move(Obj));
181+
if (!EF)
182+
return EF.takeError();
183+
184+
return std::unique_ptr<SubProcessFunctionExecutorImpl>(
185+
new SubProcessFunctionExecutorImpl(State, std::move(*EF), Key));
186+
}
187+
188+
private:
164189
SubProcessFunctionExecutorImpl(const LLVMState &State,
165-
object::OwningBinary<object::ObjectFile> Obj,
190+
ExecutableFunction Function,
166191
const BenchmarkKey &Key)
167-
: State(State), Function(State.createTargetMachine(), std::move(Obj)),
168-
Key(Key) {}
192+
: State(State), Function(std::move(Function)), Key(Key) {}
169193

170-
private:
171194
enum ChildProcessExitCodeE {
172195
CounterFDReadFailed = 1,
173196
RSeqDisableFailed,
@@ -497,17 +520,27 @@ BenchmarkRunner::createFunctionExecutor(
497520
object::OwningBinary<object::ObjectFile> ObjectFile,
498521
const BenchmarkKey &Key) const {
499522
switch (ExecutionMode) {
500-
case ExecutionModeE::InProcess:
501-
return std::make_unique<InProcessFunctionExecutorImpl>(
523+
case ExecutionModeE::InProcess: {
524+
auto InProcessExecutorOrErr = InProcessFunctionExecutorImpl::create(
502525
State, std::move(ObjectFile), Scratch.get());
503-
case ExecutionModeE::SubProcess:
526+
if (!InProcessExecutorOrErr)
527+
return InProcessExecutorOrErr.takeError();
528+
529+
return std::move(*InProcessExecutorOrErr);
530+
}
531+
case ExecutionModeE::SubProcess: {
504532
#ifdef __linux__
505-
return std::make_unique<SubProcessFunctionExecutorImpl>(
533+
auto SubProcessExecutorOrErr = SubProcessFunctionExecutorImpl::create(
506534
State, std::move(ObjectFile), Key);
535+
if (!SubProcessExecutorOrErr)
536+
return SubProcessExecutorOrErr.takeError();
537+
538+
return std::move(*SubProcessExecutorOrErr);
507539
#else
508540
return make_error<Failure>(
509541
"The subprocess execution mode is only supported on Linux");
510542
#endif
543+
}
511544
}
512545
llvm_unreachable("ExecutionMode is outside expected range");
513546
}

llvm/unittests/tools/llvm-exegesis/Common/AssemblerUtils.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "llvm/MC/TargetRegistry.h"
2121
#include "llvm/Support/TargetSelect.h"
2222
#include "llvm/TargetParser/Host.h"
23+
#include "llvm/Testing/Support/Error.h"
2324
#include "gmock/gmock.h"
2425
#include "gtest/gtest.h"
2526

@@ -82,8 +83,13 @@ class MachineFunctionGeneratorBaseTest : public ::testing::Test {
8283
EXPECT_FALSE(assembleToStream(*ET, createTargetMachine(), /*LiveIns=*/{},
8384
RegisterInitialValues, Fill, AsmStream, Key,
8485
false));
85-
return ExecutableFunction(createTargetMachine(),
86-
getObjectFromBuffer(AsmStream.str()));
86+
Expected<ExecutableFunction> ExecFunc = ExecutableFunction::create(
87+
createTargetMachine(), getObjectFromBuffer(AsmStream.str()));
88+
89+
// We can't use ASSERT_THAT_EXPECTED here as it doesn't work inside of
90+
// non-void functions.
91+
EXPECT_TRUE(detail::TakeExpected(ExecFunc).Success());
92+
return std::move(*ExecFunc);
8793
}
8894

8995
const std::string TT;

0 commit comments

Comments
 (0)