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

struct xilinx_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_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<SigBit> 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<SigBit> index_5_key_type;
  typedef std::tuple<Cell*> index_5_value_type;
  dict<index_5_key_type, vector<index_5_value_type>> index_5;
  typedef std::tuple<SigBit> index_12_key_type;
  typedef std::tuple<Cell*, int> index_12_value_type;
  dict<index_12_key_type, vector<index_12_value_type>> index_12;
  dict<SigBit, pool<Cell*>> sigusers;
  pool<Cell*> blacklist_cells;
  pool<Cell*> autoremove_cells;
  dict<Cell*,int> rollback_cache;
  int rollback;

  struct state_xilinx_dsp_cascade_t {
    int AREG;
    int BREG;
    SigSpec argD;
    SigSpec argQ;
    SigBit clock;
    Cell* ff;
    int ffoffset;
    Cell* first;
    Cell* next;
    Cell* nextP;
    Cell* nextP_shift17;
  } st_xilinx_dsp_cascade;

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

  IdString id_b_A{"\\A"};
  IdString id_b_ACASCREG{"\\ACASCREG"};
  IdString id_b_ACIN{"\\ACIN"};
  IdString id_b_ACOUT{"\\ACOUT"};
  IdString id_b_AREG{"\\AREG"};
  IdString id_b_A_INPUT{"\\A_INPUT"};
  IdString id_b_B{"\\B"};
  IdString id_b_B0REG{"\\B0REG"};
  IdString id_b_B1REG{"\\B1REG"};
  IdString id_b_BCASCREG{"\\BCASCREG"};
  IdString id_b_BCIN{"\\BCIN"};
  IdString id_b_BCOUT{"\\BCOUT"};
  IdString id_b_BREG{"\\BREG"};
  IdString id_b_B_INPUT{"\\B_INPUT"};
  IdString id_b_C{"\\C"};
  IdString id_b_CEA1{"\\CEA1"};
  IdString id_b_CEA2{"\\CEA2"};
  IdString id_b_CEB{"\\CEB"};
  IdString id_b_CEB1{"\\CEB1"};
  IdString id_b_CEB2{"\\CEB2"};
  IdString id_b_CLK{"\\CLK"};
  IdString id_b_CLK_POLARITY{"\\CLK_POLARITY"};
  IdString id_b_CREG{"\\CREG"};
  IdString id_b_D{"\\D"};
  IdString id_b_DSP48A{"\\DSP48A"};
  IdString id_b_DSP48A1{"\\DSP48A1"};
  IdString id_b_DSP48E1{"\\DSP48E1"};
  IdString id_b_EN{"\\EN"};
  IdString id_b_EN_POLARITY{"\\EN_POLARITY"};
  IdString id_b_OPMODE{"\\OPMODE"};
  IdString id_b_P{"\\P"};
  IdString id_b_PCIN{"\\PCIN"};
  IdString id_b_PCOUT{"\\PCOUT"};
  IdString id_b_Q{"\\Q"};
  IdString id_b_RSTA{"\\RSTA"};
  IdString id_b_RSTB{"\\RSTB"};
  IdString id_b_SRST{"\\SRST"};
  IdString id_b_SRST_POLARITY{"\\SRST_POLARITY"};
  IdString id_b_SRST_VALUE{"\\SRST_VALUE"};
  IdString id_b_init{"\\init"};
  IdString id_b_keep{"\\keep"};
  IdString id_d_dff{"$dff"};
  IdString id_d_dffe{"$dffe"};
  IdString id_d_sdff{"$sdff"};
  IdString id_d_sdffe{"$sdffe"};

  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_dsp_cascade_pm(Module *module, const vector<Cell*> &cells) :
      module(module), sigmap(module), setup_done(false), generate_mode(false), rngseed(12345678) {
    setup(cells);
  }

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

  void setup(const vector<Cell*> &cells) {
    ud_xilinx_dsp_cascade.chain = vector<std::tuple<Cell*,int,int,int>>();
    ud_xilinx_dsp_cascade.dff = nullptr;
    ud_xilinx_dsp_cascade.dffD = SigSpec();
    ud_xilinx_dsp_cascade.dffQ = SigSpec();
    ud_xilinx_dsp_cascade.dffclock = SigBit();
    ud_xilinx_dsp_cascade.longest_chain = vector<std::tuple<Cell*,int,int,int>>();
    ud_xilinx_dsp_cascade.unextend = std::function<SigSpec(const SigSpec&)>();
    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_b_DSP48A, id_b_DSP48A1) && port(first, id_b_OPMODE, Const(0, 8)).extract(2,2) == Const::from_string("00")) || (first->type.in(id_b_DSP48E1) && port(first, id_b_OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("000")))) continue;
        if (!(nusers(port(first, id_b_PCOUT, SigSpec())) <= 1)) continue;
        index_1_key_type key;
        index_1[key].push_back(value);
      } while (0);
      do {
        Cell *nextP = cell;
        index_4_value_type value;
        std::get<0>(value) = cell;
        if (!(!nextP->type.in(id_b_DSP48E1) || !param(nextP, id_b_CREG).as_bool())) continue;
        if (!((nextP->type.in(id_b_DSP48A, id_b_DSP48A1) && port(nextP, id_b_OPMODE, Const(0, 8)).extract(2,2) == Const::from_string("11")) || (nextP->type.in(id_b_DSP48E1) && port(nextP, id_b_OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011")))) continue;
        if (!(nusers(port(nextP, id_b_C, SigSpec())) > 1)) continue;
        if (!(nusers(port(nextP, id_b_PCIN, SigSpec())) == 0)) continue;
        index_4_key_type key;
        std::get<0>(key) = port(nextP, id_b_C)[0];
        index_4[key].push_back(value);
      } while (0);
      do {
        Cell *nextP_shift17 = cell;
        index_5_value_type value;
        std::get<0>(value) = cell;
        if (!(nextP_shift17->type.in(id_b_DSP48E1))) continue;
        if (!(!param(nextP_shift17, id_b_CREG).as_bool())) continue;
        if (!(port(nextP_shift17, id_b_OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011"))) continue;
        if (!(nusers(port(nextP_shift17, id_b_C, SigSpec())) > 1)) continue;
        if (!(nusers(port(nextP_shift17, id_b_PCIN, SigSpec())) == 0)) continue;
        index_5_key_type key;
        std::get<0>(key) = port(nextP_shift17, id_b_C)[0];
        index_5[key].push_back(value);
      } while (0);
      do {
        Cell *ff = cell;
        index_12_value_type value;
        std::get<0>(value) = cell;
        if (!(ff->type.in(id_d_dff, id_d_dffe, id_d_sdff, id_d_sdffe))) continue;
        if (!(param(ff, id_b_CLK_POLARITY).as_bool())) continue;
        int &offset = std::get<1>(value);
        for (offset = 0; offset < GetSize(port(ff, id_b_D)); offset++) {
        index_12_key_type key;
        std::get<0>(key) = port(ff, id_b_Q)[offset];
        index_12[key].push_back(value);
        }
      } while (0);
    }
  }

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

  int run_xilinx_dsp_cascade(std::function<void()> on_accept_f) {
    log_assert(setup_done);
    accept_cnt = 0;
    on_accept = on_accept_f;
    rollback = 0;
    st_xilinx_dsp_cascade.AREG = int();
    st_xilinx_dsp_cascade.BREG = int();
    st_xilinx_dsp_cascade.argD = SigSpec();
    st_xilinx_dsp_cascade.argQ = SigSpec();
    st_xilinx_dsp_cascade.clock = SigBit();
    st_xilinx_dsp_cascade.ff = nullptr;
    st_xilinx_dsp_cascade.ffoffset = int();
    st_xilinx_dsp_cascade.first = nullptr;
    st_xilinx_dsp_cascade.next = nullptr;
    st_xilinx_dsp_cascade.nextP = nullptr;
    st_xilinx_dsp_cascade.nextP_shift17 = nullptr;
    block_0(1);
    log_assert(rollback_cache.empty());
    return accept_cnt;
  }

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

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

  void block_subpattern_xilinx_dsp_cascade_(int recursion) { block_0(recursion); }
  void block_subpattern_xilinx_dsp_cascade_in_dffe(int recursion) { block_11(recursion); }
  void block_subpattern_xilinx_dsp_cascade_tail(int recursion) { block_4(recursion); }

  // techlibs/xilinx/xilinx_dsp_cascade.pmg:59
  void block_0(int recursion YS_MAYBE_UNUSED) {
    vector<std::tuple<Cell*,int,int,int>> &chain YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.chain;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffclock;
    vector<std::tuple<Cell*,int,int,int>> &longest_chain YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.longest_chain;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.unextend;

#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_xilinx_dsp_cascade_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
  #define MAX_DSP_CASCADE 20

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

rollback_label:
    YS_MAYBE_UNUSED;
  }

  // techlibs/xilinx/xilinx_dsp_cascade.pmg:66
  void block_1(int recursion YS_MAYBE_UNUSED) {
    Cell* &first YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.first;
    vector<std::tuple<Cell*,int,int,int>> &chain YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.chain;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffclock;
    vector<std::tuple<Cell*,int,int,int>> &longest_chain YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.longest_chain;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.unextend;
    Cell* _pmg_backup_first = first;

    index_1_key_type key;
    auto cells_ptr = index_1.find(key);

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

  // techlibs/xilinx/xilinx_dsp_cascade.pmg:75
  void block_2(int recursion YS_MAYBE_UNUSED) {
    Cell* const &first YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.first;
    vector<std::tuple<Cell*,int,int,int>> &chain YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.chain;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffclock;
    vector<std::tuple<Cell*,int,int,int>> &longest_chain YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.longest_chain;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.unextend;

#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_xilinx_dsp_cascade_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    longest_chain.clear();
    chain.emplace_back(first, -1, -1, -1);
    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) > 1) {
      Cell *dsp = std::get<0>(longest_chain.front());
      Cell *dsp_pcin;
      int P, AREG, BREG;
      for (int i = 1; i < GetSize(longest_chain); i++) {
        std::tie(dsp_pcin,P,AREG,BREG) = longest_chain[i];
        if (i % MAX_DSP_CASCADE > 0) {
          if (P >= 0) {
            Wire *cascade = module->addWire(NEW_ID, 48);
            dsp_pcin->setPort(id_b_C, Const(0, 48));
            dsp_pcin->setPort(id_b_PCIN, cascade);
            dsp->setPort(id_b_PCOUT, cascade);
            add_siguser(cascade, dsp_pcin);
            add_siguser(cascade, dsp);
            SigSpec opmode = port(dsp_pcin, id_b_OPMODE, Const(0, 7));
            if (dsp->type.in(id_b_DSP48A, id_b_DSP48A1)) {
              log_assert(P == 0);
              opmode[3] = State::S0;
              opmode[2] = State::S1;
            }
            else if (dsp->type.in(id_b_DSP48E1)) {
              if (P == 17)
                opmode[6] = State::S1;
              else if (P == 0)
                opmode[6] = State::S0;
              else log_abort();
              opmode[5] = State::S0;
              opmode[4] = State::S1;
            }
            dsp_pcin->setPort(id_b_OPMODE, opmode);
            log_debug("PCOUT -> PCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
          }
          if (AREG >= 0) {
            Wire *cascade = module->addWire(NEW_ID, 30);
            dsp_pcin->setPort(id_b_A, Const(0, 30));
            dsp_pcin->setPort(id_b_ACIN, cascade);
            dsp->setPort(id_b_ACOUT, cascade);
            add_siguser(cascade, dsp_pcin);
            add_siguser(cascade, dsp);
            if (dsp->type.in(id_b_DSP48E1))
              dsp->setParam(id_b_ACASCREG, AREG);
            dsp_pcin->setParam(id_b_A_INPUT, Const("CASCADE"));
            log_debug("ACOUT -> ACIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
          }
          if (BREG >= 0) {
            Wire *cascade = module->addWire(NEW_ID, 18);
            if (dsp->type.in(id_b_DSP48A, id_b_DSP48A1)) {
              // According to UG389 p9 [https://www.xilinx.com/support/documentation/user_guides/ug389.pdf]
              // "The DSP48A1 component uses this input when cascading
              //   BCOUT from an adjacent DSP48A1 slice. The tools then
              //   translate BCOUT cascading to the dedicated BCIN input
              //   and set the B_INPUT attribute for implementation."
              dsp_pcin->setPort(id_b_B, cascade);
            }
            else {
              dsp_pcin->setPort(id_b_B, Const(0, 18));
              dsp_pcin->setPort(id_b_BCIN, cascade);
            }
            dsp->setPort(id_b_BCOUT, cascade);
            add_siguser(cascade, dsp_pcin);
            add_siguser(cascade, dsp);
            if (dsp->type.in(id_b_DSP48E1)) {
              dsp->setParam(id_b_BCASCREG, BREG);
              // According to UG389 p13 [https://www.xilinx.com/support/documentation/user_guides/ug389.pdf]
              // "The attribute is only used by place and route tools and
              //   is not necessary for the users to set for synthesis. The
              //   attribute is determined by the connection to the B port
              //   of the DSP48A1 slice. If the B port is connected to the
              //   BCOUT of another DSP48A1 slice, then the tools automatically
              //   set the attribute to 'CASCADE', otherwise it is set to
              //   'DIRECT'".
              dsp_pcin->setParam(id_b_B_INPUT, Const("CASCADE"));
            }
            log_debug("BCOUT -> BCIN 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_3(int recursion YS_MAYBE_UNUSED) {
  }

  // techlibs/xilinx/xilinx_dsp_cascade.pmg:188
  void block_4(int recursion YS_MAYBE_UNUSED) {
    Cell* const &first YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.first;
    Cell* const &next YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.next;
    Cell* &nextP YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.nextP;
    vector<std::tuple<Cell*,int,int,int>> &chain YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.chain;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffclock;
    vector<std::tuple<Cell*,int,int,int>> &longest_chain YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.longest_chain;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.unextend;
    Cell* _pmg_backup_nextP = nextP;

    index_4_key_type key;
    std::get<0>(key) = port(std::get<0>(chain.back()), id_b_P)[0];
    auto cells_ptr = index_4.find(key);
    bool found_any_match = false;

    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++) {
        nextP = std::get<0>(cells[_pmg_idx]);
        if (blacklist_cells.count(nextP)) continue;
        found_any_match = true;
        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) {
            nextP = _pmg_backup_nextP;
            return;
          }
          rollback = 0;
        }
      }
    }

    nextP = nullptr;
    if (!found_any_match) block_5(recursion+1);
    nextP = _pmg_backup_nextP;
  }

  // techlibs/xilinx/xilinx_dsp_cascade.pmg:199
  void block_5(int recursion YS_MAYBE_UNUSED) {
    Cell* const &first YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.first;
    Cell* const &next YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.next;
    Cell* const &nextP YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.nextP;
    Cell* &nextP_shift17 YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.nextP_shift17;
    vector<std::tuple<Cell*,int,int,int>> &chain YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.chain;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffclock;
    vector<std::tuple<Cell*,int,int,int>> &longest_chain YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.longest_chain;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.unextend;
    Cell* _pmg_backup_nextP_shift17 = nextP_shift17;

    if (!(!nextP)) {
      nextP_shift17 = nullptr;
      block_6(recursion+1);
      nextP_shift17 = _pmg_backup_nextP_shift17;
      return;
    }

    index_5_key_type key;
    std::get<0>(key) = port(std::get<0>(chain.back()), id_b_P)[17];
    auto cells_ptr = index_5.find(key);
    bool found_any_match = false;

    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_shift17 = std::get<0>(cells[_pmg_idx]);
        if (blacklist_cells.count(nextP_shift17)) continue;
        found_any_match = true;
        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_shift17 = _pmg_backup_nextP_shift17;
            return;
          }
          rollback = 0;
        }
      }
    }

    nextP_shift17 = nullptr;
    if (!found_any_match) block_6(recursion+1);
    nextP_shift17 = _pmg_backup_nextP_shift17;
  }

  // techlibs/xilinx/xilinx_dsp_cascade.pmg:210
  void block_6(int recursion YS_MAYBE_UNUSED) {
    Cell* const &first YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.first;
    Cell* const &nextP YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.nextP;
    Cell* const &nextP_shift17 YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.nextP_shift17;
    Cell* &next YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.next;
    vector<std::tuple<Cell*,int,int,int>> &chain YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.chain;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffclock;
    vector<std::tuple<Cell*,int,int,int>> &longest_chain YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.longest_chain;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.unextend;

    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_xilinx_dsp_cascade_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    next = nextP;
    if (!nextP)
      next = nextP_shift17;
    if (next) {
      if (next->type != first->type)
        reject;
      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_7(recursion+1);
#undef reject
#undef accept
#undef finish
#undef branch
#undef subpattern

rollback_label:
    YS_MAYBE_UNUSED;

    next = _pmg_backup_next;
  }

  // techlibs/xilinx/xilinx_dsp_cascade.pmg:238
  void block_7(int recursion YS_MAYBE_UNUSED) {
    Cell* const &first YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.first;
    Cell* const &next YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.next;
    Cell* const &nextP YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.nextP;
    Cell* const &nextP_shift17 YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.nextP_shift17;
    int &AREG YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.AREG;
    SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.argQ;
    SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.clock;
    vector<std::tuple<Cell*,int,int,int>> &chain YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.chain;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffclock;
    vector<std::tuple<Cell*,int,int,int>> &longest_chain YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.longest_chain;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.unextend;

#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_xilinx_dsp_cascade_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    AREG = -1;
    if (next && next->type.in(id_b_DSP48E1)) {
      Cell *prev = std::get<0>(chain.back());
      if (param(next, id_b_A_INPUT).decode_string() == "DIRECT" &&
          port(next, id_b_ACIN, SigSpec()).is_fully_zero() &&
          nusers(port(prev, id_b_ACOUT, SigSpec())) <= 1) {
        if (param(prev, id_b_AREG) == 0) {
          if (port(prev, id_b_A) == port(next, id_b_A))
            AREG = 0;
        }
        else {
          argQ = unextend(port(next, id_b_A));
          clock = port(prev, id_b_CLK);
          subpattern(in_dffe);
          if (dff) {
            if (!dff->type.in(id_d_sdff, id_d_sdffe) && port(prev, id_b_RSTA, State::S0) != State::S0)
              goto reject_AREG;
            if (dff->type.in(id_d_sdff, id_d_sdffe) && (port(dff, id_b_SRST) != port(prev, id_b_RSTA, State::S0) || !param(dff, id_b_SRST_POLARITY).as_bool()))
              goto reject_AREG;
            IdString CEA;
            if (param(prev, id_b_AREG) == 1)
              CEA = id_b_CEA2;
            else if (param(prev, id_b_AREG) == 2)
              CEA = id_b_CEA1;
            else log_abort();
            if (!dff->type.in(id_d_dffe, id_d_sdffe) && port(prev, CEA, State::S0) != State::S1)
              goto reject_AREG;
            if (dff->type.in(id_d_dffe, id_d_sdffe) && (port(dff, id_b_EN) != port(prev, CEA, State::S0) || !param(dff, id_b_EN_POLARITY).as_bool()))
              goto reject_AREG;
            if (dffD == unextend(port(prev, id_b_A)))
              AREG = 1;
          }
        }
      }
  reject_AREG:  ;
    }

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

rollback_label:
    YS_MAYBE_UNUSED;

    AREG = int();
    argQ = SigSpec();
    clock = SigBit();
  }

  // techlibs/xilinx/xilinx_dsp_cascade.pmg:279
  void block_8(int recursion YS_MAYBE_UNUSED) {
    const int &AREG YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.AREG;
    Cell* const &first YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.first;
    Cell* const &next YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.next;
    Cell* const &nextP YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.nextP;
    Cell* const &nextP_shift17 YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.nextP_shift17;
    int &BREG YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.BREG;
    SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.argQ;
    SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.clock;
    vector<std::tuple<Cell*,int,int,int>> &chain YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.chain;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffclock;
    vector<std::tuple<Cell*,int,int,int>> &longest_chain YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.longest_chain;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.unextend;

    SigSpec _pmg_backup_argQ = argQ;
    SigBit _pmg_backup_clock = clock;

#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_9(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_xilinx_dsp_cascade_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    BREG = -1;
    if (next) {
      Cell *prev = std::get<0>(chain.back());
      if ((next->type != id_b_DSP48E1 || param(next, id_b_B_INPUT).decode_string() == "DIRECT") &&
          port(next, id_b_BCIN, SigSpec()).is_fully_zero() &&
          nusers(port(prev, id_b_BCOUT, SigSpec())) <= 1) {
        if ((next->type.in(id_b_DSP48A, id_b_DSP48A1) && param(prev, id_b_B0REG) == 0 && param(prev, id_b_B1REG) == 0) ||
          (next->type.in(id_b_DSP48E1) && param(prev, id_b_BREG) == 0)) {
          if (port(prev, id_b_B) == port(next, id_b_B))
            BREG = 0;
        }
        else {
          argQ = unextend(port(next, id_b_B));
          clock = port(prev, id_b_CLK);
          subpattern(in_dffe);
          if (dff) {
            if (!dff->type.in(id_d_sdff, id_d_sdffe) && port(prev, id_b_RSTB, State::S0) != State::S0)
              goto reject_BREG;
            if (dff->type.in(id_d_sdff, id_d_sdffe) && (port(dff, id_b_SRST) != port(prev, id_b_RSTB, State::S0) || !param(dff, id_b_SRST_POLARITY).as_bool()))
              goto reject_BREG;
            IdString CEB;
            if (next->type.in(id_b_DSP48A, id_b_DSP48A1))
              CEB = id_b_CEB;
            else if (next->type.in(id_b_DSP48E1)) {
              if (param(prev, id_b_BREG) == 1)
                CEB = id_b_CEB2;
              else if (param(prev, id_b_BREG) == 2)
                CEB = id_b_CEB1;
              else log_abort();
            }
            else log_abort();
            if (!dff->type.in(id_d_dffe, id_d_sdffe) && port(prev, CEB, State::S0) != State::S1)
              goto reject_BREG;
            if (dff->type.in(id_d_dffe, id_d_sdffe) && (port(dff, id_b_EN) != port(prev, CEB, State::S0) || !param(dff, id_b_EN_POLARITY).as_bool()))
              goto reject_BREG;
            if (dffD == unextend(port(prev, id_b_B))) {
              if (next->type.in(id_b_DSP48A, id_b_DSP48A1) && param(prev, id_b_B0REG) != 0)
                goto reject_BREG;
              BREG = 1;
            }
          }
        }
      }
  reject_BREG:  ;
    }

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

rollback_label:
    YS_MAYBE_UNUSED;

    argQ = _pmg_backup_argQ;
    clock = _pmg_backup_clock;
    BREG = int();
  }

  // techlibs/xilinx/xilinx_dsp_cascade.pmg:329
  void block_9(int recursion YS_MAYBE_UNUSED) {
    const int &AREG YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.AREG;
    const int &BREG YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.BREG;
    const SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.argQ;
    const SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.clock;
    Cell* const &first YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.first;
    Cell* const &next YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.next;
    Cell* const &nextP YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.nextP;
    Cell* const &nextP_shift17 YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.nextP_shift17;
    vector<std::tuple<Cell*,int,int,int>> &chain YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.chain;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffclock;
    vector<std::tuple<Cell*,int,int,int>> &longest_chain YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.longest_chain;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.unextend;

#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_10(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_xilinx_dsp_cascade_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    if (next) {
      chain.emplace_back(next, nextP_shift17 ? 17 : nextP ? 0 : -1, AREG, BREG);
      SigSpec sigC = unextend(port(next, id_b_C));
      if (nextP_shift17) {
        if (GetSize(sigC)+17 <= GetSize(port(std::get<0>(chain.back()), id_b_P)) &&
            port(std::get<0>(chain.back()), id_b_P).extract(17, GetSize(sigC)) != sigC)
          subpattern(tail);
      }
      else {
        if (GetSize(sigC) <= GetSize(port(std::get<0>(chain.back()), id_b_P)) &&
            port(std::get<0>(chain.back()), id_b_P).extract(0, GetSize(sigC)) != sigC)
          subpattern(tail);
      }
    } else {
      if (GetSize(chain) > GetSize(longest_chain))
        longest_chain = chain;
    }

    block_10(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_10(int recursion YS_MAYBE_UNUSED) {
  }

  // techlibs/xilinx/xilinx_dsp_cascade.pmg:362
  void block_11(int recursion YS_MAYBE_UNUSED) {
    const SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.argQ;
    const SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.clock;
    vector<std::tuple<Cell*,int,int,int>> &chain YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.chain;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffclock;
    vector<std::tuple<Cell*,int,int,int>> &longest_chain YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.longest_chain;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.unextend;

#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_12(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_xilinx_dsp_cascade_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    dff = nullptr;
    if (argQ.empty())
      reject;
    for (const auto &c : argQ.chunks()) {
      // Abandon matches when 'Q' is a constant
      if (!c.wire)
        reject;
      // Abandon matches when 'Q' has the keep attribute set
      if (c.wire->get_bool_attribute(id_b_keep))
        reject;
      // Abandon matches when 'Q' has a non-zero init attribute set
      // (not supported by DSP48E1)
      Const init = c.wire->attributes.at(id_b_init, Const());
      if (!init.empty())
        for (auto b : init.extract(c.offset, c.width))
          if (b != State::Sx && b != State::S0)
            reject;
    }

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

rollback_label:
    YS_MAYBE_UNUSED;
  }

  // techlibs/xilinx/xilinx_dsp_cascade.pmg:383
  void block_12(int recursion YS_MAYBE_UNUSED) {
    const SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.argQ;
    const SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.clock;
    Cell* &ff YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.ff;
    vector<std::tuple<Cell*,int,int,int>> &chain YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.chain;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffclock;
    vector<std::tuple<Cell*,int,int,int>> &longest_chain YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.longest_chain;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.unextend;
    Cell* _pmg_backup_ff = ff;

    index_12_key_type key;
    std::get<0>(key) = argQ[0];
    auto cells_ptr = index_12.find(key);

    if (cells_ptr != index_12.end()) {
      const vector<index_12_value_type> &cells = cells_ptr->second;
      for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {
        ff = std::get<0>(cells[_pmg_idx]);
        const int &offset YS_MAYBE_UNUSED = std::get<1>(cells[_pmg_idx]);
        if (blacklist_cells.count(ff)) continue;
        if (!(ff->type.in(id_d_dff, id_d_dffe) || param(ff, id_b_SRST_VALUE).is_fully_zero())) continue;
        if (!(GetSize(port(ff, id_b_Q)) >= offset + GetSize(argQ))) continue;
        if (!(port(ff, id_b_Q).extract(offset, GetSize(argQ)) == argQ)) continue;
        if (!(clock == SigBit() || port(ff, id_b_CLK)[0] == clock)) continue;
        auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));
        block_13(recursion+1);
        if (rollback_ptr.second)
          rollback_cache.erase(rollback_ptr.first);
        if (rollback) {
          if (rollback != recursion) {
            ff = _pmg_backup_ff;
            return;
          }
          rollback = 0;
        }
      }
    }

    ff = nullptr;
    ff = _pmg_backup_ff;
  }

  // techlibs/xilinx/xilinx_dsp_cascade.pmg:401
  void block_13(int recursion YS_MAYBE_UNUSED) {
    const SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.clock;
    Cell* const &ff YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.ff;
    SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_cascade.argQ;
    vector<std::tuple<Cell*,int,int,int>> &chain YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.chain;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.dffclock;
    vector<std::tuple<Cell*,int,int,int>> &longest_chain YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.longest_chain;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_xilinx_dsp_cascade.unextend;

    SigSpec _pmg_backup_argQ = argQ;

#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_14(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_xilinx_dsp_cascade_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    SigSpec Q = port(ff, id_b_Q);
    dff = ff;
    dffclock = port(ff, id_b_CLK);
    dffD = argQ;
    SigSpec D = port(ff, id_b_D);
    argQ = Q;
    dffD.replace(argQ, D);

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

rollback_label:
    YS_MAYBE_UNUSED;

    argQ = _pmg_backup_argQ;
  }

  void block_14(int recursion YS_MAYBE_UNUSED) {
  }
};
