<commit_before>//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Axel Naumann <axel@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "IncrementalExecutor.h"
#include "IncrementalJIT.h"
#include "Threading.h"

#include "cling/Interpreter/Value.h"
#include "cling/Interpreter/Transaction.h"
#include "cling/Utils/AST.h"
#include "cling/Utils/Output.h"
#include "cling/Utils/Platform.h"

#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/TargetOptions.h"
#include "clang/Frontend/CodeGenOptions.h"
#include "clang/Frontend/CompilerInstance.h"

#include "llvm/IR/Constants.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Target/TargetMachine.h"

using namespace llvm;

namespace cling {

namespace {

static std::unique_ptr<TargetMachine>
CreateHostTargetMachine(const clang::CompilerInstance& CI, unsigned Fmt = 0) {
  const clang::TargetOptions& TargetOpts = CI.getTargetOpts();
  const clang::CodeGenOptions& CGOpt = CI.getCodeGenOpts();
  Triple TheTriple(TargetOpts.Triple);
  if (Fmt) {
    assert(Fmt > llvm::Triple::UnknownObjectFormat &&
           Fmt <= llvm::Triple::MachO && "Invalid Format");
    TheTriple.setObjectFormat(static_cast<llvm::Triple::ObjectFormatType>(Fmt));
  }

  std::string Error;
  const Target *TheTarget
    = TargetRegistry::lookupTarget(TheTriple.getTriple(), Error);
  if (!TheTarget) {
    cling::errs() << "cling::IncrementalExecutor: unable to find target:\n"
                  << Error;
    return std::unique_ptr<TargetMachine>();
  }

  std::string MCPU;
  std::string FeaturesStr;

  llvm::TargetOptions Options = llvm::TargetOptions();
// We have to use large code model for PowerPC64 because TOC and text sections
// can be more than 2GB apart.
#if defined(__powerpc64__) || defined(__PPC64__)
  CodeModel::Model CMModel = CodeModel::Large;
#else
  CodeModel::Model CMModel = CodeModel::JITDefault;
#endif
  CodeGenOpt::Level OptLevel = CodeGenOpt::Default;
  switch (CGOpt.OptimizationLevel) {
    case 0: OptLevel = CodeGenOpt::None; break;
    case 1: OptLevel = CodeGenOpt::Less; break;
    case 2: OptLevel = CodeGenOpt::Default; break;
    case 3: OptLevel = CodeGenOpt::Aggressive; break;
    default: OptLevel = CodeGenOpt::Default;
  }

  std::unique_ptr<TargetMachine> TM;
  TM.reset(TheTarget->createTargetMachine(TheTriple.getTriple(),
                                          MCPU, FeaturesStr,
                                          Options,
                                          Optional<Reloc::Model>(),
                                          CMModel,
                                          OptLevel));
  return TM;
}
} // anonymous namespace

IncrementalExecutor::IncrementalExecutor(clang::DiagnosticsEngine& diags,
                                         const clang::CompilerInstance& CI):
  m_externalIncrementalExecutor(nullptr)
#if 0
  : m_Diags(diags)
#endif
{

  // MSVC doesn't support m_AtExitFuncsSpinLock=ATOMIC_FLAG_INIT; in the class definition
  std::atomic_flag_clear( &m_AtExitFuncsSpinLock );

  // No need to protect this access of m_AtExitFuncs, since nobody
  // can use this object yet.
  m_AtExitFuncs.reserve(256);

  std::unique_ptr<TargetMachine>
    TM(CreateHostTargetMachine(CI));
  m_BackendPasses.reset(new BackendPasses(CI.getCodeGenOpts(),
                                          CI.getTargetOpts(),
                                          CI.getLangOpts(),
                                          *TM));
  m_JIT.reset(new IncrementalJIT(*this, std::move(TM)));
}

// Keep in source: ~unique_ptr<ClingJIT> needs ClingJIT
IncrementalExecutor::~IncrementalExecutor() {}

void IncrementalExecutor::shuttingDown() {
  // No need to protect this access, since hopefully there is no concurrent
  // shutdown request.
  for (size_t I = 0, N = m_AtExitFuncs.size(); I < N; ++I) {
    const CXAAtExitElement& AEE = m_AtExitFuncs[N - I - 1];
    (*AEE.m_Func)(AEE.m_Arg);
  }
}

void IncrementalExecutor::AddAtExitFunc(void (*func) (void*), void* arg,
                                        llvm::Module* M) {
  // Register a CXAAtExit function
  cling::internal::SpinLockGuard slg(m_AtExitFuncsSpinLock);
  m_AtExitFuncs.push_back(CXAAtExitElement(func, arg, M));
}

void unresolvedSymbol()
{
  // This might get called recursively, or a billion of times. Do not generate
  // useless output; unresolvedSymbol() is always handed out with an error
  // message - that's enough.
  //cling::errs() << "IncrementalExecutor: calling unresolved symbol, "
  //  "see previous error message!\n";

  // throw exception instead?
}

void* IncrementalExecutor::HandleMissingFunction(const std::string& mangled_name)
{
  // Not found in the map, add the symbol in the list of unresolved symbols
  if (m_unresolvedSymbols.insert(mangled_name).second) {
    //cling::errs() << "IncrementalExecutor: use of undefined symbol '"
    //             << mangled_name << "'!\n";
  }

  return utils::FunctionToVoidPtr(&unresolvedSymbol);
}

void* IncrementalExecutor::NotifyLazyFunctionCreators(const std::string& mangled_name)
{
  for (std::vector<LazyFunctionCreatorFunc_t>::iterator it
         = m_lazyFuncCreator.begin(), et = m_lazyFuncCreator.end();
       it != et; ++it) {
    void* ret = (void*)((LazyFunctionCreatorFunc_t)*it)(mangled_name);
    if (ret)
      return ret;
  }
  void *address = nullptr;
  if (m_externalIncrementalExecutor)
   address = m_externalIncrementalExecutor->getAddressOfGlobal(mangled_name);

  return (address ? address : HandleMissingFunction(mangled_name));
}

#if 0
// FIXME: employ to empty module dependencies *within* the *current* module.
static void
freeCallersOfUnresolvedSymbols(llvm::SmallVectorImpl<llvm::Function*>&
                               funcsToFree, llvm::ExecutionEngine* engine) {
  llvm::SmallPtrSet<llvm::Function*, 40> funcsToFreeUnique;
  for (size_t i = 0; i < funcsToFree.size(); ++i) {
    llvm::Function* func = funcsToFree[i];
    assert(func && "Cannot free NULL function");
    if (funcsToFreeUnique.insert(func).second) {
      for (llvm::Value::use_iterator IU = func->use_begin(),
             EU = func->use_end(); IU != EU; ++IU) {
        llvm::Instruction* instUser = llvm::dyn_cast<llvm::Instruction>(*IU);
        if (!instUser) continue;
        if (!instUser->getParent()) continue;
        if (llvm::Function* userFunc = instUser->getParent()->getParent())
          funcsToFree.push_back(userFunc);
      }
    }
  }
  for (llvm::SmallPtrSet<llvm::Function*, 40>::iterator
         I = funcsToFreeUnique.begin(), E = funcsToFreeUnique.end();
       I != E; ++I) {
    // This should force the JIT to recompile the function. But the stubs stay,
    // and the JIT reuses the stubs now pointing nowhere, i.e. without updating
    // the machine code address. Fix the JIT, or hope that MCJIT helps.
    //engine->freeMachineCodeForFunction(*I);
    engine->updateGlobalMapping(*I, 0);
  }
}
#endif

IncrementalExecutor::ExecutionResult
IncrementalExecutor::runStaticInitializersOnce(const Transaction& T) {
  llvm::Module* m = T.getModule();
  assert(m && "Module must not be null");

  // We don't care whether something was unresolved before.
  m_unresolvedSymbols.clear();

  // check if there is any unresolved symbol in the list
  if (diagnoseUnresolvedSymbols("static initializers"))
    return kExeUnresolvedSymbols;

  llvm::GlobalVariable* GV
     = m->getGlobalVariable("llvm.global_ctors", true);
  // Nothing to do is good, too.
  if (!GV) return kExeSuccess;

  // Close similarity to
  // m_engine->runStaticConstructorsDestructors(false) aka
  // llvm::ExecutionEngine::runStaticConstructorsDestructors()
  // is intentional; we do an extra pass to check whether the JIT
  // managed to collect all the symbols needed by the niitializers.
  // Should be an array of '{ i32, void ()* }' structs.  The first value is
  // the init priority, which we ignore.
  llvm::ConstantArray *InitList
    = llvm::dyn_cast<llvm::ConstantArray>(GV->getInitializer());

  // We need to delete it here just in case we have recursive inits, otherwise
  // it will call inits multiple times.
  GV->eraseFromParent();

  if (InitList == 0)
    return kExeSuccess;

  //SmallVector<Function*, 2> initFuncs;

  for (unsigned i = 0, e = InitList->getNumOperands(); i != e; ++i) {
    llvm::ConstantStruct *CS
      = llvm::dyn_cast<llvm::ConstantStruct>(InitList->getOperand(i));
    if (CS == 0) continue;

    llvm::Constant *FP = CS->getOperand(1);
    if (FP->isNullValue())
      continue;  // Found a sentinal value, ignore.

    // Strip off constant expression casts.
    if (llvm::ConstantExpr *CE = llvm::dyn_cast<llvm::ConstantExpr>(FP))
      if (CE->isCast())
        FP = CE->getOperand(0);

    // Execute the ctor/dtor function!
    if (llvm::Function *F = llvm::dyn_cast<llvm::Function>(FP)) {
      const llvm::StringRef fName = F->getName();
      executeInit(fName);
/*
      initFuncs.push_back(F);
      if (fName.startswith("_GLOBAL__sub_I_")) {
        BasicBlock& BB = F->getEntryBlock();
        for (BasicBlock::iterator I = BB.begin(), E = BB.end(); I != E; ++I)
          if (CallInst* call = dyn_cast<CallInst>(I))
            initFuncs.push_back(call->getCalledFunction());
      }
*/
    }
  }

/*
  for (SmallVector<Function*,2>::iterator I = initFuncs.begin(),
         E = initFuncs.end(); I != E; ++I) {
    // Cleanup also the dangling init functions. They are in the form:
    // define internal void @_GLOBAL__I_aN() section "..."{
    // entry:
    //   call void @__cxx_global_var_init(N-1)()
    //   call void @__cxx_global_var_initM()
    //   ret void
    // }
    //
    // define internal void @__cxx_global_var_init(N-1)() section "..." {
    // entry:
    //   call void @_ZN7MyClassC1Ev(%struct.MyClass* @n)
    //   ret void
    // }

    // Erase __cxx_global_var_init(N-1)() first.
    (*I)->removeDeadConstantUsers();
    (*I)->eraseFromParent();
  }
*/

  return kExeSuccess;
}

void IncrementalExecutor::runAndRemoveStaticDestructors(Transaction* T) {
  assert(T && "Must be set");
  // Collect all the dtors bound to this transaction.
  AtExitFunctions boundToT;

  {
    cling::internal::SpinLockGuard slg(m_AtExitFuncsSpinLock);
    for (AtExitFunctions::iterator I = m_AtExitFuncs.begin();
         I != m_AtExitFuncs.end();)
      if (I->m_FromM == T->getModule()) {
        boundToT.push_back(*I);
        I = m_AtExitFuncs.erase(I);
      }
      else
        ++I;
  } // end of spin lock lifetime block.

  // 'Unload' the cxa_atexit entities.
  for (AtExitFunctions::reverse_iterator I = boundToT.rbegin(),
         E = boundToT.rend(); I != E; ++I) {
    const CXAAtExitElement& AEE = *I;
    (*AEE.m_Func)(AEE.m_Arg);
  }
}

void
IncrementalExecutor::installLazyFunctionCreator(LazyFunctionCreatorFunc_t fp)
{
  m_lazyFuncCreator.push_back(fp);
}

bool
IncrementalExecutor::addSymbol(const char* Name,  void* Addr,
                               bool Jit) {
  return m_JIT->lookupSymbol(Name, Addr, Jit).second;
}

void* IncrementalExecutor::getAddressOfGlobal(llvm::StringRef symbolName,
                                              bool* fromJIT /*=0*/) {
  // Return a symbol's address, and whether it was jitted.
  void* address = m_JIT->lookupSymbol(symbolName).first;

  // It's not from the JIT if it's in a dylib.
  if (fromJIT)
    *fromJIT = !address;

  if (!address)
    return (void*)m_JIT->getSymbolAddress(symbolName, false /*no dlsym*/);

  return address;
}

void*
IncrementalExecutor::getPointerToGlobalFromJIT(const llvm::GlobalValue& GV) {
  // Get the function / variable pointer referenced by GV.

  // We don't care whether something was unresolved before.
  m_unresolvedSymbols.clear();

  void* addr = (void*)m_JIT->getSymbolAddress(GV.getName(),
                                              false /*no dlsym*/);

  if (diagnoseUnresolvedSymbols(GV.getName(), "symbol"))
    return 0;
  return addr;
}

bool IncrementalExecutor::diagnoseUnresolvedSymbols(llvm::StringRef trigger,
                                                    llvm::StringRef title) {
  if (m_unresolvedSymbols.empty())
    return false;

  llvm::SmallVector<llvm::Function*, 128> funcsToFree;
  for (std::set<std::string>::const_iterator i = m_unresolvedSymbols.begin(),
         e = m_unresolvedSymbols.end(); i != e; ++i) {
#if 0
    // FIXME: This causes a lot of test failures, for some reason it causes
    // the call to HandleMissingFunction to be elided.
    unsigned diagID = m_Diags.getCustomDiagID(clang::DiagnosticsEngine::Error,
                                              "%0 unresolved while jitting %1");
    (void)diagID;
    //m_Diags.Report(diagID) << *i << funcname; // TODO: demangle the names.
#endif

    cling::errs() << "IncrementalExecutor::executeFunction: symbol '" << *i
                  << "' unresolved while linking ";
    if (trigger.find(utils::Synthesize::UniquePrefix) != llvm::StringRef::npos)
      cling::errs() << "[cling interface function]";
    else {
      if (!title.empty())
        cling::errs() << title << " '";
      cling::errs() << trigger;
      if (!title.empty())
        cling::errs() << "'";
    }
    cling::errs() << "!\n";

    // Be helpful, demangle!
    std::string demangledName = platform::Demangle(*i);
    if (!demangledName.empty()) {
       cling::errs()
          << "You are probably missing the definition of "
          << demangledName << "\n"
          << "Maybe you need to load the corresponding shared library?\n";
    }

    //llvm::Function *ff = m_engine->FindFunctionNamed(i->c_str());
    // i could also reference a global variable, in which case ff == 0.
    //if (ff)
    //  funcsToFree.push_back(ff);
  }
  //freeCallersOfUnresolvedSymbols(funcsToFree, m_engine.get());
  m_unresolvedSymbols.clear();
  return true;
}

}// end namespace cling
<commit_msg>Cleanup cruft from CreateHostTargetMachine.<commit_after>//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Axel Naumann <axel@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "IncrementalExecutor.h"
#include "IncrementalJIT.h"
#include "Threading.h"

