// Generated by pmgen.py from techlibs/microchip/microchip_dsp_cascade.pmg

struct microchip_dsp_cascade_pm {
  Module *module;
  SigMap sigmap;
  std::function<void()> on_accept;
  bool setup_done;
  bool generate_mode;
  int accept_cnt;

  uint32_t rngseed;
  int rng(unsigned int n) {
    rngseed ^= rngseed << 13;
    rngseed ^= rngseed >> 17;
    rngseed ^= rngseed << 5;
    return rngseed % n;
  }

  typedef std::tuple<> index_2_key_type;
  typedef std::tuple<Cell*> index_2_value_type;
  dict<index_2_key_type, vector<index_2_value_type>> index_2;
  typedef std::tuple<> index_5_key_type;
  typedef std::tuple<Cell*> index_5_value_type;
  dict<index_5_key_type, vector<index_5_value_type>> index_5;
  dict<SigBit, pool<Cell*>> sigusers;
  pool<Cell*> blacklist_cells;
  pool<Cell*> autoremove_cells;
  dict<Cell*,int> rollback_cache;
  int rollback;

  struct state_microchip_dsp_cascade_t {
    SigSpec argD;
    SigSpec argQ;
    SigSpec clock;
    int ffoffset;
    Cell* first;
    Cell* next;
    Cell* nextP;
  } st_microchip_dsp_cascade;

  struct udata_microchip_dsp_cascade_t {
    vector<std::tuple<Cell*,int>> chain;
    Cell* dff;
    SigSpec dffD;
    SigSpec dffQ;
    SigBit dffclock;
    vector<std::tuple<Cell*,int>> longest_chain;
    std::function<SigSpec(const SigSpec&)> unextend;
    std::set<Cell*> visited;
  } ud_microchip_dsp_cascade;

  IdString id_b_ARSHFT17{"\\ARSHFT17"};
  IdString id_b_ARSHFT17_BYPASS{"\\ARSHFT17_BYPASS"};
  IdString id_b_C{"\\C"};
  IdString id_b_CDIN{"\\CDIN"};
  IdString id_b_CDIN_FDBK_SEL{"\\CDIN_FDBK_SEL"};
  IdString id_b_CDOUT{"\\CDOUT"};
  IdString id_b_C_BYPASS{"\\C_BYPASS"};
  IdString id_b_MACC_PA{"\\MACC_PA"};
  IdString id_b_P{"\\P"};
  IdString id_b_PCIN{"\\PCIN"};

  void add_siguser(const SigSpec &sig, Cell *cell) {
    for (auto bit : sigmap(sig)) {
      if (bit.wire == nullptr) continue;
      sigusers[bit].insert(cell);
    }
  }

  void blacklist(Cell *cell) {
    if (cell != nullptr && blacklist_cells.insert(cell).second) {
      auto ptr = rollback_cache.find(cell);
      if (ptr == rollback_cache.end()) return;
      int rb = ptr->second;
      if (rollback == 0 || rollback > rb)
        rollback = rb;
    }
  }

  void autoremove(Cell *cell) {
    if (cell != nullptr) {
      autoremove_cells.insert(cell);
      blacklist(cell);
    }
  }

  SigSpec port(Cell *cell, IdString portname) {
    try {
      return sigmap(cell->getPort(portname));
    } catch(std::out_of_range&) { log_error("Accessing non existing port %s\n",portname); }
  }

  SigSpec port(Cell *cell, IdString portname, const SigSpec& defval) {
    return sigmap(cell->connections_.at(portname, defval));
  }

  Const param(Cell *cell, IdString paramname) {
    try {
      return cell->getParam(paramname);
    } catch(std::out_of_range&) { log_error("Accessing non existing parameter %s\n",paramname); }
  }

  Const param(Cell *cell, IdString paramname, const Const& defval) {
    return cell->parameters.at(paramname, defval);
  }

  int nusers(const SigSpec &sig) {
    pool<Cell*> users;
    for (auto bit : sigmap(sig))
      for (auto user : sigusers[bit])
        users.insert(user);
    return GetSize(users);
  }

