<commit_before>//===- Symbols.cpp --------------------------------------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "Symbols.h"
#include "InputFiles.h"
#include "InputSection.h"
#include "OutputSections.h"
#include "SyntheticSections.h"
#include "Target.h"
#include "Writer.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Strings.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Path.h"
#include <cstring>

using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;

using namespace lld;
using namespace lld::elf;

Defined *ElfSym::Bss;
Defined *ElfSym::Etext1;
Defined *ElfSym::Etext2;
Defined *ElfSym::Edata1;
Defined *ElfSym::Edata2;
Defined *ElfSym::End1;
Defined *ElfSym::End2;
Defined *ElfSym::GlobalOffsetTable;
Defined *ElfSym::MipsGp;
Defined *ElfSym::MipsGpDisp;
Defined *ElfSym::MipsLocalGp;
Defined *ElfSym::RelaIpltEnd;

static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) {
  switch (Sym.kind()) {
  case Symbol::DefinedKind: {
    auto &D = cast<Defined>(Sym);
    SectionBase *IS = D.Section;

    // According to the ELF spec reference to a local symbol from outside
    // the group are not allowed. Unfortunately .eh_frame breaks that rule
    // and must be treated specially. For now we just replace the symbol with
    // 0.
    if (IS == &InputSection::Discarded)
      return 0;

    // This is an absolute symbol.
    if (!IS)
      return D.Value;

    IS = IS->Repl;

    uint64_t Offset = D.Value;

    // An object in an SHF_MERGE section might be referenced via a
    // section symbol (as a hack for reducing the number of local
    // symbols).
    // Depending on the addend, the reference via a section symbol
    // refers to a different object in the merge section.
    // Since the objects in the merge section are not necessarily
    // contiguous in the output, the addend can thus affect the final
    // VA in a non-linear way.
    // To make this work, we incorporate the addend into the section
    // offset (and zero out the addend for later processing) so that
    // we find the right object in the section.
    if (D.isSection()) {
      Offset += Addend;
      Addend = 0;
    }

    // In the typical case, this is actually very simple and boils
    // down to adding together 3 numbers:
    // 1. The address of the output section.
    // 2. The offset of the input section within the output section.
    // 3. The offset within the input section (this addition happens
    //    inside InputSection::getOffset).
    //
    // If you understand the data structures involved with this next
    // line (and how they get built), then you have a pretty good
    // understanding of the linker.
    uint64_t VA = IS->getVA(Offset);

    if (D.isTls() && !Config->Relocatable) {
      if (!Out::TlsPhdr)
        fatal(toString(D.File) +
              " has an STT_TLS symbol but doesn't have an SHF_TLS section");
      return VA - Out::TlsPhdr->p_vaddr;
    }
    return VA;
  }
  case Symbol::SharedKind: {
    auto &SS = cast<SharedSymbol>(Sym);
    if (SS.CopyRelSec)
      return SS.CopyRelSec->getVA(0);
    if (SS.NeedsPltAddr)
      return Sym.getPltVA();
    return 0;
  }
  case Symbol::UndefinedKind:
    return 0;
  case Symbol::LazyArchiveKind:
  case Symbol::LazyObjectKind:
    llvm_unreachable("lazy symbol reached writer");
  }
  llvm_unreachable("invalid symbol kind");
}

uint64_t Symbol::getVA(int64_t Addend) const {
  uint64_t OutVA = getSymVA(*this, Addend);
  return OutVA + Addend;
}

uint64_t Symbol::getGotVA() const { return InX::Got->getVA() + getGotOffset(); }

uint64_t Symbol::getGotOffset() const {
  return GotIndex * Target->GotEntrySize;
}

uint64_t Symbol::getGotPltVA() const {
  if (this->IsInIgot)
    return InX::IgotPlt->getVA() + getGotPltOffset();
  return InX::GotPlt->getVA() + getGotPltOffset();
}

uint64_t Symbol::getGotPltOffset() const {
  return GotPltIndex * Target->GotPltEntrySize;
}

uint64_t Symbol::getPltVA() const {
  if (this->IsInIplt)
    return InX::Iplt->getVA() + PltIndex * Target->PltEntrySize;
  return InX::Plt->getVA() + Target->getPltEntryOffset(PltIndex);
}

uint64_t Symbol::getSize() const {
  if (const auto *DR = dyn_cast<Defined>(this))
    return DR->Size;
  if (const auto *S = dyn_cast<SharedSymbol>(this))
    return S->Size;
  return 0;
}

OutputSection *Symbol::getOutputSection() const {
  if (auto *S = dyn_cast<Defined>(this)) {
    if (auto *Sec = S->Section)
      return Sec->Repl->getOutputSection();
    return nullptr;
  }

  if (auto *S = dyn_cast<SharedSymbol>(this)) {
    if (S->CopyRelSec)
      return S->CopyRelSec->getParent();
    return nullptr;
  }

  return nullptr;
}

// If a symbol name contains '@', the characters after that is
// a symbol version name. This function parses that.
void Symbol::parseSymbolVersion() {
  StringRef S = getName();
  size_t Pos = S.find('@');
  if (Pos == 0 || Pos == StringRef::npos)
    return;
  StringRef Verstr = S.substr(Pos + 1);
  if (Verstr.empty())
    return;

  // Truncate the symbol name so that it doesn't include the version string.
  NameSize = Pos;

  // If this is not in this DSO, it is not a definition.
  if (!isDefined())
    return;

  // '@@' in a symbol name means the default version.
  // It is usually the most recent one.
  bool IsDefault = (Verstr[0] == '@');
  if (IsDefault)
    Verstr = Verstr.substr(1);

  for (VersionDefinition &Ver : Config->VersionDefinitions) {
    if (Ver.Name != Verstr)
      continue;

    if (IsDefault)
      VersionId = Ver.Id;
    else
      VersionId = Ver.Id | VERSYM_HIDDEN;
    return;
  }

  // It is an error if the specified version is not defined.
  // Usually version script is not provided when linking executable,
  // but we may still want to override a versioned symbol from DSO,
  // so we do not report error in this case.
  if (Config->Shared)
    error(toString(File) + ": symbol " + S + " has undefined version " +
          Verstr);
}

InputFile *LazyArchive::fetch() { return cast<ArchiveFile>(File)->fetch(Sym); }

uint8_t Symbol::computeBinding() const {
  if (Config->Relocatable)
    return Binding;
  if (Visibility != STV_DEFAULT && Visibility != STV_PROTECTED)
    return STB_LOCAL;
  if (VersionId == VER_NDX_LOCAL && isDefined())
    return STB_LOCAL;
  if (!Config->GnuUnique && Binding == STB_GNU_UNIQUE)
    return STB_GLOBAL;
  return Binding;
}

bool Symbol::includeInDynsym() const {
  if (!Config->HasDynSymTab)
    return false;
  if (computeBinding() == STB_LOCAL)
    return false;
  if (!isDefined())
    return true;
  return ExportDynamic;
}

// Print out a log message for --trace-symbol.
void elf::printTraceSymbol(Symbol *Sym) {
  std::string S;
  if (Sym->isUndefined())
    S = ": reference to ";
  else if (Sym->isLazy())
    S = ": lazy definition of ";
  else if (Sym->isShared())
    S = ": shared definition of ";
  else if (dyn_cast_or_null<BssSection>(cast<Defined>(Sym)->Section))
    S = ": common definition of ";
  else
    S = ": definition of ";

  message(toString(Sym->File) + S + Sym->getName());
}

void elf::warnUnorderableSymbol(const Symbol *Sym) {
  if (!Config->WarnSymbolOrdering)
    return;
  const InputFile *File = Sym->File;
  auto *D = dyn_cast<Defined>(Sym);
  if (Sym->isUndefined())
    warn(toString(File) +
         ": unable to order undefined symbol: " + Sym->getName());
  else if (Sym->isShared())
    warn(toString(File) + ": unable to order shared symbol: " + Sym->getName());
  else if (D && !D->Section)
    warn(toString(File) +
         ": unable to order absolute symbol: " + Sym->getName());
  else if (D && isa<OutputSection>(D->Section))
    warn(toString(File) +
         ": unable to order synthetic symbol: " + Sym->getName());
  else if (D && !D->Section->Repl->Live)
    warn(toString(File) +
         ": unable to order discarded symbol: " + Sym->getName());
}

// Returns a symbol for an error message.
std::string lld::toString(const Symbol &B) {
  if (Config->Demangle)
    if (Optional<std::string> S = demangleItanium(B.getName()))
      return *S;
  return B.getName();
}
<commit_msg>Simplify. NFC.<commit_after>//===- Symbols.cpp --------------------------------------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "Symbols.h"
#include "InputFiles.h"
#include "InputSection.h"
#include "OutputSections.h"
#include "SyntheticSections.h"
#include "Target.h"
#include "Writer.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Strings.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Path.h"
#include <cstring>

using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;

using namespace lld;
using namespace lld::elf;

Defined *ElfSym::Bss;
Defined *ElfSym::Etext1;
Defined *ElfSym::Etext2;
Defined *ElfSym::Edata1;
Defined *ElfSym::Edata2;
Defined *ElfSym::End1;
Defined *ElfSym::End2;
Defined *ElfSym::GlobalOffsetTable;
Defined *ElfSym::MipsGp;
Defined *ElfSym::MipsGpDisp;
Defined *ElfSym::MipsLocalGp;
Defined *ElfSym::RelaIpltEnd;

static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) {
  switch (Sym.kind()) {
  case Symbol::DefinedKind: {
    auto &D = cast<Defined>(Sym);
    SectionBase *IS = D.Section;

    // According to the ELF spec reference to a local symbol from outside
    // the group are not allowed. Unfortunately .eh_frame breaks that rule
    // and must be treated specially. For now we just replace the symbol with
    // 0.
    if (IS == &InputSection::Discarded)
      return 0;

    // This is an absolute symbol.
    if (!IS)
      return D.Value;

    IS = IS->Repl;

    uint64_t Offset = D.Value;

    // An object in an SHF_MERGE section might be referenced via a
    // section symbol (as a hack for reducing the number of local
    // symbols).
    // Depending on the addend, the reference via a section symbol
    // refers to a different object in the merge section.
    // Since the objects in the merge section are not necessarily
    // contiguous in the output, the addend can thus affect the final
    // VA in a non-linear way.
    // To make this work, we incorporate the addend into the section
    // offset (and zero out the addend for later processing) so that
    // we find the right object in the section.
    if (D.isSection()) {
      Offset += Addend;
      Addend = 0;
    }

    // In the typical case, this is actually very simple and boils
    // down to adding together 3 numbers:
    // 1. The address of the output section.
    // 2. The offset of the input section within the output section.
    // 3. The offset within the input section (this addition happens
    //    inside InputSection::getOffset).
    //
    // If you understand the data structures involved with this next
    // line (and how they get built), then you have a pretty good
    // understanding of the linker.
    uint64_t VA = IS->getVA(Offset);

    if (D.isTls() && !Config->Relocatable) {
      if (!Out::TlsPhdr)
        fatal(toString(D.File) +
              " has an STT_TLS symbol but doesn't have an SHF_TLS section");
      return VA - Out::TlsPhdr->p_vaddr;
    }
    return VA;
  }
  case Symbol::SharedKind: {
    auto &SS = cast<SharedSymbol>(Sym);
    if (SS.CopyRelSec)
      return SS.CopyRelSec->getVA(0);
    if (SS.NeedsPltAddr)
      return Sym.getPltVA();
    return 0;
  }
  case Symbol::UndefinedKind:
    return 0;
  case Symbol::LazyArchiveKind:
  case Symbol::LazyObjectKind:
    llvm_unreachable("lazy symbol reached writer");
  }
  llvm_unreachable("invalid symbol kind");
}

uint64_t Symbol::getVA(int64_t Addend) const {
  uint64_t OutVA = getSymVA(*this, Addend);
  return OutVA + Addend;
}

uint64_t Symbol::getGotVA() const { return InX::Got->getVA() + getGotOffset(); }

uint64_t Symbol::getGotOffset() const {
  return GotIndex * Target->GotEntrySize;
}

uint64_t Symbol::getGotPltVA() const {
  if (this->IsInIgot)
    return InX::IgotPlt->getVA() + getGotPltOffset();
  return InX::GotPlt->getVA() + getGotPltOffset();
}

uint64_t Symbol::getGotPltOffset() const {
  return GotPltIndex * Target->GotPltEntrySize;
}

uint64_t Symbol::getPltVA() const {
  if (this->IsInIplt)
    return InX::Iplt->getVA() + PltIndex * Target->PltEntrySize;
  return InX::Plt->getVA() + Target->getPltEntryOffset(PltIndex);
}