#include "cling/Interpreter/Value.h"
#include "cling/Interpreter/Transaction.h"
#include "cling/Utils/AST.h"
#include "cling/Utils/Output.h"
#include "cling/Utils/Platform.h"

#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/TargetOptions.h"
#include "clang/Frontend/CodeGenOptions.h"
#include "clang/Frontend/CompilerInstance.h"

#include "llvm/IR/Constants.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Target/TargetMachine.h"

using namespace llvm;

namespace cling {

namespace {

static std::unique_ptr<TargetMachine>
CreateHostTargetMachine(const clang::CompilerInstance& CI) {
  const clang::TargetOptions& TargetOpts = CI.getTargetOpts();
  const clang::CodeGenOptions& CGOpt = CI.getCodeGenOpts();
  const std::string& Triple = TargetOpts.Triple;

  std::string Error;
  const Target *TheTarget = TargetRegistry::lookupTarget(Triple, Error);
  if (!TheTarget) {
    cling::errs() << "cling::IncrementalExecutor: unable to find target:\n"
                  << Error;
    return std::unique_ptr<TargetMachine>();
  }

// We have to use large code model for PowerPC64 because TOC and text sections
// can be more than 2GB apart.
#if defined(__powerpc64__) || defined(__PPC64__)
  CodeModel::Model CMModel = CodeModel::Large;
#else
  CodeModel::Model CMModel = CodeModel::JITDefault;
#endif
  CodeGenOpt::Level OptLevel = CodeGenOpt::Default;
  switch (CGOpt.OptimizationLevel) {
    case 0: OptLevel = CodeGenOpt::None; break;
    case 1: OptLevel = CodeGenOpt::Less; break;
    case 2: OptLevel = CodeGenOpt::Default; break;
    case 3: OptLevel = CodeGenOpt::Aggressive; break;
    default: OptLevel = CodeGenOpt::Default;
  }

  std::string MCPU;
  std::string FeaturesStr;

  return std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(Triple,
                                        MCPU, FeaturesStr,
                                        llvm::TargetOptions(),
                                        Optional<Reloc::Model>(), CMModel,
                                        OptLevel));
}

} // anonymous namespace

IncrementalExecutor::IncrementalExecutor(clang::DiagnosticsEngine& diags,
                                         const clang::CompilerInstance& CI):
  m_externalIncrementalExecutor(nullptr)
#if 0
  : m_Diags(diags)
#endif
{

  // MSVC doesn't support m_AtExitFuncsSpinLock=ATOMIC_FLAG_INIT; in the class definition
  std::atomic_flag_clear( &m_AtExitFuncsSpinLock );

  // No need to protect this access of m_AtExitFuncs, since nobody
  // can use this object yet.
  m_AtExitFuncs.reserve(256);

  std::unique_ptr<TargetMachine> TM(CreateHostTargetMachine(CI));
  m_BackendPasses.reset(new BackendPasses(CI.getCodeGenOpts(),
                                          CI.getTargetOpts(),
                                          CI.getLangOpts(),
                                          *TM));
  m_JIT.reset(new IncrementalJIT(*this, std::move(TM)));
}

// Keep in source: ~unique_ptr<ClingJIT> needs ClingJIT
IncrementalExecutor::~IncrementalExecutor() {}

void IncrementalExecutor::shuttingDown() {
  // No need to protect this access, since hopefully there is no concurrent
  // shutdown request.
  for (size_t I = 0, N = m_AtExitFuncs.size(); I < N; ++I) {
    const CXAAtExitElement& AEE = m_AtExitFuncs[N - I - 1];
    (*AEE.m_Func)(AEE.m_Arg);
  }
}

void IncrementalExecutor::AddAtExitFunc(void (*func) (void*), void* arg,
                                        llvm::Module* M) {
  // Register a CXAAtExit function
  cling::internal::SpinLockGuard slg(m_AtExitFuncsSpinLock);
  m_AtExitFuncs.push_back(CXAAtExitElement(func, arg, M));
}

void unresolvedSymbol()
{
  // This might get called recursively, or a billion of times. Do not generate
  // useless output; unresolvedSymbol() is always handed out with an error
  // message - that's enough.
  //cling::errs() << "IncrementalExecutor: calling unresolved symbol, "
  //  "see previous error message!\n";

  // throw exception instead?
}

void* IncrementalExecutor::HandleMissingFunction(const std::string& mangled_name)
{
  // Not found in the map, add the symbol in the list of unresolved symbols
  if (m_unresolvedSymbols.insert(mangled_name).second) {
    //cling::errs() << "IncrementalExecutor: use of undefined symbol '"
    //             << mangled_name << "'!\n";
  }

  return utils::FunctionToVoidPtr(&unresolvedSymbol);
}

void* IncrementalExecutor::NotifyLazyFunctionCreators(const std::string& mangled_name)
{
  for (std::vector<LazyFunctionCreatorFunc_t>::iterator it
         = m_lazyFuncCreator.begin(), et = m_lazyFuncCreator.end();
       it != et; ++it) {
    void* ret = (void*)((LazyFunctionCreatorFunc_t)*it)(mangled_name);
    if (ret)
      return ret;
  }
  void *address = nullptr;
  if (m_externalIncrementalExecutor)
   address = m_externalIncrementalExecutor->getAddressOfGlobal(mangled_name);

  return (address ? address : HandleMissingFunction(mangled_name));
}

#if 0
// FIXME: employ to empty module dependencies *within* the *current* module.
static void
freeCallersOfUnresolvedSymbols(llvm::SmallVectorImpl<llvm::Function*>&
                               funcsToFree, llvm::ExecutionEngine* engine) {
  llvm::SmallPtrSet<llvm::Function*, 40> funcsToFreeUnique;
  for (size_t i = 0; i < funcsToFree.size(); ++i) {
    llvm::Function* func = funcsToFree[i];
    assert(func && "Cannot free NULL function");
    if (funcsToFreeUnique.insert(func).second) {
      for (llvm::Value::use_iterator IU = func->use_begin(),
             EU = func->use_end(); IU != EU; ++IU) {
        llvm::Instruction* instUser = llvm::dyn_cast<llvm::Instruction>(*IU);
        if (!instUser) continue;
        if (!instUser->getParent()) continue;
        if (llvm::Function* userFunc = instUser->getParent()->getParent())
          funcsToFree.push_back(userFunc);
      }
    }
  }
  for (llvm::SmallPtrSet<llvm::Function*, 40>::iterator
         I = funcsToFreeUnique.begin(), E = funcsToFreeUnique.end();
       I != E; ++I) {
    // This should force the JIT to recompile the function. But the stubs stay,
    // and the JIT reuses the stubs now pointing nowhere, i.e. without updating
    // the machine code address. Fix the JIT, or hope that MCJIT helps.
    //engine->freeMachineCodeForFunction(*I);
    engine->updateGlobalMapping(*I, 0);
  }
}
#endif

