P4C
The P4 Compiler
Loading...
Searching...
No Matches
phv_slicing_dfs_iterator.h
1
19#ifndef BF_P4C_PHV_SLICING_PHV_SLICING_DFS_ITERATOR_H_
20#define BF_P4C_PHV_SLICING_PHV_SLICING_DFS_ITERATOR_H_
21
22#include <utility>
23
24#include "bf-p4c/lib/assoc.h"
25#include "bf-p4c/parde/check_parser_multi_write.h"
26#include "bf-p4c/phv/slicing/phv_slicing_iterator.h"
27#include "bf-p4c/phv/slicing/phv_slicing_split.h"
28#include "bf-p4c/phv/slicing/types.h"
29#include "bf-p4c/phv/utils/utils.h"
30#include "lib/ordered_set.h"
31
32namespace PHV {
33namespace Slicing {
34
36using SliceListLoc = std::pair<SuperCluster *, SuperCluster::SliceList *>;
37
40 // all possible container sizes, used for initialization.
41 static const bitvec all_container_sizes;
42
43 // ConstraintType abstracts this constraint.
44 enum class ConstraintType {
45 EXACT = 0, // must be placed in container of the size.
46 MIN = 1, // must be placed in container at least the size.
47 NONE = 2, // no new constraint.
48 };
49
50 // default constructor
51 AfterSplitConstraint() : sizes(all_container_sizes) {}
52 // construct from set.
53 explicit AfterSplitConstraint(const bitvec &sizes);
54 // construct based on type.
55 explicit AfterSplitConstraint(ConstraintType t, int v = 0);
56
57 // available container sizes for this constraint.
58 bitvec sizes;
59
60 // return the type of this constraint.
61 ConstraintType type() const;
62
63 // return the smallest available container size;
64 int min() const { return *sizes.begin(); }
65
66 // returns the intersection of two AfterSplitConstraint.
67 // e.g. MIN(8) ^ EXACT(32) = EXACT(32)
68 // MIN(8) ^ MIN(16) = MIN(16)
69 // EXACT(8) ^ EXACT(16) = std::nullopt
70 std::optional<AfterSplitConstraint> intersect(const AfterSplitConstraint &other) const;
71
72 // return true if field with this AfterSplitConstraint can
73 // be placed in a container of size @p n bits.
74 bool ok(const int n) const;
75};
76
77std::ostream &operator<<(std::ostream &out, const AfterSplitConstraint &c);
78
81
83enum class SplitChoice {
84 B = 8,
85 H = 16,
86 W = 32,
87};
88
93 public:
94 // input
95 const PhvInfo &phv_i;
96 const SuperCluster *sc_i;
97 const PHVContainerSizeLayout pa_i;
98 const ActionPackingValidatorInterface &action_packing_validator_i;
99 const ParserPackingValidatorInterface &parser_packing_validator_i;
100 const PackConflictChecker has_pack_conflict_i;
101 const IsReferencedChecker is_used_i;
102 IteratorConfig config_i;
103 CheckWriteModeConsistency check_write_mode_consistency_i;
104
105 // if a pa_container_size asks a field to be allocated to containers larger than it's
106 // size, it's recorded here and will be used during pruning. Note that for one field,
107 // there can only one upcasting piece, which is the tailing part.
109
111 // split_decisions collects slicing decisions already made and
112 // map fieldslice -> the after split constraints of the fieldslice.
113 // The trick here is what we use a value, FieldSlice, representing that,
114 // if a slicelist contains any fieldslice in this set, then the split decision
115 // for that slicelist has already been made.
117
118 // slicelist_on_stack stores all slice list that were split during DFS in a stack-style.
119 // and the number of choices left for the slice list to try different slicing.
120 std::vector<const SuperCluster::SliceList *> slicelist_on_stack_i;
121
122 // done_i is a set of super clusters that
123 // (1) all slice slices are already split, or cannot be split further.
124 // (2) the super cluster is well-formed, i.e. SuperCluster::is_well_formed() = true.
126
127 // super clusters left to be sliced.
128 ordered_set<SuperCluster *> to_be_split_i;
129
130 // true if has itr generated before. one context can only generate one iterator.
131 bool has_itr_i = false;
132
133 // tracking dfs depth.
134 int dfs_depth_i = 0;
135
136 // a step counter records how many steps the search has tried.
137 int n_steps_i = 0;
138
139 // last solution was found at n_steps_since_last_solution before.
140 int n_steps_since_last_solution = 0;
141
142 // Set of rejected SplitChoice options from previous slice-lists
143 std::set<SplitChoice> reject_sizes;
144
145 // if not nullptr, backtrack to the stack that to_invalidate is not on stack,
146 // i.e. not a part of the DFS path.
147 const SuperCluster::SliceList *to_invalidate = nullptr;
148
149 // When an action cannot be synthesized, either we can try to split the destination more,
150 // or we try to split sources differently so that maybe then src and dest slices happen to
151 // be aligned magically or two sources are packed together (very rare, but not impossible).
152 // Then problem is that, we do not know which one to try first. So we always invalidate the
153 // most recent choice in DFS. However,consider this:
154 // dfs-1: incorrect decision on a destination field.
155 // ....
156 // dfs-99: finally made a decision on the source fields.
157 // If we always revert what we have done in dsf-99, and retry, it will take 2^98 times to
158 // make it backtrack to dfs-1, to correct the wrong.
159 // So we set the counter here that as long as the number of times that a slice list is called
160 // to be invalidated, but ignored, exceeds the max allowance, we will try to backtrack to that
161 // slice list. Counter is cleared when we backtracked to the list.
162 static constexpr int to_invalidate_max_ignore = 8;
164
165 public:
166 DfsItrContext(const PhvInfo &phv, const MapFieldToParserStates &field_to_states,
167 const CollectParserInfo &parser_info, const SuperCluster *sc,
168 const PHVContainerSizeLayout &pa,
169 const ActionPackingValidatorInterface &action_packing_validator,
170 const ParserPackingValidatorInterface &parser_packing_validator,
171 const PackConflictChecker &pack_conflict, const IsReferencedChecker is_used)
172 : phv_i(phv),
173 sc_i(sc),
174 pa_i(pa),
175 action_packing_validator_i(action_packing_validator),
176 parser_packing_validator_i(parser_packing_validator),
177 has_pack_conflict_i(pack_conflict),
178 is_used_i(is_used),
179 config_i(false, false, true, true, false, (1 << 25), (1 << 19)),
180 check_write_mode_consistency_i(phv, field_to_states, parser_info) {}
181
183 void iterate(const IterateCb &cb) override;
184
189 void invalidate(const SuperCluster::SliceList *sl) override;
190
192 void set_config(const IteratorConfig &cfg) override { config_i = cfg; }
193
196 bool dfs(const IterateCb &yield, const ordered_set<SuperCluster *> &unchecked);
197
199 std::optional<std::list<SuperCluster *>> split_by_pa_container_size(
200 const SuperCluster *sc, const PHVContainerSizeLayout &pa);
201
204 std::optional<std::list<SuperCluster *>> split_by_adjacent_no_pack(SuperCluster *sc) const;
205
207 std::optional<std::list<SuperCluster *>> split_by_deparsed_bottom_bits(SuperCluster *sc) const;
208
211 std::optional<std::list<SuperCluster *>> split_by_adjacent_deparsed_and_non_deparsed(
212 SuperCluster *sc) const;
213
217 std::optional<std::list<SuperCluster *>> split_by_valid_container_range(SuperCluster *sc) const;
218
221 std::optional<std::list<SuperCluster *>> split_by_long_fieldslices(SuperCluster *sc) const;
222
224 std::optional<std::list<SuperCluster *>> split_by_parser_write_mode(SuperCluster *sc);
225
233 std::vector<SplitChoice> make_choices(const SliceListLoc &target) const;
234
239 bool dfs_prune(const ordered_set<SuperCluster *> &unchecked);
240
244 bool dfs_prune_unwell_formed(const SuperCluster *sc) const;
245
246 bool dfs_prune_invalid_parser_packing(const SuperCluster *sc) const;
247
250 const SuperCluster *sc) const;
251
261 const SuperCluster *sc) const;
262
273 const SuperCluster *sc) const;
274
279
282 std::optional<SplitDecision> collect_aftersplit_constraints(const SuperCluster *sc) const;
283
293 const SuperCluster *sc) const;
294
297 std::optional<SliceListLoc> dfs_pick_next() const;
298
306 void propagate_8bit_exact_container_split(SuperCluster *sc, SuperCluster::SliceList *target,
307 SplitSchema *schema, SplitDecision *decisions) const;
308
311 bool propagate_tail_split(SuperCluster *sc, const SplitDecision &constraints,
312 const SplitDecision *decisions,
313 const SuperCluster::SliceList *just_split_target,
314 const int n_just_split_bits, SplitSchema *schema) const;
315
318 std::optional<std::pair<SplitSchema, SplitDecision>> make_split_meta(
319 SuperCluster *sc, SuperCluster::SliceList *sl, int first_n_bits) const;
320
322 bool need_further_split(const SuperCluster::SliceList *sl) const;
323
325 bool check_pack_conflict(const SuperCluster::SliceList *sl) const;
326
329 std::vector<SuperCluster *> get_well_formed_no_more_split() const;
330
331 // For some superclusters, slicing iterator is prone to generating duplicate slicing plans.
332 // Duplicate slicing plans mean that although the slice lists have been sliced into a different
333 // way, the slicing plan does not effectively change the result of PHV allocation. For example,
334 // a supercluster may look like this:
335 // slice lists:
336 // [a[0-31]]
337 // [b[0-31]]
338 // rotational clusters:
339 // [[a[0-31]], [b[0-31]]]
340 //
341 // one slicing plan can be:
342 // slice lists:
343 // [a[0-31]]
344 // [b[0-31]]
345 // rotational clusters:
346 // [[a[0-31]], [b[0-31]]]
347 // This slicing plan changes nothing.
348 //
349 // Another slicing plan can be:
350 // slice lists:
351 // [a[0-15], a[16-31]]
352 // [b[0-15]]
353 // [b[16-31]]
354 // rotational clusters:
355 // [[a[0-15]], [b[0-15]]]
356 // [[a[16-31]], [b[16-31]]]
357 // For the first slicing plan, a and b will be allocated to 32-bit container, since they are
358 // in two slice lists that both are size of 32 bits.
359 // For the second slicing plan, a will be allocated to a 32-bit container, since it is in a
360 // 32-bit slice list. And b[0-15] shares the same rotational cluster with a[0-15], so b has to
361 // be allocate to a 32-bit container. Therefore, this slicing does not effectively change the
362 // PHV allocation result and should be pruned during DFS.
363 // This function collects supercluster that has the aformentioned pattern. To minimize the
364 // impact on PHV allocation, this function is very conservative. If a supercluster only have
365 // 32-bit fieldslices that are all in individual slice lists, and all fieldslices are in the
366 // same rotational cluster, this supercluster will be checked for the duplicate slicing plans.
367 void need_to_check_duplicate();
368 // check duplicate is -1: duplication check has not been performed.
369 // check duplicate is 0: no need to check duplication.
370 // check duplicate is 1: need to check duplication and the first slicing plan(first slicing plan
371 // is always slice nothing and output the original supercluster) has not been yielded.
372 // check duplicate is 2: need to check duplication and the first slicing plan has been yielded.
373 int check_duplicate = -1;
374 // We only want to
375 const int duplicate_check_supercluster_size = 5;
376 // If a supercluster with check_duplicate flag on still has at least one 32-bit slice list,
377 // all fieldslices will be allocated to 32-bit containers and this is a duplicate slicing plan.
378 bool check_duplicate_slicing_plan(const ordered_set<SuperCluster *>);
379};
380
381} // namespace Slicing
382} // namespace PHV
383
384#endif /* BF_P4C_PHV_SLICING_PHV_SLICING_DFS_ITERATOR_H_ */
Definition bitvec.h:120
Definition ordered_map.h:32
Definition ordered_set.h:32
Definition action_packing_validator_interface.h:33
Definition parser_packing_validator_interface.h:26
Definition phv_slicing_dfs_iterator.h:92
void propagate_8bit_exact_container_split(SuperCluster *sc, SuperCluster::SliceList *target, SplitSchema *schema, SplitDecision *decisions) const
Definition phv_slicing_dfs_iterator.cpp:615
void iterate(const IterateCb &cb) override
iterate will pass valid slicing results to cb. Stop when cb returns false.
Definition phv_slicing_dfs_iterator.cpp:1187
bool dfs_prune_unsat_slicelist_max_size(const SplitDecision &constraints, const SuperCluster *sc) const
return true if exists constraint unsat due to the limit of slice list size.
Definition phv_slicing_dfs_iterator.cpp:1687
void invalidate(const SuperCluster::SliceList *sl) override
Definition phv_slicing_dfs_iterator.cpp:2183
std::vector< SplitChoice > make_choices(const SliceListLoc &target) const
Definition phv_slicing_dfs_iterator.cpp:433
std::optional< std::list< SuperCluster * > > split_by_deparsed_bottom_bits(SuperCluster *sc) const
split_by_deparsed_bottom_bits will split at the beginning of deparsed_bottom_bits field.
Definition phv_slicing_dfs_iterator.cpp:942
bool dfs_prune_unsat_exact_list_size_mismatch(const SplitDecision &decided_sz, const SuperCluster *sc) const
Definition phv_slicing_dfs_iterator.cpp:1846
std::optional< std::list< SuperCluster * > > split_by_pa_container_size(const SuperCluster *sc, const PHVContainerSizeLayout &pa)
split_by_pa_container_size will split sc by pa container size.
Definition phv_slicing_dfs_iterator.cpp:1053
std::optional< std::pair< SplitSchema, SplitDecision > > make_split_meta(SuperCluster *sc, SuperCluster::SliceList *sl, int first_n_bits) const
Definition phv_slicing_dfs_iterator.cpp:794
bool dfs_prune_unsat_slicelist_constraints(const SplitDecision &constraints, const SuperCluster *sc) const
Definition phv_slicing_dfs_iterator.cpp:1719
std::optional< std::list< SuperCluster * > > split_by_valid_container_range(SuperCluster *sc) const
Definition phv_slicing_dfs_iterator.cpp:965
bool need_further_split(const SuperCluster::SliceList *sl) const
return true if the slicelist needs to be further split.
Definition phv_slicing_dfs_iterator.cpp:1331
std::optional< std::list< SuperCluster * > > split_by_long_fieldslices(SuperCluster *sc) const
Definition phv_slicing_dfs_iterator.cpp:987
bool propagate_tail_split(SuperCluster *sc, const SplitDecision &constraints, const SplitDecision *decisions, const SuperCluster::SliceList *just_split_target, const int n_just_split_bits, SplitSchema *schema) const
Definition phv_slicing_dfs_iterator.cpp:697
std::optional< std::list< SuperCluster * > > split_by_adjacent_deparsed_and_non_deparsed(SuperCluster *sc) const
Definition phv_slicing_dfs_iterator.cpp:914
bool dfs(const IterateCb &yield, const ordered_set< SuperCluster * > &unchecked)
Definition phv_slicing_dfs_iterator.cpp:2036
std::optional< SliceListLoc > dfs_pick_next() const
Definition phv_slicing_dfs_iterator.cpp:564
std::optional< SplitDecision > collect_aftersplit_constraints(const SuperCluster *sc) const
Definition phv_slicing_dfs_iterator.cpp:1489
bool collect_implicit_container_sz_constraint(SplitDecision *decided_sz, const SuperCluster *sc) const
Definition phv_slicing_dfs_iterator.cpp:1612
bool check_pack_conflict(const SuperCluster::SliceList *sl) const
return true if there are pack_conflicts in sl.
Definition phv_slicing_dfs_iterator.cpp:1394
void set_config(const IteratorConfig &cfg) override
set configs.
Definition phv_slicing_dfs_iterator.h:192
std::optional< std::list< SuperCluster * > > split_by_adjacent_no_pack(SuperCluster *sc) const
Definition phv_slicing_dfs_iterator.cpp:872
std::vector< SuperCluster * > get_well_formed_no_more_split() const
Definition phv_slicing_dfs_iterator.cpp:1965
bool dfs_prune_invalid_packing(const SuperCluster *sc)
Definition phv_slicing_dfs_iterator.cpp:1883
bool dfs_prune_unwell_formed(const SuperCluster *sc) const
Definition phv_slicing_dfs_iterator.cpp:1424
bool dfs_prune(const ordered_set< SuperCluster * > &unchecked)
Definition phv_slicing_dfs_iterator.cpp:1917
std::optional< std::list< SuperCluster * > > split_by_parser_write_mode(SuperCluster *sc)
split_by_parser_write_mode will split based on incompatible parser write modes
Definition phv_slicing_dfs_iterator.cpp:1016
The interface that the iterator must satisfy.
Definition slicing/types.h:108
Definition phv/utils/utils.h:1049
Definition phv_fields.h:1095
Definition assoc.h:403
The namespace encapsulating PHV-related stuff.
Definition gateway.h:32
Definition check_parser_multi_write.h:47
Map field to the parser states in which they are extracted or assigned from checksums.
Definition phv_fields.h:1755
constraints introduced on fieldslices of container sizes after splitting a slice list.
Definition phv_slicing_dfs_iterator.h:39
Definition slicing/types.h:52