// Generated by pmgen.py from passes/opt/peepopt_shiftmul_right.pmg
// Generated by pmgen.py from passes/opt/peepopt_shiftmul_left.pmg
// Generated by pmgen.py from passes/opt/peepopt_shiftadd.pmg
// Generated by pmgen.py from passes/opt/peepopt_muldiv.pmg
// Generated by pmgen.py from passes/opt/peepopt_muldiv_c.pmg
// Generated by pmgen.py from passes/opt/peepopt_formal_clockgateff.pmg

struct peepopt_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_2_key_type;
  typedef std::tuple<Cell*, IdString, IdString> index_2_value_type;
  dict<index_2_key_type, vector<index_2_value_type>> index_2;
  typedef std::tuple<> index_5_key_type;
  typedef std::tuple<Cell*> index_5_value_type;
  dict<index_5_key_type, vector<index_5_value_type>> index_5;
  typedef std::tuple<SigSpec> index_6_key_type;
  typedef std::tuple<Cell*> index_6_value_type;
  dict<index_6_key_type, vector<index_6_value_type>> index_6;
  typedef std::tuple<SigSpec> index_8_key_type;
  typedef std::tuple<Cell*, IdString, IdString> index_8_value_type;
  dict<index_8_key_type, vector<index_8_value_type>> index_8;
  typedef std::tuple<> index_11_key_type;
  typedef std::tuple<Cell*> index_11_value_type;
  dict<index_11_key_type, vector<index_11_value_type>> index_11;
  typedef std::tuple<SigSpec> index_13_key_type;
  typedef std::tuple<Cell*, IdString, IdString, bool, bool, bool, bool, bool, bool> index_13_value_type;
  dict<index_13_key_type, vector<index_13_value_type>> index_13;
  typedef std::tuple<> index_16_key_type;
  typedef std::tuple<Cell*> index_16_value_type;
  dict<index_16_key_type, vector<index_16_value_type>> index_16;
  typedef std::tuple<SigSpec, SigSpec> index_18_key_type;
  typedef std::tuple<Cell*> index_18_value_type;
  dict<index_18_key_type, vector<index_18_value_type>> index_18;
  typedef std::tuple<> index_21_key_type;
  typedef std::tuple<Cell*> index_21_value_type;
  dict<index_21_key_type, vector<index_21_value_type>> index_21;
  typedef std::tuple<SigSpec> index_23_key_type;
  typedef std::tuple<Cell*> index_23_value_type;
  dict<index_23_key_type, vector<index_23_value_type>> index_23;
  typedef std::tuple<> index_26_key_type;
  typedef std::tuple<Cell*> index_26_value_type;
  dict<index_26_key_type, vector<index_26_value_type>> index_26;
  typedef std::tuple<SigSpec, SigSpec> index_27_key_type;
  typedef std::tuple<Cell*, IdString, IdString> index_27_value_type;
  dict<index_27_key_type, vector<index_27_value_type>> index_27;
  dict<SigBit, pool<Cell*>> sigusers;
  pool<Cell*> blacklist_cells;
  pool<Cell*> autoremove_cells;
  dict<Cell*,int> rollback_cache;
  int rollback;

  struct state_formal_clockgateff_t {
    Cell* and_gate;
    SigSpec clk;
    SigSpec en;
    SigSpec gated_clk;
    Cell* latch;
    SigSpec latched_en;
    IdString latched_en_port_name;
  } st_formal_clockgateff;

  struct udata_formal_clockgateff_t {
  } ud_formal_clockgateff;

  struct state_muldiv_t {
    Cell* div;
    bool is_signed;
    Cell* mul;
    SigSpec t;
    SigSpec x;
    SigSpec y;
  } st_muldiv;

  struct udata_muldiv_t {
  } ud_muldiv;

  struct state_muldiv_c_t {
    SigSpec a;
    SigSpec b_const;
    Cell* div;
    Cell* mul;
    SigSpec mul_y;
  } st_muldiv_c;

  struct udata_muldiv_c_t {
  } ud_muldiv_c;

  struct state_shiftadd_t {
    Cell* add;
    int log2scale;
    bool msb_zeros;
    int offset;
    Cell* shift;
    SigSpec shift_amount;
    SigSpec var_signal;
    bool var_signed;
  } st_shiftadd;

  struct udata_shiftadd_t {
  } ud_shiftadd;

  struct state_shiftmul_left_t {
    int log2scale;
    Cell* mul;
    Const mul_const;
    SigSpec mul_din;
    Cell* neg;
    Cell* shift;
    SigSpec shift_amount;
  } st_shiftmul_left;

  struct udata_shiftmul_left_t {
  } ud_shiftmul_left;

  struct state_shiftmul_right_t {
    int log2scale;
    Cell* mul;
    Const mul_const;
    SigSpec mul_din;
    Cell* shift;
    SigSpec shift_amount;
  } st_shiftmul_right;

  struct udata_shiftmul_right_t {
  } ud_shiftmul_right;

  IdString id_b_A{"\\A"};
  IdString id_b_A_SIGNED{"\\A_SIGNED"};
  IdString id_b_A_WIDTH{"\\A_WIDTH"};
  IdString id_b_B{"\\B"};
  IdString id_b_B_SIGNED{"\\B_SIGNED"};
  IdString id_b_B_WIDTH{"\\B_WIDTH"};
  IdString id_b_D{"\\D"};
  IdString id_b_EN{"\\EN"};
  IdString id_b_EN_POLARITY{"\\EN_POLARITY"};
  IdString id_b_Q{"\\Q"};
  IdString id_b_WIDTH{"\\WIDTH"};
  IdString id_b_Y{"\\Y"};
  IdString id_b_Y_WIDTH{"\\Y_WIDTH"};
  IdString id_d_add{"$add"};
  IdString id_d_and{"$and"};
  IdString id_d_div{"$div"};
  IdString id_d_dlatch{"$dlatch"};
  IdString id_d_logic_and{"$logic_and"};
  IdString id_d_mul{"$mul"};
  IdString id_d_neg{"$neg"};
  IdString id_d_shift{"$shift"};
  IdString id_d_shiftx{"$shiftx"};
  IdString id_d_shl{"$shl"};
  IdString id_d_shr{"$shr"};
  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);
  }

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

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

  void setup(const vector<Cell*> &cells) {
    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 *shift = cell;
        index_0_value_type value;
        std::get<0>(value) = cell;
        if (!(shift->type.in(id_d_shift, id_d_shiftx, id_d_shr))) continue;
        index_0_key_type key;
        index_0[key].push_back(value);
      } while (0);
      do {
        Cell *mul = cell;
        index_2_value_type value;
        std::get<0>(value) = cell;
        if (!(mul->type.in(id_d_mul))) continue;
        vector<IdString> _pmg_choices_constport = {id_b_A, id_b_B};
        for (const IdString &constport : _pmg_choices_constport) {
        std::get<1>(value) = constport;
        IdString &varport = std::get<2>(value);
        varport = (constport == id_b_A ? id_b_B : id_b_A);
        index_2_key_type key;
        std::get<0>(key) = port(mul, id_b_Y);
        index_2[key].push_back(value);
        }
      } while (0);
      do {
        Cell *shift = cell;
        index_5_value_type value;
        std::get<0>(value) = cell;
        if (!(shift->type.in(id_d_shift, id_d_shiftx, id_d_shl))) continue;
        if (!(shift->type.in(id_d_shl) || param(shift, id_b_B_SIGNED).as_bool())) continue;
        index_5_key_type key;
        index_5[key].push_back(value);
      } while (0);
      do {
        Cell *neg = cell;
        index_6_value_type value;
        std::get<0>(value) = cell;
        if (!(neg->type == id_d_neg)) continue;
        index_6_key_type key;
        std::get<0>(key) = port(neg, id_b_Y);
        index_6[key].push_back(value);
      } while (0);
      do {
        Cell *mul = cell;
        index_8_value_type value;
        std::get<0>(value) = cell;
        if (!(mul->type.in(id_d_mul))) continue;
        vector<IdString> _pmg_choices_constport = {id_b_A, id_b_B};
        for (const IdString &constport : _pmg_choices_constport) {
        std::get<1>(value) = constport;
        IdString &varport = std::get<2>(value);
        varport = (constport == id_b_A ? id_b_B : id_b_A);
        index_8_key_type key;
        std::get<0>(key) = port(mul, id_b_Y);
        index_8[key].push_back(value);
        }
      } while (0);
      do {
        Cell *shift = cell;
        index_11_value_type value;
        std::get<0>(value) = cell;
        if (!(shift->type.in(id_d_shift, id_d_shiftx, id_d_shr))) continue;
        index_11_key_type key;
        index_11[key].push_back(value);
      } while (0);
      do {
        Cell *add = cell;
        index_13_value_type value;
        std::get<0>(value) = cell;
        if (!(add->type.in(id_d_add, id_d_sub))) continue;
        vector<IdString> _pmg_choices_constport = {id_b_A, id_b_B};
        for (const IdString &constport : _pmg_choices_constport) {
        std::get<1>(value) = constport;
        if (!(!port(add, constport).empty())) continue;
        if (!(port(add, constport).is_fully_const())) continue;
        IdString &varport = std::get<2>(value);
        varport = (constport == id_b_A ? id_b_B : id_b_A);
        if (!((GetSize(port(add, constport)) <= 24))) continue;
        if (!(( GetSize(port(add, id_b_Y)) > max(GetSize(port(add, id_b_A)), GetSize(port(add, id_b_B))) ))) continue;
        bool &varport_A = std::get<3>(value);
        varport_A = (varport == id_b_A);
        bool &is_sub = std::get<4>(value);
        is_sub = add->type.in(id_d_sub);
        bool &constport_signed = std::get<5>(value);
        constport_signed = param(add, !varport_A ? id_b_A_SIGNED : id_b_B_SIGNED).as_bool();
        bool &varport_signed = std::get<6>(value);
        varport_signed = param(add, varport_A ? id_b_A_SIGNED : id_b_B_SIGNED).as_bool();;
        bool &const_negative = std::get<7>(value);
        const_negative = (constport_signed && (port(add, constport).bits().back() == State::S1));
        bool &offset_negative = std::get<8>(value);
        offset_negative = ((is_sub && varport_A) ^ const_negative);
        if (!((add->type.in(id_d_add) || varport == id_b_A))) continue;
        index_13_key_type key;
        std::get<0>(key) = port(add, id_b_Y);
        index_13[key].push_back(value);
        }
      } while (0);
      do {
        Cell *mul = cell;
        index_16_value_type value;
        std::get<0>(value) = cell;
        if (!(mul->type == id_d_mul)) continue;
        if (!(GetSize(port(mul, id_b_A)) + GetSize(port(mul, id_b_B)) <= GetSize(port(mul, id_b_Y)))) continue;
        index_16_key_type key;
        index_16[key].push_back(value);
      } while (0);
      do {
        Cell *div = cell;
        index_18_value_type value;
        std::get<0>(value) = cell;
        if (!(div->type.in(id_d_div))) continue;
        index_18_key_type key;
        std::get<0>(key) = port(div, id_b_A);
        std::get<1>(key) = port(div, id_b_B);
        index_18[key].push_back(value);
      } while (0);
      do {
        Cell *mul = cell;
        index_21_value_type value;
        std::get<0>(value) = cell;
        if (!(mul->type == id_d_mul)) continue;
        index_21_key_type key;
        index_21[key].push_back(value);
      } while (0);
      do {
        Cell *div = cell;
        index_23_value_type value;
        std::get<0>(value) = cell;
        if (!(div->type == id_d_div)) continue;
        index_23_key_type key;
        std::get<0>(key) = remove_bottom_padding(port(div, id_b_A));
        index_23[key].push_back(value);
      } while (0);
      do {
        Cell *latch = cell;
        index_26_value_type value;
        std::get<0>(value) = cell;
        if (!(latch->type == id_d_dlatch)) continue;
        if (!(param(latch, id_b_WIDTH) == 1)) continue;
        if (!(param(latch, id_b_EN_POLARITY).as_bool() == false)) continue;
        index_26_key_type key;
        index_26[key].push_back(value);
      } while (0);
      do {
        Cell *and_gate = cell;
        index_27_value_type value;
        std::get<0>(value) = cell;
        if (!(and_gate->type.in(id_d_and, id_d_logic_and))) continue;
        if (!(param(and_gate, id_b_A_WIDTH) == 1)) continue;
        if (!(param(and_gate, id_b_B_WIDTH) == 1)) continue;
        if (!(param(and_gate, id_b_Y_WIDTH) == 1)) continue;
        vector<IdString> _pmg_choices_clk_port = {id_b_A, id_b_B};
        for (const IdString &clk_port : _pmg_choices_clk_port) {
        std::get<1>(value) = clk_port;
        IdString &latch_port = std::get<2>(value);
        latch_port = {clk_port == id_b_A ? id_b_B : id_b_A};
        index_27_key_type key;
        std::get<0>(key) = port(and_gate, clk_port);
        std::get<1>(key) = port(and_gate, latch_port);
        index_27[key].push_back(value);
        }
      } while (0);
    }
  }

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

  int run_formal_clockgateff(std::function<void()> on_accept_f) {
    log_assert(setup_done);
    accept_cnt = 0;
    on_accept = on_accept_f;
    rollback = 0;
    st_formal_clockgateff.and_gate = nullptr;
    st_formal_clockgateff.clk = SigSpec();
    st_formal_clockgateff.en = SigSpec();
    st_formal_clockgateff.gated_clk = SigSpec();
    st_formal_clockgateff.latch = nullptr;
    st_formal_clockgateff.latched_en = SigSpec();
    st_formal_clockgateff.latched_en_port_name = IdString();
    block_26(1);
    log_assert(rollback_cache.empty());
    return accept_cnt;
  }

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

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

  int run_muldiv(std::function<void()> on_accept_f) {
    log_assert(setup_done);
    accept_cnt = 0;
    on_accept = on_accept_f;
    rollback = 0;
    st_muldiv.div = nullptr;
    st_muldiv.is_signed = bool();
    st_muldiv.mul = nullptr;
    st_muldiv.t = SigSpec();
    st_muldiv.x = SigSpec();
    st_muldiv.y = SigSpec();
    block_16(1);
    log_assert(rollback_cache.empty());
    return accept_cnt;
  }

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

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

  int run_muldiv_c(std::function<void()> on_accept_f) {
    log_assert(setup_done);
    accept_cnt = 0;
    on_accept = on_accept_f;
    rollback = 0;
    st_muldiv_c.a = SigSpec();
    st_muldiv_c.b_const = SigSpec();
    st_muldiv_c.div = nullptr;
    st_muldiv_c.mul = nullptr;
    st_muldiv_c.mul_y = SigSpec();
    block_21(1);
    log_assert(rollback_cache.empty());
    return accept_cnt;
  }

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

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

  int run_shiftadd(std::function<void()> on_accept_f) {
    log_assert(setup_done);
    accept_cnt = 0;
    on_accept = on_accept_f;
    rollback = 0;
    st_shiftadd.add = nullptr;
    st_shiftadd.log2scale = int();
    st_shiftadd.msb_zeros = bool();
    st_shiftadd.offset = int();
    st_shiftadd.shift = nullptr;
    st_shiftadd.shift_amount = SigSpec();
    st_shiftadd.var_signal = SigSpec();
    st_shiftadd.var_signed = bool();
    block_11(1);
    log_assert(rollback_cache.empty());
    return accept_cnt;
  }

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

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

  int run_shiftmul_left(std::function<void()> on_accept_f) {
    log_assert(setup_done);
    accept_cnt = 0;
    on_accept = on_accept_f;
    rollback = 0;
    st_shiftmul_left.log2scale = int();
    st_shiftmul_left.mul = nullptr;
    st_shiftmul_left.mul_const = Const();
    st_shiftmul_left.mul_din = SigSpec();
    st_shiftmul_left.neg = nullptr;
    st_shiftmul_left.shift = nullptr;
    st_shiftmul_left.shift_amount = SigSpec();
    block_5(1);
    log_assert(rollback_cache.empty());
    return accept_cnt;
  }

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

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

  int run_shiftmul_right(std::function<void()> on_accept_f) {
    log_assert(setup_done);
    accept_cnt = 0;
    on_accept = on_accept_f;
    rollback = 0;
    st_shiftmul_right.log2scale = int();
    st_shiftmul_right.mul = nullptr;
    st_shiftmul_right.mul_const = Const();
    st_shiftmul_right.mul_din = SigSpec();
    st_shiftmul_right.shift = nullptr;
    st_shiftmul_right.shift_amount = SigSpec();
    block_0(1);
    log_assert(rollback_cache.empty());
    return accept_cnt;
  }

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

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

  void block_subpattern_formal_clockgateff_(int recursion) { block_26(recursion); }
  void block_subpattern_muldiv_(int recursion) { block_16(recursion); }
  void block_subpattern_muldiv_c_(int recursion) { block_21(recursion); }
  void block_subpattern_shiftadd_(int recursion) { block_11(recursion); }
  void block_subpattern_shiftmul_left_(int recursion) { block_5(recursion); }
  void block_subpattern_shiftmul_right_(int recursion) { block_0(recursion); }

  // passes/opt/peepopt_shiftmul_right.pmg:6
  void block_0(int recursion YS_MAYBE_UNUSED) {
    Cell* &shift YS_MAYBE_UNUSED = st_shiftmul_right.shift;
    Cell* _pmg_backup_shift = shift;

    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++) {
        shift = std::get<0>(cells[_pmg_idx]);
        if (blacklist_cells.count(shift)) continue;
        if (!(!port(shift, id_b_B).empty())) 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) {
            shift = _pmg_backup_shift;
            return;
          }
          rollback = 0;
        }
      }
    }

    shift = nullptr;
    shift = _pmg_backup_shift;
  }

  // passes/opt/peepopt_shiftmul_right.pmg:17
  void block_1(int recursion YS_MAYBE_UNUSED) {
    Cell* const &shift YS_MAYBE_UNUSED = st_shiftmul_right.shift;
    int &log2scale YS_MAYBE_UNUSED = st_shiftmul_right.log2scale;
    SigSpec &shift_amount YS_MAYBE_UNUSED = st_shiftmul_right.shift_amount;

#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_shiftmul_right_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    shift_amount = port(shift, id_b_B);
    if (shift->type.in(id_d_shr) || !param(shift, id_b_B_SIGNED).as_bool())
      shift_amount.append(State::S0);
    // at this point shift_amount is signed, make
    // sure we can never go negative
    if (shift_amount.bits().back() != State::S0)
      reject;
    while (shift_amount.bits().back() == State::S0) {
      shift_amount.remove(GetSize(shift_amount) - 1);
      if (shift_amount.empty()) reject;
    }
    log2scale = 0;
    while (shift_amount[0] == State::S0) {
      shift_amount.remove(0);
      if (shift_amount.empty()) reject;
      log2scale++;
    }
    if (GetSize(shift_amount) > 20)
      reject;

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

rollback_label:
    YS_MAYBE_UNUSED;

    log2scale = int();
    shift_amount = SigSpec();
  }

  // passes/opt/peepopt_shiftmul_right.pmg:46
  void block_2(int recursion YS_MAYBE_UNUSED) {
    const int &log2scale YS_MAYBE_UNUSED = st_shiftmul_right.log2scale;
    Cell* const &shift YS_MAYBE_UNUSED = st_shiftmul_right.shift;
    const SigSpec &shift_amount YS_MAYBE_UNUSED = st_shiftmul_right.shift_amount;
    Cell* &mul YS_MAYBE_UNUSED = st_shiftmul_right.mul;
    Const &mul_const YS_MAYBE_UNUSED = st_shiftmul_right.mul_const;
    SigSpec &mul_din YS_MAYBE_UNUSED = st_shiftmul_right.mul_din;
    Cell* _pmg_backup_mul = mul;

    index_2_key_type key;
    std::get<0>(key) = shift_amount;
    auto cells_ptr = index_2.find(key);

    if (cells_ptr != index_2.end()) {
      const vector<index_2_value_type> &cells = cells_ptr->second;
      for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {
        mul = std::get<0>(cells[_pmg_idx]);
        const IdString &constport YS_MAYBE_UNUSED = std::get<1>(cells[_pmg_idx]);
        const IdString &varport YS_MAYBE_UNUSED = std::get<2>(cells[_pmg_idx]);
        if (blacklist_cells.count(mul)) continue;
        if (!(!param(mul, id_b_A_SIGNED).as_bool())) continue;
        if (!(port(mul, constport).is_fully_const())) continue;
        auto _pmg_backup_mul_const = mul_const;
        mul_const = SigSpec({port(mul, constport), SigSpec(State::S0, log2scale)}).as_const();
        auto _pmg_backup_mul_din = mul_din;
        mul_din = mul->getPort(varport);
        auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));
        block_3(recursion+1);
        mul_const = _pmg_backup_mul_const;
        mul_din = _pmg_backup_mul_din;
        if (rollback_ptr.second)
          rollback_cache.erase(rollback_ptr.first);
        if (rollback) {
          if (rollback != recursion) {
            mul = _pmg_backup_mul;
            return;
          }
          rollback = 0;
        }
      }
    }

    mul = nullptr;
    mul = _pmg_backup_mul;
  }

  // passes/opt/peepopt_shiftmul_right.pmg:63
  void block_3(int recursion YS_MAYBE_UNUSED) {
    const int &log2scale YS_MAYBE_UNUSED = st_shiftmul_right.log2scale;
    Cell* const &mul YS_MAYBE_UNUSED = st_shiftmul_right.mul;
    const Const &mul_const YS_MAYBE_UNUSED = st_shiftmul_right.mul_const;
    const SigSpec &mul_din YS_MAYBE_UNUSED = st_shiftmul_right.mul_din;
    Cell* const &shift YS_MAYBE_UNUSED = st_shiftmul_right.shift;
    const SigSpec &shift_amount YS_MAYBE_UNUSED = st_shiftmul_right.shift_amount;

#define reject do { goto rollback_label; } while(0)
#define accept do { accept_cnt++; on_accept(); if (rollback) goto rollback_label; } while(0)
#define finish do { rollback = -1; goto rollback_label; } while(0)
#define branch do { block_4(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_shiftmul_right_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
  {
    if (mul_const.empty() || GetSize(mul_const) > 20)
      reject;
    // make sure there's no overlap in the signal
    // selections by the shiftmul pattern
    if (GetSize(port(shift, id_b_Y)) > mul_const.as_int())
      reject;
    int factor_bits = ceil_log2(mul_const.as_int());
    // make sure the multiplication never wraps around
    if (GetSize(shift_amount) + log2scale < factor_bits + GetSize(mul_din))
      reject;
    did_something = true;
    log("right shiftmul pattern in %s: shift=%s, mul=%s\n", log_id(module), log_id(shift), log_id(mul));
    int const_factor = mul_const.as_int();
    int new_const_factor = 1 << factor_bits;
    SigSpec padding(State::Sx, new_const_factor-const_factor);
    SigSpec old_a = port(shift, id_b_A), new_a;
    for (int i = 0; i*const_factor < GetSize(old_a); i++) {
      if ((i+1)*const_factor < GetSize(old_a)) {
        SigSpec slice = old_a.extract(i*const_factor, const_factor);
        new_a.append(slice);
        new_a.append(padding);
      } else {
        new_a.append(old_a.extract_end(i*const_factor));
      }
    }
    SigSpec new_b = {mul_din, SigSpec(State::S0, factor_bits)};
    if (param(shift, id_b_B_SIGNED).as_bool())
      new_b.append(State::S0);
    shift->setPort(id_b_A, new_a);
    shift->setParam(id_b_A_WIDTH, GetSize(new_a));
    shift->setPort(id_b_B, new_b);
    shift->setParam(id_b_B_WIDTH, GetSize(new_b));
    blacklist(shift);
    accept;
  }

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

rollback_label:
    YS_MAYBE_UNUSED;
  }

  void block_4(int recursion YS_MAYBE_UNUSED) {
  }

  // passes/opt/peepopt_shiftmul_left.pmg:6
  void block_5(int recursion YS_MAYBE_UNUSED) {
    Cell* &shift YS_MAYBE_UNUSED = st_shiftmul_left.shift;
    Cell* _pmg_backup_shift = shift;

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

    if (cells_ptr != index_5.end()) {
      const vector<index_5_value_type> &cells = cells_ptr->second;
      for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {
        shift = std::get<0>(cells[_pmg_idx]);
        if (blacklist_cells.count(shift)) continue;
        if (!(!port(shift, id_b_B).empty())) 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) {
            shift = _pmg_backup_shift;
            return;
          }
          rollback = 0;
        }
      }
    }

    shift = nullptr;
    shift = _pmg_backup_shift;
  }

  // passes/opt/peepopt_shiftmul_left.pmg:12
  void block_6(int recursion YS_MAYBE_UNUSED) {
    Cell* const &shift YS_MAYBE_UNUSED = st_shiftmul_left.shift;
    Cell* &neg YS_MAYBE_UNUSED = st_shiftmul_left.neg;
    Cell* _pmg_backup_neg = neg;

    if (!(shift->type.in(id_d_shift, id_d_shiftx))) {
      neg = nullptr;
      block_7(recursion+1);
      neg = _pmg_backup_neg;
      return;
    }

    index_6_key_type key;
    std::get<0>(key) = port(shift, id_b_B);
    auto cells_ptr = index_6.find(key);

    if (cells_ptr != index_6.end()) {
      const vector<index_6_value_type> &cells = cells_ptr->second;
      for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {
        neg = std::get<0>(cells[_pmg_idx]);
        if (blacklist_cells.count(neg)) continue;
        if (!(!port(shift, id_b_A).empty())) continue;
        auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));
        block_7(recursion+1);
        if (rollback_ptr.second)
          rollback_cache.erase(rollback_ptr.first);
        if (rollback) {
          if (rollback != recursion) {
            neg = _pmg_backup_neg;
            return;
          }
          rollback = 0;
        }
      }
    }

    neg = nullptr;
    neg = _pmg_backup_neg;
  }

  // passes/opt/peepopt_shiftmul_left.pmg:25
  void block_7(int recursion YS_MAYBE_UNUSED) {
    Cell* const &neg YS_MAYBE_UNUSED = st_shiftmul_left.neg;
    Cell* const &shift YS_MAYBE_UNUSED = st_shiftmul_left.shift;
    int &log2scale YS_MAYBE_UNUSED = st_shiftmul_left.log2scale;
    SigSpec &shift_amount YS_MAYBE_UNUSED = st_shiftmul_left.shift_amount;

#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_shiftmul_left_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    if (neg) {
      // case of `id_d_shift`, `id_d_shiftx`
      shift_amount = port(neg, id_b_A);
      if (!param(neg, id_b_A_SIGNED).as_bool())
        shift_amount.append(State::S0);
    } else {
      // case of `id_d_shl`
      shift_amount = port(shift, id_b_B);
      if (!param(shift, id_b_B_SIGNED).as_bool())
        shift_amount.append(State::S0);
    }
    // at this point shift_amount is signed, make
    // sure we can never go negative
    if (shift_amount.bits().back() != State::S0)
      reject;
    while (shift_amount.bits().back() == State::S0) {
      shift_amount.remove(GetSize(shift_amount) - 1);
      if (shift_amount.empty()) reject;
    }
    log2scale = 0;
    while (shift_amount[0] == State::S0) {
      shift_amount.remove(0);
      if (shift_amount.empty()) reject;
      log2scale++;
    }
    if (GetSize(shift_amount) > 20)
      reject;

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