IncrementalExecutor::ExecutionResult
IncrementalExecutor::runStaticInitializersOnce(const Transaction& T) {
  llvm::Module* m = T.getModule();
  assert(m && "Module must not be null");

  // We don't care whether something was unresolved before.
  m_unresolvedSymbols.clear();

  // check if there is any unresolved symbol in the list
  if (diagnoseUnresolvedSymbols("static initializers"))
    return kExeUnresolvedSymbols;

  llvm::GlobalVariable* GV
     = m->getGlobalVariable("llvm.global_ctors", true);
  // Nothing to do is good, too.
  if (!GV) return kExeSuccess;

  // Close similarity to
  // m_engine->runStaticConstructorsDestructors(false) aka
  // llvm::ExecutionEngine::runStaticConstructorsDestructors()
  // is intentional; we do an extra pass to check whether the JIT
  // managed to collect all the symbols needed by the niitializers.
  // Should be an array of '{ i32, void ()* }' structs.  The first value is
  // the init priority, which we ignore.
  llvm::ConstantArray *InitList
    = llvm::dyn_cast<llvm::ConstantArray>(GV->getInitializer());

  // We need to delete it here just in case we have recursive inits, otherwise
  // it will call inits multiple times.
  GV->eraseFromParent();

  if (InitList == 0)
    return kExeSuccess;

  //SmallVector<Function*, 2> initFuncs;

  for (unsigned i = 0, e = InitList->getNumOperands(); i != e; ++i) {
    llvm::ConstantStruct *CS
      = llvm::dyn_cast<llvm::ConstantStruct>(InitList->getOperand(i));
    if (CS == 0) continue;

    llvm::Constant *FP = CS->getOperand(1);
    if (FP->isNullValue())
      continue;  // Found a sentinal value, ignore.

    // Strip off constant expression casts.
    if (llvm::ConstantExpr *CE = llvm::dyn_cast<llvm::ConstantExpr>(FP))
      if (CE->isCast())
        FP = CE->getOperand(0);

    // Execute the ctor/dtor function!
    if (llvm::Function *F = llvm::dyn_cast<llvm::Function>(FP)) {
      const llvm::StringRef fName = F->getName();
      executeInit(fName);
/*
      initFuncs.push_back(F);
      if (fName.startswith("_GLOBAL__sub_I_")) {
        BasicBlock& BB = F->getEntryBlock();
        for (BasicBlock::iterator I = BB.begin(), E = BB.end(); I != E; ++I)
          if (CallInst* call = dyn_cast<CallInst>(I))
            initFuncs.push_back(call->getCalledFunction());
      }
*/
    }
  }

/*
  for (SmallVector<Function*,2>::iterator I = initFuncs.begin(),
         E = initFuncs.end(); I != E; ++I) {
    // Cleanup also the dangling init functions. They are in the form:
    // define internal void @_GLOBAL__I_aN() section "..."{
    // entry:
    //   call void @__cxx_global_var_init(N-1)()
    //   call void @__cxx_global_var_initM()
    //   ret void
    // }
    //
    // define internal void @__cxx_global_var_init(N-1)() section "..." {
    // entry:
    //   call void @_ZN7MyClassC1Ev(%struct.MyClass* @n)
    //   ret void
    // }

    // Erase __cxx_global_var_init(N-1)() first.
    (*I)->removeDeadConstantUsers();
    (*I)->eraseFromParent();
  }
*/

  return kExeSuccess;
}

void IncrementalExecutor::runAndRemoveStaticDestructors(Transaction* T) {
  assert(T && "Must be set");
  // Collect all the dtors bound to this transaction.
  AtExitFunctions boundToT;

  {
    cling::internal::SpinLockGuard slg(m_AtExitFuncsSpinLock);
    for (AtExitFunctions::iterator I = m_AtExitFuncs.begin();
         I != m_AtExitFuncs.end();)
      if (I->m_FromM == T->getModule()) {
        boundToT.push_back(*I);
        I = m_AtExitFuncs.erase(I);
      }
      else
        ++I;
  } // end of spin lock lifetime block.

  // 'Unload' the cxa_atexit entities.
  for (AtExitFunctions::reverse_iterator I = boundToT.rbegin(),
         E = boundToT.rend(); I != E; ++I) {
    const CXAAtExitElement& AEE = *I;
    (*AEE.m_Func)(AEE.m_Arg);
  }
}

void
IncrementalExecutor::installLazyFunctionCreator(LazyFunctionCreatorFunc_t fp)
{
  m_lazyFuncCreator.push_back(fp);
}

bool
IncrementalExecutor::addSymbol(const char* Name,  void* Addr,
                               bool Jit) {
  return m_JIT->lookupSymbol(Name, Addr, Jit).second;
}

void* IncrementalExecutor::getAddressOfGlobal(llvm::StringRef symbolName,
                                              bool* fromJIT /*=0*/) {
  // Return a symbol's address, and whether it was jitted.
  void* address = m_JIT->lookupSymbol(symbolName).first;

  // It's not from the JIT if it's in a dylib.
  if (fromJIT)
    *fromJIT = !address;

  if (!address)
    return (void*)m_JIT->getSymbolAddress(symbolName, false /*no dlsym*/);

  return address;
}

void*
IncrementalExecutor::getPointerToGlobalFromJIT(const llvm::GlobalValue& GV) {
  // Get the function / variable pointer referenced by GV.

  // We don't care whether something was unresolved before.
  m_unresolvedSymbols.clear();

  void* addr = (void*)m_JIT->getSymbolAddress(GV.getName(),
                                              false /*no dlsym*/);

  if (diagnoseUnresolvedSymbols(GV.getName(), "symbol"))
    return 0;
  return addr;
}

bool IncrementalExecutor::diagnoseUnresolvedSymbols(llvm::StringRef trigger,
                                                    llvm::StringRef title) {
  if (m_unresolvedSymbols.empty())
    return false;

  llvm::SmallVector<llvm::Function*, 128> funcsToFree;
  for (std::set<std::string>::const_iterator i = m_unresolvedSymbols.begin(),
         e = m_unresolvedSymbols.end(); i != e; ++i) {
#if 0
    // FIXME: This causes a lot of test failures, for some reason it causes
    // the call to HandleMissingFunction to be elided.
    unsigned diagID = m_Diags.getCustomDiagID(clang::DiagnosticsEngine::Error,
                                              "%0 unresolved while jitting %1");
    (void)diagID;
    //m_Diags.Report(diagID) << *i << funcname; // TODO: demangle the names.
#endif

    cling::errs() << "IncrementalExecutor::executeFunction: symbol '" << *i
                  << "' unresolved while linking ";
    if (trigger.find(utils::Synthesize::UniquePrefix) != llvm::StringRef::npos)
      cling::errs() << "[cling interface function]";
    else {
      if (!title.empty())
        cling::errs() << title << " '";
      cling::errs() << trigger;
      if (!title.empty())
        cling::errs() << "'";
    }
    cling::errs() << "!\n";

    // Be helpful, demangle!
    std::string demangledName = platform::Demangle(*i);
    if (!demangledName.empty()) {
       cling::errs()
          << "You are probably missing the definition of "
          << demangledName << "\n"
          << "Maybe you need to load the corresponding shared library?\n";
    }

    //llvm::Function *ff = m_engine->FindFunctionNamed(i->c_str());
    // i could also reference a global variable, in which case ff == 0.
    //if (ff)
    //  funcsToFree.push_back(ff);
  }
  //freeCallersOfUnresolvedSymbols(funcsToFree, m_engine.get());
  m_unresolvedSymbols.clear();
  return true;
}

}// end namespace cling
<|endoftext|>


<commit_before>/*
Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANNTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/**
Testcase Scenarios :

 1) Test hipHostMalloc() api with ptr as nullptr and check for return value.
 2) Test hipHostMalloc() api with size as max(size_t) and check for OOM error.
 3) Test hipHostMalloc() api with flags as max(unsigned int) and validate
 return value.
 4) Pass size as zero for hipHostMalloc() api and check ptr is reset with
 with return value success.
*/

#include <hip_test_common.hh>
#include <hip_test_helper.hh>

/**
 * Performs argument validation of hipHostMalloc api.
 */
TEST_CASE("Unit_hipHostMalloc_ArgValidation") {
#if HT_NVIDIA
  HipTest::HIP_SKIP_TEST("TODO: Need to debug");
#endif
  constexpr size_t allocSize = 1000;
  char* ptr;

  SECTION("Pass ptr as nullptr") {
    HIP_CHECK_ERROR(hipHostMalloc(static_cast<void**>(nullptr), allocSize), hipErrorInvalidValue);
  }

  SECTION("Size as max(size_t)") {
    HIP_CHECK_ERROR(hipHostMalloc(&ptr, std::numeric_limits<std::size_t>::max()),
                    hipErrorMemoryAllocation);
  }

  SECTION("Flags as max(uint)") {
    HIP_CHECK_ERROR(hipHostMalloc(&ptr, allocSize, std::numeric_limits<unsigned int>::max()),
                    hipErrorInvalidValue);
  }

  SECTION("Pass size as zero and check ptr reset") {
    HIP_CHECK(hipHostMalloc(&ptr, 0));
    REQUIRE(ptr == nullptr);
  }

  SECTION("Pass hipHostMallocCoherent and hipHostMallocNonCoherent simultaneously") {
    HIP_CHECK_ERROR(
        hipHostMalloc(&ptr, allocSize, hipHostMallocCoherent | hipHostMallocNonCoherent),
        hipErrorInvalidValue);
  }
}
<commit_msg>Update hipHostMallocTests.cc<commit_after>/*
Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANNTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/**
Testcase Scenarios :

 1) Test hipHostMalloc() api with ptr as nullptr and check for return value.
 2) Test hipHostMalloc() api with size as max(size_t) and check for OOM error.
 3) Test hipHostMalloc() api with flags as max(unsigned int) and validate
 return value.
 4) Pass size as zero for hipHostMalloc() api and check ptr is reset with
 with return value success.
*/

#include <hip_test_common.hh>
#include <hip_test_helper.hh>

/**
 * Performs argument validation of hipHostMalloc api.
 */