  microchip_dsp_cascade_pm(Module *module, const vector<Cell*> &cells) :
      module(module), sigmap(module), setup_done(false), generate_mode(false), rngseed(12345678) {
    setup(cells);
  }

  microchip_dsp_cascade_pm(Module *module) :
      module(module), sigmap(module), setup_done(false), generate_mode(false), rngseed(12345678) {
  }

  void setup(const vector<Cell*> &cells) {
    ud_microchip_dsp_cascade.chain = vector<std::tuple<Cell*,int>>();
    ud_microchip_dsp_cascade.dff = nullptr;
    ud_microchip_dsp_cascade.dffD = SigSpec();
    ud_microchip_dsp_cascade.dffQ = SigSpec();
    ud_microchip_dsp_cascade.dffclock = SigBit();
    ud_microchip_dsp_cascade.longest_chain = vector<std::tuple<Cell*,int>>();
    ud_microchip_dsp_cascade.unextend = std::function<SigSpec(const SigSpec&)>();
    ud_microchip_dsp_cascade.visited = std::set<Cell*>();
    log_assert(!setup_done);
    setup_done = true;
    for (auto port : module->ports)
      add_siguser(module->wire(port), nullptr);
    for (auto cell : module->cells())
      for (auto &conn : cell->connections())
        add_siguser(conn.second, cell);
    for (auto cell : cells) {
      do {
        Cell *first = cell;
        index_2_value_type value;
        std::get<0>(value) = cell;
        if (!(first->type.in(id_b_MACC_PA) && port(first, id_b_CDIN_FDBK_SEL, Const(0, 2)) == Const::from_string("00"))) continue;
        if (!(nusers(port(first, id_b_CDOUT, SigSpec())) <= 1)) continue;
        index_2_key_type key;
        index_2[key].push_back(value);
      } while (0);
      do {
        Cell *nextP = cell;
        index_5_value_type value;
        std::get<0>(value) = cell;
        if (!(port(nextP, id_b_C_BYPASS, SigSpec()).is_fully_ones())) continue;
        if (!(nextP->type.in(id_b_MACC_PA))) continue;
        if (!(nusers(port(nextP, id_b_C, SigSpec())) > 1)) continue;
        if (!(nusers(port(nextP, id_b_PCIN, SigSpec())) == 0)) continue;
        if (!(port(nextP, id_b_CDIN_FDBK_SEL, SigSpec()).is_fully_zero())) continue;
        if (!(port(nextP, id_b_ARSHFT17_BYPASS).is_fully_ones())) continue;
        if (!(port(nextP, id_b_ARSHFT17).is_fully_zero())) continue;
        if (!(nusers(port(nextP, id_b_ARSHFT17, SigSpec())) == 0)) continue;
        index_5_key_type key;
        index_5[key].push_back(value);
      } while (0);
    }
  }

  ~microchip_dsp_cascade_pm() {
    for (auto cell : autoremove_cells)
      module->remove(cell);
  }

  int run_microchip_dsp_cascade(std::function<void()> on_accept_f) {
    log_assert(setup_done);
    accept_cnt = 0;
    on_accept = on_accept_f;
    rollback = 0;
    st_microchip_dsp_cascade.argD = SigSpec();
    st_microchip_dsp_cascade.argQ = SigSpec();
    st_microchip_dsp_cascade.clock = SigSpec();
    st_microchip_dsp_cascade.ffoffset = int();
    st_microchip_dsp_cascade.first = nullptr;
    st_microchip_dsp_cascade.next = nullptr;
    st_microchip_dsp_cascade.nextP = nullptr;
    block_0(1);
    log_assert(rollback_cache.empty());
    return accept_cnt;
  }

  int run_microchip_dsp_cascade(std::function<void(microchip_dsp_cascade_pm&)> on_accept_f) {
    return run_microchip_dsp_cascade([&](){on_accept_f(*this);});
  }

  int run_microchip_dsp_cascade() {
    return run_microchip_dsp_cascade([](){});
  }

  void block_subpattern_microchip_dsp_cascade_(int recursion) { block_0(recursion); }
  void block_subpattern_microchip_dsp_cascade_tail(int recursion) { block_5(recursion); }

