static uint
getFileIdFromMap(uint file_id)
{
  uint id = file_id;
  if (Prof::m_mapFileIDs.find(file_id) != Prof::m_mapFileIDs.end()) {
    // the file ID should redirected to another file ID which has
    // exactly the same filename
    id = Prof::m_mapFileIDs[file_id];
  }
  return id;
}

// Part of HPCToolkit (hpctoolkit.org)
// Information about sources of support for research and development of
// HPCToolkit is at 'hpctoolkit.org' and in 'README.Acknowledgments'.
// All rights reserved.

//   [The purpose of this file]
//   [The set of functions, macros, etc. defined in the file]

//************************* System Include Files ****************************

#include <iostream>
using std::ostream;
using std::endl;
using std::hex;
using std::dec;

#include <string>
using std::string;

#include <vector>
using std::vector;

#include <set>
using std::set;

#include <typeinfo>

//*************************** User Include Files ****************************

#include <include/gcc-attr.h>
#include <include/uint.h>

#include "CCT-Tree.hpp"
#include "CallPath-Profile.hpp" // for CCT::Tree::metadata()

#include <lib/xml/xml.hpp>

#include <lib/support/diagnostics.h>
#include <lib/support/Logic.hpp>
#include <lib/support/SrcFile.hpp>
using SrcFile::ln_NULL;
#include <lib/support/StrUtil.hpp>
#include <lib/support/Trace.hpp>

#include <lib/support/dictionary.h>

// Tree

namespace Prof {

extern std::map<uint, uint> m_mapFileIDs;      // map between file IDs
extern std::map<uint, uint> m_mapProcIDs;      // map between proc IDs
extern std::map<uint, uint> m_mapLoadModuleIDs;      // map between load module IDs

// local function to convert from the original procedure ID into a
// a "compact" id to reduce redundancy if the procedure of the same
// file has exactly the same name.
// Note that this map doesn't differentiate between "foo" and "foo(int)"
static uint
getProcIdFromMap(uint proc_id)
{
  uint id = proc_id;
  if (Prof::m_mapProcIDs.find(proc_id) != Prof::m_mapProcIDs.end()) {
    // the file ID should redirected to another file ID which has
    // exactly the same filename
    id = Prof::m_mapProcIDs[proc_id];
  }
  return id;
}

// local method to convert from the original load module into
// a compact ID to reduce redundancy.
static uint
getLoadModuleFromMap(uint lm_id)
{
  uint id = lm_id;
  std::map<uint, uint>::iterator it = m_mapLoadModuleIDs.find(lm_id);
  if (it != Prof::m_mapLoadModuleIDs.end()) {
    // the file ID should redirected to another file ID which has
    // exactly the same filename
    id = it->second;
  }
  return id;
}

namespace CCT {

Tree::Tree(const CallPath::Profile* metadata)
  : m_root(NULL), m_metadata(metadata),
    m_maxDenseId(0), m_nodeidMap(NULL),
    m_mergeCtxt(NULL)
{
}

Tree::~Tree()
{
  delete m_root;
  m_metadata = NULL;
  delete m_nodeidMap;
  delete m_mergeCtxt;
}

MergeEffectList*
Tree::merge(const Tree* y, uint x_newMetricBegIdx, uint mrgFlag, uint oFlag)
{
  Tree* x = this;
  ANode* x_root = root();
  ANode* y_root = y->root();

  // Merge pre-condition: both x and y should be "locally merged",
  // i.e., the nodes themselves should be equal (modulo metrics & children)
  bool isPrecondition = false;
  if (typeid(*x_root) == typeid(CCT::Root)
      && typeid(*y_root) == typeid(CCT::Root)) {
    isPrecondition = true;
  }
  else {
    ADynNode* x_dyn = dynamic_cast<ADynNode*>(x_root);
    ADynNode* y_dyn = dynamic_cast<ADynNode*>(y_root);
    if (x_dyn && y_dyn && ADynNode::isMergable(*x_dyn, *y_dyn)) {
      // Case 2a
      isPrecondition = true;
    }
    else if ((x_dyn->childCount() == 0 || y_dyn->childCount() == 0)
	     && x_dyn->isPrimarySynthRoot() && y_dyn->isPrimarySynthRoot()) {
      // Case 2b (A special sub-case of Case 1): (a) Neither tree x
      // nor y have been canonicalized (and therefore do not have a
      // CCT::Root node); and (b) one of x or y is a single-node tree.
      isPrecondition = true;
    }
  }
  DIAG_Assert(isPrecondition, "Prof::CCT::Tree::merge: Merge precondition fails!");

  if (!m_mergeCtxt) {
    bool doTrackCPIds = !x->metadata()->traceFileNameSet().empty();
    m_mergeCtxt = new MergeContext(x, doTrackCPIds);
  }
  m_mergeCtxt->flags(mrgFlag);

  MergeEffectList* mrgEffects =
    x_root->mergeDeep(y_root, x_newMetricBegIdx, *m_mergeCtxt, oFlag);

  DIAG_If(0 /*public diag level*/) {
    verifyUniqueCPIds();
  }

  return mrgEffects;
}

void
Tree::pruneCCTByNodeId(const uint8_t* prunedNodes)
{
  m_root->pruneChildrenByNodeId(prunedNodes);
  DIAG_Assert(!prunedNodes[m_root->id()], "Prof::CCT::Tree::pruneCCTByNodeId(): cannot delete root!");

  //DIAG_Assert(m_root, "Prof::CCT::Tree::pruneCCTByNodeId(): cannot delete root!");
}

uint
Tree::makeDensePreorderIds()
{
  uint nextId = 1; // cf. s_nextUniqueId
  nextId = m_root->makeDensePreorderIds(nextId);

  m_maxDenseId = (nextId - 1);
  return m_maxDenseId;
}

ANode*
Tree::findNode(uint nodeId) const
{
  if (!m_nodeidMap) {
    m_nodeidMap = new NodeIdToANodeMap;
    for (ANodeIterator it(m_root); it.Current(); ++it) {
      ANode* n = it.current();
      m_nodeidMap->insert(std::make_pair(n->id(), n));
    }
  }
  NodeIdToANodeMap::iterator it = m_nodeidMap->find(nodeId);
  return (it != m_nodeidMap->end()) ? it->second : NULL;
}

bool
Tree::verifyUniqueCPIds()
{
  bool areAllUnique = true;
  MergeContext::CPIdSet cpIdSet;

  for (ANodeIterator it(root()); it.Current(); ++it) {
    ANode* n = it.current();

    ADynNode* n_dyn = dynamic_cast<ADynNode*>(n);
    if (n_dyn && n_dyn->cpId() != HPCRUN_FMT_CCTNodeId_NULL) {
      std::pair<MergeContext::CPIdSet::iterator, bool> ret =
	cpIdSet.insert(n_dyn->cpId());
      bool isInserted = ret.second;
      areAllUnique = areAllUnique && isInserted;
      DIAG_MsgIf(!isInserted,  "CCT::Tree::verifyUniqueCPIds: Duplicate CPId: " << n_dyn->cpId());
    }
  }

  return areAllUnique;
}

std::ostream&
Tree::writeXML(std::ostream& os, uint metricBeg, uint metricEnd,
	       uint oFlags) const
{
  if (m_root) {
    m_root->writeXML(os, metricBeg, metricEnd, oFlags);
  }
  return os;
}

std::ostream&
Tree::dump(std::ostream& os, uint oFlags) const
{
  writeXML(os, Metric::IData::npos, Metric::IData::npos, oFlags);
  return os;
}

void
Tree::ddump() const
{
  dump(std::cerr, Tree::OFlg_DebugAll);
}

} // namespace CCT

} // namespace Prof

