// Generated by pmgen.py from techlibs/xilinx/xilinx_srl.pmg

struct xilinx_srl_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_1_key_type;
  typedef std::tuple<Cell*> index_1_value_type;
  dict<index_1_key_type, vector<index_1_value_type>> index_1;
  typedef std::tuple<> index_4_key_type;
  typedef std::tuple<Cell*> index_4_value_type;
  dict<index_4_key_type, vector<index_4_value_type>> index_4;
  typedef std::tuple<IdString, SigBit> index_6_key_type;
  typedef std::tuple<Cell*> index_6_value_type;
  dict<index_6_key_type, vector<index_6_value_type>> index_6;
  typedef std::tuple<IdString, SigBit> index_9_key_type;
  typedef std::tuple<Cell*> index_9_value_type;
  dict<index_9_key_type, vector<index_9_value_type>> index_9;
  typedef std::tuple<> index_13_key_type;
  typedef std::tuple<Cell*> index_13_value_type;
  dict<index_13_key_type, vector<index_13_value_type>> index_13;
  typedef std::tuple<SigBit> index_15_key_type;
  typedef std::tuple<Cell*, int> index_15_value_type;
  dict<index_15_key_type, vector<index_15_value_type>> index_15;
  typedef std::tuple<IdString, SigBit, SigBit> index_18_key_type;
  typedef std::tuple<Cell*, int> index_18_value_type;
  dict<index_18_key_type, vector<index_18_value_type>> index_18;
  dict<SigBit, pool<Cell*>> sigusers;
  pool<Cell*> blacklist_cells;
  pool<Cell*> autoremove_cells;
  dict<Cell*,int> rollback_cache;
  int rollback;

  struct state_fixed_t {
    IdString clk_port;
    IdString en_port;
    Cell* first;
    Cell* next;
  } st_fixed;

  struct udata_fixed_t {
    vector<Cell*> chain;
    vector<Cell*> longest_chain;
    int minlen;
    pool<Cell*> non_first_cells;
  } ud_fixed;

  struct state_variable_t {
    IdString clk_port;
    IdString en_port;
    Cell* first;
    Cell* next;
    Cell* shiftx;
    int shiftx_width;
    int slice;
  } st_variable;

  struct udata_variable_t {
    vector<pair<Cell*,int>> chain;
    pool<SigBit> chain_bits;
    int minlen;
  } ud_variable;

  IdString id_b_A{"\\A"};
  IdString id_b_A_WIDTH{"\\A_WIDTH"};
  IdString id_b_C{"\\C"};
  IdString id_b_CE{"\\CE"};
  IdString id_b_CLK{"\\CLK"};
  IdString id_b_CLK_POLARITY{"\\CLK_POLARITY"};
  IdString id_b_D{"\\D"};
  IdString id_b_E{"\\E"};
  IdString id_b_EN{"\\EN"};
  IdString id_b_EN_POLARITY{"\\EN_POLARITY"};
  IdString id_b_FDRE{"\\FDRE"};
  IdString id_b_FDRE_1{"\\FDRE_1"};
  IdString id_b_IS_C_INVERTED{"\\IS_C_INVERTED"};
  IdString id_b_IS_D_INVERTED{"\\IS_D_INVERTED"};
  IdString id_b_IS_R_INVERTED{"\\IS_R_INVERTED"};
  IdString id_b_Q{"\\Q"};
  IdString id_b_R{"\\R"};
  IdString id_b_Y_WIDTH{"\\Y_WIDTH"};
  IdString id_b_keep{"\\keep"};
  IdString id_d__DFFE_NN_{"$_DFFE_NN_"};
  IdString id_d__DFFE_NP_{"$_DFFE_NP_"};
  IdString id_d__DFFE_PN_{"$_DFFE_PN_"};
  IdString id_d__DFFE_PP_{"$_DFFE_PP_"};
  IdString id_d__DFF_N_{"$_DFF_N_"};
  IdString id_d__DFF_P_{"$_DFF_P_"};
  IdString id_d_dff{"$dff"};
  IdString id_d_dffe{"$dffe"};
  IdString id_d_shiftx{"$shiftx"};

  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);
  }

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

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

  void setup(const vector<Cell*> &cells) {
    ud_fixed.chain = vector<Cell*>();
    ud_fixed.longest_chain = vector<Cell*>();
    ud_fixed.minlen = int();
    ud_fixed.non_first_cells = pool<Cell*>();
    ud_variable.chain = vector<pair<Cell*,int>>();
    ud_variable.chain_bits = pool<SigBit>();
    ud_variable.minlen = int();
    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_1_value_type value;
        std::get<0>(value) = cell;
        if (!(first->type.in(id_d__DFF_N_, id_d__DFF_P_, id_d__DFFE_NN_, id_d__DFFE_NP_, id_d__DFFE_PN_, id_d__DFFE_PP_, id_b_FDRE, id_b_FDRE_1))) continue;
        if (!(!first->has_keep_attr())) continue;
        if (!(!first->type.in(id_b_FDRE) || !param(first, id_b_IS_R_INVERTED).as_bool())) continue;
        if (!(!first->type.in(id_b_FDRE) || !param(first, id_b_IS_D_INVERTED).as_bool())) continue;
        if (!(!first->type.in(id_b_FDRE, id_b_FDRE_1) || port(first, id_b_R, State::S0).is_fully_zero())) continue;
        index_1_key_type key;
        index_1[key].push_back(value);
      } while (0);
      do {
        Cell *first = cell;
        index_4_value_type value;
        std::get<0>(value) = cell;
        if (!(first->type.in(id_d__DFF_N_, id_d__DFF_P_, id_d__DFFE_NN_, id_d__DFFE_NP_, id_d__DFFE_PN_, id_d__DFFE_PP_, id_b_FDRE, id_b_FDRE_1))) continue;
        if (!(!first->has_keep_attr())) continue;
        if (!(!first->type.in(id_b_FDRE) || !param(first, id_b_IS_R_INVERTED).as_bool())) continue;
        if (!(!first->type.in(id_b_FDRE) || !param(first, id_b_IS_D_INVERTED).as_bool())) continue;
        if (!(!first->type.in(id_b_FDRE, id_b_FDRE_1) || port(first, id_b_R, State::S0).is_fully_zero())) continue;
        index_4_key_type key;
        index_4[key].push_back(value);
      } while (0);
      do {
        Cell *next = cell;
        index_6_value_type value;
        std::get<0>(value) = cell;
        if (!(next->type.in(id_d__DFF_N_, id_d__DFF_P_, id_d__DFFE_NN_, id_d__DFFE_NP_, id_d__DFFE_PN_, id_d__DFFE_PP_, id_b_FDRE, id_b_FDRE_1))) continue;
        if (!(!next->has_keep_attr())) continue;
        if (!(port(next, id_b_D)[0].wire && !port(next, id_b_D)[0].wire->get_bool_attribute(id_b_keep))) continue;
        if (!(nusers(port(next, id_b_Q)) == 2)) continue;
        index_6_key_type key;
        std::get<0>(key) = next->type;
        std::get<1>(key) = port(next, id_b_Q);
        index_6[key].push_back(value);
      } while (0);
      do {
        Cell *next = cell;
        index_9_value_type value;
        std::get<0>(value) = cell;
        if (!(next->type.in(id_d__DFF_N_, id_d__DFF_P_, id_d__DFFE_NN_, id_d__DFFE_NP_, id_d__DFFE_PN_, id_d__DFFE_PP_, id_b_FDRE, id_b_FDRE_1))) continue;
        if (!(!next->has_keep_attr())) continue;
        if (!(port(next, id_b_D)[0].wire && !port(next, id_b_D)[0].wire->get_bool_attribute(id_b_keep))) continue;
        if (!(nusers(port(next, id_b_Q)) == 2)) continue;
        index_9_key_type key;
        std::get<0>(key) = next->type;
        std::get<1>(key) = port(next, id_b_Q);
        index_9[key].push_back(value);
      } while (0);
      do {
        Cell *shiftx = cell;
        index_13_value_type value;
        std::get<0>(value) = cell;
        if (!(shiftx->type.in(id_d_shiftx))) continue;
        if (!(!shiftx->has_keep_attr())) continue;
        if (!(param(shiftx, id_b_Y_WIDTH).as_int() == 1)) continue;
        index_13_key_type key;
        index_13[key].push_back(value);
      } while (0);
      do {
        Cell *first = cell;
        index_15_value_type value;
        std::get<0>(value) = cell;
        if (!(first->type.in(id_d__DFF_N_, id_d__DFF_P_, id_d__DFFE_NN_, id_d__DFFE_NP_, id_d__DFFE_PN_, id_d__DFFE_PP_, id_d_dff, id_d_dffe))) continue;
        if (!(!first->has_keep_attr())) continue;
        if (!(port(first, id_b_Q)[0].wire && !port(first, id_b_Q)[0].wire->get_bool_attribute(id_b_keep))) continue;
        int &idx = std::get<1>(value);
        for (idx = 0; idx < GetSize(port(first, id_b_Q)); idx++) {
        if (!(nusers(port(first, id_b_Q)[idx]) <= 2)) continue;
        index_15_key_type key;
        std::get<0>(key) = port(first, id_b_Q)[idx];
        index_15[key].push_back(value);
        }
      } while (0);
      do {
        Cell *next = cell;
        index_18_value_type value;
        std::get<0>(value) = cell;
        if (!(next->type.in(id_d__DFF_N_, id_d__DFF_P_, id_d__DFFE_NN_, id_d__DFFE_NP_, id_d__DFFE_PN_, id_d__DFFE_PP_, id_d_dff, id_d_dffe))) continue;
        if (!(!next->has_keep_attr())) continue;
        if (!(port(next, id_b_D)[0].wire && !port(next, id_b_D)[0].wire->get_bool_attribute(id_b_keep))) continue;
        int &idx = std::get<1>(value);
        for (idx = 0; idx < GetSize(port(next, id_b_Q)); idx++) {
        if (!(nusers(port(next, id_b_Q)[idx]) <= 3)) continue;
        index_18_key_type key;
        std::get<0>(key) = next->type;
        std::get<1>(key) = port(next, id_b_Q)[idx];
        std::get<2>(key) = port(next, id_b_Q)[idx];
        index_18[key].push_back(value);
        }
      } while (0);
    }
  }

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

  int run_fixed(std::function<void()> on_accept_f) {
    log_assert(setup_done);
    accept_cnt = 0;
    on_accept = on_accept_f;
    rollback = 0;
    st_fixed.clk_port = IdString();
    st_fixed.en_port = IdString();
    st_fixed.first = nullptr;
    st_fixed.next = nullptr;
    block_0(1);
    log_assert(rollback_cache.empty());
    return accept_cnt;
  }

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

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

  int run_variable(std::function<void()> on_accept_f) {
    log_assert(setup_done);
    accept_cnt = 0;
    on_accept = on_accept_f;
    rollback = 0;
    st_variable.clk_port = IdString();
    st_variable.en_port = IdString();
    st_variable.first = nullptr;
    st_variable.next = nullptr;
    st_variable.shiftx = nullptr;
    st_variable.shiftx_width = int();
    st_variable.slice = int();
    block_12(1);
    log_assert(rollback_cache.empty());
    return accept_cnt;
  }

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

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

  void block_subpattern_fixed_(int recursion) { block_0(recursion); }
  void block_subpattern_fixed_setup(int recursion) { block_4(recursion); }
  void block_subpattern_fixed_tail(int recursion) { block_9(recursion); }
  void block_subpattern_variable_(int recursion) { block_12(recursion); }
  void block_subpattern_variable_tail(int recursion) { block_18(recursion); }

  // techlibs/xilinx/xilinx_srl.pmg:8
  void block_0(int recursion YS_MAYBE_UNUSED) {
    vector<Cell*> &chain YS_MAYBE_UNUSED = ud_fixed.chain;
    vector<Cell*> &longest_chain YS_MAYBE_UNUSED = ud_fixed.longest_chain;
    int &minlen YS_MAYBE_UNUSED = ud_fixed.minlen;
    pool<Cell*> &non_first_cells YS_MAYBE_UNUSED = ud_fixed.non_first_cells;

#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_fixed_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    non_first_cells.clear();
    subpattern(setup);

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

rollback_label:
    YS_MAYBE_UNUSED;
  }

  // techlibs/xilinx/xilinx_srl.pmg:13
  void block_1(int recursion YS_MAYBE_UNUSED) {
    Cell* &first YS_MAYBE_UNUSED = st_fixed.first;
    vector<Cell*> &chain YS_MAYBE_UNUSED = ud_fixed.chain;
    vector<Cell*> &longest_chain YS_MAYBE_UNUSED = ud_fixed.longest_chain;
    int &minlen YS_MAYBE_UNUSED = ud_fixed.minlen;
    pool<Cell*> &non_first_cells YS_MAYBE_UNUSED = ud_fixed.non_first_cells;
    Cell* _pmg_backup_first = first;

    index_1_key_type key;
    auto cells_ptr = index_1.find(key);
    bool found_any_match = false;

    if (cells_ptr != index_1.end()) {
      const vector<index_1_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;
        if (!(!non_first_cells.count(first))) continue;
        found_any_match = true;
        auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));
        block_2(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;
#define finish do { rollback = -1; return; } while(0)
    if (generate_mode && rng(100) < (found_any_match ? 0 : 100)) {
        SigSpec C = module->addWire(NEW_ID);
        SigSpec D = module->addWire(NEW_ID);
        SigSpec Q = module->addWire(NEW_ID);
        auto r = rng(8);
        Cell* cell;
        switch (r)
        {
        case 0:
        case 1:
          cell = module->addCell(NEW_ID, id_b_FDRE);
          cell->setPort(id_b_C, C);
          cell->setPort(id_b_D, D);
          cell->setPort(id_b_Q, Q);
          cell->setPort(id_b_CE, module->addWire(NEW_ID));
          if (r & 1)
            cell->setPort(id_b_R, module->addWire(NEW_ID));
          else {
            if (rng(2) == 0)
              cell->setPort(id_b_R, State::S0);
          }
          break;
        case 2:
        case 3:
          cell = module->addDffGate(NEW_ID, C, D, Q, r & 1);
          break;
        case 4:
        case 5:
        case 6:
        case 7:
          cell = module->addDffeGate(NEW_ID, C, module->addWire(NEW_ID), D, Q, r & 1, r & 2);
          break;
        default: log_abort();
        }
    }
#undef finish
  }

  // techlibs/xilinx/xilinx_srl.pmg:56
  void block_2(int recursion YS_MAYBE_UNUSED) {
    Cell* const &first YS_MAYBE_UNUSED = st_fixed.first;
    IdString &clk_port YS_MAYBE_UNUSED = st_fixed.clk_port;
    IdString &en_port YS_MAYBE_UNUSED = st_fixed.en_port;
    vector<Cell*> &chain YS_MAYBE_UNUSED = ud_fixed.chain;
    vector<Cell*> &longest_chain YS_MAYBE_UNUSED = ud_fixed.longest_chain;
    int &minlen YS_MAYBE_UNUSED = ud_fixed.minlen;
    pool<Cell*> &non_first_cells YS_MAYBE_UNUSED = ud_fixed.non_first_cells;

#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_3(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_fixed_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    if (first->type.in(id_d__DFF_N_, id_d__DFF_P_, id_d__DFFE_NN_, id_d__DFFE_NP_, id_d__DFFE_PN_, id_d__DFFE_PP_, id_b_FDRE, id_b_FDRE_1))
      clk_port = id_b_C;
    else log_abort();
    if (first->type.in(id_d__DFF_N_, id_d__DFF_P_))
      en_port = IdString();
    else if (first->type.in(id_d__DFFE_NN_, id_d__DFFE_NP_, id_d__DFFE_PN_, id_d__DFFE_PP_))
      en_port = id_b_E;
    else if (first->type.in(id_b_FDRE, id_b_FDRE_1))
      en_port = id_b_CE;
    else log_abort();
    longest_chain.clear();
    chain.push_back(first);
    subpattern(tail);

    block_3(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)
    chain.pop_back();
    log_assert(chain.empty());
    if (GetSize(longest_chain) >= minlen)
      accept;
finish_label:
    YS_MAYBE_UNUSED;
#undef accept
#undef finish

    clk_port = IdString();
    en_port = IdString();
  }

  void block_3(int recursion YS_MAYBE_UNUSED) {
  }

  // techlibs/xilinx/xilinx_srl.pmg:84
  void block_4(int recursion YS_MAYBE_UNUSED) {
    const IdString &clk_port YS_MAYBE_UNUSED = st_fixed.clk_port;
    const IdString &en_port YS_MAYBE_UNUSED = st_fixed.en_port;
    Cell* &first YS_MAYBE_UNUSED = st_fixed.first;
    vector<Cell*> &chain YS_MAYBE_UNUSED = ud_fixed.chain;
    vector<Cell*> &longest_chain YS_MAYBE_UNUSED = ud_fixed.longest_chain;
    int &minlen YS_MAYBE_UNUSED = ud_fixed.minlen;
    pool<Cell*> &non_first_cells YS_MAYBE_UNUSED = ud_fixed.non_first_cells;
    Cell* _pmg_backup_first = first;

    index_4_key_type key;
    auto cells_ptr = index_4.find(key);

    if (cells_ptr != index_4.end()) {
      const vector<index_4_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_5(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/xilinx/xilinx_srl.pmg:92
  void block_5(int recursion YS_MAYBE_UNUSED) {
    Cell* const &first YS_MAYBE_UNUSED = st_fixed.first;
    IdString &clk_port YS_MAYBE_UNUSED = st_fixed.clk_port;
    IdString &en_port YS_MAYBE_UNUSED = st_fixed.en_port;
    vector<Cell*> &chain YS_MAYBE_UNUSED = ud_fixed.chain;
    vector<Cell*> &longest_chain YS_MAYBE_UNUSED = ud_fixed.longest_chain;
    int &minlen YS_MAYBE_UNUSED = ud_fixed.minlen;
    pool<Cell*> &non_first_cells YS_MAYBE_UNUSED = ud_fixed.non_first_cells;

    IdString _pmg_backup_clk_port = clk_port;
    IdString _pmg_backup_en_port = en_port;

#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_6(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_fixed_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    if (first->type.in(id_d__DFF_N_, id_d__DFF_P_, id_d__DFFE_NN_, id_d__DFFE_NP_, id_d__DFFE_PN_, id_d__DFFE_PP_, id_b_FDRE, id_b_FDRE_1))
      clk_port = id_b_C;
    else log_abort();
    if (first->type.in(id_d__DFF_N_, id_d__DFF_P_))
      en_port = IdString();
    else if (first->type.in(id_d__DFFE_NN_, id_d__DFFE_NP_, id_d__DFFE_PN_, id_d__DFFE_PP_))
      en_port = id_b_E;
    else if (first->type.in(id_b_FDRE, id_b_FDRE_1))
      en_port = id_b_CE;
    else log_abort();

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

rollback_label:
    YS_MAYBE_UNUSED;

    clk_port = _pmg_backup_clk_port;
    en_port = _pmg_backup_en_port;
  }

  // techlibs/xilinx/xilinx_srl.pmg:105
  void block_6(int recursion YS_MAYBE_UNUSED) {
    const IdString &clk_port YS_MAYBE_UNUSED = st_fixed.clk_port;
    const IdString &en_port YS_MAYBE_UNUSED = st_fixed.en_port;
    Cell* const &first YS_MAYBE_UNUSED = st_fixed.first;
    Cell* &next YS_MAYBE_UNUSED = st_fixed.next;
    vector<Cell*> &chain YS_MAYBE_UNUSED = ud_fixed.chain;
    vector<Cell*> &longest_chain YS_MAYBE_UNUSED = ud_fixed.longest_chain;
    int &minlen YS_MAYBE_UNUSED = ud_fixed.minlen;
    pool<Cell*> &non_first_cells YS_MAYBE_UNUSED = ud_fixed.non_first_cells;
    Cell* _pmg_backup_next = next;

    index_6_key_type key;
    std::get<0>(key) = first->type;
    std::get<1>(key) = port(first, id_b_D);
    auto cells_ptr = index_6.find(key);

    if (cells_ptr != index_6.end()) {
      const vector<index_6_value_type> &cells = cells_ptr->second;
      for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {
        next = std::get<0>(cells[_pmg_idx]);
        if (blacklist_cells.count(next)) continue;
        if (!(port(next, clk_port) == port(first, clk_port))) continue;
        if (!(en_port == IdString() || port(next, en_port) == port(first, en_port))) continue;
        if (!(!first->type.in(id_b_FDRE) || param(next, id_b_IS_C_INVERTED).as_bool() == param(first, id_b_IS_C_INVERTED).as_bool())) continue;
        if (!(!first->type.in(id_b_FDRE) || param(next, id_b_IS_D_INVERTED).as_bool() == param(first, id_b_IS_D_INVERTED).as_bool())) continue;
        if (!(!first->type.in(id_b_FDRE) || param(next, id_b_IS_R_INVERTED).as_bool() == param(first, id_b_IS_R_INVERTED).as_bool())) continue;
        if (!(!first->type.in(id_b_FDRE, id_b_FDRE_1) || port(next, id_b_R, State::S0).is_fully_zero())) continue;
        auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));
        block_7(recursion+1);
        if (rollback_ptr.second)
          rollback_cache.erase(rollback_ptr.first);
        if (rollback) {
          if (rollback != recursion) {
            next = _pmg_backup_next;
            return;
          }
          rollback = 0;
        }
      }
    }

    next = nullptr;
    next = _pmg_backup_next;
  }

  // techlibs/xilinx/xilinx_srl.pmg:120
  void block_7(int recursion YS_MAYBE_UNUSED) {
    const IdString &clk_port YS_MAYBE_UNUSED = st_fixed.clk_port;
    const IdString &en_port YS_MAYBE_UNUSED = st_fixed.en_port;
    Cell* const &first YS_MAYBE_UNUSED = st_fixed.first;
    Cell* const &next YS_MAYBE_UNUSED = st_fixed.next;
    vector<Cell*> &chain YS_MAYBE_UNUSED = ud_fixed.chain;
    vector<Cell*> &longest_chain YS_MAYBE_UNUSED = ud_fixed.longest_chain;
    int &minlen YS_MAYBE_UNUSED = ud_fixed.minlen;
    pool<Cell*> &non_first_cells YS_MAYBE_UNUSED = ud_fixed.non_first_cells;

#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_fixed_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    non_first_cells.insert(next);

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

rollback_label:
    YS_MAYBE_UNUSED;
  }

  void block_8(int recursion YS_MAYBE_UNUSED) {
  }

  // techlibs/xilinx/xilinx_srl.pmg:131
  void block_9(int recursion YS_MAYBE_UNUSED) {
    const IdString &clk_port YS_MAYBE_UNUSED = st_fixed.clk_port;
    const IdString &en_port YS_MAYBE_UNUSED = st_fixed.en_port;
    Cell* const &first YS_MAYBE_UNUSED = st_fixed.first;
    Cell* &next YS_MAYBE_UNUSED = st_fixed.next;
    vector<Cell*> &chain YS_MAYBE_UNUSED = ud_fixed.chain;
    vector<Cell*> &longest_chain YS_MAYBE_UNUSED = ud_fixed.longest_chain;
    int &minlen YS_MAYBE_UNUSED = ud_fixed.minlen;
    pool<Cell*> &non_first_cells YS_MAYBE_UNUSED = ud_fixed.non_first_cells;
    Cell* _pmg_backup_next = next;

    index_9_key_type key;
    std::get<0>(key) = chain.back()->type;
    std::get<1>(key) = port(chain.back(), id_b_D);
    auto cells_ptr = index_9.find(key);
    bool found_any_match = false;

    if (cells_ptr != index_9.end()) {
      const vector<index_9_value_type> &cells = cells_ptr->second;
      for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {
        next = std::get<0>(cells[_pmg_idx]);
        if (blacklist_cells.count(next)) continue;
        if (!(port(next, clk_port) == port(first, clk_port))) continue;
        if (!(en_port == IdString() || port(next, en_port) == port(first, en_port))) continue;
        if (!(!first->type.in(id_b_FDRE) || param(next, id_b_IS_C_INVERTED).as_bool() == param(first, id_b_IS_C_INVERTED).as_bool())) continue;
        if (!(!first->type.in(id_b_FDRE) || param(next, id_b_IS_D_INVERTED).as_bool() == param(first, id_b_IS_D_INVERTED).as_bool())) continue;
        if (!(!first->type.in(id_b_FDRE) || param(next, id_b_IS_R_INVERTED).as_bool() == param(first, id_b_IS_R_INVERTED).as_bool())) continue;
        if (!(!first->type.in(id_b_FDRE, id_b_FDRE_1) || port(next, id_b_R, State::S0).is_fully_zero())) continue;
        found_any_match = true;
        auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));
        block_10(recursion+1);
        if (rollback_ptr.second)
          rollback_cache.erase(rollback_ptr.first);
        if (rollback) {
          if (rollback != recursion) {
            next = _pmg_backup_next;
            return;
          }
          rollback = 0;
        }
      }
    }

    next = nullptr;
    if (!found_any_match) block_10(recursion+1);
    next = _pmg_backup_next;
