[ORC] Add helper functions for running finalize / dealloc actions.

runFinalizeActions takes an AllocActions vector and attempts to run its finalize
actions. If any finalize action fails then all paired dealloc actions up to the
failing pair are run, and the error(s) returned. If all finalize actions succeed
then a vector containing the dealloc actions is returned.

runDeallocActions takes a vector<WrapperFunctionCall> containing dealloc action
calls and runs them all, returning any error(s).

These helpers are intended to simplify the implementation of
JITLinkMemoryManager::InFlightAlloc::finalize and
JITLinkMemoryManager::deallocate overrides by taking care of execution (and
potential roll-back) of allocation actions.
This commit is contained in:
Lang Hames
2022-01-10 19:08:59 +11:00
parent b645bcd98a
commit c0fdc74887
6 changed files with 98 additions and 22 deletions

View File

@@ -43,6 +43,33 @@ struct AllocActionCallPair {
/// actions will be run in reverse order at deallocation time.
using AllocActions = std::vector<AllocActionCallPair>;
/// Returns the number of deallocaton actions in the given AllocActions array.
///
/// This can be useful if clients want to pre-allocate room for deallocation
/// actions with the rest of their memory.
inline size_t numDeallocActions(const AllocActions &AAs) {
return llvm::count_if(
AAs, [](const AllocActionCallPair &P) { return !!P.Dealloc; });
}
/// Run finalize actions.
///
/// If any finalize action fails then the corresponding dealloc actions will be
/// run in reverse order (not including the deallocation action for the failed
/// finalize action), and the error for the failing action will be returned.
///
/// If all finalize actions succeed then a vector of deallocation actions will
/// be returned. The dealloc actions should be run by calling
/// runDeallocationActions. If this function succeeds then the AA argument will
/// be cleared before the function returns.
Expected<std::vector<WrapperFunctionCall>>
runFinalizeActions(AllocActions &AAs);
/// Run deallocation actions.
/// Dealloc actions will be run in reverse order (from last element of DAs to
/// first).
Error runDeallocActions(ArrayRef<WrapperFunctionCall> DAs);
using SPSAllocActionCallPair =
SPSTuple<SPSWrapperFunctionCall, SPSWrapperFunctionCall>;

View File

@@ -660,7 +660,7 @@ public:
explicit operator bool() const { return !!FnAddr; }
/// Run call returning raw WrapperFunctionResult.
shared::WrapperFunctionResult run() {
shared::WrapperFunctionResult run() const {
using FnTy =
shared::CWrapperFunctionResult(const char *ArgData, size_t ArgSize);
return shared::WrapperFunctionResult(
@@ -670,7 +670,7 @@ public:
/// Run call and deserialize result using SPS.
template <typename SPSRetT, typename RetT>
std::enable_if_t<!std::is_same<SPSRetT, void>::value, Error>
runWithSPSRet(RetT &RetVal) {
runWithSPSRet(RetT &RetVal) const {
auto WFR = run();
if (const char *ErrMsg = WFR.getOutOfBandError())
return make_error<StringError>(ErrMsg, inconvertibleErrorCode());
@@ -684,14 +684,15 @@ public:
/// Overload for SPS functions returning void.
template <typename SPSRetT>
std::enable_if_t<std::is_same<SPSRetT, void>::value, Error> runWithSPSRet() {
std::enable_if_t<std::is_same<SPSRetT, void>::value, Error>
runWithSPSRet() const {
shared::SPSEmpty E;
return runWithSPSRet<shared::SPSEmpty>(E);
}
/// Run call and deserialize an SPSError result. SPSError returns and
/// deserialization failures are merged into the returned error.
Error runWithSPSRetErrorMerged() {
Error runWithSPSRetErrorMerged() const {
detail::SPSSerializableError RetErr;
if (auto Err = runWithSPSRet<SPSError>(RetErr))
return Err;

View File

@@ -247,19 +247,11 @@ public:
}
// Run finalization actions.
// FIXME: Roll back previous successful actions on failure.
std::vector<orc::shared::WrapperFunctionCall> DeallocActions;
DeallocActions.reserve(G.allocActions().size());
for (auto &ActPair : G.allocActions()) {
if (ActPair.Finalize)
if (auto Err = ActPair.Finalize.runWithSPSRetErrorMerged()) {
OnFinalized(std::move(Err));
return;
}
if (ActPair.Dealloc)
DeallocActions.push_back(ActPair.Dealloc);
auto DeallocActions = runFinalizeActions(G.allocActions());
if (!DeallocActions) {
OnFinalized(DeallocActions.takeError());
return;
}
G.allocActions().clear();
// Release the finalize segments slab.
if (auto EC = sys::Memory::releaseMappedMemory(FinalizationSegments)) {
@@ -269,7 +261,7 @@ public:
// Continue with finalized allocation.
OnFinalized(MemMgr.createFinalizedAlloc(std::move(StandardSegments),
std::move(DeallocActions)));
std::move(*DeallocActions)));
}
void abandon(OnAbandonedFunction OnAbandoned) override {

View File

@@ -0,0 +1,44 @@
//===----- AllocationActions.gpp -- JITLink allocation support calls -----===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/ExecutionEngine/Orc/Shared/AllocationActions.h"
namespace llvm {
namespace orc {
namespace shared {
Expected<std::vector<WrapperFunctionCall>>
runFinalizeActions(AllocActions &AAs) {
std::vector<WrapperFunctionCall> DeallocActions;
DeallocActions.reserve(numDeallocActions(AAs));
for (auto &AA : AAs) {
if (AA.Finalize)
if (auto Err = AA.Finalize.runWithSPSRetErrorMerged())
return joinErrors(std::move(Err), runDeallocActions(DeallocActions));
if (AA.Dealloc)
DeallocActions.push_back(std::move(AA.Dealloc));
}
AAs.clear();
return DeallocActions;
}
Error runDeallocActions(ArrayRef<WrapperFunctionCall> DAs) {
Error Err = Error::success();
while (!DAs.empty()) {
Err = joinErrors(std::move(Err), DAs.back().runWithSPSRetErrorMerged());
DAs = DAs.drop_back();
}
return Err;
}
} // namespace shared
} // namespace orc
} // namespace llvm

View File

@@ -1,4 +1,5 @@
add_llvm_component_library(LLVMOrcShared
AllocationActions.cpp
OrcError.cpp
OrcRTBridge.cpp
SimpleRemoteEPCUtils.cpp

View File

@@ -397,6 +397,9 @@ static void dumpSectionContents(raw_ostream &OS, LinkGraph &G) {
class JITLinkSlabAllocator final : public JITLinkMemoryManager {
private:
struct FinalizedAllocInfo {
FinalizedAllocInfo(sys::MemoryBlock Mem,
std::vector<shared::WrapperFunctionCall> DeallocActions)
: Mem(Mem), DeallocActions(std::move(DeallocActions)) {}
sys::MemoryBlock Mem;
std::vector<shared::WrapperFunctionCall> DeallocActions;
};
@@ -430,12 +433,20 @@ public:
return;
}
// FIXME: Run finalize actions.
assert(BL.graphAllocActions().empty() &&
"Support function calls not supported yet");
auto DeallocActions = runFinalizeActions(BL.graphAllocActions());
if (!DeallocActions) {
OnFinalized(DeallocActions.takeError());
return;
}
OnFinalized(
FinalizedAlloc(ExecutorAddr::fromPtr(new FinalizedAllocInfo())));
if (auto Err = Parent.freeBlock(FinalizeSegs)) {
OnFinalized(
joinErrors(std::move(Err), runDeallocActions(*DeallocActions)));
return;
}
OnFinalized(FinalizedAlloc(ExecutorAddr::fromPtr(
new FinalizedAllocInfo(StandardSegs, std::move(*DeallocActions)))));
}
void abandon(OnAbandonedFunction OnAbandoned) override {