namespace Prof {

namespace CCT {

// ANodeTy `methods' (could completely replace with dynamic typing)

const string ANode::NodeNames[ANode::TyNUMBER] = {
  "Root", "PF", "Pr", "L", "C", "S", "Any"
};

const string&
ANode::ANodeTyToName(ANodeTy tp)
{
  return NodeNames[tp];
}

ANode::ANodeTy
ANode::IntToANodeType(long i)
{
  DIAG_Assert((i >= 0) && (i < TyNUMBER), "");
  return (ANodeTy)i;
}

uint ANode::s_nextUniqueId = 2;

string AProcNode::BOGUS;

// ANode, etc: Tree Navigation

#define dyn_cast_return(base, derived, expr) \
    { base* ptr = expr;  \
      if (ptr == NULL) {  \
        return NULL;  \
      } else {  \
	return dynamic_cast<derived*>(ptr);  \
      } \
    }

ANode*
ANode::ancestor(ANodeTy tp) const
{
  ANode* s = const_cast<ANode*>(this);
  while (s && s->type() != tp) {
    s = s->parent();
  }
  return s;
}

#if 0 // is this obsolete?
int
isAncestorOf(ANode* parent, ANode* son, int difference)
{
  ANode *iter = son;
  while (iter && difference > 0 && iter != parent) {
    iter = iter->Parent();
    difference--;
  }
  if (iter && iter == parent) {
    return 1;
  }
  return 0;
}
#endif

Root*
ANode::ancestorRoot() const
{
  if (Parent() == NULL) {
    return NULL;
  }
  else {
    dyn_cast_return(ANode, Root, ancestor(TyRoot));
  }
}

ProcFrm*
ANode::ancestorProcFrm() const
{
  dyn_cast_return(ANode, ProcFrm, ancestor(TyProcFrm));
}

Proc*
ANode::ancestorProc() const
{
  dyn_cast_return(ANode, Proc, ancestor(TyProc));
}

Loop*
ANode::ancestorLoop() const
{
  dyn_cast_return(ANode, Loop, ancestor(TyLoop));
}

Call*
ANode::ancestorCall() const
{
  dyn_cast_return(ANode, Call, ancestor(TyCall));
}

Stmt*
ANode::ancestorStmt() const
{
  dyn_cast_return(ANode, Stmt, ancestor(TyStmt));
}

// Metrics

void
ANode::zeroMetricsDeep(uint mBegId, uint mEndId)
{
  if ( !(mBegId < mEndId) ) {
    return; // short circuit
  }

  for (ANodeIterator it(this); it.Current(); ++it) {
    ANode* n = it.current();
    n->zeroMetrics(mBegId, mEndId);
  }
}

void
ANode::aggregateMetricsIncl(uint mBegId, uint mEndId)
{
  VMAIntervalSet ivalset; // TODO: cheat using a VMAInterval set
  for (uint mId = mBegId; mId < mEndId; ++mId) {
    ivalset.insert(VMAInterval(mId, mId + 1)); // [ )
  }
  aggregateMetricsIncl(ivalset);
}

void
ANode::aggregateMetricsIncl(const VMAIntervalSet& ivalset)
{
  if (ivalset.empty()) {
    return; // short circuit
  }

  const ANode* root = this;
  ANodeIterator it(root, NULL/*filter*/, false/*leavesOnly*/,
		   IteratorStack::PostOrder);
  for (ANode* n = NULL; (n = it.current()); ++it) {
    if (n != root) {
      ANode* n_parent = n->parent();

      for (VMAIntervalSet::const_iterator it1 = ivalset.begin();
	   it1 != ivalset.end(); ++it1) {
	const VMAInterval& ival = *it1;
	uint mBegId = (uint)ival.beg(), mEndId = (uint)ival.end();

	for (uint mId = mBegId; mId < mEndId; ++mId) {
	  double mVal = n->demandMetric(mId, mEndId/*size*/);
	  n_parent->demandMetric(mId, mEndId/*size*/) += mVal;
	}
      }
    }
  }
}

void
ANode::aggregateMetricsExcl(uint mBegId, uint mEndId)
{
  VMAIntervalSet ivalset; // TODO: cheat using a VMAInterval set
  for (uint mId = mBegId; mId < mEndId; ++mId) {
    ivalset.insert(VMAInterval(mId, mId + 1)); // [ )
  }
  aggregateMetricsExcl(ivalset);
}

void
ANode::aggregateMetricsExcl(const VMAIntervalSet& ivalset)
{
  if (ivalset.empty()) {
    return; // short circuit
  }

  AProcNode* frame = NULL; // will be set during tree traversal
  aggregateMetricsExcl(frame, ivalset);
}

void
ANode::aggregateMetricsExcl(AProcNode* frame, const VMAIntervalSet& ivalset)
{
  ANode* n = this;

  // Pre-order visit
  // laks 2015.10.21: we don't want accumulate the exclusive cost of
  // an inlined statement to the caller. Instead, we assume an inline
  // function (Proc) as the same as a normal procedure (ProcFrm).
  // And the lowest common ancestor for Proc and ProcFrm is AProcNode.
  bool isFrame = (typeid(*n) == typeid(ProcFrm));
  bool isProc  = (typeid(*n) == typeid(Proc));

  bool isInlineMacro = false;
  bool isInlineCall  = false;

  NonUniformDegreeTreeNode *parent = n->Parent();
  if (isProc && parent != NULL) {
    // if this node and the parent are proc, it is possible this node is an
    //  inline procedure callsite
    std::string myprocname = n->structure()->name();

    if (typeid(*parent) == typeid(Proc)) {
      // check if this node is an inline procedure call.
      //  a node is an inline proc call iff its parent is an empty proc and
      //  the node itself is a non-empty proc

      // condition 1: the myprocname of the parent must be empty
      Struct::ACodeNode *strct = ((ANode*)parent)->structure();
      bool isEmptyNameParent   = strct->name().empty();

      // condition 2: the myprocname of the inline is not empty
      bool isFullyNamed = !myprocname.empty();

      isInlineCall  = isEmptyNameParent && isFullyNamed;
    }
    isInlineMacro = !isInlineCall && myprocname.compare(GUARD_NAME) == 0;
  }

  bool isLogicalProc   = isFrame || isInlineCall || isInlineMacro;
  AProcNode * frameNxt = (isLogicalProc) ? static_cast<AProcNode*>(n) : frame;

  // Tree traversal
  for (ANodeChildIterator it(n); it.Current(); ++it) {
    ANode* x = it.current();
    x->aggregateMetricsExcl(frameNxt, ivalset);
  }

  // Post-order visit
  if (typeid(*n) == typeid(CCT::Stmt) || isInlineMacro) {
    ANode* n_parent = n->parent();

    for (VMAIntervalSet::const_iterator it = ivalset.begin();
        it != ivalset.end(); ++it) {
      const VMAInterval& ival = *it;
      uint mBegId = (uint)ival.beg(), mEndId = (uint)ival.end();

      for (uint mId = mBegId; mId < mEndId; ++mId) {
        double mVal = n->demandMetric(mId, mEndId/*size*/);
        n_parent->demandMetric(mId, mEndId/*size*/) += mVal;
        if (frame && frame != n_parent) {
          frame->demandMetric(mId, mEndId/*size*/) += mVal;
        }
      }
    }
  }
}

void
ANode::computeMetrics(const Metric::Mgr& mMgr, uint mBegId, uint mEndId,
		      bool doFinal)
{
  if ( !(mBegId < mEndId) ) {
    return;
  }

  // N.B. pre-order walk assumes point-wise metrics

  for (ANodeIterator it(this); it.Current(); ++it) {
    ANode* n = it.current();
    n->computeMetricsMe(mMgr, mBegId, mEndId, doFinal);
  }
}

void
ANode::computeMetricsMe(const Metric::Mgr& mMgr, uint mBegId, uint mEndId,
			bool doFinal)
{
  uint numMetrics = mMgr.size();

  for (uint mId = mBegId; mId < mEndId; ++mId) {
    const Metric::ADesc* m = mMgr.metric(mId);
    const Metric::DerivedDesc* mm = dynamic_cast<const Metric::DerivedDesc*>(m);
    if (mm && mm->expr()) {
      const Metric::AExpr* expr = mm->expr();
      expr->evalNF(*this);
      if (doFinal) {
	double val = expr->eval(*this);
	demandMetric(mId, numMetrics/*size*/) = val;
      }
    }
  }
}

void
ANode::computeMetricsIncr(const Metric::Mgr& mMgr, uint mBegId, uint mEndId,
			  Metric::AExprIncr::FnTy fn)
{
  if ( !(mBegId < mEndId) ) {
    return;
  }

  // N.B. pre-order walk assumes point-wise metrics

  for (ANodeIterator it(this); it.Current(); ++it) {
    ANode* n = it.current();
    n->computeMetricsIncrMe(mMgr, mBegId, mEndId, fn);
  }
}

void
ANode::computeMetricsIncrMe(const Metric::Mgr& mMgr, uint mBegId, uint mEndId,
			    Metric::AExprIncr::FnTy fn)
{
  for (uint mId = mBegId; mId < mEndId; ++mId) {
    const Metric::ADesc* m = mMgr.metric(mId);
    const Metric::DerivedIncrDesc* mm =
      dynamic_cast<const Metric::DerivedIncrDesc*>(m);
    if (mm && mm->expr()) {
      const Metric::AExprIncr* expr = mm->expr();
      switch (fn) {
        case Metric::AExprIncr::FnInit:
	  expr->initialize(*this); break;
        case Metric::AExprIncr::FnInitSrc:
	  expr->initializeSrc(*this); break;
        case Metric::AExprIncr::FnAccum:
	  expr->accumulate(*this); break;
        case Metric::AExprIncr::FnCombine:
	  expr->combine(*this); break;
        case Metric::AExprIncr::FnFini:
	  expr->finalize(*this); break;
        default:
	  DIAG_Die(DIAG_UnexpectedInput);
      }
    }
  }
}

void
ANode::pruneByMetrics(const Metric::Mgr& mMgr, const VMAIntervalSet& ivalset,
		      const ANode* root, double thresholdPct,
		      uint8_t* prunedNodes)
{
  for (ANodeChildIterator it(this); it.Current(); /* */) {
    ANode* x = it.current();
    it++; // advance iterator -- it is pointing at 'x'

    // Determine whether 'x' is important: any inclusive metric >= threshold
    uint numIncl = 0;
    bool isImportant = false;

    for (VMAIntervalSet::const_iterator it1 = ivalset.begin();
	 it1 != ivalset.end(); ++it1) {
      const VMAInterval& ival = *it1;
      uint mBegId = (uint)ival.beg(), mEndId = (uint)ival.end();

      for (uint mId = mBegId; mId < mEndId; ++mId) {
	const Prof::Metric::ADesc* m = mMgr.metric(mId);
	if (m->type() != Metric::ADesc::TyIncl) {
	  continue;
	}
	numIncl++;

	double total = root->metric(mId); // root->metric(m->partner()->id());

	double pct = x->metric(mId) * 100 / total;
	if (pct >= thresholdPct) {
	  isImportant = true;
	  break;
	}
      }
      if (isImportant) { break; }
    }

    if (isImportant || numIncl == 0) {
      x->pruneByMetrics(mMgr, ivalset, root, thresholdPct, prunedNodes);
    }
    else {
      deleteChaff(x, prunedNodes);
    }
  }
}

#if 0
void
ANode::pruneByNodeId(ANode*& x, const uint8_t* prunedNodes)
{
  // Visiting in preorder can save a little work since a whole subtree
  // can be deleted (factoring out the destructor's recursion)
  if (prunedNodes[x->id()]) {
    x->unlink(); // unlink 'x' from tree
    delete x;
    x = NULL;
  }
  else {
    for (ANodeChildIterator it(x); it.Current(); /* */) {
      ANode* x_child = it.current();
      it++; // advance iterator -- it is pointing at 'x_child'
      pruneByNodeId(x_child, prunedNodes);
    }
  }
}
#endif

void
ANode::pruneChildrenByNodeId(const uint8_t* prunedNodes)
{
  ANode* x = this;

  for (ANodeChildIterator it(x); it.Current(); /* */) {
    ANode* x_child = it.current();
    it++; // advance iterator -- it is pointing at 'x_child'

    // Visiting in preorder can save a little work since a whole subtree
    // can be deleted (factoring out the destructor's recursion)
    if (prunedNodes[x_child->id()]) {
      x_child->unlink(); // unlink 'x_child' from tree
      delete x_child;
    }
    else {
      x_child->pruneChildrenByNodeId(prunedNodes);
    }
  }
}

bool
ANode::deleteChaff(ANode* x, uint8_t* deletedNodes)
{
  bool x_isLeaf = x->isLeaf(); // N.B. must perform before below
  uint x_id = x->id();

  bool wereAllChildrenDeleted = true;
  for (ANodeChildIterator it(x); it.Current(); /* */) {
    ANode* x_child = it.current();
    it++; // advance iterator -- it is pointing at 'x_child'

    wereAllChildrenDeleted = (wereAllChildrenDeleted
			      && deleteChaff(x_child, deletedNodes));
  }

  bool wasDeleted = false;
  if (wereAllChildrenDeleted) {
    Prof::CCT::ADynNode* x_dyn = dynamic_cast<Prof::CCT::ADynNode*>(x);
    bool mustRetain = (x_dyn && hpcrun_fmt_doRetainId(x_dyn->cpId()));
    if ((x_isLeaf && !mustRetain) || !x_isLeaf) {
      x->unlink(); // unlink 'x' from tree
      delete x;
      if (deletedNodes) {
	deletedNodes[x_id] = 1;
      }
      wasDeleted = true;
    }
  }

  return wasDeleted;
}

// Merging

MergeEffectList*
ANode::mergeDeep(ANode* y, uint x_newMetricBegIdx, MergeContext& mrgCtxt,
		 uint oFlag)
{
  ANode* x = this;

  // 0. If y is childless, return.
  if (y->isLeaf()) {
    return NULL;
  }

  // 1. If a child y_child of y _does not_ appear as a child of x,
  //    then copy (subtree) y_child [fixing its metrics], make it a
  //    child of x and return.
  // 2. If a child y_child of y _does_ have a corresponding child
  //    x_child of x, merge [the metrics of] y_child into x_child and
  MergeEffectList* effctLst = new MergeEffectList;

  for (ANodeChildIterator it(y); it.Current(); /* */) {
    ANode* y_child = it.current();
    ADynNode* y_child_dyn = dynamic_cast<ADynNode*>(y_child);
    DIAG_Assert(y_child_dyn, "ANode::mergeDeep");
    it++; // advance iterator -- it is pointing at 'child'

    MergeEffectList* effctLst1 = NULL;

    ADynNode* x_child_dyn = x->findDynChild(*y_child_dyn);

#define MERGE_ACTION 0
#define MERGE_ERROR 0

    if (!x_child_dyn) {
      if (mrgCtxt.flags() & MrgFlg_AssertCCTMergeOnly) {
	DIAG_MsgIf(MERGE_ERROR /*(oFlag & Tree::OFlg_Debug)*/,
		   "CCT::ANode::mergeDeep: Adding not permitted:\n     "
		   << y_child->toStringMe(Tree::OFlg_Debug));
        DIAG_WMsgIf( !(mrgCtxt.flags() & MrgFlg_AssertCCTMergeOnly),
		     "CCT::ANode::mergeDeep: adding not permitted");
      } else if (!(mrgCtxt.flags() & MrgFlg_CCTMergeOnly)) {
	DIAG_MsgIf(MERGE_ACTION /*(oFlag & Tree::OFlg_Debug)*/,
		   "CCT::ANode::mergeDeep: Adding:\n     "
		   << y_child->toStringMe(Tree::OFlg_Debug));
	y_child->unlink();

	effctLst1 = y_child->mergeDeep_fixInsert(x_newMetricBegIdx, mrgCtxt);

	y_child->link(x);
      }
    }
    else {
      DIAG_MsgIf(MERGE_ACTION /*(oFlag & Tree::OFlg_Debug)*/,
		 "CCT::ANode::mergeDeep: Merging x <= y:\n"
		 << "  x: " << x_child_dyn->toStringMe(Tree::OFlg_Debug)
		 << "\n  y: " << y_child_dyn->toStringMe(Tree::OFlg_Debug));
      MergeEffect effct =
	x_child_dyn->mergeMe(*y_child_dyn, &mrgCtxt, x_newMetricBegIdx);
      if (mrgCtxt.doPropagateEffects() && !effct.isNoop()) {
	effctLst->push_back(effct);
      }

      effctLst1 = x_child_dyn->mergeDeep(y_child, x_newMetricBegIdx, mrgCtxt,
					 oFlag);
    }

    if (effctLst1 && !effctLst1->empty()) {
      effctLst->splice(effctLst->end(), *effctLst1);
      DIAG_MsgIf(0, MergeEffect::toString(*effctLst));
    }
    delete effctLst1;
  }

  return effctLst;
}

MergeEffect
ANode::merge(ANode* y)
{
  ANode* x = this;

  // 1. copy y's metrics into x
  MergeEffect effct = x->mergeMe(*y);

  // 2. copy y's children into x
  for (ANodeChildIterator it(y); it.Current(); /* */) {
    ANode* y_child = it.current();
    it++; // advance iterator -- it is pointing at 'y_child'
    y_child->unlink();
    y_child->link(x);
  }

  y->unlink();
  delete y;

  return effct;
}

MergeEffect
ANode::mergeMe(const ANode& y, MergeContext* GCC_ATTR_UNUSED mrgCtxt,
	       uint metricBegIdx, bool mayConflict)
{
  ANode* x = this;

  uint x_end = metricBegIdx + y.numMetrics(); // open upper bound
  if ( !(x_end <= x->numMetrics()) ) {
    ensureMetricsSize(x_end);
  }

  for (uint x_i = metricBegIdx, y_i = 0; x_i < x_end; ++x_i, ++y_i) {
    x->metric(x_i) += y.metric(y_i);
  }

  MergeEffect noopEffect;
  return noopEffect;
}

MergeEffect
ADynNode::mergeMe(const ANode& y, MergeContext* mrgCtxt, uint metricBegIdx, bool mayConflict)
{
  ADynNode* x = this;

  const ADynNode* y_dyn = dynamic_cast<const ADynNode*>(&y);
  DIAG_Assert(y_dyn, "ADynNode::mergeMe: " << DIAG_UnexpectedInput);

  MergeEffect effct = ANode::mergeMe(y, mrgCtxt, metricBegIdx);

  // merge cp-ids
  if (hasMergeEffects(*x, *y_dyn)) {
    effct.old_cpId = y_dyn->m_cpId;
    effct.new_cpId = m_cpId;
  }
  else if (y_dyn->cpId() == HPCRUN_FMT_CCTNodeId_NULL) {
    // 2. Trivial conflict: y's cpId is NULL; x's may or may not be NULL:
  }
  else if (m_cpId == HPCRUN_FMT_CCTNodeId_NULL) {
    // 3. Semi-trivial conflict: x's cpId is NULL, but y's is not
    //     => use y's cpId *if* it does not conflict with one already
    //        in x's tree.

    // Ignore the assertion iff the caller ensures no conflicting cpi-ids by setting mayConflict to false
    if (mayConflict) {
      // mayConflict is true, so we need to ensure uniqueness of cp-ids
      DIAG_Assert(mrgCtxt, "ADynNode::mergeMe: potentially introducing cp-id conflicts; cannot verify without MergeContext!");
      MergeContext::pair ret = mrgCtxt->ensureUniqueCPId(y_dyn->cpId());
      m_cpId = ret.cpId;
      DIAG_Assert(effct.isNoop(), DIAG_UnexpectedInput);
      effct = ret.effect;
    }
    else {
      // Since mayConflict is false, we can set m_cpId to y_dyn->cpId()
      m_cpId = y_dyn->cpId();
    }
  }

  return effct;
}

ADynNode*
ANode::findDynChild(const ADynNode& y_dyn)
{
  for (ANodeChildIterator it(this); it.Current(); ++it) {
    ANode* x = it.current();

    ADynNode* x_dyn = dynamic_cast<ADynNode*>(x);
    if (x_dyn) {
      // Base case: an ADynNode descendent
      if (ADynNode::isMergable(*x_dyn, y_dyn)) {
	return x_dyn;
      }
    }
    else {
      // Inductive case: some other type; find the first ADynNode descendents.
      ADynNode* x_dyn_descendent = x->findDynChild(y_dyn);
      if (x_dyn_descendent) {
	return x_dyn_descendent;
      }
    }
  }
  return NULL;
}

MergeEffectList*
ANode::mergeDeep_fixInsert(int newMetrics, MergeContext& mrgCtxt)
{
  // Assumes: While merging CCT::Tree y into CCT::Tree x, subtree
  // 'this', which used to live in 'y', has just been inserted into

  MergeEffectList* effctLst = new MergeEffectList();

  for (ANodeIterator it(this); it.Current(); ++it) {
    ANode* n = it.current();
    DIAG_MsgIf(MERGE_ACTION /*(oFlag & Tree::OFlg_Debug)*/,
	   "CCT:ANode::mergeDeep_fixInsert: Adding:\n     "
	   << n->toStringMe(Tree::OFlg_Debug));

    // 1. Ensure no cpId in subtree 'this' conflicts with an existing
    // cpId in CCT::Tree x.
    ADynNode* n_dyn = dynamic_cast<ADynNode*>(n);
    if (n_dyn) {
      MergeContext::pair ret = mrgCtxt.ensureUniqueCPId(n_dyn->cpId());
      n_dyn->cpId(ret.cpId);
      if (!ret.effect.isNoop()) {
	effctLst->push_back(ret.effect);
      }
    }

    // 2. Make space for the metrics of CCT::Tree x
    n->insertMetricsBefore(newMetrics);
  }

  return effctLst;
}

uint
ANode::makeDensePreorderIds(uint nextId)
{
  // N.B.: use a sorted iterator to support hpcprof-mpi where we need
  // to ensure multiple processes obtain the same numbering.

  id(nextId);
  nextId++;

  for (ANodeSortedChildIterator it(this, ANodeSortedIterator::cmpByStructureInfo);
       it.current(); it++) {
    CCT::ANode* n = it.current();
    nextId = n->makeDensePreorderIds(nextId);
  }

  return nextId;
}

// ANode, etc: CodeName methods

// NOTE: tallent: used for lush_cilkNormalize

string
ANode::codeName() const
{
  string self = ANodeTyToName(type()) + " "
    + StrUtil::toStr(begLine()) + "-" + StrUtil::toStr(endLine());
  return self;
}

string
ProcFrm::codeName() const
{
  string self = ANodeTyToName(type()) + " "
    + procName() + " @ "
    + fileName() + ":"
    + StrUtil::toStr(begLine()) + "-" + StrUtil::toStr(endLine());
  return self;
}

string
ProcFrm::procNameDbg() const
{
  string nm = procName();

  CCT::Call* caller = ancestorCall();
  if (caller) {
    nm += " <= " + caller->nameDyn();
  }

  return nm;
}

// ANode, etc: Dump contents for inspection

string
ANode::toString(uint oFlags, const char* pfx) const
{
  std::ostringstream os;
  writeXML(os, Metric::IData::npos, Metric::IData::npos, oFlags, pfx);
  return os.str();
}

string
ANode::toStringMe(uint oFlags) const
{
  string self;
  ANodeTy node_type = type();
  self = ANodeTyToName(node_type);

  SrcFile::ln lnBeg = begLine();
  string line = StrUtil::toStr(lnBeg);

  uint sId = (m_strct) ? m_strct->id() : 0;
  if (node_type == TyProcFrm || node_type == TyProc) {
    sId = getProcIdFromMap(sId);
  }

  self += " i" + xml::MakeAttrNum(m_id);
  self += " s" + xml::MakeAttrNum(sId) + " l" + xml::MakeAttrStr(line);
  if ((oFlags & Tree::OFlg_Debug) || (oFlags & Tree::OFlg_DebugAll)) {
    self += " strct" + xml::MakeAttrNum((uintptr_t)m_strct, 16);
  }

  return self;
}

string
ADynNode::assocInfo_str() const
{
  char str[LUSH_ASSOC_INFO_STR_MIN_LEN];
  lush_assoc_info_sprintf(str, m_as_info);
  return string(str);
}

string
ADynNode::lip_str() const
{
  char str[LUSH_LIP_STR_MIN_LEN];
  lush_lip_sprintf(str, m_lip);
  return string(str);
}

string
ADynNode::nameDyn() const
{
  string nm = "[assoc(" + assocInfo_str() + ") ip("
    + StrUtil::toStr(lmId_real()) + ", "
    + StrUtil::toStr(lmIP_real(), 16) + ") lip(" + lip_str() + ")]";
  return nm;
}

void
ADynNode::writeDyn(std::ostream& o, uint GCC_ATTR_UNUSED oFlags,
		   const char* pfx) const
{
  string p(pfx);

  o << std::showbase;

  o << p << assocInfo_str()
    << hex << " [ip " << m_lmIP << ", " << dec << m_opIdx << "] "
    << hex << m_lip << " [lip " << lip_str() << "]" << dec;

  o << p << " [metrics";
  for (uint i = 0; i < numMetrics(); ++i) {
    o << " " << metric(i);
  }
  o << "]" << endl;
}

string
Root::toStringMe(uint oFlags) const
{
  string self = ANode::toStringMe(oFlags) + " n" + xml::MakeAttrStr(m_name);
  return self;
}

// Goal: This function is to avoid using different ID for the same file name.
// During the writing of file dictionary table (see CallPath-Profile.cpp)
// 	if a file has the exact absolute name with a previous file,
//	then we redirect its ID to the existing ID
// This function will check if any nodes refer to redirected ID or not.
// 	if this is the case, it will return the existing file ID instead of
//	the node's file ID.

string
ProcFrm::toStringMe(uint oFlags) const
{
  string self = ANode::toStringMe(oFlags);

  if (m_strct) {
    uint lm_id = getLoadModuleFromMap(lmId());
    string lm_nm = xml::MakeAttrNum(lm_id);

    uint file_id = getFileIdFromMap(fileId());
    string fnm = xml::MakeAttrNum(file_id);

    uint proc_id = getProcIdFromMap(procId());
    string pnm = xml::MakeAttrNum(proc_id);

    if (oFlags & Tree::OFlg_DebugAll) {
      lm_nm = xml::MakeAttrStr(lmName());
      fnm = xml::MakeAttrStr(fileName());
    }
    if ( (oFlags & Tree::OFlg_Debug) || (oFlags & Tree::OFlg_DebugAll) ) {
      pnm = xml::MakeAttrStr(procNameDbg());
    }

    self += " lm" + lm_nm + " f" + fnm + " n" + pnm;

    // print the vma for debugging purpose
    int dbg_level = Diagnostics_GetDiagnosticFilterLevel();
    if (dbg_level > 2) {
      VMAIntervalSet &vma = m_strct->vmaSet();
      self += " v=\"" + vma.toString() + "\"";
    }

    if ((oFlags & CCT::Tree::OFlg_StructId) && structure() != NULL) {
      self += " str" + xml::MakeAttrNum(structure()->m_origId);
    }
  }

  return self;
}

string
Proc::toStringMe(uint oFlags) const
{
  string self = ANode::toStringMe(oFlags);

  if (m_strct) {
    string lm_nm = xml::MakeAttrNum(lmId());

    uint file_id = getFileIdFromMap(fileId());
    string fnm = xml::MakeAttrNum(file_id);

    uint proc_id = getProcIdFromMap(procId());
    string pnm = xml::MakeAttrNum(proc_id);

    if (oFlags & Tree::OFlg_DebugAll) {
      lm_nm = xml::MakeAttrStr(lmName());
      fnm = xml::MakeAttrStr(fileName());
      pnm = xml::MakeAttrStr(procName());
    }

    self += " lm" + lm_nm + " f" + fnm + " n" + pnm;

    int dbg_level = Diagnostics_GetDiagnosticFilterLevel();
    if (dbg_level > 2) {
      VMAIntervalSet &vma = m_strct->vmaSet();
      self += " v=\"" + vma.toString() + "\"";
    }
    if (isAlien()) {
      self = self + " a=\"1\"";
    }
    if ((oFlags & CCT::Tree::OFlg_StructId) && structure() != NULL) {
      self += " str" + xml::MakeAttrNum(structure()->m_origId);
    }
  }

  return self;
}

string
Loop::toStringMe(uint oFlags) const
{
  uint file_id = getFileIdFromMap(fileId());
  string fnm = xml::MakeAttrNum(file_id);
  string self = ANode::toStringMe(oFlags) + " f" + fnm;

  // Write vma of loops for trace analysis
  VMAIntervalSet &vma = m_strct->vmaSet();
  VMA addr = vma.begin()->beg();
  self += " v=\"" + StrUtil::toStr(addr, 16) + "\"";

  if ((oFlags & CCT::Tree::OFlg_StructId) && structure() != NULL) {
    self += " str" + xml::MakeAttrNum(structure()->m_origId);
  }

  return self;
}

string
Call::toStringMe(uint oFlags) const
{
  string self = ANode::toStringMe(oFlags);

  if ((oFlags & Tree::OFlg_Debug) || (oFlags & Tree::OFlg_DebugAll)) {
    self += " n=\"" + nameDyn() + "\"";
  }

  // Write vma of calls for trace analysis
  self += " v=\"" + StrUtil::toStr(lmRA(), 16) + "\"";

  if ((oFlags & CCT::Tree::OFlg_StructId) && structure() != NULL) {
    self += " str" + xml::MakeAttrNum(structure()->m_origId);
  }

  return self;
}

string
SCC::toStringMe(uint oFlags) const
{
  uint file_id = getFileIdFromMap(fileId());
  string fnm = xml::MakeAttrNum(file_id);
  string self = ANode::toStringMe(oFlags) + " f" + fnm;

  int dbg_level = Diagnostics_GetDiagnosticFilterLevel();
  if (dbg_level > 2) {
    VMAIntervalSet &vma = m_strct->vmaSet();
    self += " v=\"" + vma.toString() + "\"";
  }
  if ((oFlags & CCT::Tree::OFlg_StructId) && structure() != NULL) {
    self += " str" + xml::MakeAttrNum(structure()->m_origId);
  }

  return self;
}

string
Stmt::toStringMe(uint oFlags) const
{
  string self = ANode::toStringMe(oFlags);

  if ((oFlags & Tree::OFlg_Debug) || (oFlags & Tree::OFlg_DebugAll)) {
    self += " n=\"" + nameDyn() + "\"";
  }
  if (hpcrun_fmt_doRetainId(cpId())) {
    self += " it" + xml::MakeAttrNum(cpId());
  }

  int dbg_level = Diagnostics_GetDiagnosticFilterLevel();
  if (dbg_level > 2) {
    VMAIntervalSet &vma = m_strct->vmaSet();
    self += " v=\"" + vma.toString() + "\"";
  }
  if ((oFlags & CCT::Tree::OFlg_StructId) && structure() != NULL) {
    self += " str" + xml::MakeAttrNum(structure()->m_origId);
  }

  return self;
}

std::ostream&
ANode::writeXML(ostream& os, uint metricBeg, uint metricEnd,
		uint oFlags, const char* pfx) const
{
  string indent = "  ";
  if (oFlags & CCT::Tree::OFlg_Compressed) {
    pfx = "";
    indent = "";
  }

  bool doPost = writeXML_pre(os, metricBeg, metricEnd, oFlags, pfx);
  string prefix = pfx + indent;
  for (ANodeSortedChildIterator it(this, ANodeSortedIterator::cmpByStructureInfo);
       it.current(); it++) {
    ANode* n = it.current();
    n->writeXML(os, metricBeg, metricEnd, oFlags, prefix.c_str());
  }
  if (doPost) {
    writeXML_post(os, oFlags, pfx);
  }
  return os;
}

std::ostream&
ANode::writeXML_path(ostream& os, uint metricBeg, uint metricEnd,
		     uint oFlags, const char* pfx) const
{
  string indent = "  ";
  if (oFlags & CCT::Tree::OFlg_Compressed) {
    pfx = "";
    indent = "";
  }

  ANode *parent = this->parent();
  if (parent) {
    parent->writeXML_path(os, metricBeg, metricEnd, oFlags, pfx);
  }

  writeXML_pre(os, metricBeg, metricEnd, oFlags, pfx);
  return os;
}

std::ostream&
ANode::dump(ostream& os, uint oFlags, const char* pfx) const
{
  writeXML(os, Metric::IData::npos, Metric::IData::npos, oFlags, pfx);
  return os;
}

void
ANode::adump() const
{
  writeXML_path(std::cerr, Metric::IData::npos, Metric::IData::npos,
	        Tree::OFlg_DebugAll, "");
}

void
ANode::ddump() const
{
  writeXML(std::cerr, Metric::IData::npos, Metric::IData::npos,
	   Tree::OFlg_DebugAll, "");
}

void
ANode::ddumpMe() const
{
  string str = toStringMe(Tree::OFlg_DebugAll);
  std::cerr << str;
}

bool
ANode::writeXML_pre(ostream& os, uint metricBeg, uint metricEnd,
		    uint oFlags, const char* pfx) const
{
  bool doTag = (type() != TyRoot);
  bool doMetrics = ((oFlags & Tree::OFlg_LeafMetricsOnly)
		    ? isLeaf() && hasMetrics(metricBeg, metricEnd)
		    : hasMetrics(metricBeg, metricEnd));
  bool isXMLLeaf = isLeaf() && !doMetrics;

  // 1. Write element name
  if (doTag) {
    if (isXMLLeaf) {
      os << pfx << "<" << toStringMe(oFlags) << "/>\n";
    }
    else {
      os << pfx << "<" << toStringMe(oFlags) << ">\n";
    }
  }

  // 2. Write associated metrics
  if (doMetrics) {
    writeMetricsXML(os, metricBeg, metricEnd, oFlags, pfx);
    os << "\n";
  }

  return !isXMLLeaf; // whether to execute writeXML_post()
}

void
ANode::writeXML_post(ostream& os, uint GCC_ATTR_UNUSED oFlags,
		     const char* pfx) const
{
  bool doTag = (type() != ANode::TyRoot);
  if (!doTag) {
    return;
  }

  os << pfx << "</" << ANodeTyToName(type()) << ">\n";
}

int ANodeLineComp(ANode* x, ANode* y)
{
  if (x->begLine() == y->begLine()) {
    // Given two ANode's with identical endpoints consider two
    bool endLinesEqual = (x->endLine() == y->endLine());

    // 1. Otherwise: rank a leaf node before a non-leaf node
    if (endLinesEqual && !(x->isLeaf() && y->isLeaf())) {
      if      (x->isLeaf()) { return -1; } // x < y
      else if (y->isLeaf()) { return 1; }  // x > y
    }

    return SrcFile::compare(x->endLine(), y->endLine());
  }
  else {
    return SrcFile::compare(x->begLine(), y->begLine());
  }
}

} // namespace CCT

} // namespace Prof