  // techlibs/microchip/microchip_dsp_cascade.pmg:51
  void block_0(int recursion YS_MAYBE_UNUSED) {
    vector<std::tuple<Cell*,int>> &chain YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.chain;
    Cell* &dff YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dffclock;
    vector<std::tuple<Cell*,int>> &longest_chain YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.longest_chain;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.unextend;
    std::set<Cell*> &visited YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.visited;

#define reject do { goto rollback_label; } while(0)
#define accept do { accept_cnt++; on_accept(); if (rollback) goto rollback_label; } while(0)
#define finish do { rollback = -1; goto rollback_label; } while(0)
#define branch do { block_1(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_microchip_dsp_cascade_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
  #define MAX_DSP_CASCADE 24

    block_1(recursion+1);
#undef reject
#undef accept
#undef finish
#undef branch
#undef subpattern

rollback_label:
    YS_MAYBE_UNUSED;
  }

  // techlibs/microchip/microchip_dsp_cascade.pmg:62
  void block_1(int recursion YS_MAYBE_UNUSED) {
    vector<std::tuple<Cell*,int>> &chain YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.chain;
    Cell* &dff YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dffclock;
    vector<std::tuple<Cell*,int>> &longest_chain YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.longest_chain;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.unextend;
    std::set<Cell*> &visited YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.visited;

#define reject do { goto rollback_label; } while(0)
#define accept do { accept_cnt++; on_accept(); if (rollback) goto rollback_label; } while(0)
#define finish do { rollback = -1; goto rollback_label; } while(0)
#define branch do { block_2(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_microchip_dsp_cascade_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    unextend = [](const SigSpec &sig) {
      int i;
      for (i = GetSize(sig)-1; i > 0; i--)
        if (sig[i] != sig[i-1])
          break;
      // Do not remove non-const sign bit
      if (sig[i].wire)
        ++i;
      return sig.extract(0, i);
    };