rollback_label:
    YS_MAYBE_UNUSED;

    log2scale = int();
    shift_amount = SigSpec();
  }

  // passes/opt/peepopt_shiftmul_left.pmg:62
  void block_8(int recursion YS_MAYBE_UNUSED) {
    const int &log2scale YS_MAYBE_UNUSED = st_shiftmul_left.log2scale;
    Cell* const &neg YS_MAYBE_UNUSED = st_shiftmul_left.neg;
    Cell* const &shift YS_MAYBE_UNUSED = st_shiftmul_left.shift;
    const SigSpec &shift_amount YS_MAYBE_UNUSED = st_shiftmul_left.shift_amount;
    Cell* &mul YS_MAYBE_UNUSED = st_shiftmul_left.mul;
    Const &mul_const YS_MAYBE_UNUSED = st_shiftmul_left.mul_const;
    SigSpec &mul_din YS_MAYBE_UNUSED = st_shiftmul_left.mul_din;
    Cell* _pmg_backup_mul = mul;

    index_8_key_type key;
    std::get<0>(key) = shift_amount;
    auto cells_ptr = index_8.find(key);

    if (cells_ptr != index_8.end()) {
      const vector<index_8_value_type> &cells = cells_ptr->second;
      for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {
        mul = std::get<0>(cells[_pmg_idx]);
        const IdString &constport YS_MAYBE_UNUSED = std::get<1>(cells[_pmg_idx]);
        const IdString &varport YS_MAYBE_UNUSED = std::get<2>(cells[_pmg_idx]);
        if (blacklist_cells.count(mul)) continue;
        if (!(!param(mul, id_b_A_SIGNED).as_bool())) continue;
        if (!(port(mul, constport).is_fully_const())) continue;
        auto _pmg_backup_mul_const = mul_const;
        mul_const = SigSpec({port(mul, constport), SigSpec(State::S0, log2scale)}).as_const();
        auto _pmg_backup_mul_din = mul_din;
        mul_din = mul->getPort(varport);
        auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));
        block_9(recursion+1);
        mul_const = _pmg_backup_mul_const;
        mul_din = _pmg_backup_mul_din;
        if (rollback_ptr.second)
          rollback_cache.erase(rollback_ptr.first);
        if (rollback) {
          if (rollback != recursion) {
            mul = _pmg_backup_mul;
            return;
          }
          rollback = 0;
        }
      }
    }

    mul = nullptr;
    mul = _pmg_backup_mul;
  }

  // passes/opt/peepopt_shiftmul_left.pmg:79
  void block_9(int recursion YS_MAYBE_UNUSED) {
    const int &log2scale YS_MAYBE_UNUSED = st_shiftmul_left.log2scale;
    Cell* const &mul YS_MAYBE_UNUSED = st_shiftmul_left.mul;
    const Const &mul_const YS_MAYBE_UNUSED = st_shiftmul_left.mul_const;
    const SigSpec &mul_din YS_MAYBE_UNUSED = st_shiftmul_left.mul_din;
    Cell* const &neg YS_MAYBE_UNUSED = st_shiftmul_left.neg;
    Cell* const &shift YS_MAYBE_UNUSED = st_shiftmul_left.shift;
    const SigSpec &shift_amount YS_MAYBE_UNUSED = st_shiftmul_left.shift_amount;

#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_shiftmul_left_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
  {
    if (mul_const.empty() || GetSize(mul_const) > 20)
      reject;
    // make sure there's no overlap in the signal
    // selections by the shiftmul pattern
    if (GetSize(port(shift, id_b_A)) > mul_const.as_int())
      reject;
    int factor_bits = ceil_log2(mul_const.as_int());
    // make sure the multiplication never wraps around
    if (GetSize(shift_amount) < factor_bits + GetSize(mul_din))
      reject;
    if (neg) {
      // make sure the negation never wraps around
      if (GetSize(port(shift, id_b_B)) < factor_bits + GetSize(mul_din)
                      + log2scale + 1)
        reject;
    }
    did_something = true;
    log("left shiftmul pattern in %s: shift=%s, mul=%s\n", log_id(module), log_id(shift), log_id(mul));
    int const_factor = mul_const.as_int();
    int new_const_factor = 1 << factor_bits;
    SigSpec padding(State::Sm, new_const_factor-const_factor);
    SigSpec old_y = port(shift, id_b_Y), new_y;
    int trunc = 0;
    if (GetSize(old_y) % const_factor != 0) {
      trunc = const_factor - GetSize(old_y) % const_factor;
      old_y.append(SigSpec(State::Sm, trunc));
    }
    for (int i = 0; i*const_factor < GetSize(old_y); i++) {
      SigSpec slice = old_y.extract(i*const_factor, const_factor);
      new_y.append(slice);
      new_y.append(padding);
    }
    if (trunc > 0)
      new_y.remove(GetSize(new_y)-trunc, trunc);
    {
      // Now replace occurences of Sm in new_y with bits
      // of a dummy wire
      int padbits = 0;
      for (auto bit : new_y)
      if (bit == SigBit(State::Sm))
        padbits++;
      SigSpec padwire = module->addWire(NEW_ID, padbits);
      for (int i = new_y.size() - 1; i >= 0; i--)
      if (new_y[i] == SigBit(State::Sm)) {
        new_y[i] = padwire.bits().back();
        padwire.remove(padwire.size() - 1);
      }
    }
    SigSpec new_b = {mul_din, SigSpec(State::S0, factor_bits)};
    shift->setPort(id_b_Y, new_y);
    shift->setParam(id_b_Y_WIDTH, GetSize(new_y));
    if (shift->type == id_d_shl) {
      if (param(shift, id_b_B_SIGNED).as_bool())
        new_b.append(State::S0);
      shift->setPort(id_b_B, new_b);
      shift->setParam(id_b_B_WIDTH, GetSize(new_b));
    } else {
      SigSpec b_neg = module->addWire(NEW_ID, GetSize(new_b) + 1);
      module->addNeg(NEW_ID, new_b, b_neg);
      shift->setPort(id_b_B, b_neg);
      shift->setParam(id_b_B_WIDTH, GetSize(b_neg));
    }
    blacklist(shift);
    accept;
  }

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

rollback_label:
    YS_MAYBE_UNUSED;
  }

  void block_10(int recursion YS_MAYBE_UNUSED) {
  }

  // passes/opt/peepopt_shiftadd.pmg:9
  void block_11(int recursion YS_MAYBE_UNUSED) {
    Cell* &shift YS_MAYBE_UNUSED = st_shiftadd.shift;
    Cell* _pmg_backup_shift = shift;

    index_11_key_type key;
    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++) {
        shift = std::get<0>(cells[_pmg_idx]);
        if (blacklist_cells.count(shift)) continue;
        if (!(!port(shift, id_b_B).empty())) continue;
        auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));
        block_12(recursion+1);
        if (rollback_ptr.second)
          rollback_cache.erase(rollback_ptr.first);
        if (rollback) {
          if (rollback != recursion) {
            shift = _pmg_backup_shift;
            return;
          }
          rollback = 0;
        }
      }
    }

    shift = nullptr;
    shift = _pmg_backup_shift;
  }

  // passes/opt/peepopt_shiftadd.pmg:22
  void block_12(int recursion YS_MAYBE_UNUSED) {
    Cell* const &shift YS_MAYBE_UNUSED = st_shiftadd.shift;
    int &log2scale YS_MAYBE_UNUSED = st_shiftadd.log2scale;
    bool &msb_zeros YS_MAYBE_UNUSED = st_shiftadd.msb_zeros;
    SigSpec &shift_amount YS_MAYBE_UNUSED = st_shiftadd.shift_amount;

#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_shiftadd_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    shift_amount = port(shift, id_b_B);
    log2scale = 0;
    while (shift_amount[0] == State::S0) {
      shift_amount.remove(0);
      if (shift_amount.empty()) reject;
      log2scale++;
    }
    msb_zeros = 0;
    while (shift_amount.bits().back() == State::S0) {
      msb_zeros = true;
      shift_amount.remove(GetSize(shift_amount) - 1);
      if (shift_amount.empty()) reject;
    }

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

rollback_label:
    YS_MAYBE_UNUSED;

    log2scale = int();
    msb_zeros = bool();
    shift_amount = SigSpec();
  }

  // passes/opt/peepopt_shiftadd.pmg:45
  void block_13(int recursion YS_MAYBE_UNUSED) {
    const int &log2scale YS_MAYBE_UNUSED = st_shiftadd.log2scale;
    const bool &msb_zeros YS_MAYBE_UNUSED = st_shiftadd.msb_zeros;
    Cell* const &shift YS_MAYBE_UNUSED = st_shiftadd.shift;
    const SigSpec &shift_amount YS_MAYBE_UNUSED = st_shiftadd.shift_amount;
    Cell* &add YS_MAYBE_UNUSED = st_shiftadd.add;
    int &offset YS_MAYBE_UNUSED = st_shiftadd.offset;
    SigSpec &var_signal YS_MAYBE_UNUSED = st_shiftadd.var_signal;
    bool &var_signed YS_MAYBE_UNUSED = st_shiftadd.var_signed;
    Cell* _pmg_backup_add = add;

    index_13_key_type key;
    std::get<0>(key) = shift_amount;
    auto cells_ptr = index_13.find(key);

    if (cells_ptr != index_13.end()) {
      const vector<index_13_value_type> &cells = cells_ptr->second;
      for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {
        add = std::get<0>(cells[_pmg_idx]);
        const IdString &constport YS_MAYBE_UNUSED = std::get<1>(cells[_pmg_idx]);
        const IdString &varport YS_MAYBE_UNUSED = std::get<2>(cells[_pmg_idx]);
        const bool &varport_A YS_MAYBE_UNUSED = std::get<3>(cells[_pmg_idx]);
        const bool &is_sub YS_MAYBE_UNUSED = std::get<4>(cells[_pmg_idx]);
        const bool &constport_signed YS_MAYBE_UNUSED = std::get<5>(cells[_pmg_idx]);
        const bool &varport_signed YS_MAYBE_UNUSED = std::get<6>(cells[_pmg_idx]);
        const bool &const_negative YS_MAYBE_UNUSED = std::get<7>(cells[_pmg_idx]);
        const bool &offset_negative YS_MAYBE_UNUSED = std::get<8>(cells[_pmg_idx]);
        if (blacklist_cells.count(add)) continue;
        if (!(!(!offset_negative && varport_signed))) continue;
        if (!(!(offset_negative && !varport_signed))) continue;
        auto _pmg_backup_offset = offset;
        offset = ( (port(add, constport).as_int(constport_signed) << log2scale) * ( (is_sub && varport_A) ? -1 : 1 ) );
        auto _pmg_backup_var_signed = var_signed;
        var_signed = varport_signed;
        auto _pmg_backup_var_signal = var_signal;
        var_signal = add->getPort(varport);
        auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));
        block_14(recursion+1);
        offset = _pmg_backup_offset;
        var_signed = _pmg_backup_var_signed;
        var_signal = _pmg_backup_var_signal;
        if (rollback_ptr.second)
          rollback_cache.erase(rollback_ptr.first);
        if (rollback) {
          if (rollback != recursion) {
            add = _pmg_backup_add;
            return;
          }
          rollback = 0;
        }
      }
    }

    add = nullptr;
    add = _pmg_backup_add;
  }

  // passes/opt/peepopt_shiftadd.pmg:95
  void block_14(int recursion YS_MAYBE_UNUSED) {
    Cell* const &add YS_MAYBE_UNUSED = st_shiftadd.add;
    const int &log2scale YS_MAYBE_UNUSED = st_shiftadd.log2scale;
    const bool &msb_zeros YS_MAYBE_UNUSED = st_shiftadd.msb_zeros;
    const int &offset YS_MAYBE_UNUSED = st_shiftadd.offset;
    Cell* const &shift YS_MAYBE_UNUSED = st_shiftadd.shift;
    const SigSpec &shift_amount YS_MAYBE_UNUSED = st_shiftadd.shift_amount;
    const SigSpec &var_signal YS_MAYBE_UNUSED = st_shiftadd.var_signal;
    const bool &var_signed YS_MAYBE_UNUSED = st_shiftadd.var_signed;

#define reject do { goto rollback_label; } while(0)
#define accept do { accept_cnt++; on_accept(); if (rollback) goto rollback_label; } while(0)
#define finish do { rollback = -1; goto rollback_label; } while(0)
#define branch do { block_15(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_shiftadd_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
  {
    // positive constant offset with a signed variable (index) cannot be handled
    // the above filter should get rid of this case but 'offset' is calculated differently
    // due to limitations of state-variables in pmgen
    // it should only differ if previous passes create invalid data
    log_assert(!(offset>0 && var_signed));
    SigSpec old_a = port(shift, id_b_A); // data
    std::string location = shift->get_src_attribute();
    if(shiftadd_max_ratio>0 && offset<0 && -offset*shiftadd_max_ratio > old_a.size()) {
      log_warning("at %s: candiate for shiftadd optimization (shifting '%s' by '%s - %d' bits) "
            "was ignored to avoid high resource usage, see help peepopt\n",
            location.c_str(), log_signal(old_a), log_signal(var_signal), -offset);
      reject;
    }
    did_something = true;
    log("shiftadd pattern in %s: shift=%s, add/sub=%s, offset: %d\n", \
        log_id(module), log_id(shift), log_id(add), offset);
    SigSpec new_a;
    if(offset<0) {
      // data >> (...-c) transformed to {data, c'X} >> (...)
      SigSpec padding( (shift->type.in(id_d_shiftx) ? State::Sx : State::S0), -offset );
      new_a.append(padding);
      new_a.append(old_a);
    } else {
      // data >> (...+c) transformed to data[MAX:c] >> (...)
      if(offset < GetSize(old_a)) { // some signal bits left?
        new_a.append(old_a.extract_end(offset));
      } else {
        // warn user in case data is empty (no bits left)
        if (location.empty())
          location = shift->name.str();
        if(shift->type.in(id_d_shiftx))
          log_warning("at %s: result of indexed part-selection is always constant (selecting from '%s' with index '%s + %d')\n", \
                location.c_str(), log_signal(old_a), log_signal(var_signal), offset);
        else
          log_warning("at %s: result of shift operation is always constant (shifting '%s' by '%s + %d' bits)\n", \
                location.c_str(), log_signal(old_a), log_signal(var_signal), offset);
      }
    }
    SigSpec new_b = {var_signal, SigSpec(State::S0, log2scale)};
    if (msb_zeros || !var_signed)
      new_b.append(State::S0);
    shift->setPort(id_b_A, new_a);
    shift->setParam(id_b_A_WIDTH, GetSize(new_a));
    shift->setPort(id_b_B, new_b);
    shift->setParam(id_b_B_WIDTH, GetSize(new_b));
    blacklist(add);
    accept;
  }

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

rollback_label:
    YS_MAYBE_UNUSED;
  }

  void block_15(int recursion YS_MAYBE_UNUSED) {
  }

  // passes/opt/peepopt_muldiv.pmg:6
  void block_16(int recursion YS_MAYBE_UNUSED) {
    Cell* &mul YS_MAYBE_UNUSED = st_muldiv.mul;
    Cell* _pmg_backup_mul = mul;

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

    mul = nullptr;
    mul = _pmg_backup_mul;
  }

  // passes/opt/peepopt_muldiv.pmg:11
  void block_17(int recursion YS_MAYBE_UNUSED) {
    Cell* const &mul YS_MAYBE_UNUSED = st_muldiv.mul;
    bool &is_signed YS_MAYBE_UNUSED = st_muldiv.is_signed;
    SigSpec &t YS_MAYBE_UNUSED = st_muldiv.t;
    SigSpec &x YS_MAYBE_UNUSED = st_muldiv.x;
    SigSpec &y YS_MAYBE_UNUSED = st_muldiv.y;

#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_muldiv_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    t = port(mul, id_b_Y);
    x = port(mul, id_b_A);
    y = port(mul, id_b_B);
    is_signed = param(mul, id_b_A_SIGNED).as_bool();
    branch;
    std::swap(x, y);

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

rollback_label:
    YS_MAYBE_UNUSED;

    is_signed = bool();
    t = SigSpec();
    x = SigSpec();
    y = SigSpec();
  }

  // passes/opt/peepopt_muldiv.pmg:20
  void block_18(int recursion YS_MAYBE_UNUSED) {
    const bool &is_signed YS_MAYBE_UNUSED = st_muldiv.is_signed;
    Cell* const &mul YS_MAYBE_UNUSED = st_muldiv.mul;
    const SigSpec &t YS_MAYBE_UNUSED = st_muldiv.t;
    const SigSpec &x YS_MAYBE_UNUSED = st_muldiv.x;
    const SigSpec &y YS_MAYBE_UNUSED = st_muldiv.y;
    Cell* &div YS_MAYBE_UNUSED = st_muldiv.div;
    Cell* _pmg_backup_div = div;

    index_18_key_type key;
    std::get<0>(key) = t;
    std::get<1>(key) = x;
    auto cells_ptr = index_18.find(key);

    if (cells_ptr != index_18.end()) {
      const vector<index_18_value_type> &cells = cells_ptr->second;
      for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {
        div = std::get<0>(cells[_pmg_idx]);
        if (blacklist_cells.count(div)) continue;
        if (!(param(div, id_b_A_SIGNED).as_bool() == is_signed)) continue;
        auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));
        block_19(recursion+1);
        if (rollback_ptr.second)
          rollback_cache.erase(rollback_ptr.first);
        if (rollback) {
          if (rollback != recursion) {
            div = _pmg_backup_div;
            return;
          }
          rollback = 0;
        }
      }
    }

    div = nullptr;
    div = _pmg_backup_div;
  }

  // passes/opt/peepopt_muldiv.pmg:27
  void block_19(int recursion YS_MAYBE_UNUSED) {
    Cell* const &div YS_MAYBE_UNUSED = st_muldiv.div;
    const bool &is_signed YS_MAYBE_UNUSED = st_muldiv.is_signed;
    Cell* const &mul YS_MAYBE_UNUSED = st_muldiv.mul;
    const SigSpec &t YS_MAYBE_UNUSED = st_muldiv.t;
    const SigSpec &x YS_MAYBE_UNUSED = st_muldiv.x;
    const SigSpec &y YS_MAYBE_UNUSED = st_muldiv.y;

#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_muldiv_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    SigSpec div_y = port(div, id_b_Y);
    SigSpec val_y = y;
    if (GetSize(div_y) != GetSize(val_y))
      val_y.extend_u0(GetSize(div_y), param(div, id_b_A_SIGNED).as_bool());
    did_something = true;
    log("muldiv pattern in %s: mul=%s, div=%s\n", log_id(module), log_id(mul), log_id(div));
    module->connect(div_y, val_y);
    autoremove(div);
    accept;

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

rollback_label:
    YS_MAYBE_UNUSED;
  }

  void block_20(int recursion YS_MAYBE_UNUSED) {
  }

  // passes/opt/peepopt_muldiv_c.pmg:10
  void block_21(int recursion YS_MAYBE_UNUSED) {
    Cell* &mul YS_MAYBE_UNUSED = st_muldiv_c.mul;
    Cell* _pmg_backup_mul = mul;

    index_21_key_type key;
    auto cells_ptr = index_21.find(key);

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

    mul = nullptr;
    mul = _pmg_backup_mul;
  }

  // passes/opt/peepopt_muldiv_c.pmg:15
  void block_22(int recursion YS_MAYBE_UNUSED) {
    Cell* const &mul YS_MAYBE_UNUSED = st_muldiv_c.mul;
    SigSpec &a YS_MAYBE_UNUSED = st_muldiv_c.a;
    SigSpec &b_const YS_MAYBE_UNUSED = st_muldiv_c.b_const;
    SigSpec &mul_y YS_MAYBE_UNUSED = st_muldiv_c.mul_y;

#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_23(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_muldiv_c_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    // Get multiplier signals
    a = port(mul, id_b_A);
    b_const = port(mul, id_b_B);
    mul_y = port(mul, id_b_Y);
    // Fanout of each multiplier Y bit should be 1 (no bit-split)
    if (nusers(mul_y) != 2)
      reject;
    // A and B can be interchanged
    branch;
    std::swap(a, b_const);

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

rollback_label:
    YS_MAYBE_UNUSED;

    a = SigSpec();
    b_const = SigSpec();
    mul_y = SigSpec();
  }

  // passes/opt/peepopt_muldiv_c.pmg:30
  void block_23(int recursion YS_MAYBE_UNUSED) {
    const SigSpec &a YS_MAYBE_UNUSED = st_muldiv_c.a;
    const SigSpec &b_const YS_MAYBE_UNUSED = st_muldiv_c.b_const;
    Cell* const &mul YS_MAYBE_UNUSED = st_muldiv_c.mul;
    const SigSpec &mul_y YS_MAYBE_UNUSED = st_muldiv_c.mul_y;
    Cell* &div YS_MAYBE_UNUSED = st_muldiv_c.div;
    Cell* _pmg_backup_div = div;

    index_23_key_type key;
    std::get<0>(key) = mul_y;
    auto cells_ptr = index_23.find(key);

    if (cells_ptr != index_23.end()) {
      const vector<index_23_value_type> &cells = cells_ptr->second;
      for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {
        div = std::get<0>(cells[_pmg_idx]);
        if (blacklist_cells.count(div)) continue;
        if (!(b_const.is_fully_const())) continue;
        if (!(port(div, id_b_B).is_fully_const())) continue;
        auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));
        block_24(recursion+1);
        if (rollback_ptr.second)
          rollback_cache.erase(rollback_ptr.first);
        if (rollback) {
          if (rollback != recursion) {
            div = _pmg_backup_div;
            return;
          }
          rollback = 0;
        }
      }
    }

    div = nullptr;
    div = _pmg_backup_div;
  }

  // passes/opt/peepopt_muldiv_c.pmg:40
  void block_24(int recursion YS_MAYBE_UNUSED) {
    const SigSpec &a YS_MAYBE_UNUSED = st_muldiv_c.a;
    const SigSpec &b_const YS_MAYBE_UNUSED = st_muldiv_c.b_const;
    Cell* const &div YS_MAYBE_UNUSED = st_muldiv_c.div;
    Cell* const &mul YS_MAYBE_UNUSED = st_muldiv_c.mul;
    const SigSpec &mul_y YS_MAYBE_UNUSED = st_muldiv_c.mul_y;

#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_25(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_muldiv_c_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    // Get div signals
    SigSpec div_a = port(div, id_b_A);
    SigSpec c_const = port(div, id_b_B);
    SigSpec div_y = port(div, id_b_Y);
    // Get offset of multiplier result chunk in divider
    int offset = GetSize(div_a) - GetSize(mul_y);
    // Get properties and values of b_const and c_const
    // b_const may be coming from the A port
    // But it is an RTLIL invariant that A_SIGNED equals B_SIGNED
    bool b_const_signed = mul->getParam(ID::B_SIGNED).as_bool();
    bool c_const_signed = div->getParam(ID::B_SIGNED).as_bool();
    int b_const_int = b_const.as_int(b_const_signed);
    int c_const_int = c_const.as_int(c_const_signed);
    int b_const_int_shifted = b_const_int << offset;
    // Helper lambdas for two's complement math
    auto sign2sComplement = [](auto value, int numBits) {
        if (value & (1 << (numBits - 1))) {
           return -1;
        } else {
             return 1;
        }
    };
    auto twosComplement = [](auto value, int numBits) {
        if (value & (1 << (numBits - 1))) {
           return (~value) + 1; // invert bits before adding 1
        } else {
             return value;
        }
    };
    // Two's complement conversion
    if (b_const_signed)
      b_const_int = sign2sComplement(b_const_int, GetSize(b_const)) * twosComplement(b_const_int, GetSize(b_const));
    if (c_const_signed)
      c_const_int = sign2sComplement(c_const_int, GetSize(c_const)) * twosComplement(c_const_int, GetSize(c_const));
    // Calculate the constant and compress the width to fit the value
    Const const_ratio;
    Const b_const_actual;
    // Avoid division by zero
    if (c_const_int == 0)
      reject;
    b_const_actual = b_const_int_shifted;
    b_const_actual.compress(b_const_signed);
    const_ratio = b_const_int_shifted / c_const_int;
    const_ratio.compress(b_const_signed | c_const_signed);
    // Integer values should be lesser than 32 bits
    // This is because we are using C++ types, and int is 32 bits
    // FIXME: use long long or BigInteger to make pass work with >32 bits
    if (GetSize(mul->getParam(ID::B_WIDTH)) > 32)
      reject;
    if (GetSize(b_const) > 32)
      reject;
    if (GetSize(c_const) + offset > 32)
      reject;
    // Check for potential multiplier overflow
    if (GetSize(b_const_actual) + GetSize(a) > GetSize(mul_y))
      reject;
    // Check that there are only zeros before offset
    if (offset < 0 || !div_a.extract(0, offset).is_fully_zero())
      reject;
    // Check that b is divisible by c
    if (b_const_int_shifted % c_const_int != 0)
      reject;
    // Rewire to only keep multiplier
    mul->setPort(id_b_A, a);
    mul->setPort(id_b_B, const_ratio);
    mul->setPort(id_b_Y, div_y);
    // Remove divider
    autoremove(div);
    // Log, fixup, accept
    log("muldiv_const pattern in %s: mul=%s, div=%s\n", log_id(module), log_id(mul), log_id(div));
    mul->fixup_parameters();
    accept;

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

rollback_label:
    YS_MAYBE_UNUSED;
  }

  void block_25(int recursion YS_MAYBE_UNUSED) {
  }

  // passes/opt/peepopt_formal_clockgateff.pmg:23
  void block_26(int recursion YS_MAYBE_UNUSED) {
    SigSpec &clk YS_MAYBE_UNUSED = st_formal_clockgateff.clk;
    SigSpec &en YS_MAYBE_UNUSED = st_formal_clockgateff.en;
    Cell* &latch YS_MAYBE_UNUSED = st_formal_clockgateff.latch;
    SigSpec &latched_en YS_MAYBE_UNUSED = st_formal_clockgateff.latched_en;
    Cell* _pmg_backup_latch = latch;

    index_26_key_type key;
    auto cells_ptr = index_26.find(key);

    if (cells_ptr != index_26.end()) {
      const vector<index_26_value_type> &cells = cells_ptr->second;
      for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {
        latch = std::get<0>(cells[_pmg_idx]);
        if (blacklist_cells.count(latch)) continue;
        auto _pmg_backup_clk = clk;
        clk = port(latch, id_b_EN);
        auto _pmg_backup_en = en;
        en = port(latch, id_b_D);
        auto _pmg_backup_latched_en = latched_en;
        latched_en = port(latch, id_b_Q);
        auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));
        block_27(recursion+1);
        clk = _pmg_backup_clk;
        en = _pmg_backup_en;
        latched_en = _pmg_backup_latched_en;
        if (rollback_ptr.second)
          rollback_cache.erase(rollback_ptr.first);
        if (rollback) {
          if (rollback != recursion) {
            latch = _pmg_backup_latch;
            return;
          }
          rollback = 0;
        }
      }
    }

    latch = nullptr;
    latch = _pmg_backup_latch;
  }

  // passes/opt/peepopt_formal_clockgateff.pmg:32
  void block_27(int recursion YS_MAYBE_UNUSED) {
    const SigSpec &clk YS_MAYBE_UNUSED = st_formal_clockgateff.clk;
    const SigSpec &en YS_MAYBE_UNUSED = st_formal_clockgateff.en;
    Cell* const &latch YS_MAYBE_UNUSED = st_formal_clockgateff.latch;
    const SigSpec &latched_en YS_MAYBE_UNUSED = st_formal_clockgateff.latched_en;
    Cell* &and_gate YS_MAYBE_UNUSED = st_formal_clockgateff.and_gate;
    SigSpec &gated_clk YS_MAYBE_UNUSED = st_formal_clockgateff.gated_clk;
    IdString &latched_en_port_name YS_MAYBE_UNUSED = st_formal_clockgateff.latched_en_port_name;
    Cell* _pmg_backup_and_gate = and_gate;

    index_27_key_type key;
    std::get<0>(key) = clk;
    std::get<1>(key) = latched_en;
    auto cells_ptr = index_27.find(key);

    if (cells_ptr != index_27.end()) {
      const vector<index_27_value_type> &cells = cells_ptr->second;
      for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {
        and_gate = std::get<0>(cells[_pmg_idx]);
        const IdString &clk_port YS_MAYBE_UNUSED = std::get<1>(cells[_pmg_idx]);
        const IdString &latch_port YS_MAYBE_UNUSED = std::get<2>(cells[_pmg_idx]);
        if (blacklist_cells.count(and_gate)) continue;
        auto _pmg_backup_gated_clk = gated_clk;
        gated_clk = port(and_gate, id_b_Y);
        auto _pmg_backup_latched_en_port_name = latched_en_port_name;
        latched_en_port_name = latch_port;
        auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));
        block_28(recursion+1);
        gated_clk = _pmg_backup_gated_clk;
        latched_en_port_name = _pmg_backup_latched_en_port_name;
        if (rollback_ptr.second)
          rollback_cache.erase(rollback_ptr.first);
        if (rollback) {
          if (rollback != recursion) {
            and_gate = _pmg_backup_and_gate;
            return;
          }
          rollback = 0;
        }
      }
    }

    and_gate = nullptr;
    and_gate = _pmg_backup_and_gate;
  }

  // passes/opt/peepopt_formal_clockgateff.pmg:45
  void block_28(int recursion YS_MAYBE_UNUSED) {
    Cell* const &and_gate YS_MAYBE_UNUSED = st_formal_clockgateff.and_gate;
    const SigSpec &clk YS_MAYBE_UNUSED = st_formal_clockgateff.clk;
    const SigSpec &en YS_MAYBE_UNUSED = st_formal_clockgateff.en;
    const SigSpec &gated_clk YS_MAYBE_UNUSED = st_formal_clockgateff.gated_clk;
    Cell* const &latch YS_MAYBE_UNUSED = st_formal_clockgateff.latch;
    const SigSpec &latched_en YS_MAYBE_UNUSED = st_formal_clockgateff.latched_en;
    const IdString &latched_en_port_name YS_MAYBE_UNUSED = st_formal_clockgateff.latched_en_port_name;

#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_29(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_formal_clockgateff_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    log("replacing clock gate pattern in %s with ff: latch=%s, and=%s\n",
      log_id(module), log_id(latch), log_id(and_gate));
    // Add a flip-flop and rewire the AND gate to use the output of this flop
    // instead of the latch. We don't delete the latch in case its output is
    // used to drive other nodes. If it isn't, it will be trivially removed by
    // clean
    SigSpec flopped_en = module->addWire(NEW_ID);
    module->addDff(NEW_ID, clk, en, flopped_en, true, latch->get_src_attribute());
    and_gate->setPort(latched_en_port_name, flopped_en);
    did_something = true;
    accept;

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

rollback_label:
    YS_MAYBE_UNUSED;
  }

  void block_29(int recursion YS_MAYBE_UNUSED) {
  }
};