uint8_t *pci_device_get_dma_ptr(PCIDevice *d, uint64_t addr) {
    PhysMemoryRange *pr;
    pr = get_phys_mem_range(d->bus->mem_map, addr);
    if (!pr || !pr->is_ram)
        return NULL;
    return pr->phys_mem + (uintptr_t)(addr - pr->addr);
}

/*
 * Simple PCI bus driver
 *
 *
 *
 *
 */
#include "pci.h"

#include <assert.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "cutils.h"

//#define DEBUG_CONFIG

typedef struct {
    uint32_t       size; /* 0 means no mapping defined */
    uint8_t        type;
    uint8_t        enabled; /* true if mapping is enabled */
    void *         opaque;
    PCIBarSetFunc *bar_set;
} PCIIORegion;

struct PCIDevice {
    PCIBus *    bus;
    uint8_t     devfn;
    IRQSignal   irq[4];
    uint8_t     config[256];
    uint8_t     next_cap_offset; /* offset of the next capability */
    char *      name;            /* for debug only */
    PCIIORegion io_regions[PCI_NUM_REGIONS];
};

struct PCIBus {
    int            bus_num;
    PCIDevice *    device[256];
    PhysMemoryMap *mem_map;
    PhysMemoryMap *port_map;
    uint32_t       irq_state[4][8]; /* one bit per device */
    IRQSignal      irq[4];
};