#define finish do { rollback = -1; return; } while(0)
    if (generate_mode && rng(100) < (found_any_match ? 0 : 100)) {
        Cell *cell = module->addCell(NEW_ID, chain.back()->type);
        cell->setPort(id_b_C, chain.back()->getPort(id_b_C));
        cell->setPort(id_b_D, module->addWire(NEW_ID));
        cell->setPort(id_b_Q, chain.back()->getPort(id_b_D));
        if (cell->type == id_b_FDRE) {
          if (rng(2) == 0)
            cell->setPort(id_b_R, port(chain.back(), id_b_R, State::S0));
          cell->setPort(id_b_CE, chain.back()->getPort(id_b_CE));
        }
        else if (cell->type.begins_with("$_DFFE_"))
          cell->setPort(id_b_E, chain.back()->getPort(id_b_E));
    }
#undef finish
  }

  // techlibs/xilinx/xilinx_srl.pmg:159
  void block_10(int recursion YS_MAYBE_UNUSED) {
    const IdString &clk_port YS_MAYBE_UNUSED = st_fixed.clk_port;
    const IdString &en_port YS_MAYBE_UNUSED = st_fixed.en_port;
    Cell* const &first YS_MAYBE_UNUSED = st_fixed.first;
    Cell* const &next YS_MAYBE_UNUSED = st_fixed.next;
    vector<Cell*> &chain YS_MAYBE_UNUSED = ud_fixed.chain;
    vector<Cell*> &longest_chain YS_MAYBE_UNUSED = ud_fixed.longest_chain;
    int &minlen YS_MAYBE_UNUSED = ud_fixed.minlen;
    pool<Cell*> &non_first_cells YS_MAYBE_UNUSED = ud_fixed.non_first_cells;

#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_11(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_fixed_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    if (next) {
      chain.push_back(next);
      subpattern(tail);
    } else {
      if (GetSize(chain) > GetSize(longest_chain))
        longest_chain = chain;
    }

    block_11(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)
      chain.pop_back();