uint64_t Symbol::getSize() const {
  if (const auto *DR = dyn_cast<Defined>(this))
    return DR->Size;
  if (const auto *S = dyn_cast<SharedSymbol>(this))
    return S->Size;
  return 0;
}

OutputSection *Symbol::getOutputSection() const {
  if (auto *S = dyn_cast<Defined>(this)) {
    if (auto *Sec = S->Section)
      return Sec->Repl->getOutputSection();
    return nullptr;
  }

  if (auto *S = dyn_cast<SharedSymbol>(this)) {
    if (S->CopyRelSec)
      return S->CopyRelSec->getParent();
    return nullptr;
  }

  return nullptr;
}

// If a symbol name contains '@', the characters after that is
// a symbol version name. This function parses that.
void Symbol::parseSymbolVersion() {
  StringRef S = getName();
  size_t Pos = S.find('@');
  if (Pos == 0 || Pos == StringRef::npos)
    return;
  StringRef Verstr = S.substr(Pos + 1);
  if (Verstr.empty())
    return;

  // Truncate the symbol name so that it doesn't include the version string.
  NameSize = Pos;

  // If this is not in this DSO, it is not a definition.
  if (!isDefined())
    return;

  // '@@' in a symbol name means the default version.
  // It is usually the most recent one.
  bool IsDefault = (Verstr[0] == '@');
  if (IsDefault)
    Verstr = Verstr.substr(1);

  for (VersionDefinition &Ver : Config->VersionDefinitions) {
    if (Ver.Name != Verstr)
      continue;

    if (IsDefault)
      VersionId = Ver.Id;
    else
      VersionId = Ver.Id | VERSYM_HIDDEN;
    return;
  }

  // It is an error if the specified version is not defined.
  // Usually version script is not provided when linking executable,
  // but we may still want to override a versioned symbol from DSO,
  // so we do not report error in this case.
  if (Config->Shared)
    error(toString(File) + ": symbol " + S + " has undefined version " +
          Verstr);
}

InputFile *LazyArchive::fetch() { return cast<ArchiveFile>(File)->fetch(Sym); }

uint8_t Symbol::computeBinding() const {
  if (Config->Relocatable)
    return Binding;
  if (Visibility != STV_DEFAULT && Visibility != STV_PROTECTED)
    return STB_LOCAL;
  if (VersionId == VER_NDX_LOCAL && isDefined())
    return STB_LOCAL;
  if (!Config->GnuUnique && Binding == STB_GNU_UNIQUE)
    return STB_GLOBAL;
  return Binding;
}

bool Symbol::includeInDynsym() const {
  if (!Config->HasDynSymTab)
    return false;
  if (computeBinding() == STB_LOCAL)
    return false;
  if (!isDefined())
    return true;
  return ExportDynamic;
}

// Print out a log message for --trace-symbol.
void elf::printTraceSymbol(Symbol *Sym) {
  std::string S;
  if (Sym->isUndefined())
    S = ": reference to ";
  else if (Sym->isLazy())
    S = ": lazy definition of ";
  else if (Sym->isShared())
    S = ": shared definition of ";
  else if (dyn_cast_or_null<BssSection>(cast<Defined>(Sym)->Section))
    S = ": common definition of ";
  else
    S = ": definition of ";

  message(toString(Sym->File) + S + Sym->getName());
}

void elf::warnUnorderableSymbol(const Symbol *Sym) {
  if (!Config->WarnSymbolOrdering)
    return;

  const InputFile *File = Sym->File;
  auto *D = dyn_cast<Defined>(Sym);

  auto Warn = [&](StringRef S) { warn(toString(File) + S + Sym->getName()); };

  if (Sym->isUndefined())
    Warn(": unable to order undefined symbol: ");
  else if (Sym->isShared())
    Warn(": unable to order shared symbol: ");
  else if (D && !D->Section)
    Warn(": unable to order absolute symbol: ");
  else if (D && isa<OutputSection>(D->Section))
    Warn(": unable to order synthetic symbol: ");
  else if (D && !D->Section->Repl->Live)
    Warn(": unable to order discarded symbol: ");
}

// Returns a symbol for an error message.
std::string lld::toString(const Symbol &B) {
  if (Config->Demangle)
    if (Optional<std::string> S = demangleItanium(B.getName()))
      return *S;
  return B.getName();
}
<|endoftext|>


<commit_before>
#ifndef STEAL_QUEUE_HPP
#define STEAL_QUEUE_HPP

#include <iostream>
#include <glog/logging.h>
#include <stdlib.h>

// profiling/tracing
#include "../PerformanceTools.hpp"

#ifdef VTRACE
#include <vt_user.h>
#endif

#define MIN_INT(a, b) ( (a) < (b) ) ? (a) : (b);

GRAPPA_DECLARE_EVENT_GROUP(scheduler);

#define SS_NSTATES 1

struct workStealRequest_args;
struct workStealReply_args;

/// Type for Node ID.
#include <boost/cstdint.hpp>
typedef int16_t Node;

/// Forward declare for steal_locally
class Thread;

class StealStatistics {
  private:
    uint64_t stealq_reply_messages;
    uint64_t stealq_reply_total_bytes;
    uint64_t stealq_request_messages;
    uint64_t stealq_request_total_bytes;

  public:
    StealStatistics();
    void reset();
    void record_steal_reply( size_t msg_bytes );
    void record_steal_request( size_t msg_bytes );
    void dump();
    void merge( const StealStatistics * other );
    static StealStatistics reduce( const StealStatistics& a, const StealStatistics& b );
};

template <typename T>
class StealQueue {
    private:
        uint64_t stackSize;     /* total space avail (in number of elements) */
        uint64_t workAvail;     /* elements available for stealing */
        uint64_t bottom;   /* index of start of shared portion of stack */
        uint64_t top;           /* index of stack top */
        uint64_t maxStackDepth;                      /* stack stats */
        uint64_t nNodes, maxTreeDepth, nVisited, nLeaves;        /* tree stats: (num pushed, max depth, num popped, leaves)  */
        uint64_t nAcquire, nRelease, nStealPackets, nFail;  /* steal stats */
        uint64_t wakeups, falseWakeups, nNodes_last;

#ifdef VTRACE
  unsigned steal_queue_grp_vt;
  unsigned steal_success_ev_vt;
  unsigned steal_victim_ev_vt;
#endif

        double time[SS_NSTATES], timeLast;
        /* perf measurements */
        int entries[SS_NSTATES], curState;
        /* lock for manipulation of shared portion */
        T* stack;       /* addr of actual
                                  stack of nodes
                                  in local addr
                                  space */
        T* stack_g; /* addr of same
                              stack in global
                              addr space */

        static StealQueue<T>* staticQueueAddress;

        void steal_reply( uint64_t amt, uint64_t total, T * stolen_work, size_t stolen_size_bytes );
        void steal_request( int k, Node from );
        static void workStealReply_am( workStealReply_args * args,  size_t size, void * payload, size_t payload_size );
        static void workStealRequest_am( workStealRequest_args * args, size_t size, void * payload, size_t payload_size );

        std::ostream& dump ( std::ostream& o ) const {
            return o << "StealQueue[depth=" << depth()
                     << "; indices(top= " << top
                              << " bottom=" << bottom << ")"
                     << "; stackSize=" << stackSize << "]";
        }

    public:
        StealQueue( uint64_t numEle )
            : stackSize( numEle )
            , maxStackDepth( 0 )
            , nNodes( 0 ), maxTreeDepth( 0 ), nVisited( 0 ), nLeaves( 0 )
            , nAcquire( 0 ), nRelease( 0 ), nStealPackets( 0 ), nFail( 0 )
            , wakeups( 0 ), falseWakeups( 0 ), nNodes_last( 0 )
#ifdef VTRACE
	    , steal_queue_grp_vt( VT_COUNT_GROUP_DEF( "Steal queue" ) )
	    , steal_success_ev_vt( VT_COUNT_DEF( "Steal success", "tasks", VT_COUNT_TYPE_UNSIGNED, steal_queue_grp_vt ) )
	    , steal_victim_ev_vt( VT_COUNT_DEF( "Steal victim", "tasks", VT_COUNT_TYPE_UNSIGNED, steal_queue_grp_vt ) )
#endif
            , stats()
{

                uint64_t nbytes = numEle * sizeof(T);

                // allocate stack in shared addr space with affinity to calling thread
                // and record local addr for efficient access in sequel
                stack_g = static_cast<T*>( malloc( nbytes ) );
                stack = stack_g;

                CHECK( stack!= NULL ) << "Request for " << nbytes << " bytes for stealStack failed";

                mkEmpty();
            }

        void mkEmpty();
        void push( T c);
        T peek( );
        void pop( );
        uint64_t topPosn( ) const;
        uint64_t depth( ) const;
        void release( int k );
        int acquire( int k );
        int steal_locally( Node victim, int chunkSize );
        void setState( int state );

        uint64_t get_nNodes( ) {
            return nNodes;
        }

        /// register local address of remote steal queues
        static void registerAddress( StealQueue<T> * addr );

        template< typename U >
        friend std::ostream& operator<<( std::ostream& o, const StealQueue<U>& sq );

        StealStatistics stats;
        void reset_stats();
        void dump_stats();
};

template < typename T >
void StealQueue<T>::reset_stats() {
  stats.reset();
}

template < typename T >
void StealQueue<T>::dump_stats() {
  stats.dump();
}

static int maxint(int x, int y) { return (x>y)?x:y; }

/// Push onto top of local stack
template <typename T>
inline void StealQueue<T>::push( T c ) {
  CHECK( top < stackSize ) << "push: overflow (top:" << top << " stackSize:" << stackSize << ")";

  VLOG(5) << "stack[" << top << "] <-- push";
  stack[top] = c;
  top++;
  nNodes++;
  maxStackDepth = maxint(top, maxStackDepth);
  //s->maxTreeDepth = maxint(s->maxTreeDepth, c->height); //XXX dont want to deref c here (expensive for just a bookkeeping operation
}

/// get top element
template <typename T>
inline T StealQueue<T>::peek( ) {
    CHECK(top > bottom) << "peek: empty local stack";
    return stack[top-1];
}

/// local pop
template <typename T>
inline void StealQueue<T>::pop( ) {
  CHECK(top > bottom) << "pop: empty local stack";

#if DEBUG
  // 0 out the popped element (to detect errors)
  memset( &stack[top-1], 0, sizeof(T) );
#endif

  top--;
  nVisited++;
}

/// depth
template <typename T>
inline uint64_t StealQueue<T>::depth() const {
  return (top - bottom);
}

template <typename T>
void StealQueue<T>::setState( int state ) { return; }

/// Initialize the dedicated queue for T
template <typename T>
StealQueue<T>* StealQueue<T>::staticQueueAddress = NULL;

template <typename T>
void StealQueue<T>::registerAddress( StealQueue<T> * addr ) {
    staticQueueAddress = addr;
}

/// set queue to empty
template <typename T>
inline void StealQueue<T>::mkEmpty( ) {
    bottom = 0;
    top    = 0;
}

/// local top position:  stack index of top element
template <typename T>
uint64_t StealQueue<T>::topPosn() const
{
  CHECK ( top > bottom ) << "ss_topPosn: empty local stack";
  return top - 1;
}

//////////////////////////////////////////////////
// Work stealing
/////////////////////////////////////////////////

//#include "../SoftXMT.hpp"
#include <Communicator.hpp>
#include <tasks/TaskingScheduler.hpp>

extern TaskingScheduler global_scheduler;
// void SoftXMT_suspend();
// void SoftXMT_wake( Thread * );
// Node SoftXMT_mynode();

struct workStealRequest_args {
    int k;
    Node from;
};

struct workStealReply_args {
    int stealAmt;
    int total;
};

static int64_t local_steal_amount;
static uint64_t received_tasks;
static Thread * steal_waiter = NULL;