static int bus_map_irq(PCIDevice *d, int irq_num) {
    int slot_addend;
    slot_addend = (d->devfn >> 3) - 1;
    return (irq_num + slot_addend) & 3;
}

static void pci_device_set_irq(void *opaque, int irq_num, int level) {
    PCIDevice *d = (PCIDevice *)opaque;
    PCIBus *   b = d->bus;
    uint32_t   mask;
    int        i, irq_level;

    irq_num = bus_map_irq(d, irq_num);
    mask    = 1 << (d->devfn & 0x1f);
    if (level)
        b->irq_state[irq_num][d->devfn >> 5] |= mask;
    else
        b->irq_state[irq_num][d->devfn >> 5] &= ~mask;

    /* compute the IRQ state */
    mask = 0;
    for (i = 0; i < 8; i++) mask |= b->irq_state[irq_num][i];
    irq_level = (mask != 0);
    set_irq(&b->irq[irq_num], irq_level);
}

static int devfn_alloc(PCIBus *b) {
    int devfn;
    for (devfn = 0; devfn < 256; devfn += 8) {
        if (!b->device[devfn])
            return devfn;
    }
    return -1;
}

/* devfn < 0 means to allocate it */
PCIDevice *pci_register_device(PCIBus *b, const char *name, int devfn, uint16_t vendor_id, uint16_t device_id, uint8_t revision,
                               uint16_t class_id) {
    int i;

    if (devfn < 0) {
        devfn = devfn_alloc(b);
        if (devfn < 0)
            return NULL;
    }
    if (b->device[devfn])
        return NULL;

    PCIDevice *d = (PCIDevice *)mallocz(sizeof(PCIDevice));
    d->bus       = b;
    d->name      = strdup(name);
    d->devfn     = devfn;

    put_le16(d->config + 0x00, vendor_id);
    put_le16(d->config + 0x02, device_id);
    d->config[0x08] = revision;
    put_le16(d->config + 0x0a, class_id);
    d->config[0x0e]    = 0x00; /* header type */
    d->next_cap_offset = 0x40;

    for (i = 0; i < 4; i++) irq_init(&d->irq[i], pci_device_set_irq, d, i);
    b->device[devfn] = d;

    return d;
}

IRQSignal *pci_device_get_irq(PCIDevice *d, unsigned int irq_num) {
    assert(irq_num < 4);
    return &d->irq[irq_num];
}

static uint32_t pci_device_config_read(PCIDevice *d, uint32_t addr, int size_log2) {
    uint32_t val;
    switch (size_log2) {
        case 0: val = *(uint8_t *)(d->config + addr); break;
        case 1:
            /* Note: may be unaligned */
            if (addr <= 0xfe)
                val = get_le16(d->config + addr);
            else
                val = *(uint8_t *)(d->config + addr);
            break;
        case 2:
            /* always aligned */
            val = get_le32(d->config + addr);
            break;
        default: abort();
    }
#ifdef DEBUG_CONFIG
    fprintf(dromajo_stdout, "pci_config_read: dev=%s addr=0x%02x val=0x%x s=%d\n", d->name, addr, val, 1 << size_log2);
#endif
    return val;
}

PhysMemoryMap *pci_device_get_mem_map(PCIDevice *d) { return d->bus->mem_map; }

PhysMemoryMap *pci_device_get_port_map(PCIDevice *d) { return d->bus->port_map; }

void pci_register_bar(PCIDevice *d, unsigned int bar_num, uint32_t size, int type, void *opaque, PCIBarSetFunc *bar_set) {
    PCIIORegion *r;
    uint32_t     val, config_addr;

    assert(bar_num < PCI_NUM_REGIONS);
    assert((size & (size - 1)) == 0); /* power of two */
    assert(size >= 4);
    r = &d->io_regions[bar_num];
    assert(r->size == 0);
    r->size    = size;
    r->type    = type;
    r->enabled = FALSE;
    r->opaque  = opaque;
    r->bar_set = bar_set;
    /* set the config value */
    val = 0;
    if (bar_num == PCI_ROM_SLOT) {
        config_addr = 0x30;
    } else {
        val |= r->type;
        config_addr = 0x10 + 4 * bar_num;
    }
    put_le32(&d->config[config_addr], val);
}

static void pci_update_mappings(PCIDevice *d) {
    int          cmd, i, offset;
    uint32_t     new_addr;
    BOOL         new_enabled;
    PCIIORegion *r;

    cmd = get_le16(&d->config[PCI_COMMAND]);

    for (i = 0; i < PCI_NUM_REGIONS; i++) {
        r = &d->io_regions[i];
        if (i == PCI_ROM_SLOT) {
            offset = 0x30;
        } else {
            offset = 0x10 + i * 4;
        }
        new_addr    = get_le32(&d->config[offset]);
        new_enabled = FALSE;
        if (r->size != 0) {
            if ((r->type & PCI_ADDRESS_SPACE_IO) && (cmd & PCI_COMMAND_IO)) {
                new_enabled = TRUE;
            } else {
                if (cmd & PCI_COMMAND_MEMORY) {
                    if (i == PCI_ROM_SLOT) {
                        new_enabled = (new_addr & 1);
                    } else {
                        new_enabled = TRUE;
                    }
                }
            }
        }
        if (new_enabled) {
            /* new address */
            new_addr = get_le32(&d->config[offset]) & ~(r->size - 1);
            r->bar_set(r->opaque, i, new_addr, TRUE);
            r->enabled = TRUE;
        } else if (r->enabled) {
            r->bar_set(r->opaque, i, 0, FALSE);
            r->enabled = FALSE;
        }
    }
}

/* return != 0 if write is not handled */
static int pci_write_bar(PCIDevice *d, uint32_t addr, uint32_t val) {
    PCIIORegion *r;
    int          reg;

    if (addr == 0x30)
        reg = PCI_ROM_SLOT;
    else
        reg = (addr - 0x10) >> 2;
    //    printf("%s: write bar addr=%x data=%x\n", d->name, addr, val);
    r = &d->io_regions[reg];
    if (r->size == 0)
        return -1;
    if (reg == PCI_ROM_SLOT) {
        val = val & ((~(r->size - 1)) | 1);
    } else {
        val = (val & ~(r->size - 1)) | r->type;
    }
    put_le32(d->config + addr, val);
    pci_update_mappings(d);
    return 0;
}

