P4C
The P4 Compiler
Loading...
Searching...
No Matches
init_in_mau.h
1
19#ifndef BF_P4C_PHV_INIT_IN_MAU_H_
20#define BF_P4C_PHV_INIT_IN_MAU_H_
21
22#include "backends/tofino/bf-p4c/phv/add_initialization.h"
23#include "backends/tofino/bf-p4c/phv/phv_fields.h"
24#include "ir/ir-generated.h"
25#include "ir/visitor.h"
26
27// *****************************************************************************************
28// AddInitTable class adds a new table called __mau_inits_table__ in the beginning of the
29// first Table Sequence in the IR with one action that contains
30// zero-initializations to selected field-slices. The table is added
31// to handle initializations that cannot be done in the parser.
32//
33// Currently it handles initializations for non-extracted field which
34// are packed with extracted fields in the same slice list, thus need
35// to be allocated in the same container. An example of this happens
36// in Arista's profile 'obfucscated-map.p4' where 2 header fields are
37// aliased with 2 metadata fields. One of the metadata fields is
38// defined in the parser whereas the other is not. The profile is
39// quite complex and removing the alias results in non-fitting on
40// Tofino-1 device.
41//
42// The field-slices that get initialized are contained in
43// "mauInitFields" set. This container is populated during phv
44// allocation by "ParserPackingValidator" which checks for packing
45// constraints due to parsing. During Trivial allocation where packing
46// is minimal due to infinite phv resources we mark pairs of field-slices that
47// belong in the same slice list and one of the slices is extracted
48// whereas the other is not.
49// During regular PHV-Allocation, field-slices will not be added to "mauInitFields".
50// The packing will be rejected unless Trivial PHV-Allocation marked
51// the pair of fields for initialization which most likely means that
52// Trivial Allocation was not able to pack fields in a way that
53// initializations were not required.
54//
55// The "AddInitsInMAU" pass is run twice in the backend list of passes:
56// Once before PHV-Allocation and once after PHV-Allocation (before Table-Placement)
57// The reason for running this pass twice is due to the fact that the
58// added table is removed when progressing from Table-Placement to
59// real PHV-Allocation in the alt-phv-alloc flow and thus we lose its
60// physical stage info as well.
61//
62// *TODO* Eliminate the first instance of "AddInitsInMAU" before
63// PHV-Allocation by handling the physical-stage info issues that
64// arise.
65//
66// *TODO* Use "mauInitFields" to also handle initialization for fields
67// that are upcasted in the parser.
68// *****************************************************************************************
69class AddInitTable : public Transform {
70 private:
71 PhvInfo &phv;
72 const MapFieldToExpr &fieldToExpr;
73 std::set<PHV::FieldRange> &mauInitFields;
74 std::vector<IR::Slice *> mauInitSlices;
75 bool ingress_inits = false, egress_inits = false;
76 bool check_container;
77
78 bool same_container(PHV::FieldSlice sl1, PHV::FieldSlice sl2) {
79 if (!check_container) return true;
80
81 for (const auto &alloc_sl1 : sl1.field()->get_alloc()) {
82 // Look for corresponding allocated slice of FieldSlice sl1
83 if (sl1.range().overlaps(alloc_sl1.field_slice()) &&
84 alloc_sl1.container() != PHV::Container()) {
85 for (const auto &alloc_sl2 : sl2.field()->get_alloc()) {
86 // Look for corresponding allocated slice of FieldSlice sl2
87 if (sl2.range().overlaps(alloc_sl2.field_slice())) {
88 if (alloc_sl1.container() == alloc_sl2.container()) {
89 return true;
90 }
91 }
92 }
93 }
94 }
95 return false;
96 }
97
98 profile_t init_apply(const IR::Node *root) override {
99 auto rv = Transform::init_apply(root);
100
101 LOG3("AddInitTable: init_apply");
102 mauInitSlices.clear();
103 // Collect set of FieldRange's which do not need initialization to remove
104 std::set<PHV::FieldRange> removeInitFields;
105 for (const auto &mau_init : mauInitFields) {
106 // Get the field requiring mau init
107 if (const auto *fld1 = phv.field(mau_init.field_name)) {
108 // Get the extracted field that is possibly allocated
109 // to the same container as fld1
110 for (auto &f_range : mau_init.conflicts) {
111 const auto *fld2 = phv.field(f_range.field_name);
112 if (fld2 && same_container(PHV::FieldSlice(fld1, mau_init.range),
113 PHV::FieldSlice(fld2, f_range.range))) {
114 ingress_inits = (fld1->gress == INGRESS);
115 egress_inits = (fld1->gress == EGRESS);
116 auto slice = new IR::Slice(fieldToExpr.getExpr(fld1), mau_init.range.hi,
117 mau_init.range.lo);
118 mauInitSlices.push_back(slice);
119 LOG4("\t Added slice: " << slice << " for " << mau_init.field_name << " "
120 << mau_init.range);
121 } else {
122 removeInitFields.insert(mau_init);
123 }
124 }
125 }
126 }
127
128 for (auto remInit : removeInitFields) {
129 mauInitFields.erase(remInit);
130 LOG5("\t Removing mau init " << remInit.field_name << " " << remInit.range);
131 }
132
133 return rv;
134 }
135
136 IR::MAU::TableSeq *preorder(IR::MAU::TableSeq *tSeq) override {
137 LOG3("AddInitTable: preorder TableSeq " << tSeq);
138 gress_t seq_gress;
139 cstring tableName;
140 cstring actionName;
141 const IR::MAU::Table *init_table = nullptr;
142
143 if (tSeq->tables.empty()) {
144 LOG3("AddInitTable: empty TableSeq - skipping");
145 return tSeq;
146 } else {
147 LOG3("\t Table: " << tSeq->tables.front()->name);
148 seq_gress = tSeq->tables.front()->gress;
149 tableName = "__mau_inits_table__" + toString(seq_gress);
150 actionName = "__mau_inits_action__" + toString(seq_gress);
151 for (auto *tbl : tSeq->tables) {
152 if (tbl->name == tableName) {
153 init_table = tbl;
154 BUG_CHECK(tbl->actions.count(actionName), "Table %1% does not have action %2%?",
155 tableName, actionName);
156 }
157 }
158 }
159
160 // Check that this Table Sequence is the first one
161 const IR::MAU::Table *parent_table = nullptr;
162 parent_table = findContext<IR::MAU::Table>();
163 BUG_CHECK(!parent_table, "This Table Sequence has parent table %1% ?", parent_table->name);
164
165 prune();
166
167 if (((seq_gress == INGRESS) && !ingress_inits) ||
168 ((seq_gress == EGRESS) && !egress_inits)) {
169 LOG3("AddInitTable: Table Sequence of gress "
170 << seq_gress << " without fields of that gress requiring init - nothing to do");
171 return tSeq;
172 }
173
174 // No need to do anything if initialization table exist
175 // *TODO* This is meant to handle re-creation of the initialization
176 // table during real PHV Allocation already exists
177 // from trivial PHV-Allocation. Though this would not happen
178 // in the current implementation. But in the future if we
179 // want to update the set of fields initialized we should
180 // need to check if initialization table exists and also if
181 // if field-slices are already initialized. Thus leaving this as is for now
182 if (init_table) return tSeq;
183
184 // Create the table and action that will contain the slice inits
185 auto createDummyTableAndAction = [&]() -> std::pair<IR::MAU::Table *, IR::MAU::Action *> {
186 LOG3("AddInitTable: " << "new table name " << tableName);
187 auto table = new IR::MAU::Table(tableName, seq_gress);
188 table->gress = seq_gress;
189 auto action = new IR::MAU::Action(actionName);
190 table->match_table = new IR::P4Table(tableName.c_str(), new IR::TableProperties());
191 table->is_compiler_generated = true;
192 action->init_default = true;
193 action->default_allowed = true;
194
195 for (auto &slice : mauInitSlices) {
196 auto type = IR::Type_Bits::get(slice->getH() - slice->getL() + 1);
197 auto instr = new IR::MAU::Instruction("set"_cs, {slice, new IR::Constant(type, 0)});
198 LOG3("AddInitTable: " << "adding " << instr);
199 action->action.push_back(instr);
200 }
201
202 table->actions[action->name] = action;
203
204 return {table, action};
205 };
206
207 // Create init table if it has not been created before
208 auto [table, action] = createDummyTableAndAction();
209
210 tSeq->tables.insert(tSeq->tables.begin(), table);
211 LOG3("AddInitTable: the first tableSeq now looks like this " << tSeq);
212 return tSeq;
213 }
214
215 public:
216 explicit AddInitTable(PhvInfo &p, MapFieldToExpr &f2exp,
217 std::set<PHV::FieldRange> &to_be_inited, bool container_info)
218 : phv(p),
219 fieldToExpr(f2exp),
220 mauInitFields(to_be_inited),
221 check_container(container_info) {}
222};
223
225 public:
226 explicit AddInitsInMAU(PhvInfo &phv, std::set<PHV::FieldRange> &to_be_inited,
227 bool container_info) {
228 auto mapFieldToExpr = new MapFieldToExpr(phv);
229 addPasses({
230 mapFieldToExpr,
231 new AddInitTable(phv, *mapFieldToExpr, to_be_inited, container_info),
232 });
233 }
234};
235
236#endif /* BF_P4C_PHV_INIT_IN_MAU_H_ */
Definition init_in_mau.h:69
Definition init_in_mau.h:224
Definition node.h:94
Definition ir/pass_manager.h:40
Definition visitor.h:424
Definition visitor.h:78
Definition cstring.h:85
Definition phv.h:176
const safe_vector< PHV::AllocSlice > & get_alloc() const
Definition phv_fields.h:717
Definition phv_fields.h:898
const PHV::Field * field() const override
Definition phv_fields.h:977
const le_bitrange & range() const override
Definition phv_fields.h:980
Definition phv_fields.h:1095
bool overlaps(ClosedRange a) const
Definition lib/bitrange.h:603