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

struct xilinx_dsp_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_0_key_type;
  typedef std::tuple<Cell*> index_0_value_type;
  dict<index_0_key_type, vector<index_0_value_type>> index_0;
  typedef std::tuple<SigSpec> index_3_key_type;
  typedef std::tuple<Cell*, IdString, IdString> index_3_value_type;
  dict<index_3_key_type, vector<index_3_value_type>> index_3;
  typedef std::tuple<SigSpec> 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_11_key_type;
  typedef std::tuple<Cell*, IdString> index_11_value_type;
  dict<index_11_key_type, vector<index_11_value_type>> index_11;
  typedef std::tuple<SigSpec, SigSpec> index_14_key_type;
  typedef std::tuple<Cell*, IdString> index_14_value_type;
  dict<index_14_key_type, vector<index_14_value_type>> index_14;
  typedef std::tuple<SigSpec> index_16_key_type;
  typedef std::tuple<Cell*, Const> index_16_value_type;
  dict<index_16_key_type, vector<index_16_value_type>> index_16;
  typedef std::tuple<SigBit> index_20_key_type;
  typedef std::tuple<Cell*, int> index_20_value_type;
  dict<index_20_key_type, vector<index_20_value_type>> index_20;
  typedef std::tuple<SigBit> index_24_key_type;
  typedef std::tuple<Cell*, int> index_24_value_type;
  dict<index_24_key_type, vector<index_24_value_type>> index_24;
  dict<SigBit, pool<Cell*>> sigusers;
  pool<Cell*> blacklist_cells;
  pool<Cell*> autoremove_cells;
  dict<Cell*,int> rollback_cache;
  int rollback;

  struct state_xilinx_dsp_pack_t {
    SigSpec argD;
    SigSpec argQ;
    SigBit clock;
    Cell* dsp;
    Cell* ff;
    Cell* ffA1;
    Cell* ffA2;
    Cell* ffAD;
    Cell* ffB1;
    Cell* ffB2;
    Cell* ffD;
    Cell* ffM;
    Cell* ffP;
    Cell* overflow;
    Cell* postAdd;
    IdString postAddAB;
    Cell* postAddMux;
    IdString postAddMuxAB;
    Cell* preAdd;
    Cell* preSub;
    SigSpec sigA;
    SigSpec sigB;
    SigSpec sigC;
    SigSpec sigD;
    SigSpec sigM;
    SigSpec sigP;
  } st_xilinx_dsp_pack;

  struct udata_xilinx_dsp_pack_t {
    Cell* dff;
    SigSpec dffD;
    SigSpec dffQ;
    SigBit dffclock;
  } ud_xilinx_dsp_pack;

  IdString id_b_A{"\\A"};
  IdString id_b_ADREG{"\\ADREG"};
  IdString id_b_AREG{"\\AREG"};
  IdString id_b_B{"\\B"};
  IdString id_b_BREG{"\\BREG"};
  IdString id_b_C{"\\C"};
  IdString id_b_CLK{"\\CLK"};
  IdString id_b_CLK_POLARITY{"\\CLK_POLARITY"};
  IdString id_b_D{"\\D"};
  IdString id_b_DREG{"\\DREG"};
  IdString id_b_DSP48E1{"\\DSP48E1"};
  IdString id_b_EN{"\\EN"};
  IdString id_b_EN_POLARITY{"\\EN_POLARITY"};
  IdString id_b_INMODE{"\\INMODE"};
  IdString id_b_MREG{"\\MREG"};
  IdString id_b_OPMODE{"\\OPMODE"};
  IdString id_b_P{"\\P"};
  IdString id_b_PREG{"\\PREG"};
  IdString id_b_Q{"\\Q"};
  IdString id_b_SRST{"\\SRST"};
  IdString id_b_SRST_POLARITY{"\\SRST_POLARITY"};
  IdString id_b_SRST_VALUE{"\\SRST_VALUE"};
  IdString id_b_USE_DPORT{"\\USE_DPORT"};
  IdString id_b_USE_MULT{"\\USE_MULT"};
  IdString id_b_USE_PATTERN_DETECT{"\\USE_PATTERN_DETECT"};
  IdString id_b_Y{"\\Y"};
  IdString id_b_init{"\\init"};
  IdString id_b_keep{"\\keep"};
  IdString id_d_add{"$add"};
  IdString id_d_dff{"$dff"};
  IdString id_d_dffe{"$dffe"};
  IdString id_d_ge{"$ge"};
  IdString id_d_mul{"$mul"};
  IdString id_d_mux{"$mux"};
  IdString id_d_sdff{"$sdff"};
  IdString id_d_sdffce{"$sdffce"};
  IdString id_d_sdffe{"$sdffe"};
  IdString id_d_sub{"$sub"};

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

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

  void setup(const vector<Cell*> &cells) {
    ud_xilinx_dsp_pack.dff = nullptr;
    ud_xilinx_dsp_pack.dffD = SigSpec();
    ud_xilinx_dsp_pack.dffQ = SigSpec();
    ud_xilinx_dsp_pack.dffclock = SigBit();
    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 *dsp = cell;
        index_0_value_type value;
        std::get<0>(value) = cell;
        if (!(dsp->type.in(id_b_DSP48E1))) continue;
        index_0_key_type key;
        index_0[key].push_back(value);
      } while (0);
      do {
        Cell *preAdd = cell;
        index_3_value_type value;
        std::get<0>(value) = cell;
        if (!(preAdd->type.in(id_d_add))) continue;
        if (!(GetSize(port(preAdd, id_b_Y)) <= 25)) continue;
        if (!(nusers(port(preAdd, id_b_Y)) == 2)) continue;
        vector<IdString> _pmg_choices_AB = {id_b_A, id_b_B};
        for (const IdString &AB : _pmg_choices_AB) {
        std::get<1>(value) = AB;
        if (!(GetSize(port(preAdd, AB)) <= 30)) continue;
        IdString &BA = std::get<2>(value);
        BA = (AB == id_b_A ? id_b_B : id_b_A);
        if (!(GetSize(port(preAdd, BA)) <= 25)) continue;
        index_3_key_type key;
        std::get<0>(key) = port(preAdd, id_b_Y);
        index_3[key].push_back(value);
        }
      } while (0);
      do {
        Cell *preSub = cell;
        index_5_value_type value;
        std::get<0>(value) = cell;
        if (!(preSub->type.in(id_d_sub))) continue;
        if (!(GetSize(port(preSub, id_b_Y)) <= 25)) continue;
        if (!(nusers(port(preSub, id_b_Y)) == 2)) continue;
        if (!(GetSize(port(preSub, id_b_A)) <= 25)) continue;
        if (!(GetSize(port(preSub, id_b_B)) <= 30)) continue;
        index_5_key_type key;
        std::get<0>(key) = port(preSub, id_b_Y);
        index_5[key].push_back(value);
      } while (0);
      do {
        Cell *postAdd = cell;
        index_11_value_type value;
        std::get<0>(value) = cell;
        if (!(postAdd->type.in(id_d_add))) continue;
        if (!(GetSize(port(postAdd, id_b_Y)) <= 48)) continue;
        vector<IdString> _pmg_choices_AB = {id_b_A, id_b_B};
        for (const IdString &AB : _pmg_choices_AB) {
        std::get<1>(value) = AB;
        if (!(nusers(port(postAdd, AB)) == 2)) continue;
        index_11_key_type key;
        std::get<0>(key) = port(postAdd, AB)[0];
        index_11[key].push_back(value);
        }
      } while (0);
      do {
        Cell *postAddMux = cell;
        index_14_value_type value;
        std::get<0>(value) = cell;
        if (!(postAddMux->type.in(id_d_mux))) continue;
        if (!(nusers(port(postAddMux, id_b_Y)) == 2)) continue;
        vector<IdString> _pmg_choices_AB = {id_b_A, id_b_B};
        for (const IdString &AB : _pmg_choices_AB) {
        std::get<1>(value) = AB;
        index_14_key_type key;
        std::get<0>(key) = port(postAddMux, AB);
        std::get<1>(key) = port(postAddMux, id_b_Y);
        index_14[key].push_back(value);
        }
      } while (0);
      do {
        Cell *overflow = cell;
        index_16_value_type value;
        std::get<0>(value) = cell;
        if (!(overflow->type.in(id_d_ge))) continue;
        if (!(GetSize(port(overflow, id_b_Y)) <= 48)) continue;
        if (!(port(overflow, id_b_B).is_fully_const())) continue;
        Const &B = std::get<1>(value);
        B = port(overflow, id_b_B).as_const();
        if (!(std::count(B.begin(), B.end(), State::S1) == 1)) continue;
        index_16_key_type key;
        std::get<0>(key) = port(overflow, id_b_A);
        index_16[key].push_back(value);
      } while (0);
      do {
        Cell *ff = cell;
        index_20_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_20_key_type key;
        std::get<0>(key) = port(ff, id_b_Q)[offset];
        index_20[key].push_back(value);
        }
      } while (0);
      do {
        Cell *ff = cell;
        index_24_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_24_key_type key;
        std::get<0>(key) = port(ff, id_b_D)[offset];
        index_24[key].push_back(value);
        }
      } while (0);
    }
  }

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

  int run_xilinx_dsp_pack(std::function<void()> on_accept_f) {
    log_assert(setup_done);
    accept_cnt = 0;
    on_accept = on_accept_f;
    rollback = 0;
    st_xilinx_dsp_pack.argD = SigSpec();
    st_xilinx_dsp_pack.argQ = SigSpec();
    st_xilinx_dsp_pack.clock = SigBit();
    st_xilinx_dsp_pack.dsp = nullptr;
    st_xilinx_dsp_pack.ff = nullptr;
    st_xilinx_dsp_pack.ffA1 = nullptr;
    st_xilinx_dsp_pack.ffA2 = nullptr;
    st_xilinx_dsp_pack.ffAD = nullptr;
    st_xilinx_dsp_pack.ffB1 = nullptr;
    st_xilinx_dsp_pack.ffB2 = nullptr;
    st_xilinx_dsp_pack.ffD = nullptr;
    st_xilinx_dsp_pack.ffM = nullptr;
    st_xilinx_dsp_pack.ffP = nullptr;
    st_xilinx_dsp_pack.overflow = nullptr;
    st_xilinx_dsp_pack.postAdd = nullptr;
    st_xilinx_dsp_pack.postAddAB = IdString();
    st_xilinx_dsp_pack.postAddMux = nullptr;
    st_xilinx_dsp_pack.postAddMuxAB = IdString();
    st_xilinx_dsp_pack.preAdd = nullptr;
    st_xilinx_dsp_pack.preSub = nullptr;
    st_xilinx_dsp_pack.sigA = SigSpec();
    st_xilinx_dsp_pack.sigB = SigSpec();
    st_xilinx_dsp_pack.sigC = SigSpec();
    st_xilinx_dsp_pack.sigD = SigSpec();
    st_xilinx_dsp_pack.sigM = SigSpec();
    st_xilinx_dsp_pack.sigP = SigSpec();
    block_0(1);
    log_assert(rollback_cache.empty());
    return accept_cnt;
  }

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

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

  void block_subpattern_xilinx_dsp_pack_(int recursion) { block_0(recursion); }
  void block_subpattern_xilinx_dsp_pack_in_dffe(int recursion) { block_19(recursion); }
  void block_subpattern_xilinx_dsp_pack_out_dffe(int recursion) { block_23(recursion); }

  // techlibs/xilinx/xilinx_dsp.pmg:71
  void block_0(int recursion YS_MAYBE_UNUSED) {
    Cell* &dsp YS_MAYBE_UNUSED = st_xilinx_dsp_pack.dsp;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffclock;
    Cell* _pmg_backup_dsp = dsp;

    index_0_key_type key;
    auto cells_ptr = index_0.find(key);

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

    dsp = nullptr;
    dsp = _pmg_backup_dsp;
  }

  // techlibs/xilinx/xilinx_dsp.pmg:75
  void block_1(int recursion YS_MAYBE_UNUSED) {
    Cell* const &dsp YS_MAYBE_UNUSED = st_xilinx_dsp_pack.dsp;
    SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_pack.clock;
    SigSpec &sigA YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigA;
    SigSpec &sigB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigB;
    SigSpec &sigC YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigC;
    SigSpec &sigD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigD;
    SigSpec &sigM YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigM;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffclock;

#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_xilinx_dsp_pack_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    auto 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);
    };
    sigA = unextend(port(dsp, id_b_A));
    sigB = unextend(port(dsp, id_b_B));
    sigC = port(dsp, id_b_C, SigSpec());
    sigD = port(dsp, id_b_D, SigSpec());
    SigSpec P = port(dsp, id_b_P);
    if (param(dsp, id_b_USE_MULT).decode_string() == "MULTIPLY") {
      // Only care about those bits that are used
      int i;
      for (i = GetSize(P)-1; i >= 0; i--)
        if (nusers(P[i]) > 1)
          break;
      i++;
      log_assert(nusers(P.extract_end(i)) <= 1);
      // This sigM could have no users if downstream sinks (e.g. id_d_add) is
      //   narrower than id_d_mul result, for example
      if (i == 0)
        reject;
      sigM = P.extract(0, i);
    }
    else
      sigM = P;
    clock = port(dsp, id_b_CLK, SigBit());

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