template <typename T>
void StealQueue<T>::steal_reply( uint64_t amt, uint64_t total, T * stolen_work, size_t stolen_size_bytes ) {
    if (amt > 0) {
      GRAPPA_EVENT(steal_packet_ev, "Steal packet", 1, scheduler, amt);

#ifdef VTRACE
      //VT_COUNT_UNSIGNED_VAL( thiefStack->steal_success_ev_vt, k );
#endif

      // reclaim array space if empty
      if ( depth() == 0 ) {
        mkEmpty();
      }

      CHECK( top + amt < stackSize ) << "steal reply: overflow (top:" << top << " stackSize:" << stackSize << " amt:" << amt << ")";
      memcpy(&stack[top], stolen_work, stolen_size_bytes);

      received_tasks += amt;
      VLOG(5) << "Steal packet returns with amt=" << amt << ", received=" << received_tasks << " / total=" << total;
      top += amt;
      nStealPackets++;

      /// The steal requestor stays asleep until all received, but other threads can take advantage
      /// of the tasks that have been copied in
      if ( received_tasks == total ) {
        GRAPPA_EVENT(steal_success_ev, "Steal success", 1, scheduler, total);
        VLOG(5) << "Last packet; will wake steal_waiter=" << steal_waiter;
        local_steal_amount = total;
        if ( steal_waiter != NULL ) {
          //SoftXMT_wake( steal_waiter );
          global_scheduler.thread_wake( steal_waiter );
          steal_waiter = NULL;
        }
      }

    } else {
      local_steal_amount = 0;
      nFail++;

      if ( steal_waiter != NULL ) {
        //SoftXMT_wake( steal_waiter );
        global_scheduler.thread_wake( steal_waiter );
        steal_waiter = NULL;
      }
    }
}

template <typename T>
void StealQueue<T>::workStealReply_am( workStealReply_args * args,  size_t size, void * payload, size_t payload_size ) {
    CHECK ( local_steal_amount == -1 ) << "local_steal_amount=" << local_steal_amount << " when steal reply arrives";
    CHECK( args->stealAmt * sizeof(T) == payload_size ) << "steal amount in bytes != payload size";

    T * stolen_work = static_cast<T*>( payload );

    StealQueue<T>* thiefStack = StealQueue<T>::staticQueueAddress;
    thiefStack->steal_reply( args->stealAmt, args->total, stolen_work, payload_size );
}

template <typename T>
void StealQueue<T>::steal_request( int k, Node from ) {
    const int victimHalfWorkAvail = (top - bottom) / 2;
    const int stealAmt = MIN_INT( victimHalfWorkAvail, k );
    bool ok = stealAmt > 0;

    VLOG(4) << "Victim of thief=" << from << " victimHalfWorkAvail=" << victimHalfWorkAvail;
    if (ok) {

      /* reserve a chunk */
      bottom = bottom + stealAmt;

      GRAPPA_EVENT(steal_victim_ev, "Steal victim", 1, scheduler, stealAmt);
#ifdef VTRACE
      //VT_COUNT_UNSIGNED_VAL( steal_victim_ev_vt, k );
#endif

      T* victimStackBase = stack;
      T* victimStealStart = victimStackBase + bottom;

      const int bufsize = 110; // TODO: I now 32B*110 < aggreg bufsize-header size
                               // but do this precisely
      int offset = 0;
      for ( int remain = stealAmt; remain > 0; ) {
        int transfer_amt = (remain < bufsize) ? remain : bufsize;
        VLOG(5) << "sending steal packet of transfer_amt=" << transfer_amt << " remain=" << remain << " / stealAmt=" << stealAmt;
        remain -= transfer_amt;
        workStealReply_args reply_args = { transfer_amt, stealAmt };
        size_t msg_size = SoftXMT_call_on( from, &StealQueue<T>::workStealReply_am,
            &reply_args, sizeof(workStealReply_args),
            victimStealStart + offset, transfer_amt*sizeof( T ));
        stats.record_steal_reply( msg_size );

        offset += transfer_amt;
      }

#if DEBUG
      // 0 out the stolen stuff (to detect errors)
      memset( victimStealStart, 0, stealAmt*sizeof( T ) );
#endif

    } else {
      workStealReply_args reply_args = { 0, 0 };
      SoftXMT_call_on( from, &StealQueue<T>::workStealReply_am, &reply_args );
    }

}

template <typename T>
void StealQueue<T>::workStealRequest_am(workStealRequest_args * args, size_t size, void * payload, size_t payload_size) {
    int k = args->k;
    Node from = args->from;

    StealQueue<T>* victimStack = StealQueue<T>::staticQueueAddress;
    victimStack->steal_request( k, from );
}

#include "Thread.hpp"
template <typename T>
int StealQueue<T>::steal_locally( Node victim, int op ) {

    // initialize stealing state
    local_steal_amount = -1;
    received_tasks = 0;

    workStealRequest_args req_args = { op, global_communicator.mynode() };
    size_t msg_size = SoftXMT_call_on( victim, &StealQueue<T>::workStealRequest_am, &req_args );
    stats.record_steal_request( msg_size );

    GRAPPA_PROFILE_CREATE( stealprof, "steal_locally", "(suspended)", GRAPPA_SUSPEND_GROUP );

    // reclaim array space if empty
    if ( depth() == 0 ) {
      mkEmpty();
    }

    // steal is blocking
    // TODO: use suspend-wake mechanism
    while ( local_steal_amount == -1 ) {
        steal_waiter = global_scheduler.get_current_thread();

        GRAPPA_PROFILE_THREAD_START( stealprof, global_scheduler.get_current_thread() );

        global_scheduler.thread_suspend();
        //SoftXMT_suspend();

        GRAPPA_PROFILE_THREAD_STOP( stealprof, global_scheduler.get_current_thread() );
    }

    return local_steal_amount;
}
/////////////////////////////////////////////////////////

template <typename T>
std::ostream& operator<<( std::ostream& o, const StealQueue<T>& sq ) {
    return sq.dump( o );
}

#endif
<commit_msg>fix bug: need to use old stack bottom to calculate steal location for memcpy. Was mixed up when refactored into steal_request()<commit_after>
#ifndef STEAL_QUEUE_HPP
#define STEAL_QUEUE_HPP

#include <iostream>
#include <glog/logging.h>
#include <stdlib.h>

// profiling/tracing
#include "../PerformanceTools.hpp"

#ifdef VTRACE
#include <vt_user.h>
#endif

#define MIN_INT(a, b) ( (a) < (b) ) ? (a) : (b);

GRAPPA_DECLARE_EVENT_GROUP(scheduler);

#define SS_NSTATES 1

struct workStealRequest_args;
struct workStealReply_args;

/// Type for Node ID.
#include <boost/cstdint.hpp>
typedef int16_t Node;

/// Forward declare for steal_locally
class Thread;

class StealStatistics {
  private:
    uint64_t stealq_reply_messages;
    uint64_t stealq_reply_total_bytes;
    uint64_t stealq_request_messages;
    uint64_t stealq_request_total_bytes;

  public:
    StealStatistics();
    void reset();
    void record_steal_reply( size_t msg_bytes );
    void record_steal_request( size_t msg_bytes );
    void dump();
    void merge( const StealStatistics * other );
    static StealStatistics reduce( const StealStatistics& a, const StealStatistics& b );
};

template <typename T>
class StealQueue {
    private:
        uint64_t stackSize;     /* total space avail (in number of elements) */
        uint64_t workAvail;     /* elements available for stealing */
        uint64_t bottom;   /* index of start of shared portion of stack */
        uint64_t top;           /* index of stack top */
        uint64_t maxStackDepth;                      /* stack stats */
        uint64_t nNodes, maxTreeDepth, nVisited, nLeaves;        /* tree stats: (num pushed, max depth, num popped, leaves)  */
        uint64_t nAcquire, nRelease, nStealPackets, nFail;  /* steal stats */
        uint64_t wakeups, falseWakeups, nNodes_last;

#ifdef VTRACE
  unsigned steal_queue_grp_vt;
  unsigned steal_success_ev_vt;
  unsigned steal_victim_ev_vt;
#endif

        double time[SS_NSTATES], timeLast;
        /* perf measurements */
        int entries[SS_NSTATES], curState;
        /* lock for manipulation of shared portion */
        T* stack;       /* addr of actual
                                  stack of nodes
                                  in local addr
                                  space */
        T* stack_g; /* addr of same
                              stack in global
                              addr space */

        static StealQueue<T>* staticQueueAddress;

        void steal_reply( uint64_t amt, uint64_t total, T * stolen_work, size_t stolen_size_bytes );
        void steal_request( int k, Node from );
        static void workStealReply_am( workStealReply_args * args,  size_t size, void * payload, size_t payload_size );
        static void workStealRequest_am( workStealRequest_args * args, size_t size, void * payload, size_t payload_size );

        std::ostream& dump ( std::ostream& o ) const {
            return o << "StealQueue[depth=" << depth()
                     << "; indices(top= " << top
                              << " bottom=" << bottom << ")"
                     << "; stackSize=" << stackSize << "]";
        }

    public:
        StealQueue( uint64_t numEle )
            : stackSize( numEle )
            , maxStackDepth( 0 )
            , nNodes( 0 ), maxTreeDepth( 0 ), nVisited( 0 ), nLeaves( 0 )
            , nAcquire( 0 ), nRelease( 0 ), nStealPackets( 0 ), nFail( 0 )
            , wakeups( 0 ), falseWakeups( 0 ), nNodes_last( 0 )
#ifdef VTRACE
	    , steal_queue_grp_vt( VT_COUNT_GROUP_DEF( "Steal queue" ) )
	    , steal_success_ev_vt( VT_COUNT_DEF( "Steal success", "tasks", VT_COUNT_TYPE_UNSIGNED, steal_queue_grp_vt ) )
	    , steal_victim_ev_vt( VT_COUNT_DEF( "Steal victim", "tasks", VT_COUNT_TYPE_UNSIGNED, steal_queue_grp_vt ) )
#endif
            , stats()
{

                uint64_t nbytes = numEle * sizeof(T);

                // allocate stack in shared addr space with affinity to calling thread
                // and record local addr for efficient access in sequel
                stack_g = static_cast<T*>( malloc( nbytes ) );
                stack = stack_g;

                CHECK( stack!= NULL ) << "Request for " << nbytes << " bytes for stealStack failed";

                mkEmpty();
            }

        void mkEmpty();
        void push( T c);
        T peek( );
        void pop( );
        uint64_t topPosn( ) const;
        uint64_t depth( ) const;
        void release( int k );
        int acquire( int k );
        int steal_locally( Node victim, int chunkSize );
        void setState( int state );

        uint64_t get_nNodes( ) {
            return nNodes;
        }

        /// register local address of remote steal queues
        static void registerAddress( StealQueue<T> * addr );

        template< typename U >
        friend std::ostream& operator<<( std::ostream& o, const StealQueue<U>& sq );

        StealStatistics stats;
        void reset_stats();
        void dump_stats();
};

template < typename T >
void StealQueue<T>::reset_stats() {
  stats.reset();
}

template < typename T >
void StealQueue<T>::dump_stats() {
  stats.dump();
}

static int maxint(int x, int y) { return (x>y)?x:y; }

/// Push onto top of local stack
template <typename T>
inline void StealQueue<T>::push( T c ) {
  CHECK( top < stackSize ) << "push: overflow (top:" << top << " stackSize:" << stackSize << ")";

  VLOG(5) << "stack[" << top << "] <-- push";
  stack[top] = c;
  top++;
  nNodes++;
  maxStackDepth = maxint(top, maxStackDepth);
  //s->maxTreeDepth = maxint(s->maxTreeDepth, c->height); //XXX dont want to deref c here (expensive for just a bookkeeping operation
}

/// get top element
template <typename T>
inline T StealQueue<T>::peek( ) {
    CHECK(top > bottom) << "peek: empty local stack";
    return stack[top-1];
}

/// local pop
template <typename T>
inline void StealQueue<T>::pop( ) {
  CHECK(top > bottom) << "pop: empty local stack";

#if DEBUG
  // 0 out the popped element (to detect errors)
  memset( &stack[top-1], 0, sizeof(T) );
#endif

  top--;
  nVisited++;
}

/// depth
template <typename T>
inline uint64_t StealQueue<T>::depth() const {
  return (top - bottom);
}

template <typename T>
void StealQueue<T>::setState( int state ) { return; }

/// Initialize the dedicated queue for T
template <typename T>
StealQueue<T>* StealQueue<T>::staticQueueAddress = NULL;

template <typename T>
void StealQueue<T>::registerAddress( StealQueue<T> * addr ) {
    staticQueueAddress = addr;
}

/// set queue to empty
template <typename T>
inline void StealQueue<T>::mkEmpty( ) {
    bottom = 0;
    top    = 0;
}

/// local top position:  stack index of top element
template <typename T>
uint64_t StealQueue<T>::topPosn() const
{
  CHECK ( top > bottom ) << "ss_topPosn: empty local stack";
  return top - 1;
}

//////////////////////////////////////////////////
// Work stealing
/////////////////////////////////////////////////

//#include "../SoftXMT.hpp"
#include <Communicator.hpp>
#include <tasks/TaskingScheduler.hpp>