    block_2(recursion+1);
#undef reject
#undef accept
#undef finish
#undef branch
#undef subpattern

rollback_label:
    YS_MAYBE_UNUSED;
  }

  // techlibs/microchip/microchip_dsp_cascade.pmg:78
  void block_2(int recursion YS_MAYBE_UNUSED) {
    Cell* &first YS_MAYBE_UNUSED = st_microchip_dsp_cascade.first;
    vector<std::tuple<Cell*,int>> &chain YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.chain;
    Cell* &dff YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dffclock;
    vector<std::tuple<Cell*,int>> &longest_chain YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.longest_chain;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.unextend;
    std::set<Cell*> &visited YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.visited;
    Cell* _pmg_backup_first = first;

    index_2_key_type key;
    auto cells_ptr = index_2.find(key);

    if (cells_ptr != index_2.end()) {
      const vector<index_2_value_type> &cells = cells_ptr->second;
      for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {
        first = std::get<0>(cells[_pmg_idx]);
        if (blacklist_cells.count(first)) continue;
        auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));
        block_3(recursion+1);
        if (rollback_ptr.second)
          rollback_cache.erase(rollback_ptr.first);
        if (rollback) {
          if (rollback != recursion) {
            first = _pmg_backup_first;
            return;
          }
          rollback = 0;
        }
      }
    }

    first = nullptr;
    first = _pmg_backup_first;
  }

  // techlibs/microchip/microchip_dsp_cascade.pmg:87
  void block_3(int recursion YS_MAYBE_UNUSED) {
    Cell* const &first YS_MAYBE_UNUSED = st_microchip_dsp_cascade.first;
    vector<std::tuple<Cell*,int>> &chain YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.chain;
    Cell* &dff YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dffclock;
    vector<std::tuple<Cell*,int>> &longest_chain YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.longest_chain;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.unextend;
    std::set<Cell*> &visited YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.visited;

#define reject do { goto rollback_label; } while(0)
#define accept do { accept_cnt++; on_accept(); if (rollback) goto rollback_label; } while(0)
#define finish do { rollback = -1; goto rollback_label; } while(0)
#define branch do { block_4(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_microchip_dsp_cascade_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    visited.clear();
    visited.insert(first);
    longest_chain.clear();
    chain.emplace_back(first, -1);
    subpattern(tail);

    block_4(recursion+1);
#undef reject
#undef accept
#undef finish
#undef branch
#undef subpattern

rollback_label:
    YS_MAYBE_UNUSED;
#define accept do { accept_cnt++; on_accept(); } while(0)
#define finish do { rollback = -1; goto finish_label; } while(0)
    // longest cascade chain has been found with DSP "first" being the head of the chain
    // do some post processing
    chain.pop_back();
    visited.clear();
    log_assert(chain.empty());
    if (GetSize(longest_chain) > 1) {
      Cell *dsp = std::get<0>(longest_chain.front());
      Cell *dsp_pcin;
      int SHIFT = -1;
      for (int i = 1; i < GetSize(longest_chain); i++) {
        log_assert(dsp->type.in(id_b_MACC_PA));
        std::tie(dsp_pcin,SHIFT) = longest_chain[i];
        // Chain length exceeds the maximum cascade length, must split it up
        if (i % MAX_DSP_CASCADE > 0) {
          Wire *cascade = module->addWire(NEW_ID, 48);
          // zero port C and move wire to cascade
          dsp_pcin->setPort(id_b_C, Const(0, 48));
          dsp_pcin->setPort(id_b_CDIN, cascade);
          dsp->setPort(id_b_CDOUT, cascade);
          // Configure wire to cascade the dsps
          add_siguser(cascade, dsp_pcin);
          add_siguser(cascade, dsp);
          // configure mux to use cascade for signal E
          SigSpec cdin_fdbk_sel = port(dsp_pcin, id_b_CDIN_FDBK_SEL, Const(0, 2));
          cdin_fdbk_sel[1] = State::S1;
          dsp_pcin->setPort(id_b_CDIN_FDBK_SEL, cdin_fdbk_sel);
          // check if shifting is required for wide multiplier implmentation
          if (SHIFT == 17)
          {
            dsp_pcin->setPort(id_b_ARSHFT17, State::S1);
          }
          log_debug("PCOUT -> PCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
        } else {
          log_debug("  Blocking %s -> %s cascade (exceeds max: %d)\n", log_id(dsp), log_id(dsp_pcin), MAX_DSP_CASCADE);
        }
        dsp = dsp_pcin;
      }
      accept;
    }
finish_label:
    YS_MAYBE_UNUSED;
#undef accept
#undef finish
  }

  void block_4(int recursion YS_MAYBE_UNUSED) {
  }

  // techlibs/microchip/microchip_dsp_cascade.pmg:161
  void block_5(int recursion YS_MAYBE_UNUSED) {
    Cell* const &first YS_MAYBE_UNUSED = st_microchip_dsp_cascade.first;
    Cell* const &next YS_MAYBE_UNUSED = st_microchip_dsp_cascade.next;
    Cell* &nextP YS_MAYBE_UNUSED = st_microchip_dsp_cascade.nextP;
    vector<std::tuple<Cell*,int>> &chain YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.chain;
    Cell* &dff YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dffclock;
    vector<std::tuple<Cell*,int>> &longest_chain YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.longest_chain;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.unextend;
    std::set<Cell*> &visited YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.visited;
    Cell* _pmg_backup_nextP = nextP;

    index_5_key_type key;
    auto cells_ptr = index_5.find(key);

    if (cells_ptr != index_5.end()) {
      const vector<index_5_value_type> &cells = cells_ptr->second;
      for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {
        nextP = std::get<0>(cells[_pmg_idx]);
        if (blacklist_cells.count(nextP)) continue;
        if (!(port(nextP, id_b_C)[0] == port(std::get<0>(chain.back()), id_b_P)[0] || port(nextP, id_b_C)[0] == port(std::get<0>(chain.back()), id_b_P)[17])) continue;
        auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));
        block_6(recursion+1);
        if (rollback_ptr.second)
          rollback_cache.erase(rollback_ptr.first);
        if (rollback) {
          if (rollback != recursion) {
            nextP = _pmg_backup_nextP;
            return;
          }
          rollback = 0;
        }
      }
    }

    nextP = nullptr;
    block_6(recursion+1);
    nextP = _pmg_backup_nextP;
  }

  // techlibs/microchip/microchip_dsp_cascade.pmg:194
  void block_6(int recursion YS_MAYBE_UNUSED) {
    Cell* const &first YS_MAYBE_UNUSED = st_microchip_dsp_cascade.first;
    Cell* const &nextP YS_MAYBE_UNUSED = st_microchip_dsp_cascade.nextP;
    Cell* &next YS_MAYBE_UNUSED = st_microchip_dsp_cascade.next;
    vector<std::tuple<Cell*,int>> &chain YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.chain;
    Cell* &dff YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dffclock;
    vector<std::tuple<Cell*,int>> &longest_chain YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.longest_chain;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.unextend;
    std::set<Cell*> &visited YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.visited;

    Cell* _pmg_backup_next = next;

#define reject do { goto rollback_label; } while(0)
#define accept do { accept_cnt++; on_accept(); if (rollback) goto rollback_label; } while(0)
#define finish do { rollback = -1; goto rollback_label; } while(0)
#define branch do { block_7(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_microchip_dsp_cascade_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    next = nextP;
    // keep DSP type consistent in the chain
    // currently since we only have one type anyways, this line is always false
    if (next && next->type != first->type) reject;
    // break infinite recursion when there's a combinational loop
    if (visited.count(next) > 0) reject;

    block_7(recursion+1);
#undef reject
#undef accept
#undef finish
#undef branch
#undef subpattern

rollback_label:
    YS_MAYBE_UNUSED;

    next = _pmg_backup_next;
  }

  // techlibs/microchip/microchip_dsp_cascade.pmg:208
  void block_7(int recursion YS_MAYBE_UNUSED) {
    Cell* const &first YS_MAYBE_UNUSED = st_microchip_dsp_cascade.first;
    Cell* const &next YS_MAYBE_UNUSED = st_microchip_dsp_cascade.next;
    Cell* const &nextP YS_MAYBE_UNUSED = st_microchip_dsp_cascade.nextP;
    vector<std::tuple<Cell*,int>> &chain YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.chain;
    Cell* &dff YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.dffclock;
    vector<std::tuple<Cell*,int>> &longest_chain YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.longest_chain;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.unextend;
    std::set<Cell*> &visited YS_MAYBE_UNUSED = ud_microchip_dsp_cascade.visited;

#define reject do { goto rollback_label; } while(0)
#define accept do { accept_cnt++; on_accept(); if (rollback) goto rollback_label; } while(0)
#define finish do { rollback = -1; goto rollback_label; } while(0)
#define branch do { block_8(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_microchip_dsp_cascade_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    if (next) {
      SigSpec driver_sigP = port(std::get<0>(chain.back()), id_b_P);
      int shift = 0;
      if (port(next, id_b_C)[0] == port(std::get<0>(chain.back()), id_b_P)[17]) shift = 17;
      chain.emplace_back(next, shift);
      visited.insert(next);
      SigSpec sigC = unextend(port(next, id_b_C));
      // Make sure driverDSP.P === DSP.C
      if (GetSize(sigC) + shift <= GetSize(driver_sigP) && driver_sigP.extract(shift, GetSize(sigC)) == sigC)
      {
        subpattern(tail);
      }
    } else {
      if (GetSize(chain) > GetSize(longest_chain))
        longest_chain = chain;
    }

    block_8(recursion+1);
#undef reject
#undef accept
#undef finish
#undef branch
#undef subpattern

rollback_label:
    YS_MAYBE_UNUSED;
#define accept do { accept_cnt++; on_accept(); } while(0)
#define finish do { rollback = -1; goto finish_label; } while(0)
    if (next)
    {
      visited.erase(next);
      chain.pop_back();
    }
finish_label:
    YS_MAYBE_UNUSED;
#undef accept
#undef finish
  }

  void block_8(int recursion YS_MAYBE_UNUSED) {
  }
};