rollback_label:
    YS_MAYBE_UNUSED;

    clock = SigBit();
    sigA = SigSpec();
    sigB = SigSpec();
    sigC = SigSpec();
    sigD = SigSpec();
    sigM = SigSpec();
  }

  // techlibs/xilinx/xilinx_dsp.pmg:115
  void block_2(int recursion YS_MAYBE_UNUSED) {
    Cell* const &dsp YS_MAYBE_UNUSED = st_xilinx_dsp_pack.dsp;
    const SigSpec &sigB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigB;
    const SigSpec &sigC YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigC;
    const SigSpec &sigD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigD;
    const SigSpec &sigM YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigM;
    SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argQ;
    SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_pack.clock;
    Cell* &ffAD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffAD;
    SigSpec &sigA YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigA;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffclock;

    SigBit _pmg_backup_clock = clock;
    SigSpec _pmg_backup_sigA = sigA;

#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_pack_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    if (param(dsp, id_b_ADREG).as_int() == 0) {
      argQ = sigA;
      subpattern(in_dffe);
      if (dff) {
        ffAD = dff;
        clock = dffclock;
        sigA = dffD;
      }
    }

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

rollback_label:
    YS_MAYBE_UNUSED;

    clock = _pmg_backup_clock;
    sigA = _pmg_backup_sigA;
    argQ = SigSpec();
    ffAD = nullptr;
  }

  // techlibs/xilinx/xilinx_dsp.pmg:129
  void block_3(int recursion YS_MAYBE_UNUSED) {
    const SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argQ;
    const SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_pack.clock;
    Cell* const &dsp YS_MAYBE_UNUSED = st_xilinx_dsp_pack.dsp;
    Cell* const &ffAD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffAD;
    const SigSpec &sigA YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigA;
    const SigSpec &sigB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigB;
    const SigSpec &sigC YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigC;
    const SigSpec &sigD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigD;
    const SigSpec &sigM YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigM;
    Cell* &preAdd YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preAdd;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffclock;
    Cell* _pmg_backup_preAdd = preAdd;

    if (!(sigD.empty() || sigD.is_fully_zero())) {
      preAdd = nullptr;
      block_4(recursion+1);
      preAdd = _pmg_backup_preAdd;
      return;
    }

    if (!(param(dsp, id_b_USE_DPORT).decode_string() == "FALSE")) {
      preAdd = nullptr;
      block_4(recursion+1);
      preAdd = _pmg_backup_preAdd;
      return;
    }

    if (!(port(dsp, id_b_INMODE, Const(0, 5)).is_fully_zero())) {
      preAdd = nullptr;
      block_4(recursion+1);
      preAdd = _pmg_backup_preAdd;
      return;
    }

    index_3_key_type key;
    std::get<0>(key) = sigA;
    auto cells_ptr = index_3.find(key);

    if (cells_ptr != index_3.end()) {
      const vector<index_3_value_type> &cells = cells_ptr->second;
      for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {
        preAdd = std::get<0>(cells[_pmg_idx]);
        const IdString &AB YS_MAYBE_UNUSED = std::get<1>(cells[_pmg_idx]);
        const IdString &BA YS_MAYBE_UNUSED = std::get<2>(cells[_pmg_idx]);
        if (blacklist_cells.count(preAdd)) continue;
        auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));
        block_4(recursion+1);
        if (rollback_ptr.second)
          rollback_cache.erase(rollback_ptr.first);
        if (rollback) {
          if (rollback != recursion) {
            preAdd = _pmg_backup_preAdd;
            return;
          }
          rollback = 0;
        }
      }
    }

    preAdd = nullptr;
    block_4(recursion+1);
    preAdd = _pmg_backup_preAdd;
  }

  // techlibs/xilinx/xilinx_dsp.pmg:150
  void block_4(int recursion YS_MAYBE_UNUSED) {
    const SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argQ;
    const SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_pack.clock;
    Cell* const &dsp YS_MAYBE_UNUSED = st_xilinx_dsp_pack.dsp;
    Cell* const &ffAD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffAD;
    Cell* const &preAdd YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preAdd;
    const SigSpec &sigB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigB;
    const SigSpec &sigC YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigC;
    const SigSpec &sigM YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigM;
    SigSpec &sigA YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigA;
    SigSpec &sigD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigD;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffclock;

    SigSpec _pmg_backup_sigA = sigA;
    SigSpec _pmg_backup_sigD = sigD;

#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_5(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_xilinx_dsp_pack_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    if (preAdd) {
      sigA = port(preAdd, id_b_A);
      sigD = port(preAdd, id_b_B);
    }

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