TEST_CASE("Unit_hipHostMalloc_ArgValidation") {
#if HT_NVIDIA
  HipTest::HIP_SKIP_TEST("TODO: Need to debug");
#endif
  constexpr size_t allocSize = 1000;
  char* ptr;

  SECTION("Pass ptr as nullptr") {
    HIP_CHECK_ERROR(hipHostMalloc(static_cast<void**>(nullptr), allocSize), hipErrorInvalidValue);
  }

  SECTION("Size as max(size_t)") {
    HIP_CHECK_ERROR(hipHostMalloc(&ptr, (std::numeric_limits<std::size_t>::max)()),
                    hipErrorMemoryAllocation);
  }

  SECTION("Flags as max(uint)") {
    HIP_CHECK_ERROR(hipHostMalloc(&ptr, allocSize, (std::numeric_limits<unsigned int>::max)()),
                    hipErrorInvalidValue);
  }

  SECTION("Pass size as zero and check ptr reset") {
    HIP_CHECK(hipHostMalloc(&ptr, 0));
    REQUIRE(ptr == nullptr);
  }

  SECTION("Pass hipHostMallocCoherent and hipHostMallocNonCoherent simultaneously") {
    HIP_CHECK_ERROR(
        hipHostMalloc(&ptr, allocSize, hipHostMallocCoherent | hipHostMallocNonCoherent),
        hipErrorInvalidValue);
  }
}
<|endoftext|>


<commit_before>/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include <iterator>
#include <memory>
#include <tuple>
#include <utility>

#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Debug.h"
#include "mlir/IR/Attributes.h"  // from @llvm-project
#include "mlir/IR/Builders.h"  // from @llvm-project
#include "mlir/IR/BuiltinOps.h"  // from @llvm-project
#include "mlir/IR/OpImplementation.h"  // from @llvm-project
#include "mlir/IR/Operation.h"  // from @llvm-project
#include "mlir/IR/OperationSupport.h"  // from @llvm-project
#include "mlir/IR/Types.h"  // from @llvm-project
#include "mlir/IR/Value.h"  // from @llvm-project
#include "mlir/IR/Visitors.h"  // from @llvm-project
#include "mlir/Pass/Pass.h"  // from @llvm-project
#include "mlir/Pass/PassRegistry.h"  // from @llvm-project
#include "mlir/Support/LogicalResult.h"  // from @llvm-project
#include "tensorflow/compiler/mlir/tensorflow/analysis/resource_alias_analysis.h"
#include "tensorflow/compiler/mlir/tensorflow/ir/tf_ops.h"
#include "tensorflow/compiler/mlir/tensorflow/ir/tf_types.h"
#include "tensorflow/compiler/mlir/tensorflow/transforms/passes_detail.h"

#define DEBUG_TYPE "tf-resource-device-inference"

namespace mlir {
namespace TF {

namespace {
constexpr char kDeviceAttr[] = "device";
constexpr char kFuncDeviceAttr[] = "tf.device";

// A pass that propagates device assignment of resources on a module. It
// performs in-function propagation, as well as cross-function propagation from
// callers to callees.
//
// This pass changes the module by adding "tf.device" attribute to function
// arguments and adding "device" attribute to TF ops.
struct ResourceDeviceInference
    : public ResourceDeviceInferencePassBase<ResourceDeviceInference> {
  void runOnOperation() override;
};

// A class that records each resource's device assignment in a function.
class PerFunctionResult {
 public:
  explicit PerFunctionResult(
      FuncOp func_op, const TF::ResourceAliasAnalysis::Info& alias_analysis)
      : alias_analysis_(alias_analysis) {}

  // Returns the recorded device assignment for a resource, if any.
  Optional<StringRef> DeviceForResource(Value resource) const {
    Optional<StringRef> result;
    if (alias_analysis_.IsUnknownResource(resource)) return llvm::None;
    for (int64_t id : alias_analysis_.GetResourceUniqueIds(resource)) {
      auto it = resource_id_to_device_.find(id);
      if (it == resource_id_to_device_.end()) continue;
      if (!result || result == it->second) {
        result = it->getSecond();
        continue;
      }
      // Got conflicting assignments
      return llvm::None;
    }
    return result;
  }

  // Records the device assignment for a resource. If the new assignment
  // conflicts with an existing one, returns an error.
  //
  // If `changed` is provided, assign *changed to true if anything is modified.
  LogicalResult AddResourceDevice(Value resource, StringRef device,
                                  bool* changed = nullptr) {
    if (alias_analysis_.IsUnknownResource(resource)) return success();
    for (int64_t id : alias_analysis_.GetResourceUniqueIds(resource)) {
      auto emplace_res = resource_id_to_device_.try_emplace(id, device);
      if (emplace_res.second) {
        if (changed) *changed = true;
      } else if (emplace_res.first->getSecond() != device) {
        // Existing assignment does not equal the new assignment.
        return failure();
      }
    }
    return success();
  }

 private:
  llvm::SmallDenseMap<int64_t, StringRef, 8> resource_id_to_device_;
  const TF::ResourceAliasAnalysis::Info& alias_analysis_;
};

// Tries to record device assignment for a resource.
LogicalResult AddResourceDeviceAndEmitError(Value resource, StringRef device,
                                            Operation* error_reporting_op,
                                            PerFunctionResult* result,
                                            bool* changed = nullptr) {
  auto res = result->AddResourceDevice(resource, device, changed);
  if (failed(res)) {
    error_reporting_op->emitError()
        << "Conflicting device assignment for resource";
  }
  return res;
}

// Extracts and canonicalizes the device attribute.
inline StringRef GetDeviceAttr(FuncOp func, int arg_no) {
  auto device_attr =
      func.getArgAttrOfType<mlir::StringAttr>(arg_no, kFuncDeviceAttr);
  return device_attr ? device_attr.getValue() : "";
}

// Extracts and canonicalizes the device attribute.
inline StringRef GetDeviceAttr(Operation* op) {
  auto device_attr = op->getAttrOfType<mlir::StringAttr>(kDeviceAttr);
  return device_attr ? device_attr.getValue() : "";
}

// Print operation with debug info (to get line number info for debugging)
void dump(StringRef message, Operation* op) {
  llvm::dbgs() << message;
  op->print(llvm::dbgs(), OpPrintingFlags().enableDebugInfo(true));
  llvm::dbgs() << "\n";
}

// Propagates device assignment inside a function.
LogicalResult ComputeResourceDevicesInComputation(FuncOp func_op,
                                                  PerFunctionResult* result) {
  OpBuilder builder(func_op);
  // Function arguments.
  for (auto arg : filter_resources(func_op.getArguments())) {
    StringRef device_attr = GetDeviceAttr(func_op, arg.getArgNumber());
    if (device_attr.empty()) {
      // If device_attr does not exist, try to construct it from any recorded
      // assignment.
      if (auto device = result->DeviceForResource(arg)) {
        func_op.setArgAttr(arg.getArgNumber(), kFuncDeviceAttr,
                           builder.getStringAttr(*device));
      }
      continue;
    }
    // Record the attribute.
    auto res = AddResourceDeviceAndEmitError(arg, device_attr, func_op, result);
    if (failed(res)) return res;
  }

  // To support WhileRegion, we need to propagate device attributes from
  // WhileRegion operands to body/cond region arguments *prior* to visiting
  // these regions.
  WalkResult walk_res = func_op->walk([&](Operation* op,
                                          const WalkStage& stage) {
    // We just need to visit operations in pre-order mode.
    if (!stage.isBeforeAllRegions()) return WalkResult::advance();

    if (auto var_handle = dyn_cast<VarHandleOp>(op)) {
      // Record VarHandleOp's device attribute.
      StringRef device_attr = GetDeviceAttr(op);
      if (device_attr.empty()) return WalkResult::advance();
      auto res = AddResourceDeviceAndEmitError(var_handle.resource(),
                                               device_attr, op, result);
      if (failed(res)) return WalkResult::interrupt();
    } else if (auto identity = dyn_cast<IdentityOp>(op)) {
      LLVM_DEBUG(dump("Visiting ", identity));
      // Try to construct IdentityOp's attribute from recorded assignment.
      if (!GetDeviceAttr(op).empty()) return WalkResult::advance();
      for (auto output : filter_resources(op->getResults())) {
        LLVM_DEBUG(llvm::dbgs() << "  Processing output #"
                                << output.getResultNumber() << "\n");
        if (auto device = result->DeviceForResource(output)) {
          LLVM_DEBUG(llvm::dbgs() << " Setting device = " << *device << "\n");
          identity->setAttr(kDeviceAttr, builder.getStringAttr(*device));
        }
      }
    } else if (auto while_region = dyn_cast<WhileRegionOp>(op)) {
      // For WhileRegion, do local analysis prior to visiting the attached
      // regions and propagate device annotations to the cond and body
      // region arguments. The annotations are the union of annotations
      // on the input and result. Resource alias analysis already propagates
      // resource ID from the inputs to the results for a while, so just
      // need to consider the results.
      LLVM_DEBUG(llvm::dbgs() << "Visiting WhileRegion\n");

      for (auto output : filter_resources(while_region.getResults())) {
        auto device = result->DeviceForResource(output);
        int output_index = output.getResultNumber();
        if (!device) {
          LLVM_DEBUG(llvm::dbgs()
                     << "  No device for output #" << output_index << "\n");
          continue;
        }
        // Transfer the annotation to both region arguments
        for (Region* region : while_region.getRegions()) {
          BlockArgument arg = region->getArgument(output_index);
          LLVM_DEBUG(llvm::dbgs()
                     << "  Propagating device = '" << *device << "' to arg #"
                     << output_index << " of region #"
                     << region->getRegionNumber() << "\n");
          if (failed(AddResourceDeviceAndEmitError(arg, *device, while_region,
                                                   result)))
            return WalkResult::interrupt();
        }
      }
    }
    return WalkResult::advance();
  });
  return failure(walk_res.wasInterrupted());
}

void ResourceDeviceInference::runOnOperation() {
  auto module = getOperation();
  const auto& resource_alias_analysis =
      getAnalysis<TF::ResourceAliasAnalysis>();

  llvm::SmallDenseMap<FuncOp, PerFunctionResult, 4> per_function_results;
  llvm::SetVector<FuncOp> worklist;
  for (auto func_op : module.getOps<FuncOp>()) {
    worklist.insert(func_op);
    per_function_results.try_emplace(
        func_op, func_op, resource_alias_analysis.GetAnalysisForFunc(func_op));
  }
  // Helper that propagates an op's recorded operand device assignments to its
  // called function's arguments.
  auto propagate_operands_to_callee_arguments =
      [&](Operation* caller, Operation::operand_range caller_operands,
          ArrayRef<FuncOp> callees, const PerFunctionResult& caller_res) {
        for (FuncOp callee : callees) {
          assert(callee);
          auto& callee_res = per_function_results.find(callee)->getSecond();
          bool callee_needs_recompute = false;
          for (BlockArgument arg : filter_resources(callee.getArguments())) {
            Value arg_operand = caller_operands[arg.getArgNumber()];
            auto device = caller_res.DeviceForResource(arg_operand);
            if (!device) continue;
            LLVM_DEBUG(llvm::dbgs()
                       << "Propagating '" << *device << "' to arg #"
                       << arg.getArgNumber() << " of function @"
                       << callee.getName() << "\n");
            if (failed(AddResourceDeviceAndEmitError(arg, *device, caller,
                                                     &callee_res,
                                                     &callee_needs_recompute)))
              return failure();
          }
          // If the callee recording is modified, make sure that it will be
          // reprocessed.
          if (callee_needs_recompute) worklist.insert(callee);
        }
        return success();
      };

  while (!worklist.empty()) {
    auto func_op = worklist.pop_back_val();
    auto& func_res = per_function_results.find(func_op)->getSecond();
    // In-function propagation.
    if (failed(ComputeResourceDevicesInComputation(func_op, &func_res)))
      return signalPassFailure();

    // Propagation to callees.
    auto walk_res = func_op.walk([&](Operation* op) {
      if (auto while_op = dyn_cast<WhileOp>(op)) {
        if (failed(propagate_operands_to_callee_arguments(
                while_op, while_op.getOperands(),
                {while_op.body_function(), while_op.cond_function()},
                func_res)))
          return WalkResult::interrupt();
      } else if (auto if_op = dyn_cast<IfOp>(op)) {
        if (failed(propagate_operands_to_callee_arguments(
                if_op, if_op.input(),
                {if_op.then_function(), if_op.else_function()}, func_res)))
          return WalkResult::interrupt();
      } else if (auto call = dyn_cast<CallOpInterface>(op)) {
        auto func = dyn_cast<FuncOp>(call.resolveCallable());
        if (!func) {
          op->emitError(
              "Cannot propagate device attribute to callee: Unable to resolve "
              "call");
          return WalkResult::interrupt();
        }
        LLVM_DEBUG(llvm::dbgs()
                   << "Visiting call to function @" << func.getName() << "\n");
        if (failed(propagate_operands_to_callee_arguments(
                call, call.getArgOperands(), {func}, func_res)))
          return WalkResult::interrupt();
      }
      return WalkResult::advance();
    });
    if (walk_res.wasInterrupted()) return signalPassFailure();
  }
}

}  // namespace

std::unique_ptr<OperationPass<ModuleOp>> CreateResourceDeviceInferencePass() {
  return std::make_unique<ResourceDeviceInference>();
}

}  // namespace TF
}  // namespace mlir
<commit_msg>[MLIR] Use MLIR's pre-order walker instead of generic walker. - Fix resource_device_inference pass to use MLIR's pre-order walker. It currently uses   MLIR's generic walker to implement a pre-order walk, but that functionality is now    supported directly by MLIR core walkers.<commit_after>/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include <iterator>
#include <memory>
#include <tuple>
#include <utility>

