P4C
The P4 Compiler
Loading...
Searching...
No Matches
asm_format_hash.h
1
19#ifndef BF_P4C_MAU_ASM_FORMAT_HASH_H_
20#define BF_P4C_MAU_ASM_FORMAT_HASH_H_
21
22#include <iterator>
23#include <map>
24#include <memory>
25#include <regex>
26#include <string>
27#include <vector>
28
29#include "backends/tofino/bf-p4c/common/ir_utils.h"
30#include "backends/tofino/bf-p4c/common/slice.h"
31#include "backends/tofino/bf-p4c/ir/tofino_write_context.h"
32#include "backends/tofino/bf-p4c/lib/error_type.h"
33#include "backends/tofino/bf-p4c/mau/asm_output.h"
34#include "backends/tofino/bf-p4c/phv/asm_output.h"
35#include "boost/range/adaptor/reversed.hpp"
36#include "lib/algorithm.h"
37#include "lib/bitops.h"
38#include "lib/bitrange.h"
39#include "lib/hex.h"
40#include "lib/indent.h"
41#include "lib/stringref.h"
42
43using namespace P4;
44
45struct FormatHash {
46 using RangeOfConstant = std::map<le_bitrange, const IR::Constant *>;
47 const safe_vector<Slice> *match_data;
48 const std::multimap<int, Slice> *match_data_map;
49 const RangeOfConstant *constant_map;
50 const Slice *ghost;
52 int total_bits = 0;
53 le_bitrange *field_range;
54
55 FormatHash(const safe_vector<Slice> *md, const std::multimap<int, Slice> *mdm,
56 const std::map<le_bitrange, const IR::Constant *> *cm, const Slice *g,
57 IR::MAU::HashFunction f, int tb = 0, le_bitrange *fr = nullptr)
58 : match_data(md),
59 match_data_map(mdm),
60 constant_map(cm),
61 ghost(g),
62 func(f),
63 total_bits(tb),
64 field_range(fr) {
65 BUG_CHECK(match_data == nullptr || match_data_map == nullptr,
66 "FormatHash not "
67 "configured correctly");
68 }
69
70 // functors for formatting expressions as hash expression in bfa. FIXME -- Should be
71 // folded in to operator<< or as non-static methods? Problem is we need dynamic
72 // dispatch on IR types, which can only be done with visitors (or messy dynamic_cast
73 // chains) So we just define them as nested classes here.
74 class SliceWidth; // find maximum slice width that can output as one line
75 class ZeroHash; // true iff slice contributes nothing to hash table (all 0s)
76 class Output; // output a .bfa expression for a slice of a hash expression
77};
78
79inline std::ostream &operator<<(std::ostream &out, const FormatHash &hash) {
80 if (hash.field_range != nullptr) {
81 FormatHash hash2(hash.match_data, hash.match_data_map, hash.constant_map, hash.ghost,
82 hash.func, hash.total_bits);
83 out << "slice(" << hash2 << ", " << hash.field_range->lo << "..";
84 out << hash.field_range->hi << ")";
85 return out;
86 }
87
88 if (hash.func.type == IR::MAU::HashFunction::IDENTITY) {
89 BUG_CHECK(hash.match_data, "For an identity, must be a standard vector");
90 out << "stripe(" << emit_vector(*hash.match_data) << ")";
91 } else if (hash.func.type == IR::MAU::HashFunction::RANDOM) {
92 BUG_CHECK(hash.match_data, "For a random, must be a standard vector");
93 if (!hash.match_data->empty()) {
94 out << "random(" << emit_vector(*hash.match_data, ", ") << ")";
95 if (hash.ghost) out << " ^ ";
96 }
97 if (hash.ghost) {
98 out << *hash.ghost;
99 }
100 } else if (hash.func.type == IR::MAU::HashFunction::CRC) {
101 BUG_CHECK(hash.match_data_map, "For a crc, must be a map");
102 out << "stripe(crc";
103 if (hash.func.reverse) out << "_rev";
104 out << "(0x" << hex(hash.func.poly) << ", ";
105 out << "0x" << hex(hash.func.init) << ", ";
106 out << "0x" << hex(hash.func.final_xor) << ", ";
107 out << hash.total_bits << ", ";
108 out << *hash.match_data_map;
109 // could not use the operator<< on std::map because the le_bitrange
110 // does not print to valid json range.
111 if (hash.constant_map) {
112 out << ", {";
113 const char *sep = " ";
114 for (const auto &kv : *hash.constant_map) {
115 out << sep << kv.first.lo << ".." << kv.first.hi << ": " << kv.second;
116 sep = ", ";
117 }
118 out << " }";
119 }
120 out << ")";
121 // FIXME -- final_xor needs to go into the seed for the hash group
122 out << ")";
123 } else if (hash.func.type == IR::MAU::HashFunction::XOR) {
124 /* -- note: constants are computed by the midend into the seed value.
125 * There is no need to pass them through the xor directive. */
126 BUG_CHECK(hash.match_data_map, "For a xor, must be a map");
127 out << "stripe(xor(" << "0x" << hex(hash.func.size) << ", " << *hash.match_data_map << "))";
128 } else if (hash.func.type == IR::MAU::HashFunction::CSUM) {
129 BUG("csum hashing algorithm not supported");
130 } else {
131 BUG("unknown hashing algorithm %d", hash.func.type);
132 }
133 return out;
134}
135
136/* Find the maximum width of a slice starting from bit that can be output as a single
137 * expression in a hash table in a .bfa file
138 */
140 const PhvInfo &phv;
141 safe_vector<Slice> &match_data;
142 int bit;
143 int width;
144 bool preorder(const IR::Annotation *) { return false; }
145 bool preorder(const IR::Type *) { return false; }
146 bool preorder(const IR::BXor *) { return true; }
147 bool preorder(const IR::BAnd *) { return true; }
148 bool preorder(const IR::BOr *) { return true; }
149 bool preorder(const IR::Constant *c) {
150 if (getParent<IR::BOr>()) {
151 // can't do OR in the .bfa -- need to slice based on bit ranges
152 big_int v = c->value >> bit;
153 if (v != 0) {
154 int b(v & 1);
155 int w = 1;
156 while (((v >> w) & 1) == b) ++w;
157 if (width > w) width = w;
158 }
159 }
160 return false;
161 }
162 bool preorder(const IR::Concat *e) {
163 if (bit < e->right->type->width_bits()) {
164 if (width > e->right->type->width_bits() - bit)
165 width = e->right->type->width_bits() - bit;
166 visit(e->right, "right");
167 } else {
168 int tmp = bit;
169 bit -= e->right->type->width_bits();
170 BUG_CHECK(bit < e->left->type->width_bits(), "bit out of range in SliceWidth");
171 if (width > e->left->type->width_bits() - bit)
172 width = e->left->type->width_bits() - bit;
173 visit(e->left, "left");
174 bit = tmp;
175 }
176 return false;
177 }
178 bool preorder(const IR::ListExpression *fl) {
179 int tmp = bit;
180 for (auto *e : boost::adaptors::reverse(fl->components)) {
181 if (bit < e->type->width_bits()) {
182 if (width > e->type->width_bits() - bit) width = e->type->width_bits() - bit;
183 visit(e);
184 break;
185 }
186 bit -= e->type->width_bits();
187 }
188 bit = tmp;
189 return false;
190 }
191 bool preorder(const IR::StructExpression *sl) {
192 int tmp = bit;
193 for (auto *e : boost::adaptors::reverse(sl->components)) {
194 if (bit < e->expression->type->width_bits()) {
195 if (width > e->expression->type->width_bits() - bit)
196 width = e->expression->type->width_bits() - bit;
197 visit(e->expression);
198 break;
199 }
200 bit -= e->expression->type->width_bits();
201 }
202 bit = tmp;
203 return false;
204 }
205 bool preorder(const IR::BFN::SignExtend *e) {
206 int w = e->type->width_bits() - bit;
207 if (width > w) width = w;
208 w = width;
209 visit(e->expr, "e");
210 if (width < w && width >= e->expr->type->width_bits()) width = w;
211 return false;
212 }
213 bool preorder(const IR::Expression *e) {
214 int w = e->type->width_bits() - bit;
215 if (width > w) width = w;
216 Slice sl(phv, e, bit, bit + width - 1);
217 BUG_CHECK(sl, "Invalid expression %s in FormatHash::SliceWidth", e);
218 for (auto &md : match_data) {
219 if (auto tmp = sl & md) {
220 if (tmp.get_lo() > sl.get_lo()) {
221 w = tmp.get_lo() - sl.get_lo();
222 if (width > w) width = w;
223 continue;
224 }
225 if (width > tmp.width()) width = tmp.width();
226 break;
227 }
228 }
229 return false;
230 }
231
232 public:
233 SliceWidth(const PhvInfo &p, const IR::Expression *e, int b, safe_vector<Slice> &md)
234 : phv(p), match_data(md), bit(b), width(e->type->width_bits() - b) {
235 e->apply(*this);
236 }
237 operator int() const { return width; }
238};
239
240/* True if a slice of a hash expression is all 0s in the GF matrix. Slice cannot be
241 * be wider than what is returned by SliceWidth for its start bit */
243 const PhvInfo &phv;
244 le_bitrange slice;
245 safe_vector<Slice> &match_data;
246 bool rv = false;
247 bool preorder(const IR::Annotation *) { return false; }
248 bool preorder(const IR::Type *) { return false; }
249 bool preorder(const IR::BXor *e) {
250 if (!rv) {
251 visit(e->left, "left");
252 bool tmp = rv;
253 rv = false;
254 visit(e->right, "right");
255 rv &= tmp;
256 }
257 return false;
258 }
259 bool preorder(const IR::BAnd *) { return true; }
260 bool preorder(const IR::BOr *) { return true; }
261 bool preorder(const IR::BFN::SignExtend *) { return true; }
262 bool preorder(const IR::Constant *k) {
263 if (getParent<IR::BAnd>() || getParent<IR::BOr>()) {
264 big_int v = k->value, mask = 1;
265 v >>= slice.lo;
266 mask <<= slice.size();
267 mask -= 1;
268 v &= mask;
269 if (getParent<IR::BAnd>()) {
270 if (v == 0) rv = true;
271 } else if (v == mask) {
272 rv = true;
273 } else {
274 BUG_CHECK(v == 0, "Incorrect slicing of BOr in hash expression");
275 }
276 } else {
277 rv = true;
278 }
279 return false;
280 }
281 bool preorder(const IR::Concat *e) {
282 int rwidth = e->right->type->width_bits();
283 if (slice.lo < rwidth) {
284 BUG_CHECK(slice.hi < rwidth, "Slice too wide in FormatHash::ZeroHash");
285 visit(e->right, "right");
286 } else {
287 auto tmp = slice;
288 slice = slice.shiftedByBits(-rwidth);
289 visit(e->left, "left");
290 slice = tmp;
291 }
292 return false;
293 }
294 bool preorder(const IR::ListExpression *fl) {
295 auto tmp = slice;
296 for (auto *e : boost::adaptors::reverse(fl->components)) {
297 int width = e->type->width_bits();
298 if (slice.lo < width) {
299 BUG_CHECK(slice.hi < width, "Slice too wide in FormatHash::ZeroHash");
300 visit(e);
301 break;
302 }
303 slice = slice.shiftedByBits(-width);
304 }
305 slice = tmp;
306 return false;
307 }
308 bool preorder(const IR::StructExpression *sl) {
309 auto tmp = slice;
310 for (auto *e : boost::adaptors::reverse(sl->components)) {
311 int width = e->expression->type->width_bits();
312 if (slice.lo < width) {
313 BUG_CHECK(slice.hi < width, "Slice too wide in FormatHash::ZeroHash");
314 visit(e);
315 break;
316 }
317 slice = slice.shiftedByBits(-width);
318 }
319 slice = tmp;
320 return false;
321 }
322 bool preorder(const IR::Expression *e) {
323 Slice sl(phv, e, slice);
324 BUG_CHECK(sl, "Invalid expression %s in FormatHash::ZeroHash", e);
325 for (auto &md : match_data) {
326 if (auto tmp = sl & md) {
327 return false;
328 }
329 }
330 rv = true;
331 return false;
332 }
333
334 public:
335 ZeroHash(const PhvInfo &p, const IR::Expression *e, le_bitrange s, safe_vector<Slice> &md)
336 : phv(p), slice(s), match_data(md) {
337 e->apply(*this);
338 }
339 explicit operator bool() const { return rv; }
340};
341
342/* output a slice of a hash expression as a .bfa hash expression */
344 const PhvInfo &phv;
345 std::ostream &out;
346 le_bitrange slice;
347 safe_vector<Slice> &match_data;
348 bool preorder(const IR::Annotation *) { return false; }
349 bool preorder(const IR::Type *) { return false; }
350 bool preorder(const IR::BXor *e) {
351 bool need_left = !ZeroHash(phv, e->left, slice, match_data);
352 bool need_right = !ZeroHash(phv, e->right, slice, match_data);
353 if (need_left) visit(e->left, "left");
354 if (need_left && need_right) out << " ^ ";
355 if (need_right) visit(e->right, "right");
356 return false;
357 }
358 bool preorder(const IR::BAnd *e) {
359 visit(e->left, "left");
360 out << "&";
361 visit(e->right, "right");
362 return false;
363 }
364 bool preorder(const IR::BOr *e) {
365 auto k = e->left->to<IR::Constant>();
366 auto &op = k ? e->right : e->left;
367 if (!k) k = e->right->to<IR::Constant>();
368 big_int v = k->value >> slice.lo;
369 if ((v & 1) == 0) {
370 visit(op);
371 } else {
372 out << "0";
373 }
374 return false;
375 }
376 bool preorder(const IR::Constant *k) {
377 big_int v = k->value, mask = 1;
378 v >>= slice.lo;
379 mask <<= slice.size();
380 mask -= 1;
381 v &= mask;
382 out << "0x" << std::hex << v << std::dec;
383 return false;
384 }
385 bool preorder(const IR::Concat *e) {
386 if (slice.lo < e->right->type->width_bits()) {
387 visit(e->right, "right");
388 } else {
389 auto tmp = slice;
390 slice = slice.shiftedByBits(-e->right->type->width_bits());
391 visit(e->left, "left");
392 slice = tmp;
393 }
394 return false;
395 }
396 bool preorder(const IR::ListExpression *fl) {
397 auto tmp = slice;
398 for (auto *e : boost::adaptors::reverse(fl->components)) {
399 if (slice.lo < e->type->width_bits()) {
400 visit(e);
401 break;
402 }
403 slice = slice.shiftedByBits(-e->type->width_bits());
404 }
405 slice = tmp;
406 return false;
407 }
408 bool preorder(const IR::StructExpression *sl) {
409 auto tmp = slice;
410 for (auto *e : boost::adaptors::reverse(sl->components)) {
411 if (slice.lo < e->expression->type->width_bits()) {
412 visit(e->expression);
413 break;
414 }
415 slice = slice.shiftedByBits(-e->expression->type->width_bits());
416 }
417 slice = tmp;
418 return false;
419 }
420 bool preorder(const IR::BFN::SignExtend *e) {
421 if (slice.hi < e->expr->type->width_bits()) {
422 // a slice or mask on a sign extension such that we don't need any of the
423 // sign extended bits. Could have been eliinated earlier, but ignore it here
424 return true;
425 }
426 auto tmp = slice;
427 slice.hi = e->expr->type->width_bits() - 1;
428 out << "sign_extend(";
429 visit(e->expr, "expr");
430 slice = tmp;
431 out << ")";
432 return false;
433 }
434 bool preorder(const IR::Expression *e) {
435 Slice sl(phv, e, slice);
436 BUG_CHECK(sl, "Invalid expression %s in FormatHash::Output", e);
437 for (auto &md : match_data) {
438 if (auto tmp = sl & md) {
439 out << tmp;
440 break;
441 }
442 }
443 return false;
444 }
445
446 public:
447 Output(const PhvInfo &p, std::ostream &o, const IR::Expression *e, le_bitrange s,
449 : phv(p), out(o), slice(s), match_data(md) {
450 e->apply(*this);
451 }
452};
453
454#endif /* BF_P4C_MAU_ASM_FORMAT_HASH_H_ */
Definition asm_format_hash.h:343
Definition asm_format_hash.h:139
Definition asm_format_hash.h:242
Definition visitor.h:400
Definition hex.h:27
Definition safe_vector.h:27
Definition phv_fields.h:1095
Definition common/asm_output.h:47
TODO: this is not really specific to BMV2, it should reside somewhere else.
Definition applyOptionsPragmas.cpp:24
Definition asm_format_hash.h:45
ClosedRange< RangeUnit::Bit, Order > shiftedByBits(int offset) const
Definition lib/bitrange.h:556
int lo
Definition lib/bitrange.h:694
ssize_t size() const
Definition lib/bitrange.h:539
int hi
Definition lib/bitrange.h:700
Definition hash_function.h:28