rollback_label:
    YS_MAYBE_UNUSED;

    sigA = _pmg_backup_sigA;
    sigD = _pmg_backup_sigD;
  }

  // techlibs/xilinx/xilinx_dsp.pmg:159
  void block_5(int recursion YS_MAYBE_UNUSED) {
    const SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argQ;
    const SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_pack.clock;
    Cell* const &dsp YS_MAYBE_UNUSED = st_xilinx_dsp_pack.dsp;
    Cell* const &ffAD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffAD;
    Cell* const &preAdd YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preAdd;
    const SigSpec &sigA YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigA;
    const SigSpec &sigB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigB;
    const SigSpec &sigC YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigC;
    const SigSpec &sigD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigD;
    const SigSpec &sigM YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigM;
    Cell* &preSub YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preSub;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffclock;
    Cell* _pmg_backup_preSub = preSub;

    if (!(sigD.empty() || sigD.is_fully_zero())) {
      preSub = nullptr;
      block_6(recursion+1);
      preSub = _pmg_backup_preSub;
      return;
    }

    if (!(param(dsp, id_b_USE_DPORT).decode_string() == "FALSE")) {
      preSub = nullptr;
      block_6(recursion+1);
      preSub = _pmg_backup_preSub;
      return;
    }

    if (!(port(dsp, id_b_INMODE, Const(0, 5)).is_fully_zero())) {
      preSub = nullptr;
      block_6(recursion+1);
      preSub = _pmg_backup_preSub;
      return;
    }

    index_5_key_type key;
    std::get<0>(key) = sigA;
    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++) {
        preSub = std::get<0>(cells[_pmg_idx]);
        if (blacklist_cells.count(preSub)) 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) {
            preSub = _pmg_backup_preSub;
            return;
          }
          rollback = 0;
        }
      }
    }

    preSub = nullptr;
    block_6(recursion+1);
    preSub = _pmg_backup_preSub;
  }

  // techlibs/xilinx/xilinx_dsp.pmg:178
  void block_6(int recursion YS_MAYBE_UNUSED) {
    const SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argQ;
    const SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_pack.clock;
    Cell* const &dsp YS_MAYBE_UNUSED = st_xilinx_dsp_pack.dsp;
    Cell* const &ffAD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffAD;
    Cell* const &preAdd YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preAdd;
    Cell* const &preSub YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preSub;
    const SigSpec &sigB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigB;
    const SigSpec &sigC YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigC;
    const SigSpec &sigM YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigM;
    SigSpec &sigA YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigA;
    SigSpec &sigD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigD;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffclock;

    SigSpec _pmg_backup_sigA = sigA;
    SigSpec _pmg_backup_sigD = sigD;

#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_pack_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    if (preSub) {
      sigD = port(preSub, id_b_A);
      sigA = port(preSub, id_b_B);
    }

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