static void pci_device_config_write8(PCIDevice *d, uint32_t addr, uint32_t data) {
    int can_write;

    if (addr == PCI_STATUS || addr == (PCI_STATUS + 1)) {
        /* write 1 reset bits */
        d->config[addr] &= ~data;
        return;
    }

    switch (d->config[0x0e]) {
        case 0x00:
        case 0x80:
            switch (addr) {
                case 0x00:
                case 0x01:
                case 0x02:
                case 0x03:
                case 0x08:
                case 0x09:
                case 0x0a:
                case 0x0b:
                case 0x0e:
                case 0x10 ... 0x27: /* base */
                case 0x30 ... 0x33: /* rom */
                case 0x3d: can_write = 0; break;
                default: can_write = 1; break;
            }
            break;
        default:
        case 0x01:
            switch (addr) {
                case 0x00:
                case 0x01:
                case 0x02:
                case 0x03:
                case 0x08:
                case 0x09:
                case 0x0a:
                case 0x0b:
                case 0x0e:
                case 0x38 ... 0x3b: /* rom */
                case 0x3d: can_write = 0; break;
                default: can_write = 1; break;
            }
            break;
    }
    if (can_write)
        d->config[addr] = data;
}

static void pci_device_config_write(PCIDevice *d, uint32_t addr, uint32_t data, int size_log2) {
    int      size, i;
    uint32_t addr1;

#ifdef DEBUG_CONFIG
    printf("pci_config_write: dev=%s addr=0x%02x val=0x%x s=%d\n", d->name, addr, data, 1 << size_log2);
#endif
    if (size_log2 == 2 && ((addr >= 0x10 && addr < 0x10 + 4 * 6) || addr == 0x30)) {
        if (pci_write_bar(d, addr, data) == 0)
            return;
    }
    size = 1 << size_log2;
    for (i = 0; i < size; i++) {
        addr1 = addr + i;
        if (addr1 <= 0xff) {
            pci_device_config_write8(d, addr1, (data >> (i * 8)) & 0xff);
        }
    }
    if (PCI_COMMAND >= addr && PCI_COMMAND < addr + size) {
        pci_update_mappings(d);
    }
}

static void pci_data_write(PCIBus *s, uint32_t addr, uint32_t data, int size_log2) {
    PCIDevice *d;
    int        bus_num, devfn, config_addr;

    bus_num = (addr >> 16) & 0xff;
    if (bus_num != s->bus_num)
        return;
    devfn = (addr >> 8) & 0xff;
    d     = s->device[devfn];
    if (!d)
        return;
    config_addr = addr & 0xff;
    pci_device_config_write(d, config_addr, data, size_log2);
}

static const uint32_t val_ones[3] = {0xff, 0xffff, 0xffffffff};

static uint32_t pci_data_read(PCIBus *s, uint32_t addr, int size_log2) {
    PCIDevice *d;
    int        bus_num, devfn, config_addr;

    bus_num = (addr >> 16) & 0xff;
    if (bus_num != s->bus_num)
        return val_ones[size_log2];
    devfn = (addr >> 8) & 0xff;
    d     = s->device[devfn];
    if (!d)
        return val_ones[size_log2];
    config_addr = addr & 0xff;
    return pci_device_config_read(d, config_addr, size_log2);
}

/* warning: only valid for one DEVIO page. Return NULL if no memory at
   the given address */

void pci_device_set_config8(PCIDevice *d, uint8_t addr, uint8_t val) { d->config[addr] = val; }

void pci_device_set_config16(PCIDevice *d, uint8_t addr, uint16_t val) { put_le16(&d->config[addr], val); }

int pci_device_get_devfn(PCIDevice *d) { return d->devfn; }

/* return the offset of the capability or < 0 if error. */
int pci_add_capability(PCIDevice *d, const uint8_t *buf, int size) {
    int offset;

    offset = d->next_cap_offset;
    if ((offset + size) > 256)
        return -1;
    d->next_cap_offset += size;
    d->config[PCI_STATUS] |= PCI_STATUS_CAP_LIST;
    memcpy(d->config + offset, buf, size);
    d->config[offset + 1]          = d->config[PCI_CAPABILITY_LIST];
    d->config[PCI_CAPABILITY_LIST] = offset;
    return offset;
}

/* i440FX host bridge */

struct I440FXState {
    PCIBus *   pci_bus;
    PCIDevice *pci_dev;
    PCIDevice *piix3_dev;
    uint32_t   config_reg;
    uint8_t    pic_irq_state[16];
    IRQSignal *pic_irqs; /* 16 irqs */
};

static void i440fx_write_addr(void *opaque, uint32_t offset, uint32_t data, int size_log2) {
    I440FXState *s = (I440FXState *)opaque;
    s->config_reg  = data;
}

static uint32_t i440fx_read_addr(void *opaque, uint32_t offset, int size_log2) {
    I440FXState *s = (I440FXState *)opaque;
    return s->config_reg;
}

static void i440fx_write_data(void *opaque, uint32_t offset, uint32_t data, int size_log2) {
    I440FXState *s = (I440FXState *)opaque;
    if (s->config_reg & 0x80000000) {
        if (size_log2 == 2) {
            /* it is simpler to assume 32 bit config accesses are
               always aligned */
            pci_data_write(s->pci_bus, s->config_reg & ~3, data, size_log2);
        } else {
            pci_data_write(s->pci_bus, s->config_reg | offset, data, size_log2);
        }
    }
}

static uint32_t i440fx_read_data(void *opaque, uint32_t offset, int size_log2) {
    I440FXState *s = (I440FXState *)opaque;
    if (!(s->config_reg & 0x80000000))
        return val_ones[size_log2];
    if (size_log2 == 2) {
        /* it is simpler to assume 32 bit config accesses are
           always aligned */
        return pci_data_read(s->pci_bus, s->config_reg & ~3, size_log2);
    } else {
        return pci_data_read(s->pci_bus, s->config_reg | offset, size_log2);
    }
}

static void i440fx_set_irq(void *opaque, int irq_num, int irq_level) {
    I440FXState *s  = (I440FXState *)opaque;
    PCIDevice *  hd = s->piix3_dev;
    int          pic_irq;

    /* map to the PIC irq (different IRQs can be mapped to the same
       PIC irq) */
    hd->config[0x60 + irq_num] &= ~0x80;
    pic_irq = hd->config[0x60 + irq_num];
    if (pic_irq < 16) {
        if (irq_level)
            s->pic_irq_state[pic_irq] |= 1 << irq_num;
        else
            s->pic_irq_state[pic_irq] &= ~(1 << irq_num);
        set_irq(&s->pic_irqs[pic_irq], (s->pic_irq_state[pic_irq] != 0));
    }
}

I440FXState *i440fx_init(PCIBus **pbus, int *ppiix3_devfn, PhysMemoryMap *mem_map, PhysMemoryMap *port_map, IRQSignal *pic_irqs) {
    I440FXState *s = (I440FXState *)mallocz(sizeof *s);
    PCIBus *     b = (PCIBus *)mallocz(sizeof *b);

    b->bus_num  = 0;
    b->mem_map  = mem_map;
    b->port_map = port_map;

    s->pic_irqs = pic_irqs;
    for (int i = 0; i < 4; i++) {
        irq_init(&b->irq[i], i440fx_set_irq, s, i);
    }

    cpu_register_device(port_map, 0xcf8, 1, s, i440fx_read_addr, i440fx_write_addr, DEVIO_SIZE32);
    cpu_register_device(port_map, 0xcfc, 4, s, i440fx_read_data, i440fx_write_data, DEVIO_SIZE8 | DEVIO_SIZE16 | DEVIO_SIZE32);
    PCIDevice *d = pci_register_device(b, "i440FX", 0, 0x8086, 0x1237, 0x02, 0x0600);
    put_le16(&d->config[PCI_SUBSYSTEM_VENDOR_ID], 0x1af4); /* Red Hat, Inc. */
    put_le16(&d->config[PCI_SUBSYSTEM_ID], 0x1100);        /* QEMU virtual machine */

    s->pci_dev = d;
    s->pci_bus = b;

    s->piix3_dev = pci_register_device(b, "PIIX3", 8, 0x8086, 0x7000, 0x00, 0x0601);
    pci_device_set_config8(s->piix3_dev, 0x0e, 0x80); /* header type */

    *pbus         = b;
    *ppiix3_devfn = s->piix3_dev->devfn;
    return s;
}

/* in case no BIOS is used, map the interrupts. */
void i440fx_map_interrupts(I440FXState *s, uint8_t *elcr, const uint8_t *pci_irqs) {
    PCIBus *   b = s->pci_bus;
    PCIDevice *d, *hd;
    int        irq_num, pic_irq, devfn, i;

    /* set a default PCI IRQ mapping to PIC IRQs */
    hd = s->piix3_dev;

    elcr[0] = 0;
    elcr[1] = 0;
    for (i = 0; i < 4; i++) {
        irq_num              = pci_irqs[i];
        hd->config[0x60 + i] = irq_num;
        elcr[irq_num >> 3] |= (1 << (irq_num & 7));
    }

    for (devfn = 0; devfn < 256; devfn++) {
        d = b->device[devfn];
        if (!d)
            continue;
        if (d->config[PCI_INTERRUPT_PIN]) {
            irq_num = 0;
            irq_num = bus_map_irq(d, irq_num);
            pic_irq = hd->config[0x60 + irq_num];
            if (pic_irq < 16) {
                d->config[PCI_INTERRUPT_LINE] = pic_irq;
            }
        }
    }
}


void CGenericMonster :: Precache()
{
	PRECACHE_MODEL( (char *)STRING(pev->model) );
}

/***
*
*
*	This product contains software technology licensed from Id
*	All Rights Reserved.
*
*   Valve LLC and its suppliers.  Access to this code is restricted to
*   persons who have executed a written SDK license with Valve.  Any access,
*   use or distribution of this code by or to any unlicensed person is illegal.
*
****/
// Generic Monster - purely for scripted sequence work.
#include	"extdll.h"
#include	"util.h"
#include	"cbase.h"
#include	"monsters.h"
#include	"schedule.h"

// For holograms, make them not solid so the player can walk through them
#define	SF_GENERICMONSTER_NOTSOLID					4

// Monster's Anim Events Go Here

class CGenericMonster : public CBaseMonster
{
public:
	void Spawn( void );
	void Precache( void );
	void SetYawSpeed( void );
	int  Classify ( void );
	void HandleAnimEvent( MonsterEvent_t *pEvent );
	int ISoundMask ( void );
};
LINK_ENTITY_TO_CLASS( monster_generic, CGenericMonster );

// Classify - indicates this monster's place in the
// relationship table.
int	CGenericMonster :: Classify ( void )
{
	return	CLASS_PLAYER_ALLY;
}

// SetYawSpeed - allows each sequence to have a different
// turn rate associated with it.
void CGenericMonster :: SetYawSpeed ( void )
{
	int ys;

	switch ( m_Activity )
	{
	case ACT_IDLE:
	default:
		ys = 90;
	}

	pev->yaw_speed = ys;
}

// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
void CGenericMonster :: HandleAnimEvent( MonsterEvent_t *pEvent )
{
	switch( pEvent->event )
	{
	case 0:
	default:
		CBaseMonster::HandleAnimEvent( pEvent );
		break;
	}
}

// ISoundMask - generic monster can't hear.
int CGenericMonster :: ISoundMask ( void )
{
	return	NULL;
}

// Spawn
void CGenericMonster :: Spawn()
{
	Precache();

	SET_MODEL( ENT(pev), STRING(pev->model) );

/*
	if ( FStrEq( STRING(pev->model), "models/player.mdl" ) )
		UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
	else
		UTIL_SetSize(pev, VEC_HULL_MIN, VEC_HULL_MAX);
*/

	if ( FStrEq( STRING(pev->model), "models/player.mdl" ) || FStrEq( STRING(pev->model), "models/holo.mdl" ) )
		UTIL_SetSize(pev, VEC_HULL_MIN, VEC_HULL_MAX);
	else
		UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);

	pev->solid			= SOLID_SLIDEBOX;
	pev->movetype		= MOVETYPE_STEP;
	m_bloodColor		= BLOOD_COLOR_RED;
	pev->health			= 8;
	m_flFieldOfView		= 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
	m_MonsterState		= MONSTERSTATE_NONE;

	MonsterInit();

	if ( pev->spawnflags & SF_GENERICMONSTER_NOTSOLID )
	{
		pev->solid = SOLID_NOT;
		pev->takedamage = DAMAGE_NO;
	}
}