extern TaskingScheduler global_scheduler;
// void SoftXMT_suspend();
// void SoftXMT_wake( Thread * );
// Node SoftXMT_mynode();

struct workStealRequest_args {
    int k;
    Node from;
};

struct workStealReply_args {
    int stealAmt;
    int total;
};

static int64_t local_steal_amount;
static uint64_t received_tasks;
static Thread * steal_waiter = NULL;

template <typename T>
void StealQueue<T>::steal_reply( uint64_t amt, uint64_t total, T * stolen_work, size_t stolen_size_bytes ) {
    if (amt > 0) {
      GRAPPA_EVENT(steal_packet_ev, "Steal packet", 1, scheduler, amt);

#ifdef VTRACE
      //VT_COUNT_UNSIGNED_VAL( thiefStack->steal_success_ev_vt, k );
#endif

      // reclaim array space if empty
      if ( depth() == 0 ) {
        mkEmpty();
      }

      CHECK( top + amt < stackSize ) << "steal reply: overflow (top:" << top << " stackSize:" << stackSize << " amt:" << amt << ")";
      memcpy(&stack[top], stolen_work, stolen_size_bytes);

      received_tasks += amt;
      VLOG(5) << "Steal packet returns with amt=" << amt << ", received=" << received_tasks << " / total=" << total;
      top += amt;
      nStealPackets++;

      /// The steal requestor stays asleep until all received, but other threads can take advantage
      /// of the tasks that have been copied in
      if ( received_tasks == total ) {
        GRAPPA_EVENT(steal_success_ev, "Steal success", 1, scheduler, total);
        VLOG(5) << "Last packet; will wake steal_waiter=" << steal_waiter;
        local_steal_amount = total;
        if ( steal_waiter != NULL ) {
          //SoftXMT_wake( steal_waiter );
          global_scheduler.thread_wake( steal_waiter );
          steal_waiter = NULL;
        }
      }

    } else {
      local_steal_amount = 0;
      nFail++;

      if ( steal_waiter != NULL ) {
        //SoftXMT_wake( steal_waiter );
        global_scheduler.thread_wake( steal_waiter );
        steal_waiter = NULL;
      }
    }
}

template <typename T>
void StealQueue<T>::workStealReply_am( workStealReply_args * args,  size_t size, void * payload, size_t payload_size ) {
    CHECK ( local_steal_amount == -1 ) << "local_steal_amount=" << local_steal_amount << " when steal reply arrives";
    CHECK( args->stealAmt * sizeof(T) == payload_size ) << "steal amount in bytes != payload size";

    T * stolen_work = static_cast<T*>( payload );

    StealQueue<T>* thiefStack = StealQueue<T>::staticQueueAddress;
    thiefStack->steal_reply( args->stealAmt, args->total, stolen_work, payload_size );
}

template <typename T>
void StealQueue<T>::steal_request( int k, Node from ) {
    int victimBottom = this->bottom;
    int victimTop = this->top;

    const int victimHalfWorkAvail = (victimTop - victimBottom) / 2;
    const int stealAmt = MIN_INT( victimHalfWorkAvail, k );
    bool ok = stealAmt > 0;

    VLOG(4) << "Victim of thief=" << from << " victimHalfWorkAvail=" << victimHalfWorkAvail;
    if (ok) {

      /* reserve a chunk */
      this->bottom = victimBottom + stealAmt;

      GRAPPA_EVENT(steal_victim_ev, "Steal victim", 1, scheduler, stealAmt);
#ifdef VTRACE
      //VT_COUNT_UNSIGNED_VAL( steal_victim_ev_vt, k );
#endif

      T* victimStackBase = this->stack;
      T* victimStealStart = victimStackBase + victimBottom;

      const int bufsize = 110; // TODO: I now 32B*110 < aggreg bufsize-header size
                               // but do this precisely
      int offset = 0;
      for ( int remain = stealAmt; remain > 0; ) {
        int transfer_amt = (remain < bufsize) ? remain : bufsize;
        VLOG(5) << "sending steal packet of transfer_amt=" << transfer_amt << " remain=" << remain << " / stealAmt=" << stealAmt;
        remain -= transfer_amt;
        workStealReply_args reply_args = { transfer_amt, stealAmt };
        size_t msg_size = SoftXMT_call_on( from, &StealQueue<T>::workStealReply_am,
            &reply_args, sizeof(workStealReply_args),
            victimStealStart + offset, transfer_amt*sizeof( T ));
        stats.record_steal_reply( msg_size );

        offset += transfer_amt;
      }

#if DEBUG
      // 0 out the stolen stuff (to detect errors)
      memset( victimStealStart, 0, stealAmt*sizeof( T ) );
#endif

    } else {
      workStealReply_args reply_args = { 0, 0 };
      SoftXMT_call_on( from, &StealQueue<T>::workStealReply_am, &reply_args );
    }

}

template <typename T>
void StealQueue<T>::workStealRequest_am(workStealRequest_args * args, size_t size, void * payload, size_t payload_size) {
    int k = args->k;
    Node from = args->from;

    StealQueue<T>* victimStack = StealQueue<T>::staticQueueAddress;
    victimStack->steal_request( k, from );
}

#include "Thread.hpp"
template <typename T>
int StealQueue<T>::steal_locally( Node victim, int op ) {

    // initialize stealing state
    local_steal_amount = -1;
    received_tasks = 0;

    workStealRequest_args req_args = { op, global_communicator.mynode() };
    size_t msg_size = SoftXMT_call_on( victim, &StealQueue<T>::workStealRequest_am, &req_args );
    stats.record_steal_request( msg_size );

    GRAPPA_PROFILE_CREATE( stealprof, "steal_locally", "(suspended)", GRAPPA_SUSPEND_GROUP );

    // reclaim array space if empty
    if ( depth() == 0 ) {
      mkEmpty();
    }

    // steal is blocking
    // TODO: use suspend-wake mechanism
    while ( local_steal_amount == -1 ) {
        steal_waiter = global_scheduler.get_current_thread();

        GRAPPA_PROFILE_THREAD_START( stealprof, global_scheduler.get_current_thread() );

        global_scheduler.thread_suspend();
        //SoftXMT_suspend();

        GRAPPA_PROFILE_THREAD_STOP( stealprof, global_scheduler.get_current_thread() );
    }

    return local_steal_amount;
}
/////////////////////////////////////////////////////////

template <typename T>
std::ostream& operator<<( std::ostream& o, const StealQueue<T>& sq ) {
    return sq.dump( o );
}

#endif
<|endoftext|>


<commit_before>#include "clay.hpp"
#include "libclaynames.hpp"

ExprPtr desugarCharLiteral(char c) {
    ExprPtr nameRef = prelude_expr_Char();
    CallPtr call = new Call(nameRef, new ExprList());
    ostringstream out;
    out << (int)c;
    call->args->add(new IntLiteral(out.str(), "i8"));
    return call.ptr();
}

ExprPtr desugarFieldRef(FieldRefPtr x) {
    ExprListPtr args = new ExprList(x->expr);
    args->add(new ObjectExpr(x->name.ptr()));
    return new Call(prelude_expr_fieldRef(), args);
}

ExprPtr desugarStaticIndexing(StaticIndexingPtr x) {
    ExprListPtr args = new ExprList(x->expr);
    ValueHolderPtr vh = sizeTToValueHolder(x->index);
    args->add(new StaticExpr(new ObjectExpr(vh.ptr())));
    return new Call(prelude_expr_staticIndex(), args);
}

ExprPtr desugarUnaryOp(UnaryOpPtr x) {
    ExprPtr callable;
    switch (x->op) {
    case DEREFERENCE :
        callable = prelude_expr_dereference();
        break;
    case ADDRESS_OF :
        callable = primitive_expr_addressOf();
        break;
    case PLUS :
        callable = prelude_expr_plus();
        break;
    case MINUS :
        callable = prelude_expr_minus();
        break;
    case NOT :
        callable = primitive_expr_boolNot();
        break;
    default :
        assert(false);
    }
    CallPtr call = new Call(callable, new ExprList());
    call->args->add(x->expr);
    return call.ptr();
}

ExprPtr desugarBinaryOp(BinaryOpPtr x) {
    ExprPtr callable;
    switch (x->op) {
    case ADD :
        callable = prelude_expr_add();
        break;
    case SUBTRACT :
        callable = prelude_expr_subtract();
        break;
    case MULTIPLY :
        callable = prelude_expr_multiply();
        break;
    case DIVIDE :
        callable = prelude_expr_divide();
        break;
    case REMAINDER :
        callable = prelude_expr_remainder();
        break;
    case EQUALS :
        callable = prelude_expr_equalsP();
        break;
    case NOT_EQUALS :
        callable = prelude_expr_notEqualsP();
        break;
    case LESSER :
        callable = prelude_expr_lesserP();
        break;
    case LESSER_EQUALS :
        callable = prelude_expr_lesserEqualsP();
        break;
    case GREATER :
        callable = prelude_expr_greaterP();
        break;
    case GREATER_EQUALS :
        callable = prelude_expr_greaterEqualsP();
        break;
    default :
        assert(false);
    }
    CallPtr call = new Call(callable, new ExprList());
    call->args->add(x->expr1);
    call->args->add(x->expr2);
    return call.ptr();
}

ExprPtr desugarIfExpr(IfExprPtr x) {
    ExprPtr callable = prelude_expr_ifExpression();
    CallPtr call = new Call(callable, new ExprList());
    call->args->add(x->condition);
    call->args->add(x->thenPart);
    call->args->add(x->elsePart);
    return call.ptr();
}

ExprPtr desugarNew(NewPtr x) {
    ExprPtr callable = prelude_expr_allocateShared();
    CallPtr call = new Call(callable, new ExprList());
    call->args->add(x->expr);
    return call.ptr();
}

ExprPtr desugarStaticExpr(StaticExprPtr x) {
    ExprPtr callable = prelude_expr_wrapStatic();
    CallPtr call = new Call(callable, new ExprList());
    call->args->add(x->expr);
    return call.ptr();
}

ExprPtr updateOperatorExpr(int op) {
    switch (op) {
    case UPDATE_ADD : return prelude_expr_addAssign();
    case UPDATE_SUBTRACT : return prelude_expr_subtractAssign();
    case UPDATE_MULTIPLY : return prelude_expr_multiplyAssign();
    case UPDATE_DIVIDE : return prelude_expr_divideAssign();
    case UPDATE_REMAINDER : return prelude_expr_remainderAssign();
    default :
        assert(false);
        return NULL;
    }
}

static vector<IdentifierPtr> identV(IdentifierPtr x) {
    vector<IdentifierPtr> v;
    v.push_back(x);
    return v;
}

StatementPtr desugarForStatement(ForPtr x) {
    IdentifierPtr exprVar = new Identifier("%expr");
    IdentifierPtr iterVar = new Identifier("%iter");

    BlockPtr block = new Block();
    vector<StatementPtr> &bs = block->statements;
    bs.push_back(new Binding(REF, identV(exprVar), new ExprList(x->expr)));

    CallPtr iteratorCall = new Call(prelude_expr_iterator(), new ExprList());
    iteratorCall->args->add(new NameRef(exprVar));
    bs.push_back(new Binding(VAR,
                             identV(iterVar),
                             new ExprList(iteratorCall.ptr())));

    CallPtr hasNextCall = new Call(prelude_expr_hasNextP(), new ExprList());
    hasNextCall->args->add(new NameRef(iterVar));
    CallPtr nextCall = new Call(prelude_expr_next(), new ExprList());
    nextCall->args->add(new NameRef(iterVar));
    ExprPtr unpackNext = new Unpack(nextCall.ptr());
    BlockPtr whileBody = new Block();
    vector<StatementPtr> &ws = whileBody->statements;
    ws.push_back(new Binding(REF, x->variables, new ExprList(unpackNext)));
    ws.push_back(x->body);

    bs.push_back(new While(hasNextCall.ptr(), whileBody.ptr()));
    return block.ptr();
}