finish_label:
    YS_MAYBE_UNUSED;
#undef accept
#undef finish
  }

  void block_11(int recursion YS_MAYBE_UNUSED) {
  }

  // techlibs/xilinx/xilinx_srl.pmg:183
  void block_12(int recursion YS_MAYBE_UNUSED) {
    vector<pair<Cell*,int>> &chain YS_MAYBE_UNUSED = ud_variable.chain;
    pool<SigBit> &chain_bits YS_MAYBE_UNUSED = ud_variable.chain_bits;
    int &minlen YS_MAYBE_UNUSED = ud_variable.minlen;

#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_13(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_variable_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    chain_bits.clear();

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

rollback_label:
    YS_MAYBE_UNUSED;
  }

  // techlibs/xilinx/xilinx_srl.pmg:187
  void block_13(int recursion YS_MAYBE_UNUSED) {
    Cell* &shiftx YS_MAYBE_UNUSED = st_variable.shiftx;
    vector<pair<Cell*,int>> &chain YS_MAYBE_UNUSED = ud_variable.chain;
    pool<SigBit> &chain_bits YS_MAYBE_UNUSED = ud_variable.chain_bits;
    int &minlen YS_MAYBE_UNUSED = ud_variable.minlen;
    Cell* _pmg_backup_shiftx = shiftx;

    index_13_key_type key;
    auto cells_ptr = index_13.find(key);
    bool found_any_match = false;

    if (cells_ptr != index_13.end()) {
      const vector<index_13_value_type> &cells = cells_ptr->second;
      for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {
        shiftx = std::get<0>(cells[_pmg_idx]);
        if (blacklist_cells.count(shiftx)) continue;
        if (!(param(shiftx, id_b_A_WIDTH).as_int() >= minlen)) continue;
        found_any_match = true;
        auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));
        block_14(recursion+1);
        if (rollback_ptr.second)
          rollback_cache.erase(rollback_ptr.first);
        if (rollback) {
          if (rollback != recursion) {
            shiftx = _pmg_backup_shiftx;
            return;
          }
          rollback = 0;
        }
      }
    }

    shiftx = nullptr;
    shiftx = _pmg_backup_shiftx;