rollback_label:
    YS_MAYBE_UNUSED;

    sigA = _pmg_backup_sigA;
    sigD = _pmg_backup_sigD;
  }

  // techlibs/xilinx/xilinx_dsp.pmg:188
  void block_7(int recursion YS_MAYBE_UNUSED) {
    Cell* const &dsp YS_MAYBE_UNUSED = st_xilinx_dsp_pack.dsp;
    Cell* const &preAdd YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preAdd;
    Cell* const &preSub YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preSub;
    const SigSpec &sigB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigB;
    const SigSpec &sigC YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigC;
    const SigSpec &sigD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigD;
    const SigSpec &sigM YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigM;
    SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argQ;
    SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_pack.clock;
    Cell* &ffA1 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffA1;
    Cell* &ffA2 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffA2;
    Cell* &ffAD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffAD;
    SigSpec &sigA YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigA;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffclock;

    SigSpec _pmg_backup_argQ = argQ;
    SigBit _pmg_backup_clock = clock;
    Cell* _pmg_backup_ffAD = ffAD;
    SigSpec _pmg_backup_sigA = sigA;

#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_pack_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    // Only search for ffA2 if there was a pre-adder
    //   (otherwise ffA2 would have been matched as ffAD)
    if (preAdd || preSub) {
      if (param(dsp, id_b_AREG).as_int() == 0) {
        argQ = sigA;
        subpattern(in_dffe);
        if (dff) {
          ffA2 = dff;
          clock = dffclock;
          sigA = dffD;
        }
      }
    }
    // And if there wasn't a pre-adder,
    //   move AD register to A
    else if (ffAD) {
      log_assert(!ffA2);
      std::swap(ffA2, ffAD);
    }
    // Now attempt to match A1
    if (ffA2) {
      argQ = sigA;
      subpattern(in_dffe);
      if (dff) {
        if (dff->type != ffA2->type)
          goto ffA1_end;
        if (dff->type.in(id_d_sdff, id_d_sdffe, id_d_sdffce)) {
          if (param(dff, id_b_SRST_POLARITY) != param(ffA2, id_b_SRST_POLARITY))
            goto ffA1_end;
          if (port(dff, id_b_SRST) != port(ffA2, id_b_SRST))
            goto ffA1_end;
        }
        if (dff->type.in(id_d_dffe, id_d_sdffe, id_d_sdffce)) {
          if (param(dff, id_b_EN_POLARITY) != param(ffA2, id_b_EN_POLARITY))
            goto ffA1_end;
          if (port(dff, id_b_EN) != port(ffA2, id_b_EN))
            goto ffA1_end;
        }
        ffA1 = dff;
        clock = dffclock;
        sigA = dffD;
  ffA1_end:    ;
      }
    }

    block_8(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;
    ffAD = _pmg_backup_ffAD;
    sigA = _pmg_backup_sigA;
    ffA1 = nullptr;
    ffA2 = nullptr;
  }

  // techlibs/xilinx/xilinx_dsp.pmg:240
  void block_8(int recursion YS_MAYBE_UNUSED) {
    Cell* const &dsp YS_MAYBE_UNUSED = st_xilinx_dsp_pack.dsp;
    Cell* const &ffA1 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffA1;
    Cell* const &ffA2 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffA2;
    Cell* const &ffAD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffAD;
    Cell* const &preAdd YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preAdd;
    Cell* const &preSub YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preSub;
    const SigSpec &sigA YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigA;
    const SigSpec &sigC YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigC;
    const SigSpec &sigD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigD;
    const SigSpec &sigM YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigM;
    SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argQ;
    SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_pack.clock;
    Cell* &ffB1 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffB1;
    Cell* &ffB2 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffB2;
    SigSpec &sigB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigB;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffclock;

    SigSpec _pmg_backup_argQ = argQ;
    SigBit _pmg_backup_clock = clock;
    SigSpec _pmg_backup_sigB = sigB;

#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_pack_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    if (param(dsp, id_b_BREG).as_int() == 0) {
      argQ = sigB;
      subpattern(in_dffe);
      if (dff) {
        ffB2 = dff;
        clock = dffclock;
        sigB = dffD;
        // Now attempt to match B1
        if (ffB2) {
          argQ = sigB;
          subpattern(in_dffe);
          if (dff) {
            if (dff->type != ffB2->type)
              goto ffB1_end;
            if (dff->type.in(id_d_sdff, id_d_sdffe, id_d_sdffce)) {
              if (param(dff, id_b_SRST_POLARITY) != param(ffB2, id_b_SRST_POLARITY))
                goto ffB1_end;
              if (port(dff, id_b_SRST) != port(ffB2, id_b_SRST))
                goto ffB1_end;
            }
            if (dff->type.in(id_d_dffe, id_d_sdffe, id_d_sdffce)) {
              if (param(dff, id_b_EN_POLARITY) != param(ffB2, id_b_EN_POLARITY))
                goto ffB1_end;
              if (port(dff, id_b_EN) != port(ffB2, id_b_EN))
                goto ffB1_end;
            }
            ffB1 = dff;
            clock = dffclock;
            sigB = dffD;
  ffB1_end:        ;
          }
        }
      }
    }

    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;
    sigB = _pmg_backup_sigB;
    ffB1 = nullptr;
    ffB2 = nullptr;
  }

  // techlibs/xilinx/xilinx_dsp.pmg:282
  void block_9(int recursion YS_MAYBE_UNUSED) {
    Cell* const &dsp YS_MAYBE_UNUSED = st_xilinx_dsp_pack.dsp;
    Cell* const &ffA1 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffA1;
    Cell* const &ffA2 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffA2;
    Cell* const &ffAD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffAD;
    Cell* const &ffB1 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffB1;
    Cell* const &ffB2 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffB2;
    Cell* const &preAdd YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preAdd;
    Cell* const &preSub YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preSub;
    const SigSpec &sigA YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigA;
    const SigSpec &sigB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigB;
    const SigSpec &sigC YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigC;
    const SigSpec &sigM YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigM;
    SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argQ;
    SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_pack.clock;
    Cell* &ffD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffD;
    SigSpec &sigD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigD;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffclock;

    SigSpec _pmg_backup_argQ = argQ;
    SigBit _pmg_backup_clock = clock;
    SigSpec _pmg_backup_sigD = sigD;

#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_pack_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    if (param(dsp, id_b_DREG).as_int() == 0) {
      argQ = sigD;
      subpattern(in_dffe);
      if (dff) {
        ffD = dff;
        clock = dffclock;
        sigD = dffD;
      }
    }

    block_10(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;
    sigD = _pmg_backup_sigD;
    ffD = nullptr;
  }

  // techlibs/xilinx/xilinx_dsp.pmg:295
  void block_10(int recursion YS_MAYBE_UNUSED) {
    const SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argQ;
    Cell* const &dsp YS_MAYBE_UNUSED = st_xilinx_dsp_pack.dsp;
    Cell* const &ffA1 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffA1;
    Cell* const &ffA2 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffA2;
    Cell* const &ffAD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffAD;
    Cell* const &ffB1 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffB1;
    Cell* const &ffB2 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffB2;
    Cell* const &ffD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffD;
    Cell* const &preAdd YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preAdd;
    Cell* const &preSub YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preSub;
    const SigSpec &sigA YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigA;
    const SigSpec &sigB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigB;
    const SigSpec &sigC YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigC;
    const SigSpec &sigD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigD;
    SigSpec &argD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argD;
    SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_pack.clock;
    Cell* &ffM YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffM;
    SigSpec &sigM YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigM;
    SigSpec &sigP YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigP;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffclock;

    SigBit _pmg_backup_clock = clock;
    SigSpec _pmg_backup_sigM = sigM;

#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_xilinx_dsp_pack_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    if (param(dsp, id_b_MREG).as_int() == 0 && nusers(sigM) == 2) {
      argD = sigM;
      subpattern(out_dffe);
      if (dff) {
        ffM = dff;
        clock = dffclock;
        sigM = dffQ;
      }
    }
    sigP = sigM;

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