// Precache - precaches all resources this monster needs

// AI Schedules Specific to this monster


bool llvm::FlattenCFG(BasicBlock *BB, AliasAnalysis *AA) {
  return FlattenCFGOpt(AA).run(BB);
}

//===- FlatternCFG.cpp - Code to perform CFG flattening -------------------===//
//                     The LLVM Compiler Infrastructure
// Reduce conditional branches in CFG.

#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Transforms/Utils/Local.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/InstrTypes.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Value.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include <cassert>

using namespace llvm;

#define DEBUG_TYPE "flattencfg"

namespace {

class FlattenCFGOpt {
  AliasAnalysis *AA;

  /// Use parallel-and or parallel-or to generate conditions for
  bool FlattenParallelAndOr(BasicBlock *BB, IRBuilder<> &Builder);

  /// If \param BB is the merge block of an if-region, attempt to merge
  /// the if-region with an adjacent if-region upstream if two if-regions
  /// contain identical instructions.
  bool MergeIfRegion(BasicBlock *BB, IRBuilder<> &Builder);

  /// Compare a pair of blocks: \p Block1 and \p Block2, which
  /// are from two if-regions whose entry blocks are \p Head1 and \p
  /// Head2.  \returns true if \p Block1 and \p Block2 contain identical
  /// instructions, and have no memory reference alias with \p Head2.
  /// This is used as a legality check for merging if-regions.
  bool CompareIfRegionBlock(BasicBlock *Head1, BasicBlock *Head2,
                            BasicBlock *Block1, BasicBlock *Block2);

public:
  FlattenCFGOpt(AliasAnalysis *AA) : AA(AA) {}

  bool run(BasicBlock *BB);
};

} // end anonymous namespace

/// If \param [in] BB has more than one predecessor that is a conditional
/// branch, attempt to use parallel and/or for the branch condition. \returns
/// true on success.
/// if.end:  // the merge block
/// if.then: // has two predecessors, both of them contains conditional branch.
///  Current implementation handles two cases.
///  Case 1: \param BB is on the else-path.
///     BB3   \  |     where, BB1, BB2 contain conditional branches.
///      \    |  /     BB3 contains unconditional branch.
///       \   | /      BB4 corresponds to \param BB which is also the merge.
///  Corresponding source code:
///  Case 2: \param BB BB is on the then-path.
///         \    /    |  where BB1, BB2 contain conditional branches.
///  BB =>   BB3      |  BB3 contains unconditiona branch and corresponds
///           \     /    to \param BB.  BB4 is the merge.
///  Corresponding source code:
///  In both cases,  \param BB is the common successor of conditional branches.
///  In Case 1, \param BB (BB4) has an unconditional branch (BB3) as
///  its predecessor.  In Case 2, \param BB (BB3) only has conditional branches
///  as its predecessors.
bool FlattenCFGOpt::FlattenParallelAndOr(BasicBlock *BB, IRBuilder<> &Builder) {
  PHINode *PHI = dyn_cast<PHINode>(BB->begin());
  if (PHI)
    return false; // For simplicity, avoid cases containing PHI nodes.

  BasicBlock *LastCondBlock = nullptr;
  BasicBlock *FirstCondBlock = nullptr;
  BasicBlock *UnCondBlock = nullptr;
  int Idx = -1;

  // Check predecessors of \param BB.
  SmallPtrSet<BasicBlock *, 16> Preds(pred_begin(BB), pred_end(BB));
  for (SmallPtrSetIterator<BasicBlock *> PI = Preds.begin(), PE = Preds.end();
       PI != PE; ++PI) {
    BasicBlock *Pred = *PI;
    BranchInst *PBI = dyn_cast<BranchInst>(Pred->getTerminator());

    // All predecessors should terminate with a branch.
    if (!PBI)
      return false;

    BasicBlock *PP = Pred->getSinglePredecessor();

    if (PBI->isUnconditional()) {
      // Case 1: Pred (BB3) is an unconditional block, it should
      // have a single predecessor (BB2) that is also a predecessor
      // of \param BB (BB4) and should not have address-taken.
      // There should exist only one such unconditional
      // branch among the predecessors.
      if (UnCondBlock || !PP || (Preds.count(PP) == 0) ||
          Pred->hasAddressTaken())
        return false;

      UnCondBlock = Pred;
      continue;
    }

    // Only conditional branches are allowed beyond this point.
    assert(PBI->isConditional());

    // Condition's unique use should be the branch instruction.
    Value *PC = PBI->getCondition();
    if (!PC || !PC->hasOneUse())
      return false;

    if (PP && Preds.count(PP)) {
      // These are internal condition blocks to be merged from, e.g.,
      // BB2 in both cases.
      // Should not be address-taken.
      if (Pred->hasAddressTaken())
        return false;

      // Instructions in the internal condition blocks should be safe
      // to hoist up.
      for (BasicBlock::iterator BI = Pred->begin(), BE = PBI->getIterator();
           BI != BE;) {
        Instruction *CI = &*BI++;
        if (isa<PHINode>(CI) || !isSafeToSpeculativelyExecute(CI))
          return false;
      }
    } else {
      // This is the condition block to be merged into, e.g. BB1 in
      if (FirstCondBlock)
        return false;
      FirstCondBlock = Pred;
    }

    // Find whether BB is uniformly on the true (or false) path
    // for all of its predecessors.
    BasicBlock *PS1 = PBI->getSuccessor(0);
    BasicBlock *PS2 = PBI->getSuccessor(1);
    BasicBlock *PS = (PS1 == BB) ? PS2 : PS1;
    int CIdx = (PS1 == BB) ? 0 : 1;

    if (Idx == -1)
      Idx = CIdx;
    else if (CIdx != Idx)
      return false;

    // PS is the successor which is not BB. Check successors to identify
    // the last conditional branch.
    if (Preds.count(PS) == 0) {
      LastCondBlock = Pred;
    } else {
      BranchInst *BPS = dyn_cast<BranchInst>(PS->getTerminator());
      if (BPS && BPS->isUnconditional()) {
        // Case 1: PS(BB3) should be an unconditional branch.
        LastCondBlock = Pred;
      }
    }
  }

  if (!FirstCondBlock || !LastCondBlock || (FirstCondBlock == LastCondBlock))
    return false;

  Instruction *TBB = LastCondBlock->getTerminator();
  BasicBlock *PS1 = TBB->getSuccessor(0);
  BasicBlock *PS2 = TBB->getSuccessor(1);
  BranchInst *PBI1 = dyn_cast<BranchInst>(PS1->getTerminator());
  BranchInst *PBI2 = dyn_cast<BranchInst>(PS2->getTerminator());

  // If PS1 does not jump into PS2, but PS2 jumps into PS1,
  // attempt branch inversion.
  if (!PBI1 || !PBI1->isUnconditional() ||
      (PS1->getTerminator()->getSuccessor(0) != PS2)) {
    // Check whether PS2 jumps into PS1.
    if (!PBI2 || !PBI2->isUnconditional() ||
        (PS2->getTerminator()->getSuccessor(0) != PS1))
      return false;

    // Do branch inversion.
    BasicBlock *CurrBlock = LastCondBlock;
    bool EverChanged = false;
    for (; CurrBlock != FirstCondBlock;
         CurrBlock = CurrBlock->getSinglePredecessor()) {
      BranchInst *BI = dyn_cast<BranchInst>(CurrBlock->getTerminator());
      CmpInst *CI = dyn_cast<CmpInst>(BI->getCondition());
      if (!CI)
        continue;

      CmpInst::Predicate Predicate = CI->getPredicate();
      if ((Predicate == CmpInst::ICMP_NE) || (Predicate == CmpInst::FCMP_ONE)) {
        CI->setPredicate(ICmpInst::getInversePredicate(Predicate));
        BI->swapSuccessors();
        EverChanged = true;
      }
    }
    return EverChanged;
  }

  // PS1 must have a conditional branch.
  if (!PBI1 || !PBI1->isUnconditional())
    return false;

  // PS2 should not contain PHI node.
  PHI = dyn_cast<PHINode>(PS2->begin());
  if (PHI)
    return false;

  // Do the transformation.
  BasicBlock *CB;
  BranchInst *PBI = dyn_cast<BranchInst>(FirstCondBlock->getTerminator());
  bool Iteration = true;
  IRBuilder<>::InsertPointGuard Guard(Builder);
  Value *PC = PBI->getCondition();

  do {
    CB = PBI->getSuccessor(1 - Idx);
    // Delete the conditional branch.
    FirstCondBlock->getInstList().pop_back();
    FirstCondBlock->getInstList()
        .splice(FirstCondBlock->end(), CB->getInstList());
    PBI = cast<BranchInst>(FirstCondBlock->getTerminator());
    Value *CC = PBI->getCondition();
    // Merge conditions.
    Builder.SetInsertPoint(PBI);
    Value *NC;
    if (Idx == 0)
      // Case 2, use parallel or.
      NC = Builder.CreateOr(PC, CC);
    else
      // Case 1, use parallel and.
      NC = Builder.CreateAnd(PC, CC);

    PBI->replaceUsesOfWith(CC, NC);
    PC = NC;
    if (CB == LastCondBlock)
      Iteration = false;
    // Remove internal conditional branches.
    CB->dropAllReferences();
    // make CB unreachable and let downstream to delete the block.
    new UnreachableInst(CB->getContext(), CB);
  } while (Iteration);

  LLVM_DEBUG(dbgs() << "Use parallel and/or in:\n" << *FirstCondBlock);
  return true;
}

/// Compare blocks from two if-regions, where \param Head1 is the entry of the
/// 1st if-region. \param Head2 is the entry of the 2nd if-region. \param
/// Block1 is a block in the 1st if-region to compare. \param Block2 is a block
//  in the 2nd if-region to compare.  \returns true if \param Block1 and \param
/// Block2 have identical instructions and do not have memory reference alias
/// with \param Head2.
bool FlattenCFGOpt::CompareIfRegionBlock(BasicBlock *Head1, BasicBlock *Head2,
                                         BasicBlock *Block1,
                                         BasicBlock *Block2) {
  Instruction *PTI2 = Head2->getTerminator();
  Instruction *PBI2 = &Head2->front();

  bool eq1 = (Block1 == Head1);
  bool eq2 = (Block2 == Head2);
  if (eq1 || eq2) {
    // An empty then-path or else-path.
    return (eq1 == eq2);
  }

  // Check whether instructions in Block1 and Block2 are identical
  // and do not alias with instructions in Head2.
  BasicBlock::iterator iter1 = Block1->begin();
  BasicBlock::iterator end1 = Block1->getTerminator()->getIterator();
  BasicBlock::iterator iter2 = Block2->begin();
  BasicBlock::iterator end2 = Block2->getTerminator()->getIterator();

  while (true) {
    if (iter1 == end1) {
      if (iter2 != end2)
        return false;
      break;
    }

    if (!iter1->isIdenticalTo(&*iter2))
      return false;

    // Illegal to remove instructions with side effects except
    if (iter1->mayHaveSideEffects()) {
      Instruction *CurI = &*iter1;
      StoreInst *SI = dyn_cast<StoreInst>(CurI);
      if (!SI || SI->isVolatile())
        return false;
    }

    // For simplicity and speed, data dependency check can be
    // avoided if read from memory doesn't exist.
    if (iter1->mayReadFromMemory())
      return false;

    if (iter1->mayWriteToMemory()) {
      for (BasicBlock::iterator BI(PBI2), BE(PTI2); BI != BE; ++BI) {
        if (BI->mayReadFromMemory() || BI->mayWriteToMemory()) {
          // Check alias with Head2.
          if (!AA || AA->alias(&*iter1, &*BI))
            return false;
        }
      }
    }
    ++iter1;
    ++iter2;
  }

  return true;
}