StatementPtr desugarCatchBlocks(const vector<CatchPtr> &catchBlocks) {
    bool lastWasAny = false;
    IfPtr lastIf;
    StatementPtr result;
    for (unsigned i = 0; i < catchBlocks.size(); ++i) {
        CatchPtr x = catchBlocks[i];
        if (lastWasAny)
            error(x, "unreachable catch block");
        if (x->exceptionType.ptr()) {
            ExprListPtr typeArg = new ExprList(x->exceptionType);
            CallPtr cond = new Call(prelude_expr_exceptionIsP(), typeArg);
            cond->location = x->exceptionType->location;

            BlockPtr block = new Block();
            CallPtr getter = new Call(prelude_expr_exceptionAs(), typeArg);
            getter->location = x->exceptionVar->location;
            BindingPtr binding =
                new Binding(VAR,
                            vector<IdentifierPtr>(1, x->exceptionVar),
                            new ExprList(getter.ptr()));
            binding->location = x->exceptionVar->location;

            block->statements.push_back(binding.ptr());
            block->statements.push_back(x->body);

            IfPtr ifStatement = new If(cond.ptr(), block.ptr());
            ifStatement->location = x->location;
            if (!result)
                result = ifStatement.ptr();
            if (lastIf.ptr())
                lastIf->elsePart = ifStatement.ptr();
            lastIf = ifStatement;
        }
        else {
            BlockPtr block = new Block();
            block->location = x->location;
            CallPtr getter = new Call(prelude_expr_exceptionAsAny(),
                                      new ExprList());
            getter->location = x->exceptionVar->location;
            BindingPtr binding =
                new Binding(VAR,
                            vector<IdentifierPtr>(1, x->exceptionVar),
                            new ExprList(getter.ptr()));
            binding->location = x->exceptionVar->location;
            block->statements.push_back(binding.ptr());
            block->statements.push_back(x->body);

            if (!result)
                result = block.ptr();
            if (lastIf.ptr())
                lastIf->elsePart = block.ptr();

            lastWasAny = true;
            lastIf = NULL;
        }
    }
    assert(result.ptr());
    if (!lastWasAny) {
        assert(lastIf.ptr());
        BlockPtr block = new Block();
        CallPtr continueException = new Call(prelude_expr_continueException(),
                                             new ExprList());
        StatementPtr stmt = new ExprStatement(continueException.ptr());
        block->statements.push_back(stmt);
//         block->statements.push_back(new Unreachable());
        lastIf->elsePart = block.ptr();
    }
    return result;
}

StatementPtr desugarSwitchStatement(SwitchPtr x) {
    BlockPtr block = new Block();
    block->location = x->location;

    // %thing is the value being switched on
    IdentifierPtr thing = new Identifier("%thing");
    thing->location = x->expr->location;
    NameRefPtr thingRef = new NameRef(thing);
    thingRef->location = x->expr->location;

    // initialize %thing
    {
        BindingPtr b = new Binding(REF, identV(thing), new ExprList(x->expr));
        block->statements.push_back(b.ptr());
    }

    StatementPtr root;
    StatementPtr *nextPtr = &root;

    // dispatch logic
    for (unsigned i = 0; i < x->caseBlocks.size(); ++i) {
        CaseBlockPtr y = x->caseBlocks[i];

        ExprPtr condition;
        for (unsigned j = 0; j < y->caseLabels->size(); ++j) {
            ExprPtr caseValue = y->caseLabels->exprs[j];
            ExprPtr compare = new BinaryOp(EQUALS, thingRef.ptr(), caseValue);
            compare->location = caseValue->location;
            if (!condition) {
                condition = compare;
            }
            else {
                condition = new Or(condition, compare);
                condition->location = y->location;
            }
        }
        assert(condition.ptr());

        IfPtr ifStmt = new If(condition, y->body);
        ifStmt->location = y->location;
        *nextPtr = ifStmt.ptr();
        nextPtr = &(ifStmt->elsePart);
    }

    if (x->defaultCase.ptr())
        *nextPtr = x->defaultCase;
    else
        *nextPtr = new Break();

    block->statements.push_back(root);

    return block.ptr();
}
<commit_msg>oops. uncomment the critical line.<commit_after>#include "clay.hpp"
#include "libclaynames.hpp"

ExprPtr desugarCharLiteral(char c) {
    ExprPtr nameRef = prelude_expr_Char();
    CallPtr call = new Call(nameRef, new ExprList());
    ostringstream out;
    out << (int)c;
    call->args->add(new IntLiteral(out.str(), "i8"));
    return call.ptr();
}

ExprPtr desugarFieldRef(FieldRefPtr x) {
    ExprListPtr args = new ExprList(x->expr);
    args->add(new ObjectExpr(x->name.ptr()));
    return new Call(prelude_expr_fieldRef(), args);
}

ExprPtr desugarStaticIndexing(StaticIndexingPtr x) {
    ExprListPtr args = new ExprList(x->expr);
    ValueHolderPtr vh = sizeTToValueHolder(x->index);
    args->add(new StaticExpr(new ObjectExpr(vh.ptr())));
    return new Call(prelude_expr_staticIndex(), args);
}

ExprPtr desugarUnaryOp(UnaryOpPtr x) {
    ExprPtr callable;
    switch (x->op) {
    case DEREFERENCE :
        callable = prelude_expr_dereference();
        break;
    case ADDRESS_OF :
        callable = primitive_expr_addressOf();
        break;
    case PLUS :
        callable = prelude_expr_plus();
        break;
    case MINUS :
        callable = prelude_expr_minus();
        break;
    case NOT :
        callable = primitive_expr_boolNot();
        break;
    default :
        assert(false);
    }
    CallPtr call = new Call(callable, new ExprList());
    call->args->add(x->expr);
    return call.ptr();
}

ExprPtr desugarBinaryOp(BinaryOpPtr x) {
    ExprPtr callable;
    switch (x->op) {
    case ADD :
        callable = prelude_expr_add();
        break;
    case SUBTRACT :
        callable = prelude_expr_subtract();
        break;
    case MULTIPLY :
        callable = prelude_expr_multiply();
        break;
    case DIVIDE :
        callable = prelude_expr_divide();
        break;
    case REMAINDER :
        callable = prelude_expr_remainder();
        break;
    case EQUALS :
        callable = prelude_expr_equalsP();
        break;
    case NOT_EQUALS :
        callable = prelude_expr_notEqualsP();
        break;
    case LESSER :
        callable = prelude_expr_lesserP();
        break;
    case LESSER_EQUALS :
        callable = prelude_expr_lesserEqualsP();
        break;
    case GREATER :
        callable = prelude_expr_greaterP();
        break;
    case GREATER_EQUALS :
        callable = prelude_expr_greaterEqualsP();
        break;
    default :
        assert(false);
    }
    CallPtr call = new Call(callable, new ExprList());
    call->args->add(x->expr1);
    call->args->add(x->expr2);
    return call.ptr();
}

ExprPtr desugarIfExpr(IfExprPtr x) {
    ExprPtr callable = prelude_expr_ifExpression();
    CallPtr call = new Call(callable, new ExprList());
    call->args->add(x->condition);
    call->args->add(x->thenPart);
    call->args->add(x->elsePart);
    return call.ptr();
}

ExprPtr desugarNew(NewPtr x) {
    ExprPtr callable = prelude_expr_allocateShared();
    CallPtr call = new Call(callable, new ExprList());
    call->args->add(x->expr);
    return call.ptr();
}

ExprPtr desugarStaticExpr(StaticExprPtr x) {
    ExprPtr callable = prelude_expr_wrapStatic();
    CallPtr call = new Call(callable, new ExprList());
    call->args->add(x->expr);
    return call.ptr();
}

ExprPtr updateOperatorExpr(int op) {
    switch (op) {
    case UPDATE_ADD : return prelude_expr_addAssign();
    case UPDATE_SUBTRACT : return prelude_expr_subtractAssign();
    case UPDATE_MULTIPLY : return prelude_expr_multiplyAssign();
    case UPDATE_DIVIDE : return prelude_expr_divideAssign();
    case UPDATE_REMAINDER : return prelude_expr_remainderAssign();
    default :
        assert(false);
        return NULL;
    }
}

static vector<IdentifierPtr> identV(IdentifierPtr x) {
    vector<IdentifierPtr> v;
    v.push_back(x);
    return v;
}

StatementPtr desugarForStatement(ForPtr x) {
    IdentifierPtr exprVar = new Identifier("%expr");
    IdentifierPtr iterVar = new Identifier("%iter");

    BlockPtr block = new Block();
    vector<StatementPtr> &bs = block->statements;
    bs.push_back(new Binding(REF, identV(exprVar), new ExprList(x->expr)));

    CallPtr iteratorCall = new Call(prelude_expr_iterator(), new ExprList());
    iteratorCall->args->add(new NameRef(exprVar));
    bs.push_back(new Binding(VAR,
                             identV(iterVar),
                             new ExprList(iteratorCall.ptr())));

    CallPtr hasNextCall = new Call(prelude_expr_hasNextP(), new ExprList());
    hasNextCall->args->add(new NameRef(iterVar));
    CallPtr nextCall = new Call(prelude_expr_next(), new ExprList());
    nextCall->args->add(new NameRef(iterVar));
    ExprPtr unpackNext = new Unpack(nextCall.ptr());
    BlockPtr whileBody = new Block();
    vector<StatementPtr> &ws = whileBody->statements;
    ws.push_back(new Binding(REF, x->variables, new ExprList(unpackNext)));
    ws.push_back(x->body);

    bs.push_back(new While(hasNextCall.ptr(), whileBody.ptr()));
    return block.ptr();
}

StatementPtr desugarCatchBlocks(const vector<CatchPtr> &catchBlocks) {
    bool lastWasAny = false;
    IfPtr lastIf;
    StatementPtr result;
    for (unsigned i = 0; i < catchBlocks.size(); ++i) {
        CatchPtr x = catchBlocks[i];
        if (lastWasAny)
            error(x, "unreachable catch block");
        if (x->exceptionType.ptr()) {
            ExprListPtr typeArg = new ExprList(x->exceptionType);
            CallPtr cond = new Call(prelude_expr_exceptionIsP(), typeArg);
            cond->location = x->exceptionType->location;

            BlockPtr block = new Block();
            CallPtr getter = new Call(prelude_expr_exceptionAs(), typeArg);
            getter->location = x->exceptionVar->location;
            BindingPtr binding =
                new Binding(VAR,
                            vector<IdentifierPtr>(1, x->exceptionVar),
                            new ExprList(getter.ptr()));
            binding->location = x->exceptionVar->location;

            block->statements.push_back(binding.ptr());
            block->statements.push_back(x->body);

            IfPtr ifStatement = new If(cond.ptr(), block.ptr());
            ifStatement->location = x->location;
            if (!result)
                result = ifStatement.ptr();
            if (lastIf.ptr())
                lastIf->elsePart = ifStatement.ptr();
            lastIf = ifStatement;
        }
        else {
            BlockPtr block = new Block();
            block->location = x->location;
            CallPtr getter = new Call(prelude_expr_exceptionAsAny(),
                                      new ExprList());
            getter->location = x->exceptionVar->location;
            BindingPtr binding =
                new Binding(VAR,
                            vector<IdentifierPtr>(1, x->exceptionVar),
                            new ExprList(getter.ptr()));
            binding->location = x->exceptionVar->location;
            block->statements.push_back(binding.ptr());
            block->statements.push_back(x->body);

            if (!result)
                result = block.ptr();
            if (lastIf.ptr())
                lastIf->elsePart = block.ptr();

            lastWasAny = true;
            lastIf = NULL;
        }
    }
    assert(result.ptr());
    if (!lastWasAny) {
        assert(lastIf.ptr());
        BlockPtr block = new Block();
        CallPtr continueException = new Call(prelude_expr_continueException(),
                                             new ExprList());
        StatementPtr stmt = new ExprStatement(continueException.ptr());
        block->statements.push_back(stmt);
        block->statements.push_back(new Unreachable());
        lastIf->elsePart = block.ptr();
    }
    return result;
}

StatementPtr desugarSwitchStatement(SwitchPtr x) {
    BlockPtr block = new Block();
    block->location = x->location;

    // %thing is the value being switched on
    IdentifierPtr thing = new Identifier("%thing");
    thing->location = x->expr->location;
    NameRefPtr thingRef = new NameRef(thing);
    thingRef->location = x->expr->location;

    // initialize %thing
    {
        BindingPtr b = new Binding(REF, identV(thing), new ExprList(x->expr));
        block->statements.push_back(b.ptr());
    }

    StatementPtr root;
    StatementPtr *nextPtr = &root;

    // dispatch logic
    for (unsigned i = 0; i < x->caseBlocks.size(); ++i) {
        CaseBlockPtr y = x->caseBlocks[i];

        ExprPtr condition;
        for (unsigned j = 0; j < y->caseLabels->size(); ++j) {
            ExprPtr caseValue = y->caseLabels->exprs[j];
            ExprPtr compare = new BinaryOp(EQUALS, thingRef.ptr(), caseValue);
            compare->location = caseValue->location;
            if (!condition) {
                condition = compare;
            }
            else {
                condition = new Or(condition, compare);
                condition->location = y->location;
            }
        }
        assert(condition.ptr());

        IfPtr ifStmt = new If(condition, y->body);
        ifStmt->location = y->location;
        *nextPtr = ifStmt.ptr();
        nextPtr = &(ifStmt->elsePart);
    }

    if (x->defaultCase.ptr())
        *nextPtr = x->defaultCase;
    else
        *nextPtr = new Break();

    block->statements.push_back(root);

    return block.ptr();
}
<|endoftext|>