rollback_label:
    YS_MAYBE_UNUSED;

    clock = _pmg_backup_clock;
    sigM = _pmg_backup_sigM;
    argD = SigSpec();
    ffM = nullptr;
    sigP = SigSpec();
  }

  // techlibs/xilinx/xilinx_dsp.pmg:313
  void block_11(int recursion YS_MAYBE_UNUSED) {
    const SigSpec &argD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argD;
    const SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argQ;
    const SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_pack.clock;
    Cell* const &dsp YS_MAYBE_UNUSED = st_xilinx_dsp_pack.dsp;
    Cell* const &ffA1 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffA1;
    Cell* const &ffA2 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffA2;
    Cell* const &ffAD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffAD;
    Cell* const &ffB1 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffB1;
    Cell* const &ffB2 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffB2;
    Cell* const &ffD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffD;
    Cell* const &ffM YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffM;
    Cell* const &preAdd YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preAdd;
    Cell* const &preSub YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preSub;
    const SigSpec &sigA YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigA;
    const SigSpec &sigB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigB;
    const SigSpec &sigC YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigC;
    const SigSpec &sigD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigD;
    const SigSpec &sigM YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigM;
    const SigSpec &sigP YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigP;
    Cell* &postAdd YS_MAYBE_UNUSED = st_xilinx_dsp_pack.postAdd;
    IdString &postAddAB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.postAddAB;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffclock;
    Cell* _pmg_backup_postAdd = postAdd;

    if (!(port(dsp, id_b_OPMODE, SigSpec(0, 7)).extract(4,3).is_fully_zero())) {
      postAdd = nullptr;
      block_12(recursion+1);
      postAdd = _pmg_backup_postAdd;
      return;
    }

    index_11_key_type key;
    std::get<0>(key) = sigP[0];
    auto cells_ptr = index_11.find(key);

    if (cells_ptr != index_11.end()) {
      const vector<index_11_value_type> &cells = cells_ptr->second;
      for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {
        postAdd = std::get<0>(cells[_pmg_idx]);
        const IdString &AB YS_MAYBE_UNUSED = std::get<1>(cells[_pmg_idx]);
        if (blacklist_cells.count(postAdd)) continue;
        if (!(GetSize(port(postAdd, AB)) >= GetSize(sigP))) continue;
        if (!(port(postAdd, AB).extract(0, GetSize(sigP)) == sigP)) continue;
        if (!(port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(sigP[GetSize(sigP)-1], GetSize(port(postAdd, AB))-GetSize(sigP)) || port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(State::S0, GetSize(port(postAdd, AB))-GetSize(sigP)))) continue;
        auto _pmg_backup_postAddAB = postAddAB;
        postAddAB = AB;
        auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));
        block_12(recursion+1);
        postAddAB = _pmg_backup_postAddAB;
        if (rollback_ptr.second)
          rollback_cache.erase(rollback_ptr.first);
        if (rollback) {
          if (rollback != recursion) {
            postAdd = _pmg_backup_postAdd;
            return;
          }
          rollback = 0;
        }
      }
    }

    postAdd = nullptr;
    block_12(recursion+1);
    postAdd = _pmg_backup_postAdd;
  }

  // techlibs/xilinx/xilinx_dsp.pmg:332
  void block_12(int recursion YS_MAYBE_UNUSED) {
    const SigSpec &argD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argD;
    const SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argQ;
    const SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_pack.clock;
    Cell* const &dsp YS_MAYBE_UNUSED = st_xilinx_dsp_pack.dsp;
    Cell* const &ffA1 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffA1;
    Cell* const &ffA2 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffA2;
    Cell* const &ffAD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffAD;
    Cell* const &ffB1 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffB1;
    Cell* const &ffB2 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffB2;
    Cell* const &ffD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffD;
    Cell* const &ffM YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffM;
    Cell* const &postAdd YS_MAYBE_UNUSED = st_xilinx_dsp_pack.postAdd;
    const IdString &postAddAB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.postAddAB;
    Cell* const &preAdd YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preAdd;
    Cell* const &preSub YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preSub;
    const SigSpec &sigA YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigA;
    const SigSpec &sigB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigB;
    const SigSpec &sigD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigD;
    const SigSpec &sigM YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigM;
    SigSpec &sigC YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigC;
    SigSpec &sigP YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigP;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffclock;

    SigSpec _pmg_backup_sigC = sigC;
    SigSpec _pmg_backup_sigP = sigP;

#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_xilinx_dsp_pack_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    if (postAdd) {
      sigC = port(postAdd, postAddAB == id_b_A ? id_b_B : id_b_A);
      sigP = port(postAdd, id_b_Y);
    }

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