#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Debug.h"
#include "mlir/IR/Attributes.h"  // from @llvm-project
#include "mlir/IR/Builders.h"  // from @llvm-project
#include "mlir/IR/BuiltinOps.h"  // from @llvm-project
#include "mlir/IR/OpImplementation.h"  // from @llvm-project
#include "mlir/IR/Operation.h"  // from @llvm-project
#include "mlir/IR/OperationSupport.h"  // from @llvm-project
#include "mlir/IR/Types.h"  // from @llvm-project
#include "mlir/IR/Value.h"  // from @llvm-project
#include "mlir/IR/Visitors.h"  // from @llvm-project
#include "mlir/Pass/Pass.h"  // from @llvm-project
#include "mlir/Pass/PassRegistry.h"  // from @llvm-project
#include "mlir/Support/LogicalResult.h"  // from @llvm-project
#include "tensorflow/compiler/mlir/tensorflow/analysis/resource_alias_analysis.h"
#include "tensorflow/compiler/mlir/tensorflow/ir/tf_ops.h"
#include "tensorflow/compiler/mlir/tensorflow/ir/tf_types.h"
#include "tensorflow/compiler/mlir/tensorflow/transforms/passes_detail.h"

#define DEBUG_TYPE "tf-resource-device-inference"

namespace mlir {
namespace TF {

namespace {
constexpr char kDeviceAttr[] = "device";
constexpr char kFuncDeviceAttr[] = "tf.device";

// A pass that propagates device assignment of resources on a module. It
// performs in-function propagation, as well as cross-function propagation from
// callers to callees.
//
// This pass changes the module by adding "tf.device" attribute to function
// arguments and adding "device" attribute to TF ops.
struct ResourceDeviceInference
    : public ResourceDeviceInferencePassBase<ResourceDeviceInference> {
  void runOnOperation() override;
};

// A class that records each resource's device assignment in a function.
class PerFunctionResult {
 public:
  explicit PerFunctionResult(
      FuncOp func_op, const TF::ResourceAliasAnalysis::Info& alias_analysis)
      : alias_analysis_(alias_analysis) {}

  // Returns the recorded device assignment for a resource, if any.
  Optional<StringRef> DeviceForResource(Value resource) const {
    Optional<StringRef> result;
    if (alias_analysis_.IsUnknownResource(resource)) return llvm::None;
    for (int64_t id : alias_analysis_.GetResourceUniqueIds(resource)) {
      auto it = resource_id_to_device_.find(id);
      if (it == resource_id_to_device_.end()) continue;
      if (!result || result == it->second) {
        result = it->getSecond();
        continue;
      }
      // Got conflicting assignments
      return llvm::None;
    }
    return result;
  }

  // Records the device assignment for a resource. If the new assignment
  // conflicts with an existing one, returns an error.
  //
  // If `changed` is provided, assign *changed to true if anything is modified.
  LogicalResult AddResourceDevice(Value resource, StringRef device,
                                  bool* changed = nullptr) {
    if (alias_analysis_.IsUnknownResource(resource)) return success();
    for (int64_t id : alias_analysis_.GetResourceUniqueIds(resource)) {
      auto emplace_res = resource_id_to_device_.try_emplace(id, device);
      if (emplace_res.second) {
        if (changed) *changed = true;
      } else if (emplace_res.first->getSecond() != device) {
        // Existing assignment does not equal the new assignment.
        return failure();
      }
    }
    return success();
  }

 private:
  llvm::SmallDenseMap<int64_t, StringRef, 8> resource_id_to_device_;
  const TF::ResourceAliasAnalysis::Info& alias_analysis_;
};

// Tries to record device assignment for a resource.
LogicalResult AddResourceDeviceAndEmitError(Value resource, StringRef device,
                                            Operation* error_reporting_op,
                                            PerFunctionResult* result,
                                            bool* changed = nullptr) {
  auto res = result->AddResourceDevice(resource, device, changed);
  if (failed(res)) {
    error_reporting_op->emitError()
        << "Conflicting device assignment for resource";
  }
  return res;
}

// Extracts and canonicalizes the device attribute.
inline StringRef GetDeviceAttr(FuncOp func, int arg_no) {
  auto device_attr =
      func.getArgAttrOfType<mlir::StringAttr>(arg_no, kFuncDeviceAttr);
  return device_attr ? device_attr.getValue() : "";
}

// Extracts and canonicalizes the device attribute.
inline StringRef GetDeviceAttr(Operation* op) {
  auto device_attr = op->getAttrOfType<mlir::StringAttr>(kDeviceAttr);
  return device_attr ? device_attr.getValue() : "";
}

// Print operation with debug info (to get line number info for debugging)
void dump(StringRef message, Operation* op) {
  llvm::dbgs() << message;
  op->print(llvm::dbgs(), OpPrintingFlags().enableDebugInfo(true));
  llvm::dbgs() << "\n";
}

// Propagates device assignment inside a function.
LogicalResult ComputeResourceDevicesInComputation(FuncOp func_op,
                                                  PerFunctionResult* result) {
  OpBuilder builder(func_op);
  // Function arguments.
  for (auto arg : filter_resources(func_op.getArguments())) {
    StringRef device_attr = GetDeviceAttr(func_op, arg.getArgNumber());
    if (device_attr.empty()) {
      // If device_attr does not exist, try to construct it from any recorded
      // assignment.
      if (auto device = result->DeviceForResource(arg)) {
        func_op.setArgAttr(arg.getArgNumber(), kFuncDeviceAttr,
                           builder.getStringAttr(*device));
      }
      continue;
    }
    // Record the attribute.
    auto res = AddResourceDeviceAndEmitError(arg, device_attr, func_op, result);
    if (failed(res)) return res;
  }

  // To support WhileRegion, we need to propagate device attributes from
  // WhileRegion operands to body/cond region arguments *prior* to visiting
  // these regions, so use a pre-order walk.
  WalkResult walk_res = func_op.walk<WalkOrder::PreOrder>([&](Operation* op) {
    if (auto var_handle = dyn_cast<VarHandleOp>(op)) {
      // Record VarHandleOp's device attribute.
      StringRef device_attr = GetDeviceAttr(op);
      if (device_attr.empty()) return WalkResult::advance();
      auto res = AddResourceDeviceAndEmitError(var_handle.resource(),
                                               device_attr, op, result);
      if (failed(res)) return WalkResult::interrupt();
    } else if (auto identity = dyn_cast<IdentityOp>(op)) {
      LLVM_DEBUG(dump("Visiting ", identity));
      // Try to construct IdentityOp's attribute from recorded assignment.
      if (!GetDeviceAttr(op).empty()) return WalkResult::advance();
      for (auto output : filter_resources(op->getResults())) {
        LLVM_DEBUG(llvm::dbgs() << "  Processing output #"
                                << output.getResultNumber() << "\n");
        if (auto device = result->DeviceForResource(output)) {
          LLVM_DEBUG(llvm::dbgs() << " Setting device = " << *device << "\n");
          identity->setAttr(kDeviceAttr, builder.getStringAttr(*device));
        }
      }
    } else if (auto while_region = dyn_cast<WhileRegionOp>(op)) {
      // For WhileRegion, do local analysis prior to visiting the attached
      // regions and propagate device annotations to the cond and body
      // region arguments. The annotations are the union of annotations
      // on the input and result. Resource alias analysis already propagates
      // resource ID from the inputs to the results for a while, so just
      // need to consider the results.
      LLVM_DEBUG(llvm::dbgs() << "Visiting WhileRegion\n");

      for (auto output : filter_resources(while_region.getResults())) {
        auto device = result->DeviceForResource(output);
        int output_index = output.getResultNumber();
        if (!device) {
          LLVM_DEBUG(llvm::dbgs()
                     << "  No device for output #" << output_index << "\n");
          continue;
        }
        // Transfer the annotation to both region arguments
        for (Region* region : while_region.getRegions()) {
          BlockArgument arg = region->getArgument(output_index);
          LLVM_DEBUG(llvm::dbgs()
                     << "  Propagating device = '" << *device << "' to arg #"
                     << output_index << " of region #"
                     << region->getRegionNumber() << "\n");
          if (failed(AddResourceDeviceAndEmitError(arg, *device, while_region,
                                                   result)))
            return WalkResult::interrupt();
        }
      }
    }
    return WalkResult::advance();
  });
  return failure(walk_res.wasInterrupted());
}

void ResourceDeviceInference::runOnOperation() {
  auto module = getOperation();
  const auto& resource_alias_analysis =
      getAnalysis<TF::ResourceAliasAnalysis>();

  llvm::SmallDenseMap<FuncOp, PerFunctionResult, 4> per_function_results;
  llvm::SetVector<FuncOp> worklist;
  for (auto func_op : module.getOps<FuncOp>()) {
    worklist.insert(func_op);
    per_function_results.try_emplace(
        func_op, func_op, resource_alias_analysis.GetAnalysisForFunc(func_op));
  }
  // Helper that propagates an op's recorded operand device assignments to its
  // called function's arguments.
  auto propagate_operands_to_callee_arguments =
      [&](Operation* caller, Operation::operand_range caller_operands,
          ArrayRef<FuncOp> callees, const PerFunctionResult& caller_res) {
        for (FuncOp callee : callees) {
          assert(callee);
          auto& callee_res = per_function_results.find(callee)->getSecond();
          bool callee_needs_recompute = false;
          for (BlockArgument arg : filter_resources(callee.getArguments())) {
            Value arg_operand = caller_operands[arg.getArgNumber()];
            auto device = caller_res.DeviceForResource(arg_operand);
            if (!device) continue;
            LLVM_DEBUG(llvm::dbgs()
                       << "Propagating '" << *device << "' to arg #"
                       << arg.getArgNumber() << " of function @"
                       << callee.getName() << "\n");
            if (failed(AddResourceDeviceAndEmitError(arg, *device, caller,
                                                     &callee_res,
                                                     &callee_needs_recompute)))
              return failure();
          }
          // If the callee recording is modified, make sure that it will be
          // reprocessed.
          if (callee_needs_recompute) worklist.insert(callee);
        }
        return success();
      };

  while (!worklist.empty()) {
    auto func_op = worklist.pop_back_val();
    auto& func_res = per_function_results.find(func_op)->getSecond();
    // In-function propagation.
    if (failed(ComputeResourceDevicesInComputation(func_op, &func_res)))
      return signalPassFailure();

    // Propagation to callees.
    auto walk_res = func_op.walk([&](Operation* op) {
      if (auto while_op = dyn_cast<WhileOp>(op)) {
        if (failed(propagate_operands_to_callee_arguments(
                while_op, while_op.getOperands(),
                {while_op.body_function(), while_op.cond_function()},
                func_res)))
          return WalkResult::interrupt();
      } else if (auto if_op = dyn_cast<IfOp>(op)) {
        if (failed(propagate_operands_to_callee_arguments(
                if_op, if_op.input(),
                {if_op.then_function(), if_op.else_function()}, func_res)))
          return WalkResult::interrupt();
      } else if (auto call = dyn_cast<CallOpInterface>(op)) {
        auto func = dyn_cast<FuncOp>(call.resolveCallable());
        if (!func) {
          op->emitError(
              "Cannot propagate device attribute to callee: Unable to resolve "
              "call");
          return WalkResult::interrupt();
        }
        LLVM_DEBUG(llvm::dbgs()
                   << "Visiting call to function @" << func.getName() << "\n");
        if (failed(propagate_operands_to_callee_arguments(
                call, call.getArgOperands(), {func}, func_res)))
          return WalkResult::interrupt();
      }
      return WalkResult::advance();
    });
    if (walk_res.wasInterrupted()) return signalPassFailure();
  }
}

}  // namespace

std::unique_ptr<OperationPass<ModuleOp>> CreateResourceDeviceInferencePass() {
  return std::make_unique<ResourceDeviceInference>();
}

}  // namespace TF
}  // namespace mlir
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/gfx/image/image.h"

