//===----------- ThreadSafeModule.h -- Layer interfaces ---------*- C++ -*-===//
 
//
 
// 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
 
//
 
//===----------------------------------------------------------------------===//
 
//
 
// Thread safe wrappers and utilities for Module and LLVMContext.
 
//
 
//===----------------------------------------------------------------------===//
 
 
 
#ifndef LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULE_H
 
#define LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULE_H
 
 
 
#include "llvm/IR/LLVMContext.h"
 
#include "llvm/IR/Module.h"
 
#include "llvm/Support/Compiler.h"
 
 
 
#include <functional>
 
#include <memory>
 
#include <mutex>
 
 
 
namespace llvm {
 
namespace orc {
 
 
 
/// An LLVMContext together with an associated mutex that can be used to lock
 
/// the context to prevent concurrent access by other threads.
 
class ThreadSafeContext {
 
private:
 
  struct State {
 
    State(std::unique_ptr<LLVMContext> Ctx) : Ctx(std::move(Ctx)) {}
 
 
 
    std::unique_ptr<LLVMContext> Ctx;
 
    std::recursive_mutex Mutex;
 
  };
 
 
 
public:
 
  // RAII based lock for ThreadSafeContext.
 
  class [[nodiscard]] Lock {
 
  public:
 
    Lock(std::shared_ptr<State> S) : S(std::move(S)), L(this->S->Mutex) {}
 
 
 
  private:
 
    std::shared_ptr<State> S;
 
    std::unique_lock<std::recursive_mutex> L;
 
  };
 
 
 
  /// Construct a null context.
 
  ThreadSafeContext() = default;
 
 
 
  /// Construct a ThreadSafeContext from the given LLVMContext.
 
  ThreadSafeContext(std::unique_ptr<LLVMContext> NewCtx)
 
      : S(std::make_shared<State>(std::move(NewCtx))) {
 
    assert(S->Ctx != nullptr &&
 
           "Can not construct a ThreadSafeContext from a nullptr");
 
  }
 
 
 
  /// Returns a pointer to the LLVMContext that was used to construct this
 
  /// instance, or null if the instance was default constructed.
 
  LLVMContext *getContext() { return S ? S->Ctx.get() : nullptr; }
 
 
 
  /// Returns a pointer to the LLVMContext that was used to construct this
 
  /// instance, or null if the instance was default constructed.
 
  const LLVMContext *getContext() const { return S ? S->Ctx.get() : nullptr; }
 
 
 
  Lock getLock() const {
 
    assert(S && "Can not lock an empty ThreadSafeContext");
 
    return Lock(S);
 
  }
 
 
 
private:
 
  std::shared_ptr<State> S;
 
};
 
 
 
/// An LLVM Module together with a shared ThreadSafeContext.
 
class ThreadSafeModule {
 
public:
 
  /// Default construct a ThreadSafeModule. This results in a null module and
 
  /// null context.
 
  ThreadSafeModule() = default;
 
 
 
  ThreadSafeModule(ThreadSafeModule &&Other) = default;
 
 
 
  ThreadSafeModule &operator=(ThreadSafeModule &&Other) {
 
    // We have to explicitly define this move operator to copy the fields in
 
    // reverse order (i.e. module first) to ensure the dependencies are
 
    // protected: The old module that is being overwritten must be destroyed
 
    // *before* the context that it depends on.
 
    // We also need to lock the context to make sure the module tear-down
 
    // does not overlap any other work on the context.
 
    if (M) {
 
      auto L = TSCtx.getLock();
 
      M = nullptr;
 
    }
 
    M = std::move(Other.M);
 
    TSCtx = std::move(Other.TSCtx);
 
    return *this;
 
  }
 
 
 
  /// Construct a ThreadSafeModule from a unique_ptr<Module> and a
 
  /// unique_ptr<LLVMContext>. This creates a new ThreadSafeContext from the
 
  /// given context.
 
  ThreadSafeModule(std::unique_ptr<Module> M, std::unique_ptr<LLVMContext> Ctx)
 
      : M(std::move(M)), TSCtx(std::move(Ctx)) {}
 
 
 
  /// Construct a ThreadSafeModule from a unique_ptr<Module> and an
 
  /// existing ThreadSafeContext.
 
  ThreadSafeModule(std::unique_ptr<Module> M, ThreadSafeContext TSCtx)
 
      : M(std::move(M)), TSCtx(std::move(TSCtx)) {}
 
 
 
  ~ThreadSafeModule() {
 
    // We need to lock the context while we destruct the module.
 
    if (M) {
 
      auto L = TSCtx.getLock();
 
      M = nullptr;
 
    }
 
  }
 
 
 
  /// Boolean conversion: This ThreadSafeModule will evaluate to true if it
 
  /// wraps a non-null module.
 
  explicit operator bool() const {
 
    if (M) {
 
      assert(TSCtx.getContext() &&
 
             "Non-null module must have non-null context");
 
      return true;
 
    }
 
    return false;
 
  }
 
 
 
  /// Locks the associated ThreadSafeContext and calls the given function
 
  /// on the contained Module.
 
  template <typename Func> decltype(auto) withModuleDo(Func &&F) {
 
    assert(M && "Can not call on null module");
 
    auto Lock = TSCtx.getLock();
 
    return F(*M);
 
  }
 
 
 
  /// Locks the associated ThreadSafeContext and calls the given function
 
  /// on the contained Module.
 
  template <typename Func> decltype(auto) withModuleDo(Func &&F) const {
 
    assert(M && "Can not call on null module");
 
    auto Lock = TSCtx.getLock();
 
    return F(*M);
 
  }
 
 
 
  /// Locks the associated ThreadSafeContext and calls the given function,
 
  /// passing the contained std::unique_ptr<Module>. The given function should
 
  /// consume the Module.
 
  template <typename Func> decltype(auto) consumingModuleDo(Func &&F) {
 
    auto Lock = TSCtx.getLock();
 
    return F(std::move(M));
 
  }
 
 
 
  /// Get a raw pointer to the contained module without locking the context.
 
  Module *getModuleUnlocked() { return M.get(); }
 
 
 
  /// Get a raw pointer to the contained module without locking the context.
 
  const Module *getModuleUnlocked() const { return M.get(); }
 
 
 
  /// Returns the context for this ThreadSafeModule.
 
  ThreadSafeContext getContext() const { return TSCtx; }
 
 
 
private:
 
  std::unique_ptr<Module> M;
 
  ThreadSafeContext TSCtx;
 
};
 
 
 
using GVPredicate = std::function<bool(const GlobalValue &)>;
 
using GVModifier = std::function<void(GlobalValue &)>;
 
 
 
/// Clones the given module on to a new context.
 
ThreadSafeModule
 
cloneToNewContext(const ThreadSafeModule &TSMW,
 
                  GVPredicate ShouldCloneDef = GVPredicate(),
 
                  GVModifier UpdateClonedDefSource = GVModifier());
 
 
 
} // End namespace orc
 
} // End namespace llvm
 
 
 
#endif // LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULE_H