rollback_label:
    YS_MAYBE_UNUSED;

    sigC = _pmg_backup_sigC;
    sigP = _pmg_backup_sigP;
  }

  // techlibs/xilinx/xilinx_dsp.pmg:340
  void block_13(int recursion YS_MAYBE_UNUSED) {
    const SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argQ;
    Cell* const &dsp YS_MAYBE_UNUSED = st_xilinx_dsp_pack.dsp;
    Cell* const &ffA1 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffA1;
    Cell* const &ffA2 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffA2;
    Cell* const &ffAD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffAD;
    Cell* const &ffB1 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffB1;
    Cell* const &ffB2 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffB2;
    Cell* const &ffD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffD;
    Cell* const &ffM YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffM;
    Cell* const &postAdd YS_MAYBE_UNUSED = st_xilinx_dsp_pack.postAdd;
    const IdString &postAddAB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.postAddAB;
    Cell* const &preAdd YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preAdd;
    Cell* const &preSub YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preSub;
    const SigSpec &sigA YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigA;
    const SigSpec &sigB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigB;
    const SigSpec &sigC YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigC;
    const SigSpec &sigD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigD;
    const SigSpec &sigM YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigM;
    SigSpec &argD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argD;
    SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_pack.clock;
    Cell* &ffP YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffP;
    SigSpec &sigP YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigP;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffclock;

    SigSpec _pmg_backup_argD = argD;
    SigBit _pmg_backup_clock = clock;
    SigSpec _pmg_backup_sigP = sigP;

#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_pack_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    if (param(dsp, id_b_PREG).as_int() == 0) {
      if (nusers(sigP) == 2) {
        argD = sigP;
        subpattern(out_dffe);
        if (dff) {
          ffP = dff;
          clock = dffclock;
          sigP = dffQ;
        }
      }
    }

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