#include <algorithm>

#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/size.h"

#if defined(TOOLKIT_GTK)
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gdk/gdk.h>
#include <glib-object.h>
#include "ui/gfx/canvas.h"
#include "ui/gfx/gtk_util.h"
#include "ui/gfx/image/cairo_cached_surface.h"
#elif defined(OS_MACOSX)
#include "base/mac/mac_util.h"
#include "skia/ext/skia_utils_mac.h"
#endif

namespace gfx {

namespace internal {

#if defined(OS_MACOSX)
// This is a wrapper around gfx::NSImageToSkBitmap() because this cross-platform
// file cannot include the [square brackets] of ObjC.
ImageSkia NSImageToImageSkia(NSImage* image);
NSImage* ImageSkiaToNSImage(const ImageSkia* image);
#endif

#if defined(TOOLKIT_GTK)
const SkBitmap* GdkPixbufToSkBitmap(GdkPixbuf* pixbuf) {
  CHECK(pixbuf);
  gfx::Canvas canvas(gfx::Size(gdk_pixbuf_get_width(pixbuf),
                               gdk_pixbuf_get_height(pixbuf)), false);
  skia::ScopedPlatformPaint scoped_platform_paint(canvas.sk_canvas());
  cairo_t* cr = scoped_platform_paint.GetPlatformSurface();
  gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
  cairo_paint(cr);
  return new SkBitmap(canvas.ExtractBitmap());
}
#endif

class ImageRepSkia;
class ImageRepGdk;
class ImageRepCairoCached;
class ImageRepCocoa;

// An ImageRep is the object that holds the backing memory for an Image. Each
// RepresentationType has an ImageRep subclass that is responsible for freeing
// the memory that the ImageRep holds. When an ImageRep is created, it expects
// to take ownership of the image, without having to retain it or increase its
// reference count.
class ImageRep {
 public:
  explicit ImageRep(Image::RepresentationType rep) : type_(rep) {}

  // Deletes the associated pixels of an ImageRep.
  virtual ~ImageRep() {}

  // Cast helpers ("fake RTTI").
  ImageRepSkia* AsImageRepSkia() {
    CHECK_EQ(type_, Image::kImageRepSkia);
    return reinterpret_cast<ImageRepSkia*>(this);
  }

#if defined(TOOLKIT_GTK)
  ImageRepGdk* AsImageRepGdk() {
    CHECK_EQ(type_, Image::kImageRepGdk);
    return reinterpret_cast<ImageRepGdk*>(this);
  }

  ImageRepCairoCached* AsImageRepCairo() {
    CHECK_EQ(type_, Image::kImageRepCairoCache);
    return reinterpret_cast<ImageRepCairoCached*>(this);
  }
#endif

#if defined(OS_MACOSX)
  ImageRepCocoa* AsImageRepCocoa() {
    CHECK_EQ(type_, Image::kImageRepCocoa);
    return reinterpret_cast<ImageRepCocoa*>(this);
  }
#endif

  Image::RepresentationType type() const { return type_; }

 private:
  Image::RepresentationType type_;
};

class ImageRepSkia : public ImageRep {
 public:
  // Takes ownership of |image|.
  explicit ImageRepSkia(ImageSkia* image)
      : ImageRep(Image::kImageRepSkia),
        image_(image) {
  }

  virtual ~ImageRepSkia() {
  }

  ImageSkia* image() { return image_.get(); }

 private:
  scoped_ptr<ImageSkia> image_;

  DISALLOW_COPY_AND_ASSIGN(ImageRepSkia);
};

#if defined(TOOLKIT_GTK)
class ImageRepGdk : public ImageRep {
 public:
  explicit ImageRepGdk(GdkPixbuf* pixbuf)
      : ImageRep(Image::kImageRepGdk),
        pixbuf_(pixbuf) {
    CHECK(pixbuf);
  }

  virtual ~ImageRepGdk() {
    if (pixbuf_) {
      g_object_unref(pixbuf_);
      pixbuf_ = NULL;
    }
  }

  GdkPixbuf* pixbuf() const { return pixbuf_; }

 private:
  GdkPixbuf* pixbuf_;

  DISALLOW_COPY_AND_ASSIGN(ImageRepGdk);
};

// Represents data that lives on the display server instead of in the client.
class ImageRepCairoCached : public ImageRep {
 public:
  explicit ImageRepCairoCached(GdkPixbuf* pixbuf)
      : ImageRep(Image::kImageRepCairoCache),
        cairo_cache_(new CairoCachedSurface) {
    CHECK(pixbuf);
    cairo_cache_->UsePixbuf(pixbuf);
  }

  virtual ~ImageRepCairoCached() {
    delete cairo_cache_;
  }

  CairoCachedSurface* surface() const { return cairo_cache_; }

 private:
  CairoCachedSurface* cairo_cache_;

  DISALLOW_COPY_AND_ASSIGN(ImageRepCairoCached);
};
#endif  // defined(TOOLKIT_GTK)

#if defined(OS_MACOSX)
class ImageRepCocoa : public ImageRep {
 public:
  explicit ImageRepCocoa(NSImage* image)
      : ImageRep(Image::kImageRepCocoa),
        image_(image) {
    CHECK(image);
  }

  virtual ~ImageRepCocoa() {
    base::mac::NSObjectRelease(image_);
    image_ = nil;
  }

  NSImage* image() const { return image_; }

 private:
  NSImage* image_;

  DISALLOW_COPY_AND_ASSIGN(ImageRepCocoa);
};
#endif  // defined(OS_MACOSX)

// The Storage class acts similarly to the pixels in a SkBitmap: the Image
// class holds a refptr instance of Storage, which in turn holds all the
// ImageReps. This way, the Image can be cheaply copied.
class ImageStorage : public base::RefCounted<ImageStorage> {
 public:
  ImageStorage(gfx::Image::RepresentationType default_type)
      : default_representation_type_(default_type) {
  }

  gfx::Image::RepresentationType default_representation_type() {
    return default_representation_type_;
  }
  gfx::Image::RepresentationMap& representations() { return representations_; }

 private:
  ~ImageStorage() {
    for (gfx::Image::RepresentationMap::iterator it = representations_.begin();
         it != representations_.end();
         ++it) {
      delete it->second;
    }
    representations_.clear();
  }

  // The type of image that was passed to the constructor. This key will always
  // exist in the |representations_| map.
  gfx::Image::RepresentationType default_representation_type_;

  // All the representations of an Image. Size will always be at least one, with
  // more for any converted representations.
  gfx::Image::RepresentationMap representations_;