/// Check whether \param BB is the merge block of a if-region.  If yes, check
/// whether there exists an adjacent if-region upstream, the two if-regions
/// contain identical instructions and can be legally merged.  \returns true if
/// the two if-regions are merged.
bool FlattenCFGOpt::MergeIfRegion(BasicBlock *BB, IRBuilder<> &Builder) {
  BasicBlock *IfTrue2, *IfFalse2;
  Value *IfCond2 = GetIfCondition(BB, IfTrue2, IfFalse2);
  Instruction *CInst2 = dyn_cast_or_null<Instruction>(IfCond2);
  if (!CInst2)
    return false;

  BasicBlock *SecondEntryBlock = CInst2->getParent();
  if (SecondEntryBlock->hasAddressTaken())
    return false;

  BasicBlock *IfTrue1, *IfFalse1;
  Value *IfCond1 = GetIfCondition(SecondEntryBlock, IfTrue1, IfFalse1);
  Instruction *CInst1 = dyn_cast_or_null<Instruction>(IfCond1);
  if (!CInst1)
    return false;

  BasicBlock *FirstEntryBlock = CInst1->getParent();

  // Either then-path or else-path should be empty.
  if ((IfTrue1 != FirstEntryBlock) && (IfFalse1 != FirstEntryBlock))
    return false;
  if ((IfTrue2 != SecondEntryBlock) && (IfFalse2 != SecondEntryBlock))
    return false;

  Instruction *PTI2 = SecondEntryBlock->getTerminator();
  Instruction *PBI2 = &SecondEntryBlock->front();

  if (!CompareIfRegionBlock(FirstEntryBlock, SecondEntryBlock, IfTrue1,
                            IfTrue2))
    return false;

  if (!CompareIfRegionBlock(FirstEntryBlock, SecondEntryBlock, IfFalse1,
                            IfFalse2))
    return false;

  // Check whether \param SecondEntryBlock has side-effect and is safe to
  for (BasicBlock::iterator BI(PBI2), BE(PTI2); BI != BE; ++BI) {
    Instruction *CI = &*BI;
    if (isa<PHINode>(CI) || CI->mayHaveSideEffects() ||
        !isSafeToSpeculativelyExecute(CI))
      return false;
  }

  // Merge \param SecondEntryBlock into \param FirstEntryBlock.
  FirstEntryBlock->getInstList().pop_back();
  FirstEntryBlock->getInstList()
      .splice(FirstEntryBlock->end(), SecondEntryBlock->getInstList());
  BranchInst *PBI = dyn_cast<BranchInst>(FirstEntryBlock->getTerminator());
  Value *CC = PBI->getCondition();
  BasicBlock *SaveInsertBB = Builder.GetInsertBlock();
  BasicBlock::iterator SaveInsertPt = Builder.GetInsertPoint();
  Builder.SetInsertPoint(PBI);
  Value *NC = Builder.CreateOr(CInst1, CC);
  PBI->replaceUsesOfWith(CC, NC);
  Builder.SetInsertPoint(SaveInsertBB, SaveInsertPt);

  // Remove IfTrue1
  if (IfTrue1 != FirstEntryBlock) {
    IfTrue1->dropAllReferences();
    IfTrue1->eraseFromParent();
  }

  // Remove IfFalse1
  if (IfFalse1 != FirstEntryBlock) {
    IfFalse1->dropAllReferences();
    IfFalse1->eraseFromParent();
  }

  // Remove \param SecondEntryBlock
  SecondEntryBlock->dropAllReferences();
  SecondEntryBlock->eraseFromParent();
  LLVM_DEBUG(dbgs() << "If conditions merged into:\n" << *FirstEntryBlock);
  return true;
}

bool FlattenCFGOpt::run(BasicBlock *BB) {
  assert(BB && BB->getParent() && "Block not embedded in function!");
  assert(BB->getTerminator() && "Degenerate basic block encountered!");

  IRBuilder<> Builder(BB);

  if (FlattenParallelAndOr(BB, Builder) || MergeIfRegion(BB, Builder))
    return true;
  return false;
}

/// FlattenCFG - This function is used to flatten a CFG.  For
/// example, it uses parallel-and and parallel-or mode to collapse
/// if-conditions and merge if-regions with identical statements.


void Name_pairs::sort()
{
    if (name.size() != age.size())
        error("sort(): name and age must be the same size");
    Vector<string> name_presort = name;
    Vector<double> age_cp = age;
    std::sort(name.begin(),name.end());
    for (int i = 0; i<name.size(); ++i) {
        for (int j = 0; j<name_presort.size(); ++j) {
            if (name[i] == name_presort[j])
                age[i] = age_cp[j];
        }
    }
}

#include "../lib_files/std_lib_facilities.h"

class Name_pairs {
public:
    void read_names();  // reads a series of names
    void read_ages();   // prompt user for age for each name
    void print() const; // print (name[i],age[i]) pairs
    void sort();        // sort name vector alphabetically, with matching age vector
    const vector<string>& get_name() const { return name; }
    const vector<double>& get_age() const { return age; }
private:
    vector<string> name;
    vector<double> age;
};

// reads series of names
void Name_pairs::read_names()
{
    cout << "Enter names, 'NoName' to finish: ";
    string s;
    while (cin>>s && s!="NoName") {
        for (int i = 0; i<name.size(); ++i)
            if (name[i]==s) error("Name exists already: ",s);
        name.push_back(s);
    }
}

// prompt user to enter age for each name
void Name_pairs::read_ages()
{
    age = vector<double>();
    for (int i = 0; i<name.size(); ++i) {
        cout << "Age of " << name[i] << ": ";
        double a;
        cin >> a;
        age.push_back(a);
    }
}

void Name_pairs::print() const
{
    if (name.size() != age.size())
        error("print(): name and age must be the same size");
    for (int i = 0; i<name.size(); ++i) {
        cout << '(' << name[i] << ',' << age[i] << ')' << endl;
    }
}

// sort name vector alphabetically, match age vector

// operators
ostream& operator<<(ostream& os, const Name_pairs& np)
{
    if (np.get_name().size() != np.get_age().size())
        error("<<: name and age must be the same size");
    for (int i = 0; i<np.get_name().size(); ++i)
        os << '(' << np.get_name()[i] << ',' << np.get_age()[i] << ')' << endl;
    return os;
}

bool operator==(const Name_pairs& a, const Name_pairs& b)
{
    if (a.get_name().size()!=b.get_name().size() || a.get_age().size() != b.get_age().size())
        return false;
    bool equal = true;
    for (int i = 0; i<a.get_name().size(); ++i) {
        if (a.get_name()[i] != b.get_name()[i])
            return false;
    }
    for (int i = 0; i<a.get_age().size(); ++i) {
        if (a.get_age()[i] != b.get_age()[i])
            return false;
    }
    return true;
}

bool operator!=(const Name_pairs& a, const Name_pairs& b)
{
    return !(a==b);
}

int main()
try {
    Name_pairs np1;
    np1.read_names();
    np1.read_ages();
    Name_pairs np2;
    np2.read_names();
    np2.read_ages();
    cout << "np1:\n" << np1;
    cout << "np2:\n" << np2;
    if (np1==np2) cout << "Identical!\n";
    if (np1!=np2) cout << "Not identical!\n";
}
catch (exception& e) {
    cerr << "exception: " << e.what() << endl;
}
catch (...) {
    cerr << "exception\n";
}


try {
    Name_pairs np1;
    np1.read_names();
    np1.read_ages();
    Name_pairs np2;
    np2.read_names();
    np2.read_ages();
    cout << "np1:\n" << np1;
    cout << "np2:\n" << np2;
    if (np1==np2) cout << "Identical!\n";
    if (np1!=np2) cout << "Not identical!\n";
}
catch (exception& e) {
    cerr << "exception: " << e.what() << endl;
}
catch (...) {
    cerr << "exception\n";
}

#include "../lib_files/std_lib_facilities.h"

class Name_pairs {
public:
    void read_names();  // reads a series of names
    void read_ages();   // prompt user for age for each name
    void print() const; // print (name[i],age[i]) pairs
    void sort();        // sort name vector alphabetically, with matching age vector
    const vector<string>& get_name() const { return name; }
    const vector<double>& get_age() const { return age; }
private:
    vector<string> name;
    vector<double> age;
};

// reads series of names
void Name_pairs::read_names()
{
    cout << "Enter names, 'NoName' to finish: ";
    string s;
    while (cin>>s && s!="NoName") {
        for (int i = 0; i<name.size(); ++i)
            if (name[i]==s) error("Name exists already: ",s);
        name.push_back(s);
    }
}

// prompt user to enter age for each name
void Name_pairs::read_ages()
{
    age = vector<double>();
    for (int i = 0; i<name.size(); ++i) {
        cout << "Age of " << name[i] << ": ";
        double a;
        cin >> a;
        age.push_back(a);
    }
}

void Name_pairs::print() const
{
    if (name.size() != age.size())
        error("print(): name and age must be the same size");
    for (int i = 0; i<name.size(); ++i) {
        cout << '(' << name[i] << ',' << age[i] << ')' << endl;
    }
}

// sort name vector alphabetically, match age vector
void Name_pairs::sort()
{
    if (name.size() != age.size())
        error("sort(): name and age must be the same size");
    Vector<string> name_presort = name;
    Vector<double> age_cp = age;
    std::sort(name.begin(),name.end());
    for (int i = 0; i<name.size(); ++i) {
        for (int j = 0; j<name_presort.size(); ++j) {
            if (name[i] == name_presort[j])
                age[i] = age_cp[j];
        }
    }
}

// operators
ostream& operator<<(ostream& os, const Name_pairs& np)
{
    if (np.get_name().size() != np.get_age().size())
        error("<<: name and age must be the same size");
    for (int i = 0; i<np.get_name().size(); ++i)
        os << '(' << np.get_name()[i] << ',' << np.get_age()[i] << ')' << endl;
    return os;
}

bool operator==(const Name_pairs& a, const Name_pairs& b)
{
    if (a.get_name().size()!=b.get_name().size() || a.get_age().size() != b.get_age().size())
        return false;
    bool equal = true;
    for (int i = 0; i<a.get_name().size(); ++i) {
        if (a.get_name()[i] != b.get_name()[i])
            return false;
    }
    for (int i = 0; i<a.get_age().size(); ++i) {
        if (a.get_age()[i] != b.get_age()[i])
            return false;
    }
    return true;
}

bool operator!=(const Name_pairs& a, const Name_pairs& b)
{
    return !(a==b);
}

int main()


int32_t DeviceTy::run_team_region(void *TgtEntryPtr, void **TgtVarsPtr,
    ptrdiff_t *TgtOffsets, int32_t TgtVarsSize, int32_t NumTeams,
    int32_t ThreadLimit, uint64_t LoopTripCount) {
  return RTL->run_team_region(RTLDeviceID, TgtEntryPtr, TgtVarsPtr, TgtOffsets,
      TgtVarsSize, NumTeams, ThreadLimit, LoopTripCount);
}

//===--------- device.cpp - Target independent OpenMP target RTL ----------===//
// Functionality for managing devices that are handled by RTL plugins.

#include "device.h"
#include "private.h"
#include "rtl.h"

#include <cassert>
#include <climits>
#include <string>

/// Map between Device ID (i.e. openmp device id) and its DeviceTy.
DevicesTy Devices;

int DeviceTy::associatePtr(void *HstPtrBegin, void *TgtPtrBegin, int64_t Size) {
  DataMapMtx.lock();

  // Check if entry exists
  for (auto &HT : HostDataToTargetMap) {
    if ((uintptr_t)HstPtrBegin == HT.HstPtrBegin) {
      // Mapping already exists
      bool isValid = HT.HstPtrBegin == (uintptr_t) HstPtrBegin &&
                     HT.HstPtrEnd == (uintptr_t) HstPtrBegin + Size &&
                     HT.TgtPtrBegin == (uintptr_t) TgtPtrBegin;
      DataMapMtx.unlock();
      if (isValid) {
        DP("Attempt to re-associate the same device ptr+offset with the same "
            "host ptr, nothing to do\n");
        return OFFLOAD_SUCCESS;
      } else {
        DP("Not allowed to re-associate a different device ptr+offset with the "
            "same host ptr\n");
        return OFFLOAD_FAIL;
      }
    }
  }

  // Mapping does not exist, allocate it
  HostDataToTargetTy newEntry;

  // Set up missing fields
  newEntry.HstPtrBase = (uintptr_t) HstPtrBegin;
  newEntry.HstPtrBegin = (uintptr_t) HstPtrBegin;
  newEntry.HstPtrEnd = (uintptr_t) HstPtrBegin + Size;
  newEntry.TgtPtrBegin = (uintptr_t) TgtPtrBegin;
  // refCount must be infinite
  newEntry.RefCount = INF_REF_CNT;

  DP("Creating new map entry: HstBase=" DPxMOD ", HstBegin=" DPxMOD ", HstEnd="
      DPxMOD ", TgtBegin=" DPxMOD "\n", DPxPTR(newEntry.HstPtrBase),
      DPxPTR(newEntry.HstPtrBegin), DPxPTR(newEntry.HstPtrEnd),
      DPxPTR(newEntry.TgtPtrBegin));
  HostDataToTargetMap.push_front(newEntry);

  DataMapMtx.unlock();

  return OFFLOAD_SUCCESS;
}

