23#define SIMPLE_INTRINSIC(GR, PFX, NAME, IF_SHIFT) \
24 DEPARSER_INTRINSIC(Tofino, GR, NAME, 1) { \
25 PFX.NAME.phv = intrin.vals[0].val->reg.deparser_id(); \
26 IF_SHIFT(PFX.NAME.shft = intrin.vals[0].val->lo;) \
27 if (!intrin.vals[0].pov.empty()) \
28 error(intrin.vals[0].pov.front().lineno, "No POV support in tofino " #NAME); \
31#define SIMPLE_INTRINSIC_RENAME(GR, PFX, NAME, REGNAME, IF_SHIFT) \
32 DEPARSER_INTRINSIC(Tofino, GR, NAME, 1) { \
33 PFX.REGNAME.phv = intrin.vals[0].val->reg.deparser_id(); \
34 IF_SHIFT(PFX.REGNAME.shft = intrin.vals[0].val->lo;) \
35 PFX.REGNAME.valid = 1; \
37#define IIR_MAIN_INTRINSIC(NAME, SHFT) SIMPLE_INTRINSIC(INGRESS, regs.input.iir.main_i, NAME, SHFT)
38#define IIR_INTRINSIC(NAME, SHFT) SIMPLE_INTRINSIC(INGRESS, regs.input.iir.ingr, NAME, SHFT)
39#define HIR_INTRINSIC(NAME, SHFT) SIMPLE_INTRINSIC(INGRESS, regs.header.hir.ingr, NAME, SHFT)
40#define HIR_INTRINSIC_RENAME(NAME, REGNAME, SHFT) \
41 SIMPLE_INTRINSIC_RENAME(INGRESS, regs.header.hir.ingr, NAME, REGNAME, SHFT)
42#define IER_MAIN_INTRINSIC(NAME, SHFT) SIMPLE_INTRINSIC(EGRESS, regs.input.ier.main_e, NAME, SHFT)
43#define HER_INTRINSIC(NAME, SHFT) SIMPLE_INTRINSIC(EGRESS, regs.header.her.egr, NAME, SHFT)
45IIR_MAIN_INTRINSIC(egress_unicast_port, NO)
46IIR_MAIN_INTRINSIC(drop_ctl, YES)
47IIR_INTRINSIC(copy_to_cpu, YES)
48HIR_INTRINSIC_RENAME(egress_multicast_group_0, egress_multicast_group[0], NO)
49HIR_INTRINSIC_RENAME(egress_multicast_group_1, egress_multicast_group[1], NO)
50HIR_INTRINSIC_RENAME(hash_lag_ecmp_mcast_0, hash_lag_ecmp_mcast[0], NO)
51HIR_INTRINSIC_RENAME(hash_lag_ecmp_mcast_1, hash_lag_ecmp_mcast[1], NO)
52HIR_INTRINSIC(copy_to_cpu_cos, YES)
53DEPARSER_INTRINSIC(
Tofino, INGRESS, ingress_port_source, 1) {
54 regs.header.hir.ingr.ingress_port.phv = intrin.vals[0].val->reg.deparser_id();
55 regs.header.hir.ingr.ingress_port.sel = 0;
57HIR_INTRINSIC(deflect_on_drop, YES)
58HIR_INTRINSIC(meter_color, YES)
59HIR_INTRINSIC(icos, YES)
60HIR_INTRINSIC(qid, YES)
64HIR_INTRINSIC(bypss_egr, YES)
65HIR_INTRINSIC(ct_disable, YES)
66HIR_INTRINSIC(ct_mcast, YES)
68IER_MAIN_INTRINSIC(egress_unicast_port, NO)
69IER_MAIN_INTRINSIC(drop_ctl, YES)
70HER_INTRINSIC(force_tx_err, YES)
71HER_INTRINSIC(tx_pkt_has_offsets, YES)
72HER_INTRINSIC(capture_tx_ts, YES)
73HER_INTRINSIC(coal, NO)
74HER_INTRINSIC(ecos, YES)
76#undef SIMPLE_INTRINSIC
77#undef IIR_MAIN_INTRINSIC
83#define TOFINO_DIGEST(GRESS, NAME, CFG, TBL, IFSHIFT, IFID, CNT) \
84 DEPARSER_DIGEST(Tofino, GRESS, NAME, CNT, IFSHIFT(can_shift = true;)) { \
85 CFG.phv = data.select->reg.deparser_id(); \
86 IFSHIFT(CFG.shft = data.shift + data.select->lo;) \
88 if (!data.select.pov.empty()) \
89 error(data.select.pov.front().lineno, "No POV bit support in tofino %s digest", \
91 for (auto &set : data.layout) { \
92 int id = set.first >> data.shift; \
94 bool first = true, ok = true; \
96 int maxidx = TBL[id].phvs.size() - 1; \
97 for (auto ® : set.second) { \
100 IFID(TBL[id].id_phv = reg->reg.deparser_id(); continue;) \
103 if (last == reg->reg.deparser_id() && reg->reg.size != 8) { \
104 error(data.lineno, "%s: %db container %s seen in consecutive locations", \
105 #NAME, reg->reg.size, reg->reg.name); \
108 for (int i = reg->reg.size / 8; i > 0; i--) { \
109 if (idx > maxidx) { \
110 error(data.lineno, "%s digest limited to %d bytes", #NAME, maxidx + 1); \
114 TBL[id].phvs[idx++] = reg->reg.deparser_id(); \
116 last = reg->reg.deparser_id(); \
124TOFINO_DIGEST(INGRESS, learning, regs.input.iir.ingr.learn_cfg, regs.input.iir.ingr.learn_tbl, NO,
126TOFINO_DIGEST(INGRESS, mirror, regs.header.hir.main_i.mirror_cfg, regs.header.hir.main_i.mirror_tbl,
128TOFINO_DIGEST(EGRESS, mirror, regs.header.her.main_e.mirror_cfg, regs.header.her.main_e.mirror_tbl,
130TOFINO_DIGEST(INGRESS, resubmit, regs.input.iir.ingr.resub_cfg, regs.input.iir.ingr.resub_tbl, YES,
136 std::vector<
Phv::Ref> &pov_order,
137 ordered_map<const
Phv::Register *,
unsigned> ®_pov,
139 json::vector &fd_entries, gress_t gress) {
140 std::map<unsigned, unsigned> pov;
143 unsigned pov_byte = 0, pov_size = 0, total_headers = 0;
144 for (
auto &ent : pov_order)
145 if (pov.count(ent->reg.deparser_id()) == 0) {
147 pov[ent->reg.deparser_id()] = pov_size;
148 pov_size += ent->reg.size;
149 for (unsigned i = 0; i < ent->reg.size; i += 8) {
150 if (pov_byte >= Target::Tofino::DEPARSER_MAX_POV_BYTES) {
152 "Exceeded hardware limit for POV bits (%d) in deparser. "
153 "Using %d or more headers. Please reduce the number of headers",
154 Target::Tofino::DEPARSER_MAX_POV_BYTES * 8, total_headers);
157 pov_layout[pov_byte++] = ent->reg.deparser_id();
160 while (pov_byte < Target::Tofino::DEPARSER_MAX_POV_BYTES) pov_layout[pov_byte++] = 0xff;
162 int row = -1, prev = -1, prev_pov = -1;
163 bool prev_is_checksum =
false;
165 unsigned total_bytes = 0;
167 for (
auto &ent : dict) {
168 unsigned size = ent.what->size();
170 int pov_bit = pov[ent.pov.front()->reg.deparser_id()] + ent.pov.front()->lo;
172 if (options.match_compiler) {
176 prev_is_checksum =
true;
178 if (prev_is_checksum) prev_pov = -1;
179 prev_is_checksum =
false;
184 int(ent.what->encode()) == prev && ent.what->size() & 6)
185 error(ent.lineno,
"16 and 32-bit container cannot be repeatedly deparsed");
187 if (pov_bit != prev_pov || pos >= 4 ) {
189 fde_control[row].num_bytes = pos & 3;
190 fde_data[row].num_bytes = pos & 3;
193 if (++row >= Target::Tofino::DEPARSER_MAX_FD_ENTRIES) {
195 "Exceeded hardware limit for "
196 "deparser field dictionary entries (%d). Using %d headers and %" PRIu64
197 " containers. Please reduce the number of headers and/or their length.",
198 Target::Tofino::DEPARSER_MAX_FD_ENTRIES, total_headers,
199 uint64_t(dict.size()));
202 fde_control[row].pov_sel = pov_bit;
203 fde_control[row].version = 0xf;
204 fde_control[row].valid = 1;
207 if (prev_row != row) {
210 fd[
"Field Dictionary Number"] = prev_row;
211 fd_entry[
"entry"] = prev_row;
212 auto prevPovReg = Phv::reg(pov_layout[fde_control[prev_row].pov_sel.value / 8]);
213 auto prevPovBit = fde_control[prev_row].pov_sel.value;
214 auto prevPovOffset = prevPovBit - reg_pov[prevPovReg];
215 Deparser::write_pov_in_json(fd, fd_entry, prevPovReg, prevPovBit, prevPovOffset);
216 fd[
"Content"] = std::move(chunk_bytes);
217 fd_entry[
"chunks"] = std::move(fd_entry_chunk_bytes);
218 fd_gress.push_back(std::move(fd));
219 fd_entries.push_back(std::move(fd_entry));
222 auto povReg = Phv::reg(pov_layout[fde_control[row].pov_sel.value / 8]);
223 auto povBit = fde_control[row].pov_sel.value % povReg->size;
227 chunk_byte[
"Byte"] = pos;
228 fd_entry_chunk_byte[
"chunk_number"] = pos;
229 auto phvReg = Phv::reg(ent.what->encode());
230 if (ent.what->encode() < CHECKSUM_ENGINE_PHVID_TOFINO_LOW ||
231 ent.what->encode() > CHECKSUM_ENGINE_PHVID_TOFINO_HIGH) {
232 write_field_name_in_json(phvReg, povReg, povBit, chunk_byte, fd_entry_chunk, 11,
235 write_csum_const_in_json(ent.what->encode(), chunk_byte, fd_entry_chunk, gress);
237 fd_entry_chunk_byte[
"chunk"] = std::move(fd_entry_chunk);
238 chunk_bytes.push_back(std::move(chunk_byte.clone()));
239 fd_entry_chunk_bytes.push_back(std::move(fd_entry_chunk_byte.clone()));
240 fde_data[row].phv[pos++] = ent.what->encode();
244 prev = ent.what->encode();
247 fde_control[row].num_bytes = pos & 3;
248 fde_data[row].num_bytes = pos & 3;
263 unsigned max_bytes_for_rows_occupied = 4 * (row + 1);
264 double occupancy = 0.0;
266 if (max_bytes_for_rows_occupied > 0)
268 static_cast<double>(total_bytes) /
static_cast<double>(max_bytes_for_rows_occupied);
270 if (total_bytes > 64 && occupancy < (240.0 / 288.0)) {
271 std::stringstream warn_msg;
272 warn_msg.precision(4);
273 warn_msg <<
"Deparser field dictionary occupancy is too sparse.";
274 warn_msg <<
"\nHardware requires an occupancy of " << 100.0 * 240.0 / 288.0
275 <<
" to deparse the output header,";
276 warn_msg <<
"\nbut the PHV layout for the header structures was such that"
277 " the occupancy was only "
278 << 100.0 * occupancy <<
".";
279 warn_msg <<
"\nThis situation is usually caused by a program that has one or"
280 " more of the following requirements:";
281 warn_msg <<
"\n 1. many 'short' headers that are not guaranteed to coexist"
282 " (e.g. less than 4 bytes)";
283 warn_msg <<
"\n 2. many packet headers that are not multiples of 4 bytes";
284 warn_msg <<
"\n 3. many conditionally updated checksums";
285 warning(0,
"%s", warn_msg.str().c_str());
289template <
typename IN_GRP,
typename IN_SPLIT,
typename EG_GRP,
typename EG_SPLIT>
290void tofino_phv_ownership(bitvec phv_use[2], IN_GRP &in_grp, IN_SPLIT &in_split, EG_GRP &eg_grp,
291 EG_SPLIT &eg_split,
unsigned first,
unsigned count) {
292 BUG_CHECK(in_grp.val.size() == eg_grp.val.size(),
"in_grp and eg_grp must have same size");
293 BUG_CHECK(in_split.val.size() == eg_split.val.size(),
294 "in_split and eg_split must have same size");
295 BUG_CHECK((in_grp.val.size() + 1) * in_split.val.size() == count,
296 "in_grp and in_split must have same size");
297 unsigned group_size = in_split.val.size();
300 unsigned reg = first;
301 for (
unsigned i = 0; i < in_grp.val.size(); i++, reg += group_size) {
302 unsigned last = reg + group_size - 1;
304 if (phv_use[INGRESS].getrange(reg, group_size)) {
305 in_grp.val |= 1U << i;
306 if (i * group_size >= 16 && i * group_size < 32)
307 error(0,
"%s..%s(R%d..R%d) used by ingress deparser but only available to egress",
308 Phv::reg(reg)->name, Phv::reg(last)->name, reg, last);
312 if (phv_use[EGRESS].getrange(reg, group_size)) {
313 eg_grp.val |= 1U << i;
314 if (i * group_size < 16)
315 error(0,
"%s..%s(R%d..R%d) used by egress deparser but only available to ingress",
316 Phv::reg(reg)->name, Phv::reg(last)->name, reg, last);
321 error(0,
"%s..%s(R%d..R%d) used by both ingress and egress deparser",
322 Phv::reg(reg)->name, Phv::reg(last)->name, reg, last);
324 in_split.val = phv_use[INGRESS].getrange(reg, group_size);
325 eg_split.val = phv_use[EGRESS].getrange(reg, group_size);
328static short tofino_phv2cksum[Target::Tofino::Phv::NUM_PHV_REGS][2] = {
702#define TAGALONG_THREAD_BASE \
703 (Target::Tofino::Phv::COUNT_8BIT_TPHV + Target::Tofino::Phv::COUNT_16BIT_TPHV + \
704 2 * Target::Tofino::Phv::COUNT_32BIT_TPHV)
706template <
typename DTYPE,
typename STYPE>
707static void copy_csum_cfg_entry(DTYPE &dst_unit, STYPE &src_unit) {
708 BUG_CHECK(dst_unit.size() == src_unit.size(),
"dst_unit and src_unit have different sizes");
710 for (
unsigned i = 0; i < dst_unit.size(); i++) {
711 auto &src = src_unit[i];
712 auto &dst = dst_unit[i];
714 dst.zero_l_s_b = src.zero_l_s_b;
715 dst.zero_m_s_b = src.zero_m_s_b;
720template <
class ENTRIES>
721static void init_tofino_checksum_entry(ENTRIES &entry) {
722 entry.zero_l_s_b = 1;
723 entry.zero_l_s_b.rewrite();
724 entry.zero_m_s_b = 1;
725 entry.zero_m_s_b.rewrite();
727 entry.swap.rewrite();
730template <
typename IPO,
typename HPO>
734 BUG_CHECK(tofino_phv2cksum[Target::Tofino::Phv::NUM_PHV_REGS - 1][0] == 143,
735 "invalid phv2cksum");
736 for (
int i = 0; i < Target::Tofino::DEPARSER_CHECKSUM_UNITS; i++) {
737 auto &main_unit = main_csum_units[i].csum_cfg_entry;
738 auto &tagalong_unit = tagalong_csum_units[i].csum_cfg_entry;
739 auto &tagalong_unit_zeros_as_ones = tagalong_csum_units[i].zeros_as_ones;
740 for (
auto &ent : main_unit) init_tofino_checksum_entry(ent);
741 for (
auto &ent : tagalong_unit) init_tofino_checksum_entry(ent);
742 if (checksum_unit[i].entries.empty())
continue;
746 BUG_CHECK(checksum_unit[i].entries.size() == 1,
747 "multiple partial checksum unit not supported");
748 auto &checksum_unit_entries = checksum_unit[i].entries[i];
749 for (
auto ® : checksum_unit_entries) {
752 int idx = reg->reg.deparser_id();
753 if (!reg.pov.empty())
754 error(reg.pov.front().lineno,
"No POV support in tofino checksum");
755 auto cksum_idx0 = tofino_phv2cksum[idx][0];
756 auto cksum_idx1 = tofino_phv2cksum[idx][1];
757 BUG_CHECK(cksum_idx0 >= 0,
"invalid phv2cksum");
759 write_checksum_entry(tagalong_unit[cksum_idx0], mask & 3, swap & 1, i,
762 write_checksum_entry(tagalong_unit[cksum_idx1], mask >> 2, swap >> 1, i,
765 BUG_CHECK((mask >> 2 == 0) && (swap >> 1 == 0),
"invalid phv2cksum");
767 write_checksum_entry(main_unit[cksum_idx0], mask & 3, swap & 1, i, reg->reg.name);
769 write_checksum_entry(main_unit[cksum_idx1], mask >> 2, swap >> 1, i,
772 BUG_CHECK((mask >> 2 == 0) && (swap >> 1 == 0),
"invalid phv2cksum");
776 int idx = i + TAGALONG_THREAD_BASE + gress * Target::Tofino::DEPARSER_CHECKSUM_UNITS;
777 write_checksum_entry(tagalong_unit[idx], 0x3, 0x0, i);
779 tagalong_unit_zeros_as_ones.en = checksum_unit[i].zeros_as_ones_en;
780 main_unit.set_modified();
781 tagalong_unit.set_modified();
785static void tofino_checksum_units(
786 Target::Tofino::deparser_regs ®s,
788 for (
unsigned id = 2;
id < MAX_DEPARSER_CHECKSUM_UNITS;
id++) {
789 if (!full_checksum_unit[0][
id].entries.empty() &&
790 !full_checksum_unit[1][
id].entries.empty())
791 error(-1,
"deparser checksum unit %d used in both ingress and egress",
id);
794 tofino_checksum_units(regs.input.iim.ii_phv_csum.csum_cfg,
795 regs.header.him.hi_tphv_csum.csum_cfg, INGRESS,
796 full_checksum_unit[INGRESS]);
797 tofino_checksum_units(regs.input.iem.ie_phv_csum.csum_cfg,
798 regs.header.hem.he_tphv_csum.csum_cfg, EGRESS,
799 full_checksum_unit[EGRESS]);
802 for (
unsigned id = 2;
id < Target::Tofino::DEPARSER_CHECKSUM_UNITS;
id++) {
803 auto &eg_main_unit = regs.input.iem.ie_phv_csum.csum_cfg[id].csum_cfg_entry;
804 auto &ig_main_unit = regs.input.iim.ii_phv_csum.csum_cfg[id].csum_cfg_entry;
806 auto &eg_tphv_unit = regs.header.hem.he_tphv_csum.csum_cfg[id].csum_cfg_entry;
807 auto &ig_tphv_unit = regs.header.him.hi_tphv_csum.csum_cfg[id].csum_cfg_entry;
809 if (!full_checksum_unit[0][
id].entries.empty()) {
810 copy_csum_cfg_entry(eg_main_unit, ig_main_unit);
811 copy_csum_cfg_entry(eg_tphv_unit, ig_tphv_unit);
812 }
else if (!full_checksum_unit[1][
id].entries.empty()) {
813 copy_csum_cfg_entry(ig_main_unit, eg_main_unit);
814 copy_csum_cfg_entry(ig_tphv_unit, eg_tphv_unit);
820void Deparser::write_config(Target::Tofino::deparser_regs ®s) {
821 regs.input.icr.inp_cfg.disable();
822 regs.input.icr.intr.disable();
823 regs.header.hem.he_edf_cfg.disable();
824 regs.header.him.hi_edf_cfg.disable();
826 tofino_checksum_units(regs, full_checksum_unit);
827 json::map field_dictionary_alloc;
828 json::vector fd_gress;
829 json::vector fde_entries_i;
830 json::vector fde_entries_e;
833 json::vector resources_deparser;
836 tofino_field_dictionary(regs.input.iim.ii_fde_pov.fde_pov, regs.header.him.hi_fde_phv.fde_phv,
837 regs.input.iir.main_i.pov.phvs, pov_order[INGRESS], pov[INGRESS],
838 dictionary[INGRESS], fd_gress, fde_entries_i, INGRESS);
839 field_dictionary_alloc[
"ingress"] = std::move(fd_gress);
841 tofino_field_dictionary(regs.input.iem.ie_fde_pov.fde_pov, regs.header.hem.he_fde_phv.fde_phv,
842 regs.input.ier.main_e.pov.phvs, pov_order[EGRESS], pov[EGRESS],
843 dictionary[EGRESS], fd_gress, fde_entries_e, EGRESS);
844 field_dictionary_alloc[
"egress"] = std::move(fd_gress);
846 if (Log::verbosity() > 0) {
847 auto json_dump = open_output(
"logs/field_dictionary.log");
848 *json_dump << &field_dictionary_alloc;
851 report_resources_deparser_json(fde_entries_i, fde_entries_e);
853 if (Phv::use(INGRESS).intersects(Phv::use(EGRESS))) {
854 warning(lineno[INGRESS],
"Registers used in both ingress and egress in pipeline: %s",
855 Phv::db_regset(Phv::use(INGRESS) & Phv::use(EGRESS)).c_str());
859 Phv::unsetuse(INGRESS, phv_use[EGRESS]);
860 Phv::unsetuse(EGRESS, phv_use[INGRESS]);
863 tofino_phv_ownership(phv_use, regs.input.iir.ingr.phv8_grp, regs.input.iir.ingr.phv8_split,
864 regs.input.ier.egr.phv8_grp, regs.input.ier.egr.phv8_split,
865 Target::Tofino::Phv::FIRST_8BIT_PHV, Target::Tofino::Phv::COUNT_8BIT_PHV);
866 tofino_phv_ownership(phv_use, regs.input.iir.ingr.phv16_grp, regs.input.iir.ingr.phv16_split,
867 regs.input.ier.egr.phv16_grp, regs.input.ier.egr.phv16_split,
868 Target::Tofino::Phv::FIRST_16BIT_PHV,
869 Target::Tofino::Phv::COUNT_16BIT_PHV);
870 tofino_phv_ownership(phv_use, regs.input.iir.ingr.phv32_grp, regs.input.iir.ingr.phv32_split,
871 regs.input.ier.egr.phv32_grp, regs.input.ier.egr.phv32_split,
872 Target::Tofino::Phv::FIRST_32BIT_PHV,
873 Target::Tofino::Phv::COUNT_32BIT_PHV);
875 for (
unsigned i = 0; i < 8; i++) {
876 if (phv_use[EGRESS].intersects(Target::Tofino::Phv::tagalong_groups[i])) {
877 regs.input.icr.tphv_cfg.i_e_assign |= 1 << i;
878 if (phv_use[INGRESS].intersects(Target::Tofino::Phv::tagalong_groups[i])) {
879 error(lineno[INGRESS],
880 "tagalong group %d used in both ingress and "
887 for (
auto &intrin : intrinsics) intrin.type->setregs(regs, *
this, intrin);
889 if (!regs.header.hir.ingr.ingress_port.sel.modified())
890 regs.header.hir.ingr.ingress_port.sel = 1;
892 for (
auto &digest : digests) digest.type->setregs(regs, *
this, digest);
901 if (error_count == 0 && options.gen_json) {
902 regs.input.emit_json(*open_output(
"regs.all.deparser.input_phase.cfg.json"));
903 regs.header.emit_json(*open_output(
"regs.all.deparser.header_phase.cfg.json"));
905 TopLevel::regs<Target::Tofino>()->reg_pipe.deparser.hdr.set(
"regs.all.deparser.header_phase",
907 TopLevel::regs<Target::Tofino>()->reg_pipe.deparser.inp.set(
"regs.all.deparser.input_phase",
912unsigned Deparser::FDEntry::Checksum::encode<Target::Tofino>() {
913 return CHECKSUM_ENGINE_PHVID_TOFINO_LOW + (gress * CHECKSUM_ENGINE_PHVID_TOFINO_PER_GRESS) +
918unsigned Deparser::FDEntry::Constant::encode<Target::Tofino>() {
919 error(lineno,
"Tofino deparser does not support constant entries");
924void Deparser::gen_learn_quanta(Target::Tofino::parser_regs ®s,
json::vector &learn_quanta) {}
Definition bf-asm/phv.h:32
Definition checked_array.h:34
Definition backends/tofino/bf-asm/json.h:300
Definition backends/tofino/bf-asm/json.h:222
void process()
optionally process the data if not done during parsing
Definition tofino/bf-asm/deparser.cpp:654
Definition tofino/bf-asm/deparser.h:40
Definition tofino/bf-asm/deparser.h:137
void warning(const char *format, Args &&...args)
Report a warning with the given message.
Definition lib/error.h:128
void error(const char *format, Args &&...args)
Report an error with the given message.
Definition lib/error.h:58
Definition tofino/action_data_bus.cpp:28
Definition tofino/bf-asm/deparser.cpp:70
Definition tofino/bf-asm/deparser.cpp:53