  friend class base::RefCounted<ImageStorage>;
};

}  // namespace internal

Image::Image() {
  // |storage_| is NULL for empty Images.
}

Image::Image(const ImageSkia& image)
    : storage_(new internal::ImageStorage(Image::kImageRepSkia)) {
  internal::ImageRepSkia* rep = new internal::ImageRepSkia(
      new ImageSkia(image));
  AddRepresentation(rep);
}

Image::Image(const SkBitmap* bitmap)
    : storage_(new internal::ImageStorage(Image::kImageRepSkia)) {
  internal::ImageRepSkia* rep = new internal::ImageRepSkia(
      new ImageSkia(bitmap));
  AddRepresentation(rep);
}

Image::Image(const SkBitmap& bitmap)
    : storage_(new internal::ImageStorage(Image::kImageRepSkia)) {
  internal::ImageRepSkia* rep =
      new internal::ImageRepSkia(new ImageSkia(bitmap));
  AddRepresentation(rep);
}

#if defined(TOOLKIT_GTK)
Image::Image(GdkPixbuf* pixbuf)
    : storage_(new internal::ImageStorage(Image::kImageRepGdk)) {
  internal::ImageRepGdk* rep = new internal::ImageRepGdk(pixbuf);
  AddRepresentation(rep);
}
#endif

#if defined(OS_MACOSX)
Image::Image(NSImage* image)
    : storage_(new internal::ImageStorage(Image::kImageRepCocoa)) {
  internal::ImageRepCocoa* rep = new internal::ImageRepCocoa(image);
  AddRepresentation(rep);
}
#endif

Image::Image(const Image& other) : storage_(other.storage_) {
}

Image& Image::operator=(const Image& other) {
  storage_ = other.storage_;
  return *this;
}

Image::~Image() {
}

const SkBitmap* Image::ToSkBitmap() const {
  internal::ImageRep* rep = GetRepresentation(Image::kImageRepSkia);
  return rep->AsImageRepSkia()->image()->bitmap();
}

const ImageSkia* Image::ToImageSkia() const {
  internal::ImageRep* rep = GetRepresentation(Image::kImageRepSkia);
  return rep->AsImageRepSkia()->image();
}

#if defined(TOOLKIT_GTK)
GdkPixbuf* Image::ToGdkPixbuf() const {
  internal::ImageRep* rep = GetRepresentation(Image::kImageRepGdk);
  return rep->AsImageRepGdk()->pixbuf();
}

CairoCachedSurface* const Image::ToCairo() const {
  internal::ImageRep* rep = GetRepresentation(Image::kImageRepCairoCache);
  return rep->AsImageRepCairo()->surface();
}
#endif

#if defined(OS_MACOSX)
NSImage* Image::ToNSImage() const {
  internal::ImageRep* rep = GetRepresentation(Image::kImageRepCocoa);
  return rep->AsImageRepCocoa()->image();
}
#endif

ImageSkia* Image::CopyImageSkia() const {
  return new ImageSkia(*ToImageSkia());
}

SkBitmap* Image::CopySkBitmap() const {
  return new SkBitmap(*ToSkBitmap());
}

#if defined(TOOLKIT_GTK)
GdkPixbuf* Image::CopyGdkPixbuf() const {
  GdkPixbuf* pixbuf = ToGdkPixbuf();
  g_object_ref(pixbuf);
  return pixbuf;
}
#endif

#if defined(OS_MACOSX)
NSImage* Image::CopyNSImage() const {
  NSImage* image = ToNSImage();
  base::mac::NSObjectRetain(image);
  return image;
}
#endif

#if defined(OS_MACOSX)
Image::operator NSImage*() const {
  return ToNSImage();
}
#endif

bool Image::HasRepresentation(RepresentationType type) const {
  return storage_.get() && storage_->representations().count(type) != 0;
}

size_t Image::RepresentationCount() const {
  if (!storage_.get())
    return 0;

  return storage_->representations().size();
}

bool Image::IsEmpty() const {
  return RepresentationCount() == 0;
}

void Image::SwapRepresentations(gfx::Image* other) {
  storage_.swap(other->storage_);
}

internal::ImageRep* Image::DefaultRepresentation() const {
  CHECK(storage_.get());
  RepresentationMap& representations = storage_->representations();
  RepresentationMap::iterator it =
      representations.find(storage_->default_representation_type());
  DCHECK(it != representations.end());
  return it->second;
}

internal::ImageRep* Image::GetRepresentation(
    RepresentationType rep_type) const {
  CHECK(storage_.get());
  // If the requested rep is the default, return it.
  internal::ImageRep* default_rep = DefaultRepresentation();
  if (rep_type == storage_->default_representation_type())
    return default_rep;

  // Check to see if the representation already exists.
  RepresentationMap::iterator it = storage_->representations().find(rep_type);
  if (it != storage_->representations().end())
    return it->second;

  // At this point, the requested rep does not exist, so it must be converted
  // from the default rep.

  // Handle native-to-Skia conversion.
  if (rep_type == Image::kImageRepSkia) {
    internal::ImageRepSkia* rep = NULL;
#if defined(TOOLKIT_GTK)
    if (storage_->default_representation_type() == Image::kImageRepGdk) {
      internal::ImageRepGdk* pixbuf_rep = default_rep->AsImageRepGdk();
      rep = new internal::ImageRepSkia(new ImageSkia(
          internal::GdkPixbufToSkBitmap(pixbuf_rep->pixbuf())));
    }
    // We don't do conversions from CairoCachedSurfaces to Skia because the
    // data lives on the display server and we'll always have a GdkPixbuf if we
    // have a CairoCachedSurface.
#elif defined(OS_MACOSX)
    if (storage_->default_representation_type() == Image::kImageRepCocoa) {
      internal::ImageRepCocoa* nsimage_rep = default_rep->AsImageRepCocoa();
      ImageSkia image_skia = internal::NSImageToImageSkia(nsimage_rep->image());
      rep = new internal::ImageRepSkia(new ImageSkia(image_skia));
    }
#endif
    CHECK(rep);
    AddRepresentation(rep);
    return rep;
  }
#if defined(TOOLKIT_GTK)
  else if (rep_type == Image::kImageRepCairoCache) {
    // Handle any-to-Cairo conversion. This may recursively create an
    // intermediate pixbuf before we send the data to the display server.
    internal::ImageRep* rep = GetRepresentation(Image::kImageRepGdk);
    internal::ImageRepCairoCached* native_rep =
        new internal::ImageRepCairoCached(rep->AsImageRepGdk()->pixbuf());

    CHECK(native_rep);
    AddRepresentation(native_rep);
    return native_rep;
  }
#endif

  // Handle Skia-to-native conversions.
  if (default_rep->type() == Image::kImageRepSkia) {
    internal::ImageRep* native_rep = NULL;
#if defined(USE_AURA)
    NOTIMPLEMENTED();
#elif defined(TOOLKIT_GTK)
    if (rep_type == Image::kImageRepGdk) {
      GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(
          default_rep->AsImageRepSkia()->image()->bitmap());
      native_rep = new internal::ImageRepGdk(pixbuf);
    }
#elif defined(OS_MACOSX)
    if (rep_type == Image::kImageRepCocoa) {
      NSImage* image = internal::ImageSkiaToNSImage(
          default_rep->AsImageRepSkia()->image());
      base::mac::NSObjectRetain(image);
      native_rep = new internal::ImageRepCocoa(image);
    }
#endif
    CHECK(native_rep);
    AddRepresentation(native_rep);
    return native_rep;
  }

  // Something went seriously wrong...
  return NULL;
}

void Image::AddRepresentation(internal::ImageRep* rep) const {
  CHECK(storage_.get());
  storage_->representations().insert(std::make_pair(rep->type(), rep));
}

}  // namespace gfx
<commit_msg>Revert 136639 - Image(SkBitmap) was calling DeleteSoon. Changed code path slightly such that this is avoided.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/gfx/image/image.h"

#include <algorithm>

#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/size.h"

#if defined(TOOLKIT_GTK)
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gdk/gdk.h>
#include <glib-object.h>
#include "ui/gfx/canvas.h"
#include "ui/gfx/gtk_util.h"
#include "ui/gfx/image/cairo_cached_surface.h"
#elif defined(OS_MACOSX)
#include "base/mac/mac_util.h"
#include "skia/ext/skia_utils_mac.h"
#endif

namespace gfx {

namespace internal {

#if defined(OS_MACOSX)
// This is a wrapper around gfx::NSImageToSkBitmap() because this cross-platform
// file cannot include the [square brackets] of ObjC.
ImageSkia NSImageToImageSkia(NSImage* image);
NSImage* ImageSkiaToNSImage(const ImageSkia* image);
#endif

#if defined(TOOLKIT_GTK)
const SkBitmap* GdkPixbufToSkBitmap(GdkPixbuf* pixbuf) {
  CHECK(pixbuf);
  gfx::Canvas canvas(gfx::Size(gdk_pixbuf_get_width(pixbuf),
                               gdk_pixbuf_get_height(pixbuf)), false);
  skia::ScopedPlatformPaint scoped_platform_paint(canvas.sk_canvas());
  cairo_t* cr = scoped_platform_paint.GetPlatformSurface();
  gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
  cairo_paint(cr);
  return new SkBitmap(canvas.ExtractBitmap());
}
#endif

class ImageRepSkia;
class ImageRepGdk;
class ImageRepCairoCached;
class ImageRepCocoa;

// An ImageRep is the object that holds the backing memory for an Image. Each
// RepresentationType has an ImageRep subclass that is responsible for freeing
// the memory that the ImageRep holds. When an ImageRep is created, it expects
// to take ownership of the image, without having to retain it or increase its
// reference count.
class ImageRep {
 public:
  explicit ImageRep(Image::RepresentationType rep) : type_(rep) {}

  // Deletes the associated pixels of an ImageRep.
  virtual ~ImageRep() {}

  // Cast helpers ("fake RTTI").
  ImageRepSkia* AsImageRepSkia() {
    CHECK_EQ(type_, Image::kImageRepSkia);
    return reinterpret_cast<ImageRepSkia*>(this);
  }

#if defined(TOOLKIT_GTK)
  ImageRepGdk* AsImageRepGdk() {
    CHECK_EQ(type_, Image::kImageRepGdk);
    return reinterpret_cast<ImageRepGdk*>(this);
  }

  ImageRepCairoCached* AsImageRepCairo() {
    CHECK_EQ(type_, Image::kImageRepCairoCache);
    return reinterpret_cast<ImageRepCairoCached*>(this);
  }
#endif

#if defined(OS_MACOSX)
  ImageRepCocoa* AsImageRepCocoa() {
    CHECK_EQ(type_, Image::kImageRepCocoa);
    return reinterpret_cast<ImageRepCocoa*>(this);
  }
#endif

  Image::RepresentationType type() const { return type_; }

 private:
  Image::RepresentationType type_;
};

class ImageRepSkia : public ImageRep {
 public:
  // Takes ownership of |image|.
  explicit ImageRepSkia(ImageSkia* image)
      : ImageRep(Image::kImageRepSkia),
        image_(image) {
  }

  virtual ~ImageRepSkia() {
  }

  ImageSkia* image() { return image_.get(); }

 private:
  scoped_ptr<ImageSkia> image_;