<commit_before>/**************************************************************************
 *
 * Copyright 2014-2016 VMware, 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 WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS 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 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 **************************************************************************/

#include "memtrace.hpp"

#include <assert.h>
#include <string.h>
#include <stdio.h>

#include <algorithm>

#include "crc32c.hpp"

#if \
    defined(__i386__) /* gcc */ || defined(_M_IX86) /* msvc */ || \
    defined(__x86_64__) /* gcc */ || defined(_M_X64) /* msvc */ || defined(_M_AMD64) /* msvc */

#define HAVE_SSE2

// TODO: Detect and leverage SSE 4.1 and 4.2 at runtime
#undef HAVE_SSE41
#undef HAVE_SSE42

#endif

#if defined(HAVE_SSE42)
#  include <nmmintrin.h>
#elif defined(HAVE_SSE41)
#  include <smmintrin.h>
#elif defined(HAVE_SSE2)
#  include <emmintrin.h>
#endif

#define BLOCK_SIZE 512

template< class T >
static inline T *
lAlignPtr(T *p, uintptr_t alignment)
{
    return reinterpret_cast<T *>(reinterpret_cast<uintptr_t>(p) & ~(alignment - 1));
}

template< class T >
static inline T *
rAlignPtr(T *p, uintptr_t alignment)
{
    return reinterpret_cast<T *>((reinterpret_cast<uintptr_t>(p) + alignment - 1) & ~(alignment - 1));
}

#ifdef HAVE_SSE2

#ifdef HAVE_SSE41
    #define mm_stream_load_si128 _mm_stream_load_si128
    #define mm_extract_epi32_0(x) _mm_extract_epi32(x, 0)
    #define mm_extract_epi32_1(x) _mm_extract_epi32(x, 1)
    #define mm_extract_epi32_2(x) _mm_extract_epi32(x, 2)
    #define mm_extract_epi32_3(x) _mm_extract_epi32(x, 3)
#else /* !HAVE_SSE41 */
    #define mm_stream_load_si128 _mm_load_si128
    #define mm_extract_epi32_0(x) _mm_cvtsi128_si32(x)
    #define mm_extract_epi32_1(x) _mm_cvtsi128_si32(_mm_shuffle_epi32(x,_MM_SHUFFLE(1,1,1,1)))
    #define mm_extract_epi32_2(x) _mm_cvtsi128_si32(_mm_shuffle_epi32(x,_MM_SHUFFLE(2,2,2,2)))
    #define mm_extract_epi32_3(x) _mm_cvtsi128_si32(_mm_shuffle_epi32(x,_MM_SHUFFLE(3,3,3,3)))
#endif /* !HAVE_SSE41 */

#ifdef HAVE_SSE42

#define mm_crc32_u32 _mm_crc32_u32

#else /* !HAVE_SSE42 */

static inline uint32_t
mm_crc32_u32(uint32_t crc, uint32_t current)
{
    uint32_t one = current ^ crc;
    crc  = crc32c_8x256_table[0][ one >> 24        ] ^
           crc32c_8x256_table[1][(one >> 16) & 0xff] ^
           crc32c_8x256_table[2][(one >>  8) & 0xff] ^
           crc32c_8x256_table[3][ one        & 0xff];
    return crc;
}

#endif /* !HAVE_SSE42 */

#endif /* HAVE_SSE2 */

uint32_t
hashBlock(const void *p)
{
    assert((intptr_t)p % BLOCK_SIZE == 0);

    uint32_t crc;

#ifdef HAVE_SSE2
    crc = 0;

    __m128i *q = (__m128i *)(void *)p;

    crc = ~crc;

    for (unsigned c = BLOCK_SIZE / (4 * sizeof *q); c; --c) {
        __m128i m0 = mm_stream_load_si128(q++);
        __m128i m1 = mm_stream_load_si128(q++);
        __m128i m2 = mm_stream_load_si128(q++);
        __m128i m3 = mm_stream_load_si128(q++);

        crc = mm_crc32_u32(crc, mm_extract_epi32_0(m0));
        crc = mm_crc32_u32(crc, mm_extract_epi32_1(m0));
        crc = mm_crc32_u32(crc, mm_extract_epi32_2(m0));
        crc = mm_crc32_u32(crc, mm_extract_epi32_3(m0));

        crc = mm_crc32_u32(crc, mm_extract_epi32_0(m1));
        crc = mm_crc32_u32(crc, mm_extract_epi32_1(m1));
        crc = mm_crc32_u32(crc, mm_extract_epi32_2(m1));
        crc = mm_crc32_u32(crc, mm_extract_epi32_3(m1));

        crc = mm_crc32_u32(crc, mm_extract_epi32_0(m2));
        crc = mm_crc32_u32(crc, mm_extract_epi32_1(m2));
        crc = mm_crc32_u32(crc, mm_extract_epi32_2(m2));
        crc = mm_crc32_u32(crc, mm_extract_epi32_3(m2));

        crc = mm_crc32_u32(crc, mm_extract_epi32_0(m3));
        crc = mm_crc32_u32(crc, mm_extract_epi32_1(m3));
        crc = mm_crc32_u32(crc, mm_extract_epi32_2(m3));
        crc = mm_crc32_u32(crc, mm_extract_epi32_3(m3));
    }

    crc = ~crc;

#else /* !HAVE_SSE2 */

    crc = crc32c_8bytes(p, BLOCK_SIZE);

#endif

    return crc;
}

// We must reset the data on discard, otherwise the old data could match just
// by chance.
//
// XXX: if the appplication writes 0xCDCDCDCD at the start or the end of the
// buffer range, we'll fail to detect.  The only way to be 100% sure things
// won't fall through would be to setup memory traps.
void MemoryShadow::zero(void *_ptr, size_t _size)
{
    memset(_ptr, 0xCD, _size);
}

void MemoryShadow::cover(void *_ptr, size_t _size, bool _discard)
{
    assert(_ptr);

    if (_size != size) {
        nBlocks = ((intptr_t)_ptr + _size + BLOCK_SIZE - 1)/BLOCK_SIZE - (intptr_t)_ptr/BLOCK_SIZE;

        hashPtr = (uint32_t *)realloc(hashPtr, nBlocks * sizeof *hashPtr);
        size = _size;
    }

    realPtr = (const uint8_t *)_ptr;

    if (_discard) {
        zero(_ptr, size);
    }

    const uint8_t *p = lAlignPtr((const uint8_t *)_ptr, BLOCK_SIZE);
    if (_discard) {
        hashPtr[0] = hashBlock(p);
        for (size_t i = 1; i < nBlocks; ++i) {
            hashPtr[i] = hashPtr[0];
        }
    } else {
        for (size_t i = 0; i < nBlocks; ++i) {
            hashPtr[i] = hashBlock(p);
            p += BLOCK_SIZE;
        }
    }
}

void MemoryShadow::update(Callback callback) const
{
    const uint8_t *realStart   = realPtr   + size;
    const uint8_t *realStop    = realPtr;

    const uint8_t *p = lAlignPtr(realPtr, BLOCK_SIZE);
    for (size_t i = 0; i < nBlocks; ++i) {
        uint32_t crc = hashBlock(p);
        if (crc != hashPtr[i]) {
            realStart = std::min(realStart, p);
            realStop  = std::max(realStop,  p + BLOCK_SIZE);
        }
        p += BLOCK_SIZE;
    }

    realStart = std::max(realStart, realPtr);
    realStop  = std::min(realStop,  realPtr + size);

    // Update the rest
    if (realStart < realStop) {
        callback(realStart, realStop - realStart);
    }
}
<commit_msg>memtrace: Fix build without SSE2.<commit_after>/**************************************************************************
 *
 * Copyright 2014-2016 VMware, 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 WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS 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 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 **************************************************************************/

#include "memtrace.hpp"

#include <assert.h>
#include <string.h>
#include <stdio.h>

#include <algorithm>

#include "crc32c.hpp"

#if \
    (defined(__i386__) && defined(__SSE2__)) /* gcc */ || \
    defined(_M_IX86) /* msvc */ || \
    defined(__x86_64__) /* gcc */ || \
    defined(_M_X64) /* msvc */ || \
    defined(_M_AMD64) /* msvc */

#  define HAVE_SSE2

// TODO: Detect and leverage SSE 4.1 and 4.2 at runtime
#  undef HAVE_SSE41
#  undef HAVE_SSE42

#endif

#if defined(HAVE_SSE42)
#  include <nmmintrin.h>
#elif defined(HAVE_SSE41)
#  include <smmintrin.h>
#elif defined(HAVE_SSE2)
#  include <emmintrin.h>
#endif

#define BLOCK_SIZE 512

template< class T >
static inline T *
lAlignPtr(T *p, uintptr_t alignment)
{
    return reinterpret_cast<T *>(reinterpret_cast<uintptr_t>(p) & ~(alignment - 1));
}

template< class T >
static inline T *
rAlignPtr(T *p, uintptr_t alignment)
{
    return reinterpret_cast<T *>((reinterpret_cast<uintptr_t>(p) + alignment - 1) & ~(alignment - 1));
}

#ifdef HAVE_SSE2

#ifdef HAVE_SSE41
    #define mm_stream_load_si128 _mm_stream_load_si128
    #define mm_extract_epi32_0(x) _mm_extract_epi32(x, 0)
    #define mm_extract_epi32_1(x) _mm_extract_epi32(x, 1)
    #define mm_extract_epi32_2(x) _mm_extract_epi32(x, 2)
    #define mm_extract_epi32_3(x) _mm_extract_epi32(x, 3)
#else /* !HAVE_SSE41 */
    #define mm_stream_load_si128 _mm_load_si128
    #define mm_extract_epi32_0(x) _mm_cvtsi128_si32(x)
    #define mm_extract_epi32_1(x) _mm_cvtsi128_si32(_mm_shuffle_epi32(x,_MM_SHUFFLE(1,1,1,1)))
    #define mm_extract_epi32_2(x) _mm_cvtsi128_si32(_mm_shuffle_epi32(x,_MM_SHUFFLE(2,2,2,2)))
    #define mm_extract_epi32_3(x) _mm_cvtsi128_si32(_mm_shuffle_epi32(x,_MM_SHUFFLE(3,3,3,3)))
#endif /* !HAVE_SSE41 */

#ifdef HAVE_SSE42

#define mm_crc32_u32 _mm_crc32_u32

#else /* !HAVE_SSE42 */

static inline uint32_t
mm_crc32_u32(uint32_t crc, uint32_t current)
{
    uint32_t one = current ^ crc;
    crc  = crc32c_8x256_table[0][ one >> 24        ] ^
           crc32c_8x256_table[1][(one >> 16) & 0xff] ^
           crc32c_8x256_table[2][(one >>  8) & 0xff] ^
           crc32c_8x256_table[3][ one        & 0xff];
    return crc;
}

#endif /* !HAVE_SSE42 */

#endif /* HAVE_SSE2 */

uint32_t
hashBlock(const void *p)
{
    assert((intptr_t)p % BLOCK_SIZE == 0);

    uint32_t crc;

#ifdef HAVE_SSE2
    crc = 0;

    __m128i *q = (__m128i *)(void *)p;

    crc = ~crc;

    for (unsigned c = BLOCK_SIZE / (4 * sizeof *q); c; --c) {
        __m128i m0 = mm_stream_load_si128(q++);
        __m128i m1 = mm_stream_load_si128(q++);
        __m128i m2 = mm_stream_load_si128(q++);
        __m128i m3 = mm_stream_load_si128(q++);

        crc = mm_crc32_u32(crc, mm_extract_epi32_0(m0));
        crc = mm_crc32_u32(crc, mm_extract_epi32_1(m0));
        crc = mm_crc32_u32(crc, mm_extract_epi32_2(m0));
        crc = mm_crc32_u32(crc, mm_extract_epi32_3(m0));

        crc = mm_crc32_u32(crc, mm_extract_epi32_0(m1));
        crc = mm_crc32_u32(crc, mm_extract_epi32_1(m1));
        crc = mm_crc32_u32(crc, mm_extract_epi32_2(m1));
        crc = mm_crc32_u32(crc, mm_extract_epi32_3(m1));

        crc = mm_crc32_u32(crc, mm_extract_epi32_0(m2));
        crc = mm_crc32_u32(crc, mm_extract_epi32_1(m2));
        crc = mm_crc32_u32(crc, mm_extract_epi32_2(m2));
        crc = mm_crc32_u32(crc, mm_extract_epi32_3(m2));

        crc = mm_crc32_u32(crc, mm_extract_epi32_0(m3));
        crc = mm_crc32_u32(crc, mm_extract_epi32_1(m3));
        crc = mm_crc32_u32(crc, mm_extract_epi32_2(m3));
        crc = mm_crc32_u32(crc, mm_extract_epi32_3(m3));
    }

    crc = ~crc;

#else /* !HAVE_SSE2 */

    crc = crc32c_8bytes(p, BLOCK_SIZE);

#endif

    return crc;
}