#define finish do { rollback = -1; return; } while(0)
    if (generate_mode && rng(100) < (found_any_match ? 0 : 100)) {
        minlen = 3;
        module->addShiftx(NEW_ID, module->addWire(NEW_ID, rng(6)+minlen), module->addWire(NEW_ID, 3), module->addWire(NEW_ID));
    }
#undef finish
  }

  // techlibs/xilinx/xilinx_srl.pmg:197
  void block_14(int recursion YS_MAYBE_UNUSED) {
    Cell* const &shiftx YS_MAYBE_UNUSED = st_variable.shiftx;
    int &shiftx_width YS_MAYBE_UNUSED = st_variable.shiftx_width;
    vector<pair<Cell*,int>> &chain YS_MAYBE_UNUSED = ud_variable.chain;
    pool<SigBit> &chain_bits YS_MAYBE_UNUSED = ud_variable.chain_bits;
    int &minlen YS_MAYBE_UNUSED = ud_variable.minlen;

#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_15(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_variable_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    shiftx_width = param(shiftx, id_b_A_WIDTH).as_int();

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

rollback_label:
    YS_MAYBE_UNUSED;

    shiftx_width = int();
  }

  // techlibs/xilinx/xilinx_srl.pmg:201
  void block_15(int recursion YS_MAYBE_UNUSED) {
    Cell* const &shiftx YS_MAYBE_UNUSED = st_variable.shiftx;
    const int &shiftx_width YS_MAYBE_UNUSED = st_variable.shiftx_width;
    Cell* &first YS_MAYBE_UNUSED = st_variable.first;
    int &slice YS_MAYBE_UNUSED = st_variable.slice;
    vector<pair<Cell*,int>> &chain YS_MAYBE_UNUSED = ud_variable.chain;
    pool<SigBit> &chain_bits YS_MAYBE_UNUSED = ud_variable.chain_bits;
    int &minlen YS_MAYBE_UNUSED = ud_variable.minlen;
    Cell* _pmg_backup_first = first;

    index_15_key_type key;
    std::get<0>(key) = port(shiftx, id_b_A)[shiftx_width-1];
    auto cells_ptr = index_15.find(key);
    bool found_any_match = false;

    if (cells_ptr != index_15.end()) {
      const vector<index_15_value_type> &cells = cells_ptr->second;
      for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {
        first = std::get<0>(cells[_pmg_idx]);
        const int &idx YS_MAYBE_UNUSED = std::get<1>(cells[_pmg_idx]);
        if (blacklist_cells.count(first)) continue;
        found_any_match = true;
        auto _pmg_backup_slice = slice;
        slice = idx;
        auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));
        block_16(recursion+1);
        slice = _pmg_backup_slice;
        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;
#define finish do { rollback = -1; return; } while(0)
    if (generate_mode && rng(100) < (found_any_match ? 0 : 100)) {
        SigSpec C = module->addWire(NEW_ID);
        auto WIDTH = rng(3)+1;
        SigSpec D = module->addWire(NEW_ID, WIDTH);
        SigSpec Q = module->addWire(NEW_ID, WIDTH);
        auto r = rng(8);
        Cell *cell = nullptr;
        switch (r)
        {
        case 0:
        case 1:
          cell = module->addDff(NEW_ID, C, D, Q, r & 1);
          break;
        case 2:
        case 3:
        case 4:
        case 5:
          //cell = module->addDffe(NEW_ID, C, module->addWire(NEW_ID), D, Q, r & 1, r & 4);
          //break;
        case 6:
        case 7:
          WIDTH = 1;
          cell = module->addDffGate(NEW_ID, C, D[0], Q[0], r & 1);
          break;
        default: log_abort();
        }
        shiftx->connections_.at(id_b_A)[shiftx_width-1] = port(cell, id_b_Q)[rng(WIDTH)];
    }
#undef finish
  }

  // techlibs/xilinx/xilinx_srl.pmg:238
  void block_16(int recursion YS_MAYBE_UNUSED) {
    Cell* const &first YS_MAYBE_UNUSED = st_variable.first;
    Cell* const &shiftx YS_MAYBE_UNUSED = st_variable.shiftx;
    const int &shiftx_width YS_MAYBE_UNUSED = st_variable.shiftx_width;
    const int &slice YS_MAYBE_UNUSED = st_variable.slice;
    IdString &clk_port YS_MAYBE_UNUSED = st_variable.clk_port;
    IdString &en_port YS_MAYBE_UNUSED = st_variable.en_port;
    vector<pair<Cell*,int>> &chain YS_MAYBE_UNUSED = ud_variable.chain;
    pool<SigBit> &chain_bits YS_MAYBE_UNUSED = ud_variable.chain_bits;
    int &minlen YS_MAYBE_UNUSED = ud_variable.minlen;

#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_17(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_variable_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    if (first->type.in(id_d__DFF_N_, id_d__DFF_P_, id_d__DFFE_NN_, id_d__DFFE_NP_, id_d__DFFE_PN_, id_d__DFFE_PP_))
      clk_port = id_b_C;
    else if (first->type.in(id_d_dff, id_d_dffe))
      clk_port = id_b_CLK;
    else log_abort();
    if (first->type.in(id_d__DFF_N_, id_d__DFF_P_, id_d_dff))
      en_port = IdString();
    else if (first->type.in(id_d__DFFE_NN_, id_d__DFFE_NP_, id_d__DFFE_PN_, id_d__DFFE_PP_))
      en_port = id_b_E;
    else if (first->type.in(id_d_dffe))
      en_port = id_b_EN;
    else log_abort();
    chain_bits.insert(port(first, id_b_Q)[slice]);
    chain.emplace_back(first, slice);
    subpattern(tail);

    block_17(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 (GetSize(chain) == shiftx_width)
      accept;
    chain.clear();
finish_label:
    YS_MAYBE_UNUSED;
#undef accept
#undef finish

    clk_port = IdString();
    en_port = IdString();
  }

  void block_17(int recursion YS_MAYBE_UNUSED) {
  }

  // techlibs/xilinx/xilinx_srl.pmg:271
  void block_18(int recursion YS_MAYBE_UNUSED) {
    const IdString &clk_port YS_MAYBE_UNUSED = st_variable.clk_port;
    const IdString &en_port YS_MAYBE_UNUSED = st_variable.en_port;
    Cell* const &first YS_MAYBE_UNUSED = st_variable.first;
    Cell* const &shiftx YS_MAYBE_UNUSED = st_variable.shiftx;
    const int &shiftx_width YS_MAYBE_UNUSED = st_variable.shiftx_width;
    Cell* &next YS_MAYBE_UNUSED = st_variable.next;
    int &slice YS_MAYBE_UNUSED = st_variable.slice;
    vector<pair<Cell*,int>> &chain YS_MAYBE_UNUSED = ud_variable.chain;
    pool<SigBit> &chain_bits YS_MAYBE_UNUSED = ud_variable.chain_bits;
    int &minlen YS_MAYBE_UNUSED = ud_variable.minlen;
    Cell* _pmg_backup_next = next;

    index_18_key_type key;
    std::get<0>(key) = chain.back().first->type;
    std::get<1>(key) = port(chain.back().first, id_b_D)[chain.back().second];
    std::get<2>(key) = port(shiftx, id_b_A)[shiftx_width-1-GetSize(chain)];
    auto cells_ptr = index_18.find(key);
    bool found_any_match = false;

    if (cells_ptr != index_18.end()) {
      const vector<index_18_value_type> &cells = cells_ptr->second;
      for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {
        next = std::get<0>(cells[_pmg_idx]);
        const int &idx YS_MAYBE_UNUSED = std::get<1>(cells[_pmg_idx]);
        if (blacklist_cells.count(next)) continue;
        if (!(port(next, clk_port) == port(first, clk_port))) continue;
        if (!(en_port == IdString() || port(next, en_port) == port(first, en_port))) continue;
        if (!(!next->type.in(id_d_dff, id_d_dffe) || param(next, id_b_CLK_POLARITY).as_bool() == param(first, id_b_CLK_POLARITY).as_bool())) continue;
        if (!(!next->type.in(id_d_dffe) || param(next, id_b_EN_POLARITY).as_bool() == param(first, id_b_EN_POLARITY).as_bool())) continue;
        if (!(!chain_bits.count(port(next, id_b_D)[idx]))) continue;
        found_any_match = true;
        auto _pmg_backup_slice = slice;
        slice = idx;
        auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));
        block_19(recursion+1);
        slice = _pmg_backup_slice;
        if (rollback_ptr.second)
          rollback_cache.erase(rollback_ptr.first);
        if (rollback) {
          if (rollback != recursion) {
            next = _pmg_backup_next;
            return;
          }
          rollback = 0;
        }
      }
    }

    next = nullptr;
    if (!found_any_match) block_19(recursion+1);
    next = _pmg_backup_next;