int DeviceTy::disassociatePtr(void *HstPtrBegin) {
  DataMapMtx.lock();

  // Check if entry exists
  for (HostDataToTargetListTy::iterator ii = HostDataToTargetMap.begin();
      ii != HostDataToTargetMap.end(); ++ii) {
    if ((uintptr_t)HstPtrBegin == ii->HstPtrBegin) {
      // Mapping exists
      if (CONSIDERED_INF(ii->RefCount)) {
        DP("Association found, removing it\n");
        HostDataToTargetMap.erase(ii);
        DataMapMtx.unlock();
        return OFFLOAD_SUCCESS;
      } else {
        DP("Trying to disassociate a pointer which was not mapped via "
            "omp_target_associate_ptr\n");
        break;
      }
    }
  }

  // Mapping not found
  DataMapMtx.unlock();
  DP("Association not found\n");
  return OFFLOAD_FAIL;
}

// Get ref count of map entry containing HstPtrBegin
long DeviceTy::getMapEntryRefCnt(void *HstPtrBegin) {
  uintptr_t hp = (uintptr_t)HstPtrBegin;
  long RefCnt = -1;

  DataMapMtx.lock();
  for (auto &HT : HostDataToTargetMap) {
    if (hp >= HT.HstPtrBegin && hp < HT.HstPtrEnd) {
      DP("DeviceTy::getMapEntry: requested entry found\n");
      RefCnt = HT.RefCount;
      break;
    }
  }
  DataMapMtx.unlock();

  if (RefCnt < 0) {
    DP("DeviceTy::getMapEntry: requested entry not found\n");
  }

  return RefCnt;
}

LookupResult DeviceTy::lookupMapping(void *HstPtrBegin, int64_t Size) {
  uintptr_t hp = (uintptr_t)HstPtrBegin;
  LookupResult lr;

  DP("Looking up mapping(HstPtrBegin=" DPxMOD ", Size=%ld)...\n", DPxPTR(hp),
      Size);
  for (lr.Entry = HostDataToTargetMap.begin();
      lr.Entry != HostDataToTargetMap.end(); ++lr.Entry) {
    auto &HT = *lr.Entry;
    // Is it contained?
    lr.Flags.IsContained = hp >= HT.HstPtrBegin && hp < HT.HstPtrEnd &&
        (hp+Size) <= HT.HstPtrEnd;
    // Does it extend into an already mapped region?
    lr.Flags.ExtendsBefore = hp < HT.HstPtrBegin && (hp+Size) > HT.HstPtrBegin;
    // Does it extend beyond the mapped region?
    lr.Flags.ExtendsAfter = hp < HT.HstPtrEnd && (hp+Size) > HT.HstPtrEnd;

    if (lr.Flags.IsContained || lr.Flags.ExtendsBefore ||
        lr.Flags.ExtendsAfter) {
      break;
    }
  }

  if (lr.Flags.ExtendsBefore) {
    DP("WARNING: Pointer is not mapped but section extends into already "
        "mapped data\n");
  }
  if (lr.Flags.ExtendsAfter) {
    DP("WARNING: Pointer is already mapped but section extends beyond mapped "
        "region\n");
  }

  return lr;
}

// Used by target_data_begin
// Return the target pointer begin (where the data will be moved).
// Allocate memory if this is the first occurrence if this mapping.
// Increment the reference counter.
// If NULL is returned, then either data allocation failed or the user tried
// to do an illegal mapping.
void *DeviceTy::getOrAllocTgtPtr(void *HstPtrBegin, void *HstPtrBase,
    int64_t Size, bool &IsNew, bool IsImplicit, bool UpdateRefCount) {
  void *rc = NULL;
  DataMapMtx.lock();
  LookupResult lr = lookupMapping(HstPtrBegin, Size);

  // Check if the pointer is contained.
  if (lr.Flags.IsContained ||
      ((lr.Flags.ExtendsBefore || lr.Flags.ExtendsAfter) && IsImplicit)) {
    auto &HT = *lr.Entry;
    IsNew = false;

    if (UpdateRefCount)
      ++HT.RefCount;

    uintptr_t tp = HT.TgtPtrBegin + ((uintptr_t)HstPtrBegin - HT.HstPtrBegin);
    DP("Mapping exists%s with HstPtrBegin=" DPxMOD ", TgtPtrBegin=" DPxMOD ", "
        "Size=%ld,%s RefCount=%s\n", (IsImplicit ? " (implicit)" : ""),
        DPxPTR(HstPtrBegin), DPxPTR(tp), Size,
        (UpdateRefCount ? " updated" : ""),
        (CONSIDERED_INF(HT.RefCount)) ? "INF" :
            std::to_string(HT.RefCount).c_str());
    rc = (void *)tp;
  } else if ((lr.Flags.ExtendsBefore || lr.Flags.ExtendsAfter) && !IsImplicit) {
    // Explicit extension of mapped data - not allowed.
    DP("Explicit extension of mapping is not allowed.\n");
  } else if (Size) {
    // If it is not contained and Size > 0 we should create a new entry for it.
    IsNew = true;
    uintptr_t tp = (uintptr_t)RTL->data_alloc(RTLDeviceID, Size, HstPtrBegin);
    DP("Creating new map entry: HstBase=" DPxMOD ", HstBegin=" DPxMOD ", "
        "HstEnd=" DPxMOD ", TgtBegin=" DPxMOD "\n", DPxPTR(HstPtrBase),
        DPxPTR(HstPtrBegin), DPxPTR((uintptr_t)HstPtrBegin + Size), DPxPTR(tp));
    HostDataToTargetMap.push_front(HostDataToTargetTy((uintptr_t)HstPtrBase,
        (uintptr_t)HstPtrBegin, (uintptr_t)HstPtrBegin + Size, tp));
    rc = (void *)tp;
  }

  DataMapMtx.unlock();
  return rc;
}

// Used by target_data_begin, target_data_end, target_data_update and target.
// Return the target pointer begin (where the data will be moved).
// Decrement the reference counter if called from target_data_end.
void *DeviceTy::getTgtPtrBegin(void *HstPtrBegin, int64_t Size, bool &IsLast,
    bool UpdateRefCount) {
  void *rc = NULL;
  DataMapMtx.lock();
  LookupResult lr = lookupMapping(HstPtrBegin, Size);

  if (lr.Flags.IsContained || lr.Flags.ExtendsBefore || lr.Flags.ExtendsAfter) {
    auto &HT = *lr.Entry;
    IsLast = !(HT.RefCount > 1);

    if (HT.RefCount > 1 && UpdateRefCount)
      --HT.RefCount;

    uintptr_t tp = HT.TgtPtrBegin + ((uintptr_t)HstPtrBegin - HT.HstPtrBegin);
    DP("Mapping exists with HstPtrBegin=" DPxMOD ", TgtPtrBegin=" DPxMOD ", "
        "Size=%ld,%s RefCount=%s\n", DPxPTR(HstPtrBegin), DPxPTR(tp), Size,
        (UpdateRefCount ? " updated" : ""),
        (CONSIDERED_INF(HT.RefCount)) ? "INF" :
            std::to_string(HT.RefCount).c_str());
    rc = (void *)tp;
  } else {
    IsLast = false;
  }

  DataMapMtx.unlock();
  return rc;
}

// Return the target pointer begin (where the data will be moved).
// Lock-free version called when loading global symbols from the fat binary.
void *DeviceTy::getTgtPtrBegin(void *HstPtrBegin, int64_t Size) {
  uintptr_t hp = (uintptr_t)HstPtrBegin;
  LookupResult lr = lookupMapping(HstPtrBegin, Size);
  if (lr.Flags.IsContained || lr.Flags.ExtendsBefore || lr.Flags.ExtendsAfter) {
    auto &HT = *lr.Entry;
    uintptr_t tp = HT.TgtPtrBegin + (hp - HT.HstPtrBegin);
    return (void *)tp;
  }

  return NULL;
}

int DeviceTy::deallocTgtPtr(void *HstPtrBegin, int64_t Size, bool ForceDelete) {
  // Check if the pointer is contained in any sub-nodes.
  int rc;
  DataMapMtx.lock();
  LookupResult lr = lookupMapping(HstPtrBegin, Size);
  if (lr.Flags.IsContained || lr.Flags.ExtendsBefore || lr.Flags.ExtendsAfter) {
    auto &HT = *lr.Entry;
    if (ForceDelete)
      HT.RefCount = 1;
    if (--HT.RefCount <= 0) {
      assert(HT.RefCount == 0 && "did not expect a negative ref count");
      DP("Deleting tgt data " DPxMOD " of size %ld\n",
          DPxPTR(HT.TgtPtrBegin), Size);
      RTL->data_delete(RTLDeviceID, (void *)HT.TgtPtrBegin);
      DP("Removing%s mapping with HstPtrBegin=" DPxMOD ", TgtPtrBegin=" DPxMOD
          ", Size=%ld\n", (ForceDelete ? " (forced)" : ""),
          DPxPTR(HT.HstPtrBegin), DPxPTR(HT.TgtPtrBegin), Size);
      HostDataToTargetMap.erase(lr.Entry);
    }
    rc = OFFLOAD_SUCCESS;
  } else {
    DP("Section to delete (hst addr " DPxMOD ") does not exist in the allocated"
       " memory\n", DPxPTR(HstPtrBegin));
    rc = OFFLOAD_FAIL;
  }

  DataMapMtx.unlock();
  return rc;
}

/// Init device, should not be called directly.
void DeviceTy::init() {
  int32_t rc = RTL->init_device(RTLDeviceID);
  if (rc == OFFLOAD_SUCCESS) {
    IsInit = true;
  }
}

/// Thread-safe method to initialize the device only once.
int32_t DeviceTy::initOnce() {
  std::call_once(InitFlag, &DeviceTy::init, this);

  // At this point, if IsInit is true, then either this thread or some other
  // thread in the past successfully initialized the device, so we can return
  // OFFLOAD_SUCCESS. If this thread executed init() via call_once() and it
  // failed, return OFFLOAD_FAIL. If call_once did not invoke init(), it means
  // that some other thread already attempted to execute init() and if IsInit
  // is still false, return OFFLOAD_FAIL.
  if (IsInit)
    return OFFLOAD_SUCCESS;
  else
    return OFFLOAD_FAIL;
}

// Load binary to device.
__tgt_target_table *DeviceTy::load_binary(void *Img) {
  RTL->Mtx.lock();
  __tgt_target_table *rc = RTL->load_binary(RTLDeviceID, Img);
  RTL->Mtx.unlock();
  return rc;
}

// Submit data to device.
int32_t DeviceTy::data_submit(void *TgtPtrBegin, void *HstPtrBegin,
    int64_t Size) {
  return RTL->data_submit(RTLDeviceID, TgtPtrBegin, HstPtrBegin, Size);
}

// Retrieve data from device.
int32_t DeviceTy::data_retrieve(void *HstPtrBegin, void *TgtPtrBegin,
    int64_t Size) {
  return RTL->data_retrieve(RTLDeviceID, HstPtrBegin, TgtPtrBegin, Size);
}

// Run region on device
int32_t DeviceTy::run_region(void *TgtEntryPtr, void **TgtVarsPtr,
    ptrdiff_t *TgtOffsets, int32_t TgtVarsSize) {
  return RTL->run_region(RTLDeviceID, TgtEntryPtr, TgtVarsPtr, TgtOffsets,
      TgtVarsSize);
}

// Run team region on device.

/// Check whether a device has an associated RTL and initialize it if it's not
bool device_is_ready(int device_num) {
  DP("Checking whether device %d is ready.\n", device_num);
  // Devices.size() can only change while registering a new
  // library, so try to acquire the lock of RTLs' mutex.
  RTLsMtx.lock();
  size_t Devices_size = Devices.size();
  RTLsMtx.unlock();
  if (Devices_size <= (size_t)device_num) {
    DP("Device ID  %d does not have a matching RTL\n", device_num);
    return false;
  }

  // Get device info
  DeviceTy &Device = Devices[device_num];

  DP("Is the device %d (local ID %d) initialized? %d\n", device_num,
       Device.RTLDeviceID, Device.IsInit);

  // Init the device if not done before
  if (!Device.IsInit && Device.initOnce() != OFFLOAD_SUCCESS) {
    DP("Failed to init device %d\n", device_num);
    return false;
  }

  DP("Device %d is ready to use.\n", device_num);

  return true;
}