// We must reset the data on discard, otherwise the old data could match just
// by chance.
//
// XXX: if the appplication writes 0xCDCDCDCD at the start or the end of the
// buffer range, we'll fail to detect.  The only way to be 100% sure things
// won't fall through would be to setup memory traps.
void MemoryShadow::zero(void *_ptr, size_t _size)
{
    memset(_ptr, 0xCD, _size);
}

void MemoryShadow::cover(void *_ptr, size_t _size, bool _discard)
{
    assert(_ptr);

    if (_size != size) {
        nBlocks = ((intptr_t)_ptr + _size + BLOCK_SIZE - 1)/BLOCK_SIZE - (intptr_t)_ptr/BLOCK_SIZE;

        hashPtr = (uint32_t *)realloc(hashPtr, nBlocks * sizeof *hashPtr);
        size = _size;
    }

    realPtr = (const uint8_t *)_ptr;

    if (_discard) {
        zero(_ptr, size);
    }

    const uint8_t *p = lAlignPtr((const uint8_t *)_ptr, BLOCK_SIZE);
    if (_discard) {
        hashPtr[0] = hashBlock(p);
        for (size_t i = 1; i < nBlocks; ++i) {
            hashPtr[i] = hashPtr[0];
        }
    } else {
        for (size_t i = 0; i < nBlocks; ++i) {
            hashPtr[i] = hashBlock(p);
            p += BLOCK_SIZE;
        }
    }
}