#define finish do { rollback = -1; return; } while(0)
    if (generate_mode && rng(100) < (found_any_match ? 0 : 100)) {
        if (GetSize(chain) < shiftx_width) {
          auto back = chain.back().first;
          auto slice = chain.back().second;
          if (back->type.in(id_d_dff, id_d_dffe)) {
            auto WIDTH = GetSize(port(back, id_b_D));
            if (rng(2) == 0 && slice < WIDTH-1) {
              auto new_slice = slice + rng(WIDTH-1-slice);
              back->connections_.at(id_b_D)[slice] = port(back, id_b_Q)[new_slice];
            }
            else {
              auto D = module->addWire(NEW_ID, WIDTH);
              if (back->type == id_d_dff)
                module->addDff(NEW_ID, port(back, id_b_CLK), D, port(back, id_b_D), param(back, id_b_CLK_POLARITY).as_bool());
              else if (back->type == id_d_dffe)
                module->addDffe(NEW_ID, port(back, id_b_CLK), port(back, id_b_EN), D, port(back, id_b_D), param(back, id_b_CLK_POLARITY).as_bool(), param(back, id_b_EN_POLARITY).as_bool());
              else
                log_abort();
            }
          }
          else if (back->type.begins_with("$_DFF_")) {
            Cell *cell = module->addCell(NEW_ID, back->type);
            cell->setPort(id_b_C, back->getPort(id_b_C));
            cell->setPort(id_b_D, module->addWire(NEW_ID));
            cell->setPort(id_b_Q, back->getPort(id_b_D));
          }
          else
            log_abort();
          shiftx->connections_.at(id_b_A)[shiftx_width-1-GetSize(chain)] = port(back, id_b_D)[slice];
        }
    }