rollback_label:
    YS_MAYBE_UNUSED;

    argD = _pmg_backup_argD;
    clock = _pmg_backup_clock;
    sigP = _pmg_backup_sigP;
    ffP = nullptr;
  }

  // techlibs/xilinx/xilinx_dsp.pmg:367
  void block_14(int recursion YS_MAYBE_UNUSED) {
    const SigSpec &argD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argD;
    const SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argQ;
    const SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_pack.clock;
    Cell* const &dsp YS_MAYBE_UNUSED = st_xilinx_dsp_pack.dsp;
    Cell* const &ffA1 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffA1;
    Cell* const &ffA2 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffA2;
    Cell* const &ffAD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffAD;
    Cell* const &ffB1 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffB1;
    Cell* const &ffB2 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffB2;
    Cell* const &ffD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffD;
    Cell* const &ffM YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffM;
    Cell* const &ffP YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffP;
    Cell* const &postAdd YS_MAYBE_UNUSED = st_xilinx_dsp_pack.postAdd;
    const IdString &postAddAB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.postAddAB;
    Cell* const &preAdd YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preAdd;
    Cell* const &preSub YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preSub;
    const SigSpec &sigA YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigA;
    const SigSpec &sigB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigB;
    const SigSpec &sigC YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigC;
    const SigSpec &sigD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigD;
    const SigSpec &sigM YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigM;
    const SigSpec &sigP YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigP;
    Cell* &postAddMux YS_MAYBE_UNUSED = st_xilinx_dsp_pack.postAddMux;
    IdString &postAddMuxAB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.postAddMuxAB;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffclock;
    Cell* _pmg_backup_postAddMux = postAddMux;

    if (!(postAdd)) {
      postAddMux = nullptr;
      block_15(recursion+1);
      postAddMux = _pmg_backup_postAddMux;
      return;
    }

    if (!(ffP)) {
      postAddMux = nullptr;
      block_15(recursion+1);
      postAddMux = _pmg_backup_postAddMux;
      return;
    }

    index_14_key_type key;
    std::get<0>(key) = sigP;
    std::get<1>(key) = sigC;
    auto cells_ptr = index_14.find(key);

    if (cells_ptr != index_14.end()) {
      const vector<index_14_value_type> &cells = cells_ptr->second;
      for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {
        postAddMux = std::get<0>(cells[_pmg_idx]);
        const IdString &AB YS_MAYBE_UNUSED = std::get<1>(cells[_pmg_idx]);
        if (blacklist_cells.count(postAddMux)) continue;
        auto _pmg_backup_postAddMuxAB = postAddMuxAB;
        postAddMuxAB = AB;
        auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));
        block_15(recursion+1);
        postAddMuxAB = _pmg_backup_postAddMuxAB;
        if (rollback_ptr.second)
          rollback_cache.erase(rollback_ptr.first);
        if (rollback) {
          if (rollback != recursion) {
            postAddMux = _pmg_backup_postAddMux;
            return;
          }
          rollback = 0;
        }
      }
    }

    postAddMux = nullptr;
    block_15(recursion+1);
    postAddMux = _pmg_backup_postAddMux;
  }

  // techlibs/xilinx/xilinx_dsp.pmg:379
  void block_15(int recursion YS_MAYBE_UNUSED) {
    const SigSpec &argD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argD;
    const SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argQ;
    const SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_pack.clock;
    Cell* const &dsp YS_MAYBE_UNUSED = st_xilinx_dsp_pack.dsp;
    Cell* const &ffA1 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffA1;
    Cell* const &ffA2 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffA2;
    Cell* const &ffAD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffAD;
    Cell* const &ffB1 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffB1;
    Cell* const &ffB2 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffB2;
    Cell* const &ffD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffD;
    Cell* const &ffM YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffM;
    Cell* const &ffP YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffP;
    Cell* const &postAdd YS_MAYBE_UNUSED = st_xilinx_dsp_pack.postAdd;
    const IdString &postAddAB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.postAddAB;
    Cell* const &postAddMux YS_MAYBE_UNUSED = st_xilinx_dsp_pack.postAddMux;
    const IdString &postAddMuxAB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.postAddMuxAB;
    Cell* const &preAdd YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preAdd;
    Cell* const &preSub YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preSub;
    const SigSpec &sigA YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigA;
    const SigSpec &sigB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigB;
    const SigSpec &sigD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigD;
    const SigSpec &sigM YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigM;
    const SigSpec &sigP YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigP;
    SigSpec &sigC YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigC;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffclock;

    SigSpec _pmg_backup_sigC = sigC;

#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_16(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_xilinx_dsp_pack_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    if (postAddMux)
      sigC = port(postAddMux, postAddMuxAB == id_b_A ? id_b_B : id_b_A);

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

rollback_label:
    YS_MAYBE_UNUSED;

    sigC = _pmg_backup_sigC;
  }

  // techlibs/xilinx/xilinx_dsp.pmg:389
  void block_16(int recursion YS_MAYBE_UNUSED) {
    const SigSpec &argD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argD;
    const SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argQ;
    const SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_pack.clock;
    Cell* const &dsp YS_MAYBE_UNUSED = st_xilinx_dsp_pack.dsp;
    Cell* const &ffA1 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffA1;
    Cell* const &ffA2 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffA2;
    Cell* const &ffAD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffAD;
    Cell* const &ffB1 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffB1;
    Cell* const &ffB2 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffB2;
    Cell* const &ffD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffD;
    Cell* const &ffM YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffM;
    Cell* const &ffP YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffP;
    Cell* const &postAdd YS_MAYBE_UNUSED = st_xilinx_dsp_pack.postAdd;
    const IdString &postAddAB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.postAddAB;
    Cell* const &postAddMux YS_MAYBE_UNUSED = st_xilinx_dsp_pack.postAddMux;
    const IdString &postAddMuxAB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.postAddMuxAB;
    Cell* const &preAdd YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preAdd;
    Cell* const &preSub YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preSub;
    const SigSpec &sigA YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigA;
    const SigSpec &sigB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigB;
    const SigSpec &sigC YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigC;
    const SigSpec &sigD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigD;
    const SigSpec &sigM YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigM;
    const SigSpec &sigP YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigP;
    Cell* &overflow YS_MAYBE_UNUSED = st_xilinx_dsp_pack.overflow;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffclock;
    Cell* _pmg_backup_overflow = overflow;

    if (!(ffP)) {
      overflow = nullptr;
      block_17(recursion+1);
      overflow = _pmg_backup_overflow;
      return;
    }

    if (!(param(dsp, id_b_USE_PATTERN_DETECT).decode_string() == "NO_PATDET")) {
      overflow = nullptr;
      block_17(recursion+1);
      overflow = _pmg_backup_overflow;
      return;
    }

    index_16_key_type key;
    std::get<0>(key) = sigP;
    auto cells_ptr = index_16.find(key);

    if (cells_ptr != index_16.end()) {
      const vector<index_16_value_type> &cells = cells_ptr->second;
      for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {
        overflow = std::get<0>(cells[_pmg_idx]);
        const Const &B YS_MAYBE_UNUSED = std::get<1>(cells[_pmg_idx]);
        if (blacklist_cells.count(overflow)) continue;
        auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));
        block_17(recursion+1);
        if (rollback_ptr.second)
          rollback_cache.erase(rollback_ptr.first);
        if (rollback) {
          if (rollback != recursion) {
            overflow = _pmg_backup_overflow;
            return;
          }
          rollback = 0;
        }
      }
    }

    overflow = nullptr;
    block_17(recursion+1);
    overflow = _pmg_backup_overflow;
  }

  // techlibs/xilinx/xilinx_dsp.pmg:401
  void block_17(int recursion YS_MAYBE_UNUSED) {
    const SigSpec &argD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argD;
    const SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argQ;
    const SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_pack.clock;
    Cell* const &dsp YS_MAYBE_UNUSED = st_xilinx_dsp_pack.dsp;
    Cell* const &ffA1 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffA1;
    Cell* const &ffA2 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffA2;
    Cell* const &ffAD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffAD;
    Cell* const &ffB1 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffB1;
    Cell* const &ffB2 YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffB2;
    Cell* const &ffD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffD;
    Cell* const &ffM YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffM;
    Cell* const &ffP YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ffP;
    Cell* const &overflow YS_MAYBE_UNUSED = st_xilinx_dsp_pack.overflow;
    Cell* const &postAdd YS_MAYBE_UNUSED = st_xilinx_dsp_pack.postAdd;
    const IdString &postAddAB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.postAddAB;
    Cell* const &postAddMux YS_MAYBE_UNUSED = st_xilinx_dsp_pack.postAddMux;
    const IdString &postAddMuxAB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.postAddMuxAB;
    Cell* const &preAdd YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preAdd;
    Cell* const &preSub YS_MAYBE_UNUSED = st_xilinx_dsp_pack.preSub;
    const SigSpec &sigA YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigA;
    const SigSpec &sigB YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigB;
    const SigSpec &sigC YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigC;
    const SigSpec &sigD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigD;
    const SigSpec &sigM YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigM;
    const SigSpec &sigP YS_MAYBE_UNUSED = st_xilinx_dsp_pack.sigP;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffclock;

#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_18(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_xilinx_dsp_pack_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    accept;

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

rollback_label:
    YS_MAYBE_UNUSED;
  }

  void block_18(int recursion YS_MAYBE_UNUSED) {
  }

  // techlibs/xilinx/xilinx_dsp.pmg:412
  void block_19(int recursion YS_MAYBE_UNUSED) {
    const SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argQ;
    const SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_pack.clock;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffclock;

#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_xilinx_dsp_pack_ ## 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_20(recursion+1);
#undef reject
#undef accept
#undef finish
#undef branch
#undef subpattern

rollback_label:
    YS_MAYBE_UNUSED;
  }

  // techlibs/xilinx/xilinx_dsp.pmg:433
  void block_20(int recursion YS_MAYBE_UNUSED) {
    const SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argQ;
    const SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_pack.clock;
    Cell* &ff YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ff;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffclock;
    Cell* _pmg_backup_ff = ff;

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

    if (cells_ptr != index_20.end()) {
      const vector<index_20_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_21(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.pmg:451
  void block_21(int recursion YS_MAYBE_UNUSED) {
    const SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_pack.clock;
    Cell* const &ff YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ff;
    SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argQ;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffclock;

    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_22(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_xilinx_dsp_pack_ ## 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_22(recursion+1);
#undef reject
#undef accept
#undef finish
#undef branch
#undef subpattern

rollback_label:
    YS_MAYBE_UNUSED;

    argQ = _pmg_backup_argQ;
  }

  void block_22(int recursion YS_MAYBE_UNUSED) {
  }

  // techlibs/xilinx/xilinx_dsp.pmg:478
  void block_23(int recursion YS_MAYBE_UNUSED) {
    const SigSpec &argD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argD;
    const SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argQ;
    const SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_pack.clock;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffclock;

#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_24(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_xilinx_dsp_pack_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    dff = nullptr;
    for (auto c : argD.chunks())
      // Abandon matches when 'D' has the keep attribute set
      if (c.wire->get_bool_attribute(id_b_keep))
        reject;

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

rollback_label:
    YS_MAYBE_UNUSED;
  }

  // techlibs/xilinx/xilinx_dsp.pmg:486
  void block_24(int recursion YS_MAYBE_UNUSED) {
    const SigSpec &argD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argD;
    const SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argQ;
    const SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_pack.clock;
    Cell* &ff YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ff;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffclock;
    Cell* _pmg_backup_ff = ff;

    index_24_key_type key;
    std::get<0>(key) = argD[0];
    auto cells_ptr = index_24.find(key);

    if (cells_ptr != index_24.end()) {
      const vector<index_24_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 (!(GetSize(port(ff, id_b_D)) >= offset + GetSize(argD))) continue;
        if (!(port(ff, id_b_D).extract(offset, GetSize(argD)) == argD)) 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_25(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.pmg:501
  void block_25(int recursion YS_MAYBE_UNUSED) {
    const SigSpec &argD YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argD;
    const SigBit &clock YS_MAYBE_UNUSED = st_xilinx_dsp_pack.clock;
    Cell* const &ff YS_MAYBE_UNUSED = st_xilinx_dsp_pack.ff;
    SigSpec &argQ YS_MAYBE_UNUSED = st_xilinx_dsp_pack.argQ;
    Cell* &dff YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_xilinx_dsp_pack.dffclock;

    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_26(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_xilinx_dsp_pack_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    SigSpec D = port(ff, id_b_D);
    SigSpec Q = port(ff, id_b_Q);
    argQ = argD;
    argQ.replace(D, Q);
    // Abandon matches when 'Q' has a non-zero init attribute set
    // (not supported by DSP48E1)
    for (auto c : argQ.chunks()) {
      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;
    }
    dff = ff;
    dffQ = argQ;
    dffclock = port(ff, id_b_CLK);

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

rollback_label:
    YS_MAYBE_UNUSED;

    argQ = _pmg_backup_argQ;
  }

  void block_26(int recursion YS_MAYBE_UNUSED) {
  }
};