void MemoryShadow::update(Callback callback) const
{
    const uint8_t *realStart   = realPtr   + size;
    const uint8_t *realStop    = realPtr;

    const uint8_t *p = lAlignPtr(realPtr, BLOCK_SIZE);
    for (size_t i = 0; i < nBlocks; ++i) {
        uint32_t crc = hashBlock(p);
        if (crc != hashPtr[i]) {
            realStart = std::min(realStart, p);
            realStop  = std::max(realStop,  p + BLOCK_SIZE);
        }
        p += BLOCK_SIZE;
    }

    realStart = std::max(realStart, realPtr);
    realStop  = std::min(realStop,  realPtr + size);

    // Update the rest
    if (realStart < realStop) {
        callback(realStart, realStop - realStart);
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2000 Stephen Williams (steve@icarus.com)
 *
 *    This source code is free software; you can redistribute it
 *    and/or modify it in source code form under the terms of the GNU
 *    General Public License as published by the Free Software
 *    Foundation; either version 2 of the License, or (at your option)
 *    any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */
#if !defined(WINNT) && !defined(macintosh)
#ident "$Id: net_event.cc,v 1.9 2000/07/29 03:55:38 steve Exp $"
#endif

# include  "netlist.h"

NetEvent::NetEvent(const string&n)
: name_(n)
{
      scope_ = 0;
      snext_ = 0;
      probes_ = 0;
      trig_ = 0;
      waitref_ = 0;
      wlist_ = 0;
}

NetEvent::~NetEvent()
{
      assert(waitref_ == 0);
      if (scope_) scope_->rem_event(this);
      while (probes_) {
	    NetEvProbe*tmp = probes_->enext_;
	    delete probes_;
	    tmp = probes_;
      }
}

string NetEvent::name() const
{
      return name_;
}

string NetEvent::full_name() const
{
      assert(scope_);
      return scope_->name() + "." + name_;
}

unsigned NetEvent::nprobe() const
{
      unsigned cnt = 0;
      NetEvProbe*cur = probes_;
      while (cur) {
	    cnt += 1;
	    cur = cur->enext_;
      }

      return cnt;
}

NetEvProbe* NetEvent::probe(unsigned idx)
{
      NetEvProbe*cur = probes_;
      while (cur && idx) {
	    cur = cur->enext_;
	    idx -= 1;
      }
      return cur;
}

unsigned NetEvent::ntrig() const
{
      unsigned cnt = 0;
      NetEvTrig*cur = trig_;
      while (cur) {
	    cnt += 1;
	    cur = cur->enext_;
      }

      return cnt;
}

unsigned NetEvent::nwait() const
{
      return waitref_;
}

NetEvent* NetEvent::find_similar_event()
{
      if (probes_ == 0)
	    return 0;
#define NCAND 256
      NetEvent*cand[NCAND];
      bool cflg[NCAND];
      unsigned ncand = 0;

      NetEvProbe*cur = probes_;

	/* First locate all the canditate events from the probe
	   objects that are connected to them and to my first
	   probe. All of these candidates have at least this probe in
	   common. */

      for (NetNode*idx = cur->next_node()
		 ; idx && (idx != cur) ;  idx = idx->next_node()) {
	    NetEvProbe*tmp = dynamic_cast<NetEvProbe*>(idx);
	    if (tmp == 0)
		  continue;
	    if (tmp->edge() != cur->edge())
		  continue;

	    cand[ncand++] = tmp->event();
	    assert(ncand <= NCAND);
      }

	/* Now go through the remaining probes, checking that all the
	   candidate events also have a probe connected to this
	   one. By the time I finish this list, I will have eliminated
	   all the candidate events that are not connected to all of
	   my probes. */

      for (cur = cur->enext_ ;  cur && ncand ;  cur = cur->enext_) {
	    for (unsigned idx = 0 ;  idx < ncand ;  idx += 1)
		  cflg[idx] = false;

	      /* For this probe, look for other probes connected to it
		 and find the event connected to it. Mark that event
		 as connected to this probe. */

	    for (NetNode*idx = cur->next_node()
		       ; idx && (idx != cur) ;  idx = idx->next_node()) {
		  NetEvProbe*tmp = dynamic_cast<NetEvProbe*>(idx);
		  if (tmp == 0)
			continue;
		  if (tmp->edge() != cur->edge())
			continue;

		  for (unsigned srch = 0 ;  srch < ncand ;  srch += 1)
			if (cand[srch] == tmp->event()) {
			      cflg[srch] = true;
			      break;
			}
	    }

	      /* Look for all the candidates that did not connect to
		 this probe (cflg is false) and eliminate them. */

	    for (unsigned idx = 0 ;  idx < ncand ;  ) {
		  if (cflg[idx]) {
			idx += 1;
			continue;
		  }

		  for (unsigned tmp = idx ;  (tmp+1) < ncand ;  tmp += 1) {
			cflg[tmp] = cflg[tmp+1];
			cand[tmp] = cand[tmp+1];
		  }
		  ncand -= 1;
	    }
      }

	/* Now, skip any of the remaining candidates for any that have
	   a different number of probes. These would be events that
	   have probes that I'm not connected to. */

      for (unsigned idx = 0 ;  idx < ncand ;  idx += 1) {
	    if (cand[idx]->nprobe() == nprobe())
		  return cand[idx];
      }

      return 0;
}

void NetEvent::replace_event(NetEvent*that)
{
      while (wlist_) {
	    wlist_->obj->replace_event(this, that);
      }
}

NetEvTrig::NetEvTrig(NetEvent*ev)
: event_(ev)
{
      enext_ = event_->trig_;
      event_->trig_ = this;
}

NetEvTrig::~NetEvTrig()
{
      if (event_->trig_ == this) {
	    event_->trig_ = enext_;

      } else {
	    NetEvTrig*cur = event_->trig_;
	    while (cur->enext_ != this) {
		  assert(cur->enext_);
		  cur = cur->enext_;
	    }

	    cur->enext_ = this->enext_;
      }
}

const NetEvent* NetEvTrig::event() const
{
      return event_;
}

NetEvProbe::NetEvProbe(const string&n, NetEvent*tgt,
		       edge_t t, unsigned p)
: NetNode(n, p), event_(tgt), edge_(t)
{
      for (unsigned idx = 0 ;  idx < p ;  idx += 1) {
	    pin(idx).set_dir(Link::INPUT);
	    pin(idx).set_name("P", idx);
      }

      enext_ = event_->probes_;
      event_->probes_ = this;
}

NetEvProbe::~NetEvProbe()
{
      if (event_->probes_ == this) {
	    event_->probes_ = enext_;

      } else {
	    NetEvProbe*cur = event_->probes_;
	    while (cur->enext_ != this) {
		  assert(cur->enext_);
		  cur = cur->enext_;
	    }

	    cur->enext_ = this->enext_;
      }
}

NetEvProbe::edge_t NetEvProbe::edge() const
{
      return edge_;
}

NetEvent* NetEvProbe::event()
{
      return event_;
}

const NetEvent* NetEvProbe::event() const
{
      return event_;
}

NetEvWait::NetEvWait(NetProc*pr)
: statement_(pr), nevents_(0), events_(0)
{
}

NetEvWait::~NetEvWait()
{
      if (events_) {
	    for (unsigned idx = 0 ;  idx < nevents_ ;  idx += 1) {
		  NetEvent*tgt = events_[idx];
		  tgt->waitref_ -= 1;

		  struct NetEvent::wcell_*tmp = tgt->wlist_;
		  if (tmp->obj == this) {
			tgt->wlist_ = tmp->next;
			delete tmp;
		  } else {
			assert(tmp->next);
			while (tmp->next->obj != this) {
			      tmp = tmp->next;
			      assert(tmp->next);
			}
			tmp->next = tmp->next->next;
			delete tmp;
		  }
	    }
	    delete[]events_;
      }
      delete statement_;
}

void NetEvWait::add_event(NetEvent*tgt)
{
      assert(tgt);
      if (nevents_ == 0) {
	    events_ = new NetEvent*[1];

      } else {
	    NetEvent**tmp = new NetEvent*[nevents_+1];
	    for (unsigned idx = 0 ;  idx < nevents_ ;  idx += 1) {
		  tmp[idx] = events_[idx];
		  assert(tmp[idx] != tgt);
	    }
	    delete[]events_;
	    events_ = tmp;
      }

      events_[nevents_] = tgt;
      nevents_ += 1;

	// Remember to tell the NetEvent that there is someone
	// pointing to it.
      tgt->waitref_ += 1;

      struct NetEvent::wcell_*tmp = new NetEvent::wcell_;
      tmp->obj = this;
      tmp->next = tgt->wlist_;
      tgt->wlist_ = tmp;
}

void NetEvWait::replace_event(NetEvent*src, NetEvent*repl)
{
      unsigned idx;
      for (idx = 0 ;  idx < nevents_ ;  idx += 1) {
	    if (events_[idx] == src)
		  break;
      }

      assert(idx < nevents_);

	/* First, remove me from the list held by the src NetEvent. */
      assert(src->waitref_ > 0);
      src->waitref_ -= 1;
      struct NetEvent::wcell_*tmp = src->wlist_;
      if (tmp->obj == this) {
	    src->wlist_ = tmp->next;
	    delete tmp;
      } else {
	    assert(tmp->next);
	    while (tmp->next->obj != this) {
		  tmp = tmp->next;
		  assert(tmp->next);
	    }
	    tmp->next = tmp->next->next;
	    delete tmp;
      }

      events_[idx] = repl;

	// Remember to tell the replacement NetEvent that there is
	// someone pointing to it.
      repl->waitref_ += 1;

      tmp = new NetEvent::wcell_;
      tmp->obj = this;
      tmp->next = repl->wlist_;
      repl->wlist_ = tmp;

}

unsigned NetEvWait::nevents() const
{
      return nevents_;
}

const NetEvent* NetEvWait::event(unsigned idx) const
{
      assert(idx < nevents_);
      return events_[idx];
}

NetEvent* NetEvWait::event(unsigned idx)
{
      assert(idx < nevents_);
      return events_[idx];
}

NetProc* NetEvWait::statement()
{
      return statement_;
}

/*
 * $Log: net_event.cc,v $
 * Revision 1.9  2000/07/29 03:55:38  steve
 *  fix problem coalescing events w/ probes.
 *
 * Revision 1.8  2000/05/31 02:26:49  steve
 *  Globally merge redundant event objects.
 *
 * Revision 1.7  2000/05/27 19:33:23  steve
 *  Merge similar probes within a module.
 *
 * Revision 1.6  2000/04/18 04:50:20  steve
 *  Clean up unneeded NetEvent objects.
 *
 * Revision 1.5  2000/04/16 23:32:18  steve
 *  Synthesis of comparator in expressions.
 *
 *  Connect the NetEvent and related classes
 *  together better.
 *
 * Revision 1.4  2000/04/12 20:02:53  steve
 *  Finally remove the NetNEvent and NetPEvent classes,
 *  Get synthesis working with the NetEvWait class,
 *  and get started supporting multiple events in a
 *  wait in vvm.
 *
 * Revision 1.3  2000/04/12 04:23:58  steve
 *  Named events really should be expressed with PEIdent
 *  objects in the pform,
 *
 *  Handle named events within the mix of net events
 *  and edges. As a unified lot they get caught together.
 *  wait statements are broken into more complex statements
 *  that include a conditional.
 *
 *  Do not generate NetPEvent or NetNEvent objects in
 *  elaboration. NetEvent, NetEvWait and NetEvProbe
 *  take over those functions in the netlist.
 *
 * Revision 1.2  2000/04/10 05:26:06  steve
 *  All events now use the NetEvent class.
 *
 * Revision 1.1  2000/04/04 03:20:15  steve
 *  Simulate named event trigger and waits.
 *
 */

<commit_msg> Typo stepping ot next probe in delete.<commit_after>/*
 * Copyright (c) 2000 Stephen Williams (steve@icarus.com)
 *
 *    This source code is free software; you can redistribute it
 *    and/or modify it in source code form under the terms of the GNU
 *    General Public License as published by the Free Software
 *    Foundation; either version 2 of the License, or (at your option)
 *    any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */
#if !defined(WINNT) && !defined(macintosh)
#ident "$Id: net_event.cc,v 1.10 2000/09/19 03:00:36 steve Exp $"
#endif

# include  "netlist.h"

NetEvent::NetEvent(const string&n)
: name_(n)
{
      scope_ = 0;
      snext_ = 0;
      probes_ = 0;
      trig_ = 0;
      waitref_ = 0;
      wlist_ = 0;
}

NetEvent::~NetEvent()
{
      assert(waitref_ == 0);
      if (scope_) scope_->rem_event(this);
      while (probes_) {
	    NetEvProbe*tmp = probes_->enext_;
	    delete probes_;
	    probes_ = tmp;
      }
}

string NetEvent::name() const
{
      return name_;
}

string NetEvent::full_name() const
{
      assert(scope_);
      return scope_->name() + "." + name_;
}

unsigned NetEvent::nprobe() const
{
      unsigned cnt = 0;
      NetEvProbe*cur = probes_;
      while (cur) {
	    cnt += 1;
	    cur = cur->enext_;
      }

      return cnt;
}

NetEvProbe* NetEvent::probe(unsigned idx)
{
      NetEvProbe*cur = probes_;
      while (cur && idx) {
	    cur = cur->enext_;
	    idx -= 1;
      }
      return cur;
}

unsigned NetEvent::ntrig() const
{
      unsigned cnt = 0;
      NetEvTrig*cur = trig_;
      while (cur) {
	    cnt += 1;
	    cur = cur->enext_;
      }

      return cnt;
}

unsigned NetEvent::nwait() const
{
      return waitref_;
}

NetEvent* NetEvent::find_similar_event()
{
      if (probes_ == 0)
	    return 0;
#define NCAND 256
      NetEvent*cand[NCAND];
      bool cflg[NCAND];
      unsigned ncand = 0;

      NetEvProbe*cur = probes_;

	/* First locate all the canditate events from the probe
	   objects that are connected to them and to my first
	   probe. All of these candidates have at least this probe in
	   common. */

      for (NetNode*idx = cur->next_node()
		 ; idx && (idx != cur) ;  idx = idx->next_node()) {
	    NetEvProbe*tmp = dynamic_cast<NetEvProbe*>(idx);
	    if (tmp == 0)
		  continue;
	    if (tmp->edge() != cur->edge())
		  continue;

	    cand[ncand++] = tmp->event();
	    assert(ncand <= NCAND);
      }

	/* Now go through the remaining probes, checking that all the
	   candidate events also have a probe connected to this
	   one. By the time I finish this list, I will have eliminated
	   all the candidate events that are not connected to all of
	   my probes. */

      for (cur = cur->enext_ ;  cur && ncand ;  cur = cur->enext_) {
	    for (unsigned idx = 0 ;  idx < ncand ;  idx += 1)
		  cflg[idx] = false;

	      /* For this probe, look for other probes connected to it
		 and find the event connected to it. Mark that event
		 as connected to this probe. */

	    for (NetNode*idx = cur->next_node()
		       ; idx && (idx != cur) ;  idx = idx->next_node()) {
		  NetEvProbe*tmp = dynamic_cast<NetEvProbe*>(idx);
		  if (tmp == 0)
			continue;
		  if (tmp->edge() != cur->edge())
			continue;

		  for (unsigned srch = 0 ;  srch < ncand ;  srch += 1)
			if (cand[srch] == tmp->event()) {
			      cflg[srch] = true;
			      break;
			}
	    }

	      /* Look for all the candidates that did not connect to
		 this probe (cflg is false) and eliminate them. */

	    for (unsigned idx = 0 ;  idx < ncand ;  ) {
		  if (cflg[idx]) {
			idx += 1;
			continue;
		  }

		  for (unsigned tmp = idx ;  (tmp+1) < ncand ;  tmp += 1) {
			cflg[tmp] = cflg[tmp+1];
			cand[tmp] = cand[tmp+1];
		  }
		  ncand -= 1;
	    }
      }

	/* Now, skip any of the remaining candidates for any that have
	   a different number of probes. These would be events that
	   have probes that I'm not connected to. */

      for (unsigned idx = 0 ;  idx < ncand ;  idx += 1) {
	    if (cand[idx]->nprobe() == nprobe())
		  return cand[idx];
      }

      return 0;
}

void NetEvent::replace_event(NetEvent*that)
{
      while (wlist_) {
	    wlist_->obj->replace_event(this, that);
      }
}

NetEvTrig::NetEvTrig(NetEvent*ev)
: event_(ev)
{
      enext_ = event_->trig_;
      event_->trig_ = this;
}

NetEvTrig::~NetEvTrig()
{
      if (event_->trig_ == this) {
	    event_->trig_ = enext_;

      } else {
	    NetEvTrig*cur = event_->trig_;
	    while (cur->enext_ != this) {
		  assert(cur->enext_);
		  cur = cur->enext_;
	    }

	    cur->enext_ = this->enext_;
      }
}

const NetEvent* NetEvTrig::event() const
{
      return event_;
}

NetEvProbe::NetEvProbe(const string&n, NetEvent*tgt,
		       edge_t t, unsigned p)
: NetNode(n, p), event_(tgt), edge_(t)
{
      for (unsigned idx = 0 ;  idx < p ;  idx += 1) {
	    pin(idx).set_dir(Link::INPUT);
	    pin(idx).set_name("P", idx);
      }

      enext_ = event_->probes_;
      event_->probes_ = this;
}

NetEvProbe::~NetEvProbe()
{
      if (event_->probes_ == this) {
	    event_->probes_ = enext_;

      } else {
	    NetEvProbe*cur = event_->probes_;
	    while (cur->enext_ != this) {
		  assert(cur->enext_);
		  cur = cur->enext_;
	    }

	    cur->enext_ = this->enext_;
      }
}

NetEvProbe::edge_t NetEvProbe::edge() const
{
      return edge_;
}

NetEvent* NetEvProbe::event()
{
      return event_;
}

const NetEvent* NetEvProbe::event() const
{
      return event_;
}

NetEvWait::NetEvWait(NetProc*pr)
: statement_(pr), nevents_(0), events_(0)
{
}

NetEvWait::~NetEvWait()
{
      if (events_) {
	    for (unsigned idx = 0 ;  idx < nevents_ ;  idx += 1) {
		  NetEvent*tgt = events_[idx];
		  tgt->waitref_ -= 1;

		  struct NetEvent::wcell_*tmp = tgt->wlist_;
		  if (tmp->obj == this) {
			tgt->wlist_ = tmp->next;
			delete tmp;
		  } else {
			assert(tmp->next);
			while (tmp->next->obj != this) {
			      tmp = tmp->next;
			      assert(tmp->next);
			}
			tmp->next = tmp->next->next;
			delete tmp;
		  }
	    }
	    delete[]events_;
      }
      delete statement_;
}

void NetEvWait::add_event(NetEvent*tgt)
{
      assert(tgt);
      if (nevents_ == 0) {
	    events_ = new NetEvent*[1];

      } else {
	    NetEvent**tmp = new NetEvent*[nevents_+1];
	    for (unsigned idx = 0 ;  idx < nevents_ ;  idx += 1) {
		  tmp[idx] = events_[idx];
		  assert(tmp[idx] != tgt);
	    }
	    delete[]events_;
	    events_ = tmp;
      }

      events_[nevents_] = tgt;
      nevents_ += 1;

	// Remember to tell the NetEvent that there is someone
	// pointing to it.
      tgt->waitref_ += 1;

      struct NetEvent::wcell_*tmp = new NetEvent::wcell_;
      tmp->obj = this;
      tmp->next = tgt->wlist_;
      tgt->wlist_ = tmp;
}

void NetEvWait::replace_event(NetEvent*src, NetEvent*repl)
{
      unsigned idx;
      for (idx = 0 ;  idx < nevents_ ;  idx += 1) {
	    if (events_[idx] == src)
		  break;
      }

      assert(idx < nevents_);

	/* First, remove me from the list held by the src NetEvent. */
      assert(src->waitref_ > 0);
      src->waitref_ -= 1;
      struct NetEvent::wcell_*tmp = src->wlist_;
      if (tmp->obj == this) {
	    src->wlist_ = tmp->next;
	    delete tmp;
      } else {
	    assert(tmp->next);
	    while (tmp->next->obj != this) {
		  tmp = tmp->next;
		  assert(tmp->next);
	    }
	    tmp->next = tmp->next->next;
	    delete tmp;
      }

      events_[idx] = repl;

	// Remember to tell the replacement NetEvent that there is
	// someone pointing to it.
      repl->waitref_ += 1;

      tmp = new NetEvent::wcell_;
      tmp->obj = this;
      tmp->next = repl->wlist_;
      repl->wlist_ = tmp;

}

unsigned NetEvWait::nevents() const
{
      return nevents_;
}

const NetEvent* NetEvWait::event(unsigned idx) const
{
      assert(idx < nevents_);
      return events_[idx];
}

NetEvent* NetEvWait::event(unsigned idx)
{
      assert(idx < nevents_);
      return events_[idx];
}

NetProc* NetEvWait::statement()
{
      return statement_;
}

/*
 * $Log: net_event.cc,v $
 * Revision 1.10  2000/09/19 03:00:36  steve
 *  Typo stepping ot next probe in delete.
 *
 * Revision 1.9  2000/07/29 03:55:38  steve
 *  fix problem coalescing events w/ probes.
 *
 * Revision 1.8  2000/05/31 02:26:49  steve
 *  Globally merge redundant event objects.
 *
 * Revision 1.7  2000/05/27 19:33:23  steve
 *  Merge similar probes within a module.
 *
 * Revision 1.6  2000/04/18 04:50:20  steve
 *  Clean up unneeded NetEvent objects.
 *
 * Revision 1.5  2000/04/16 23:32:18  steve
 *  Synthesis of comparator in expressions.
 *
 *  Connect the NetEvent and related classes
 *  together better.
 *
 * Revision 1.4  2000/04/12 20:02:53  steve
 *  Finally remove the NetNEvent and NetPEvent classes,
 *  Get synthesis working with the NetEvWait class,
 *  and get started supporting multiple events in a
 *  wait in vvm.
 *
 * Revision 1.3  2000/04/12 04:23:58  steve
 *  Named events really should be expressed with PEIdent
 *  objects in the pform,
 *
 *  Handle named events within the mix of net events
 *  and edges. As a unified lot they get caught together.
 *  wait statements are broken into more complex statements
 *  that include a conditional.
 *
 *  Do not generate NetPEvent or NetNEvent objects in
 *  elaboration. NetEvent, NetEvWait and NetEvProbe
 *  take over those functions in the netlist.
 *
 * Revision 1.2  2000/04/10 05:26:06  steve
 *  All events now use the NetEvent class.
 *
 * Revision 1.1  2000/04/04 03:20:15  steve
 *  Simulate named event trigger and waits.
 *
 */

<|endoftext|>