  DISALLOW_COPY_AND_ASSIGN(ImageRepSkia);
};

#if defined(TOOLKIT_GTK)
class ImageRepGdk : public ImageRep {
 public:
  explicit ImageRepGdk(GdkPixbuf* pixbuf)
      : ImageRep(Image::kImageRepGdk),
        pixbuf_(pixbuf) {
    CHECK(pixbuf);
  }

  virtual ~ImageRepGdk() {
    if (pixbuf_) {
      g_object_unref(pixbuf_);
      pixbuf_ = NULL;
    }
  }

  GdkPixbuf* pixbuf() const { return pixbuf_; }

 private:
  GdkPixbuf* pixbuf_;

  DISALLOW_COPY_AND_ASSIGN(ImageRepGdk);
};

// Represents data that lives on the display server instead of in the client.
class ImageRepCairoCached : public ImageRep {
 public:
  explicit ImageRepCairoCached(GdkPixbuf* pixbuf)
      : ImageRep(Image::kImageRepCairoCache),
        cairo_cache_(new CairoCachedSurface) {
    CHECK(pixbuf);
    cairo_cache_->UsePixbuf(pixbuf);
  }

  virtual ~ImageRepCairoCached() {
    delete cairo_cache_;
  }

  CairoCachedSurface* surface() const { return cairo_cache_; }

 private:
  CairoCachedSurface* cairo_cache_;

  DISALLOW_COPY_AND_ASSIGN(ImageRepCairoCached);
};
#endif  // defined(TOOLKIT_GTK)

#if defined(OS_MACOSX)
class ImageRepCocoa : public ImageRep {
 public:
  explicit ImageRepCocoa(NSImage* image)
      : ImageRep(Image::kImageRepCocoa),
        image_(image) {
    CHECK(image);
  }

  virtual ~ImageRepCocoa() {
    base::mac::NSObjectRelease(image_);
    image_ = nil;
  }

  NSImage* image() const { return image_; }

 private:
  NSImage* image_;

  DISALLOW_COPY_AND_ASSIGN(ImageRepCocoa);
};
#endif  // defined(OS_MACOSX)

// The Storage class acts similarly to the pixels in a SkBitmap: the Image
// class holds a refptr instance of Storage, which in turn holds all the
// ImageReps. This way, the Image can be cheaply copied.
class ImageStorage : public base::RefCounted<ImageStorage> {
 public:
  ImageStorage(gfx::Image::RepresentationType default_type)
      : default_representation_type_(default_type) {
  }

  gfx::Image::RepresentationType default_representation_type() {
    return default_representation_type_;
  }
  gfx::Image::RepresentationMap& representations() { return representations_; }

 private:
  ~ImageStorage() {
    for (gfx::Image::RepresentationMap::iterator it = representations_.begin();
         it != representations_.end();
         ++it) {
      delete it->second;
    }
    representations_.clear();
  }

  // The type of image that was passed to the constructor. This key will always
  // exist in the |representations_| map.
  gfx::Image::RepresentationType default_representation_type_;

  // All the representations of an Image. Size will always be at least one, with
  // more for any converted representations.
  gfx::Image::RepresentationMap representations_;

  friend class base::RefCounted<ImageStorage>;
};

}  // namespace internal

Image::Image() {
  // |storage_| is NULL for empty Images.
}

Image::Image(const ImageSkia& image)
    : storage_(new internal::ImageStorage(Image::kImageRepSkia)) {
  internal::ImageRepSkia* rep = new internal::ImageRepSkia(
      new ImageSkia(image));
  AddRepresentation(rep);
}

Image::Image(const SkBitmap* bitmap)
    : storage_(new internal::ImageStorage(Image::kImageRepSkia)) {
  internal::ImageRepSkia* rep = new internal::ImageRepSkia(
      new ImageSkia(bitmap));
  AddRepresentation(rep);
}

Image::Image(const SkBitmap& bitmap)
    : storage_(new internal::ImageStorage(Image::kImageRepSkia)) {
  internal::ImageRepSkia* rep =
      new internal::ImageRepSkia(new ImageSkia(new SkBitmap(bitmap)));
  AddRepresentation(rep);
}

#if defined(TOOLKIT_GTK)
Image::Image(GdkPixbuf* pixbuf)
    : storage_(new internal::ImageStorage(Image::kImageRepGdk)) {
  internal::ImageRepGdk* rep = new internal::ImageRepGdk(pixbuf);
  AddRepresentation(rep);
}
#endif

#if defined(OS_MACOSX)
Image::Image(NSImage* image)
    : storage_(new internal::ImageStorage(Image::kImageRepCocoa)) {
  internal::ImageRepCocoa* rep = new internal::ImageRepCocoa(image);
  AddRepresentation(rep);
}
#endif

Image::Image(const Image& other) : storage_(other.storage_) {
}

Image& Image::operator=(const Image& other) {
  storage_ = other.storage_;
  return *this;
}

Image::~Image() {
}

const SkBitmap* Image::ToSkBitmap() const {
  internal::ImageRep* rep = GetRepresentation(Image::kImageRepSkia);
  return rep->AsImageRepSkia()->image()->bitmap();
}

const ImageSkia* Image::ToImageSkia() const {
  internal::ImageRep* rep = GetRepresentation(Image::kImageRepSkia);
  return rep->AsImageRepSkia()->image();
}

#if defined(TOOLKIT_GTK)
GdkPixbuf* Image::ToGdkPixbuf() const {
  internal::ImageRep* rep = GetRepresentation(Image::kImageRepGdk);
  return rep->AsImageRepGdk()->pixbuf();
}

CairoCachedSurface* const Image::ToCairo() const {
  internal::ImageRep* rep = GetRepresentation(Image::kImageRepCairoCache);
  return rep->AsImageRepCairo()->surface();
}
#endif

#if defined(OS_MACOSX)
NSImage* Image::ToNSImage() const {
  internal::ImageRep* rep = GetRepresentation(Image::kImageRepCocoa);
  return rep->AsImageRepCocoa()->image();
}
#endif

ImageSkia* Image::CopyImageSkia() const {
  return new ImageSkia(*ToImageSkia());
}

SkBitmap* Image::CopySkBitmap() const {
  return new SkBitmap(*ToSkBitmap());
}

#if defined(TOOLKIT_GTK)
GdkPixbuf* Image::CopyGdkPixbuf() const {
  GdkPixbuf* pixbuf = ToGdkPixbuf();
  g_object_ref(pixbuf);
  return pixbuf;
}
#endif

#if defined(OS_MACOSX)
NSImage* Image::CopyNSImage() const {
  NSImage* image = ToNSImage();
  base::mac::NSObjectRetain(image);
  return image;
}
#endif

#if defined(OS_MACOSX)
Image::operator NSImage*() const {
  return ToNSImage();
}
#endif

bool Image::HasRepresentation(RepresentationType type) const {
  return storage_.get() && storage_->representations().count(type) != 0;
}

size_t Image::RepresentationCount() const {
  if (!storage_.get())
    return 0;

  return storage_->representations().size();
}

bool Image::IsEmpty() const {
  return RepresentationCount() == 0;
}

void Image::SwapRepresentations(gfx::Image* other) {
  storage_.swap(other->storage_);
}

internal::ImageRep* Image::DefaultRepresentation() const {
  CHECK(storage_.get());
  RepresentationMap& representations = storage_->representations();
  RepresentationMap::iterator it =
      representations.find(storage_->default_representation_type());
  DCHECK(it != representations.end());
  return it->second;
}

internal::ImageRep* Image::GetRepresentation(
    RepresentationType rep_type) const {
  CHECK(storage_.get());
  // If the requested rep is the default, return it.
  internal::ImageRep* default_rep = DefaultRepresentation();
  if (rep_type == storage_->default_representation_type())
    return default_rep;

  // Check to see if the representation already exists.
  RepresentationMap::iterator it = storage_->representations().find(rep_type);
  if (it != storage_->representations().end())
    return it->second;

  // At this point, the requested rep does not exist, so it must be converted
  // from the default rep.

  // Handle native-to-Skia conversion.
  if (rep_type == Image::kImageRepSkia) {
    internal::ImageRepSkia* rep = NULL;
#if defined(TOOLKIT_GTK)
    if (storage_->default_representation_type() == Image::kImageRepGdk) {
      internal::ImageRepGdk* pixbuf_rep = default_rep->AsImageRepGdk();
      rep = new internal::ImageRepSkia(new ImageSkia(
          internal::GdkPixbufToSkBitmap(pixbuf_rep->pixbuf())));
    }
    // We don't do conversions from CairoCachedSurfaces to Skia because the
    // data lives on the display server and we'll always have a GdkPixbuf if we
    // have a CairoCachedSurface.
#elif defined(OS_MACOSX)
    if (storage_->default_representation_type() == Image::kImageRepCocoa) {
      internal::ImageRepCocoa* nsimage_rep = default_rep->AsImageRepCocoa();
      ImageSkia image_skia = internal::NSImageToImageSkia(nsimage_rep->image());
      rep = new internal::ImageRepSkia(new ImageSkia(image_skia));
    }
#endif
    CHECK(rep);
    AddRepresentation(rep);
    return rep;
  }
#if defined(TOOLKIT_GTK)
  else if (rep_type == Image::kImageRepCairoCache) {
    // Handle any-to-Cairo conversion. This may recursively create an
    // intermediate pixbuf before we send the data to the display server.
    internal::ImageRep* rep = GetRepresentation(Image::kImageRepGdk);
    internal::ImageRepCairoCached* native_rep =
        new internal::ImageRepCairoCached(rep->AsImageRepGdk()->pixbuf());

    CHECK(native_rep);
    AddRepresentation(native_rep);
    return native_rep;
  }
#endif

  // Handle Skia-to-native conversions.
  if (default_rep->type() == Image::kImageRepSkia) {
    internal::ImageRep* native_rep = NULL;
#if defined(USE_AURA)
    NOTIMPLEMENTED();
#elif defined(TOOLKIT_GTK)
    if (rep_type == Image::kImageRepGdk) {
      GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(
          default_rep->AsImageRepSkia()->image()->bitmap());
      native_rep = new internal::ImageRepGdk(pixbuf);
    }
#elif defined(OS_MACOSX)
    if (rep_type == Image::kImageRepCocoa) {
      NSImage* image = internal::ImageSkiaToNSImage(
          default_rep->AsImageRepSkia()->image());
      base::mac::NSObjectRetain(image);
      native_rep = new internal::ImageRepCocoa(image);
    }
#endif
    CHECK(native_rep);
    AddRepresentation(native_rep);
    return native_rep;
  }

  // Something went seriously wrong...
  return NULL;
}

void Image::AddRepresentation(internal::ImageRep* rep) const {
  CHECK(storage_.get());
  storage_->representations().insert(std::make_pair(rep->type(), rep));
}

}  // namespace gfx
<|endoftext|>