#undef finish
  }

  // techlibs/xilinx/xilinx_srl.pmg:319
  void block_19(int recursion YS_MAYBE_UNUSED) {
    const IdString &clk_port YS_MAYBE_UNUSED = st_variable.clk_port;
    const IdString &en_port YS_MAYBE_UNUSED = st_variable.en_port;
    Cell* const &first YS_MAYBE_UNUSED = st_variable.first;
    Cell* const &next YS_MAYBE_UNUSED = st_variable.next;
    Cell* const &shiftx YS_MAYBE_UNUSED = st_variable.shiftx;
    const int &shiftx_width YS_MAYBE_UNUSED = st_variable.shiftx_width;
    const int &slice YS_MAYBE_UNUSED = st_variable.slice;
    vector<pair<Cell*,int>> &chain YS_MAYBE_UNUSED = ud_variable.chain;
    pool<SigBit> &chain_bits YS_MAYBE_UNUSED = ud_variable.chain_bits;
    int &minlen YS_MAYBE_UNUSED = ud_variable.minlen;

#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_20(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_variable_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    if (next) {
      chain_bits.insert(port(next, id_b_Q)[slice]);
      chain.emplace_back(next, slice);
      if (GetSize(chain) < shiftx_width)
        subpattern(tail);
    }

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

rollback_label:
    YS_MAYBE_UNUSED;
  }

  void block_20(int recursion YS_MAYBE_UNUSED) {
  }
};
