8#ifndef FRONTENDS_P4_DEF_USE_H_
9#define FRONTENDS_P4_DEF_USE_H_
11#include "absl/container/flat_hash_set.h"
12#include "absl/container/inlined_vector.h"
13#include "absl/container/node_hash_set.h"
14#include "frontends/common/resolveReferences/referenceMap.h"
16#include "lib/alloc_trace.h"
17#include "lib/flat_map.h"
19#include "lib/hvec_map.h"
20#include "lib/ordered_set.h"
35 mutable std::size_t computedHash = 0;
36 bool operator==(
const loc_t &a)
const {
37 if (node != a.node)
return false;
38 if (parent == a.parent)
return true;
39 if (!parent || !a.parent)
return false;
40 return *parent == *a.parent;
42 std::size_t hash()
const;
49#ifdef DEBUG_LOCATION_IDS
50 static unsigned crtid;
55 virtual ~StorageLocation() {}
58 StorageLocation(
const IR::Type *type,
cstring name)
60#ifdef DEBUG_LOCATION_IDS
67 void dbprint(std::ostream &out)
const override {
68#ifdef DEBUG_LOCATION_IDS
69 out <<
id <<
" " << name;
71 out << dbp(type) <<
" " << name;
74 cstring toString()
const {
return name; }
78 virtual void addValidBits(
LocationSet *result)
const = 0;
84 virtual void addLastIndexField(
LocationSet *result)
const = 0;
86 DECLARE_TYPEINFO(StorageLocation);
91class BaseLocation :
public StorageLocation {
93 BaseLocation(
const IR::Type *type,
cstring name) : StorageLocation(type, name) {
94 if (
auto tt = type->to<IR::Type_Tuple>())
95 BUG_CHECK(tt->getSize() == 0,
"%1%: tuples with fields are not base locations", tt);
96 else if (
auto ts = type->to<IR::Type_StructLike>())
97 BUG_CHECK(ts->fields.size() == 0,
"%1%: structs with fields are not base locations",
100 BUG_CHECK(type->is<IR::Type_Bits>() || type->is<IR::Type_Enum>() ||
101 type->is<IR::Type_Boolean>() || type->is<IR::Type_Var>() ||
102 type->is<IR::Type_Error>() || type->is<IR::Type_Varbits>() ||
103 type->is<IR::Type_Newtype>() || type->is<IR::Type_SerEnum>() ||
104 type->is<IR::Type_List>(),
105 "%1%: unexpected type", type);
108 void addLastIndexField(
LocationSet *)
const override {}
111 DECLARE_TYPEINFO(BaseLocation, StorageLocation);
115class WithFieldsLocation :
public StorageLocation {
117 absl::InlinedVector<std::pair<cstring, const StorageLocation *>, 4>>
120 void createField(
cstring name, StorageLocation *field) {
121 fieldLocations.emplace(name, field);
124 void replaceField(
cstring field, StorageLocation *replacement) {
125 fieldLocations[field] = replacement;
128 friend class StorageFactory;
131 WithFieldsLocation(
const IR::Type *type,
cstring name) : StorageLocation(type, name) {}
136 auto fields()
const {
return Values(fieldLocations); }
137 void dbprint(std::ostream &out)
const override {
138 for (
auto f : fieldLocations) out << *f.second <<
" ";
141 DECLARE_TYPEINFO(WithFieldsLocation, StorageLocation);
145class StructLocation :
public WithFieldsLocation {
147 StructLocation(
const IR::Type *type,
cstring name) : WithFieldsLocation(type, name) {
148 BUG_CHECK(type->is<IR::Type_StructLike>(),
"%1%: unexpected type", type);
150 friend class StorageFactory;
153 void addValidBits(
LocationSet *result)
const override;
154 void addLastIndexField(
LocationSet *result)
const override;
155 bool isHeader()
const {
return type->is<IR::Type_Header>(); }
156 bool isHeaderUnion()
const {
return type->is<IR::Type_HeaderUnion>(); }
157 bool isStruct()
const {
return type->is<IR::Type_Struct>(); }
159 DECLARE_TYPEINFO(StructLocation, WithFieldsLocation);
163class IndexedLocation :
public StorageLocation {
164 absl::InlinedVector<const StorageLocation *, 8> elements;
167 void createElement(
unsigned index, StorageLocation *element) {
168 elements[index] = element;
172 IndexedLocation(
const IR::Type *type,
cstring name) : StorageLocation(type, name) {
174 const auto *it = type->to<IR::Type_Indexed>();
175 BUG_CHECK(it !=
nullptr,
"%1%: unexpected type", type);
176 elements.resize(it->getSize());
179 friend class StorageFactory;
182 void addElement(
unsigned index,
LocationSet *result)
const;
183 auto begin()
const {
return elements.cbegin(); }
184 auto end()
const {
return elements.cend(); }
185 void dbprint(std::ostream &out)
const override {
186 for (
unsigned i = 0; i < elements.size(); i++) out << *elements.at(i) <<
" ";
188 size_t getSize()
const {
return elements.size(); }
191 DECLARE_TYPEINFO(IndexedLocation, StorageLocation);
195class TupleLocation :
public IndexedLocation {
197 TupleLocation(
const IR::Type *type,
cstring name) : IndexedLocation(type, name) {}
198 friend class StorageFactory;
202 void addLastIndexField(
LocationSet *)
const override {}
204 DECLARE_TYPEINFO(TupleLocation, IndexedLocation);
207class ArrayLocation :
public IndexedLocation {
210 void setLastIndexField(
const StorageLocation *location) { lastIndexField = location; }
213 ArrayLocation(
const IR::Type *type,
cstring name) : IndexedLocation(type, name) {}
214 friend class StorageFactory;
217 const StorageLocation *getLastIndexField()
const {
return lastIndexField; }
219 void addValidBits(
LocationSet *result)
const override;
221 void addLastIndexField(
LocationSet *result)
const override;
223 DECLARE_TYPEINFO(ArrayLocation, IndexedLocation);
228 mutable std::vector<std::unique_ptr<StorageLocation>> storageLocations;
231 T *construct(
const IR::Type *type,
cstring name)
const;
233 static constexpr std::string_view indexFieldName =
"$last_index";
238 static const cstring validFieldName;
245 LocationsStorage locations;
247 class canonical_iterator {
248 absl::InlinedVector<const StorageLocation *, 8> workList;
251 if (workList.empty())
return;
253 const auto *location = workList.back();
259 workList.push_back(f);
271 BUG(
"unexpected location");
275 using iterator_category = std::forward_iterator_tag;
277 using difference_type = ptrdiff_t;
278 using pointer = value_type;
279 using reference = value_type;
281 canonical_iterator() =
default;
283 explicit canonical_iterator(
const LocationsStorage &locations) {
285 workList.push_back(loc);
288 canonical_iterator &operator++() {
293 canonical_iterator operator++(
int) {
298 bool operator==(
const canonical_iterator &i)
const {
return workList == i.workList; }
299 bool operator!=(
const canonical_iterator &i)
const {
return workList != i.workList; }
300 reference operator*()
const {
return workList.back(); }
301 pointer operator->()
const {
return workList.back(); }
305 LocationSet() =
default;
308 CHECK_NULL(location);
309 locations.emplace(location);
311 static const LocationSet *empty;
313 const LocationSet *getField(
cstring field)
const;
314 const LocationSet *getValidField()
const;
315 const LocationSet *getIndex(
unsigned index)
const;
316 const LocationSet *allElements()
const;
317 const LocationSet *getArrayLastIndex()
const;
320 CHECK_NULL(location);
321 locations.emplace(location);
323 const LocationSet *join(
const LocationSet *other)
const;
328 auto begin()
const {
return locations.cbegin(); }
329 auto end()
const {
return locations.cend(); }
330 auto canon_begin()
const {
return canonical_iterator(locations); }
331 auto canon_end()
const {
return canonical_iterator(); }
334 void dbprint(std::ostream &out)
const override {
335 if (locations.empty()) out <<
"LocationSet::empty";
336 for (
auto l : locations) {
342 bool overlaps(
const LocationSet *other)
const;
343 bool operator==(
const LocationSet &other)
const;
344 bool isEmpty()
const {
return locations.empty(); }
363 auto type = typeMap->getType(decl->getNode(),
true);
364 auto loc = factory.create(type, decl->
getName() +
"/" + decl->externalName());
365 if (loc !=
nullptr) storage.emplace(decl, loc);
369 const auto *s = getStorage(decl);
370 if (s !=
nullptr)
return s;
375 auto result = ::P4::get(storage, decl);
378 void dbprint(std::ostream &out)
const override {
379 for (
auto &it : storage) out << it.first <<
": " << it.second << Log::endl;
392 absl::InlinedVector<const IR::Node *, 8> stack;
395 ProgramPoint() =
default;
396 ProgramPoint(
const ProgramPoint &other) : stack(other.stack) {}
397 explicit ProgramPoint(
const IR::Node *node) {
401 ProgramPoint(
const ProgramPoint &context,
const IR::Node *node);
405 ProgramPoint
after() {
return ProgramPoint(*
this,
nullptr); }
407 std::size_t hash()
const;
408 void dbprint(std::ostream &out)
const override {
409 if (isBeforeStart()) {
410 out <<
"<BeforeStart>";
413 for (
auto n : stack) {
414 if (!first) out <<
"//";
421 auto l = stack.back();
423 (l->is<IR::BaseAssignmentStatement>() || l->is<IR::MethodCallStatement>()))
424 out <<
"[[" << l <<
"]]";
427 void assign(
const ProgramPoint &context,
const IR::Node *node);
428 void assign(
const IR::Node *node) { stack.assign({node}); }
429 void clear() { stack.clear(); }
430 const IR::Node *last()
const {
return stack.empty() ? nullptr : stack.back(); }
431 bool isBeforeStart()
const {
return stack.empty(); }
432 auto begin()
const {
return stack.begin(); }
433 auto end()
const {
return stack.end(); }
434 ProgramPoint &operator=(
const ProgramPoint &) =
default;
435 ProgramPoint &operator=(ProgramPoint &&) =
default;
442struct hash<
P4::ProgramPoint> {
443 std::size_t operator()(
const P4::ProgramPoint &s)
const {
return s.hash(); }
447struct hash<
P4::loc_t> {
448 std::size_t operator()(
const P4::loc_t &loc)
const {
return loc.hash(); }
461 size_t operator()(
const P4::loc_t &loc)
const {
return loc.hash(); }
467 typedef absl::flat_hash_set<ProgramPoint, Util::Hash> Points;
469 explicit ProgramPoints(
const Points &points) : points(points) {}
472 ProgramPoints() =
default;
473 explicit ProgramPoints(
ProgramPoint point) { points.emplace(point); }
474 void add(
const ProgramPoints *from);
475 const ProgramPoints *merge(
const ProgramPoints *with)
const;
476 bool operator==(
const ProgramPoints &other)
const;
477 void dbprint(std::ostream &out)
const override {
479 for (
auto p : points) out << p <<
" ";
482 size_t size()
const {
return points.size(); }
483 bool containsBeforeStart()
const {
486 Points::const_iterator begin()
const {
return points.cbegin(); }
487 Points::const_iterator end()
const {
return points.cend(); }
496 bool unreachable =
false;
499 Definitions() =
default;
500 Definitions(
const Definitions &other)
501 : definitions(other.definitions), unreachable(other.unreachable) {}
502 Definitions *joinDefinitions(
const Definitions *other)
const;
508 definitions[loc] = point;
512 Definitions *setUnreachable() {
516 bool isUnreachable()
const {
return unreachable; }
518 return definitions.find(location) != definitions.end();
521 auto r = ::P4::get(definitions, location);
522 BUG_CHECK(r !=
nullptr,
"no definitions found for %1%", location);
526 bool operator==(
const Definitions &other)
const;
527 void dbprint(std::ostream &out)
const override {
529 out <<
" Unreachable" << Log::endl;
531 if (definitions.empty()) out <<
" Empty definitions";
533 for (
auto d : definitions) {
534 if (!first) out << Log::endl;
535 out <<
" " << *d.first <<
"=>" << *d.second;
539 Definitions *cloneDefinitions()
const {
return new Definitions(*
this); }
541 bool empty()
const {
return definitions.empty(); }
542 size_t size()
const {
return definitions.size(); }
557 auto it = atPoint.find(point);
558 if (it == atPoint.end()) {
559 if (emptyIfNotFound) {
561 setDefinitionsAt(point, defs,
false);
564 BUG(
"Unknown point %1% for definitions", &point);
570 auto it = atPoint.find(point);
571 if (it != atPoint.end()) {
572 LOG2(
"Overwriting definitions at " << point <<
": " << it->second <<
" with "
574 BUG_CHECK(
false,
"Overwriting definitions at %1%", point);
577 atPoint[point] = defs;
581 return storageMap.getStorage(decl);
585 return storageMap.getOrAdd(decl);
588 void dbprint(std::ostream &out)
const override {
589 for (
auto e : atPoint) out << e.first <<
" => " << e.second << Log::endl;
606 using CachedLocs = absl::node_hash_set<loc_t, Util::Hash>;
612 allDefinitions(allDefinitions),
617 virtualMethod(
false),
618 cached_locs(
new CachedLocs) {
619 CHECK_NULL(allDefinitions);
622 visitDagOnce =
false;
626 bool preorder(
const IR::Literal *expression)
override;
627 bool preorder(
const IR::AbstractSlice *expression)
override;
628 bool preorder(
const IR::TypeNameExpression *expression)
override;
629 bool preorder(
const IR::PathExpression *expression)
override;
630 bool preorder(
const IR::Member *expression)
override;
631 bool preorder(
const IR::ArrayIndex *expression)
override;
632 bool preorder(
const IR::Operation_Binary *expression)
override;
633 bool preorder(
const IR::Mux *expression)
override;
634 bool preorder(
const IR::SelectExpression *expression)
override;
635 bool preorder(
const IR::ListExpression *expression)
override;
636 bool preorder(
const IR::Operation_Unary *expression)
override;
637 bool preorder(
const IR::MethodCallExpression *expression)
override;
638 bool preorder(
const IR::DefaultExpression *expression)
override;
639 bool preorder(
const IR::Expression *expression)
override;
640 bool preorder(
const IR::InvalidHeader *expression)
override;
641 bool preorder(
const IR::InvalidHeaderUnion *expression)
override;
642 bool preorder(
const IR::P4ListExpression *expression)
override;
643 bool preorder(
const IR::ArrayExpression *expression)
override;
644 bool preorder(
const IR::StructExpression *expression)
override;
646 bool preorder(
const IR::P4Parser *parser)
override;
647 bool preorder(
const IR::P4Control *control)
override;
648 bool preorder(
const IR::P4Action *action)
override;
649 bool preorder(
const IR::P4Table *table)
override;
650 bool preorder(
const IR::Function *function)
override;
651 bool preorder(
const IR::BaseAssignmentStatement *statement)
override;
652 bool preorder(
const IR::ReturnStatement *statement)
override;
653 bool preorder(
const IR::ExitStatement *statement)
override;
654 bool preorder(
const IR::BreakStatement *statement)
override;
655 bool handleJump(
const char *tok,
Definitions *&defs);
656 bool preorder(
const IR::ContinueStatement *statement)
override;
657 bool preorder(
const IR::IfStatement *statement)
override;
658 bool preorder(
const IR::ForStatement *statement)
override;
659 bool preorder(
const IR::ForInStatement *statement)
override;
660 bool preorder(
const IR::BlockStatement *statement)
override;
661 bool preorder(
const IR::SwitchStatement *statement)
override;
662 bool preorder(
const IR::EmptyStatement *statement)
override;
663 bool preorder(
const IR::MethodCallStatement *statement)
override;
665 const LocationSet *writtenLocations(
const IR::Expression *expression) {
666 expression->apply(*
this);
667 return getWrites(expression);
687 static int nest_count;
692 std::shared_ptr<CachedLocs> cached_locs)
693 : refMap(source->refMap),
694 typeMap(source->typeMap),
696 allDefinitions(source->allDefinitions),
704 virtualMethod(false),
705 cached_locs(cached_locs) {
706 visitDagOnce =
false;
710 const loc_t *getLoc(
const Visitor::Context *ctxt);
711 const loc_t *getLoc(
const IR::Node *n,
const Visitor::Context *ctxt);
712 void enterScope(
const IR::ParameterList *parameters,
715 void exitScope(
const IR::ParameterList *parameters,
717 Definitions *getDefinitionsAfter(
const IR::ParserState *state);
718 bool setDefinitions(
Definitions *defs,
const IR::Node *who =
nullptr,
bool overwrite =
false);
721 const LocationSet *getWrites(
const IR::Expression *expression) {
722 const loc_t &exprLoc = *getLoc(expression, getChildContext());
723 auto result = ::P4::get(
writes, exprLoc);
724 BUG_CHECK(result !=
nullptr,
"No location set known for %1%", expression);
729 const LocationSet *getWrites(
const IR::Expression *expression,
const loc_t *parentLoc) {
730 const loc_t &exprLoc = *getLoc(expression, parentLoc);
731 auto result = ::P4::get(
writes, exprLoc);
732 BUG_CHECK(result !=
nullptr,
"No location set known for %1%", expression);
736 void expressionWrites(
const IR::Expression *expression,
const LocationSet *loc) {
737 CHECK_NULL(expression);
739 LOG3(expression << dbp(expression) <<
" writes " << loc);
740 const Context *ctx = getChildContext();
741 BUG_CHECK(ctx->node == expression,
"Expected ctx->node == expression.");
742 const loc_t &exprLoc = *getLoc(ctx);
743 if (
auto it =
writes.find(exprLoc); it !=
writes.end()) {
744 BUG_CHECK(*it->second == *loc || expression->is<IR::Literal>(),
745 "Expression %1% write set already set", expression);
747 writes.emplace(exprLoc, loc);
750 void dbprint(std::ostream &out)
const override {
751 if (
writes.empty()) out <<
"No writes";
752 for (
auto &it :
writes) out << it.first.node <<
" writes " << it.second << Log::endl;
754 profile_t init_apply(
const IR::Node *root)
override {
755 auto rv = Inspector::init_apply(root);
756 LOG1(
"starting ComputWriteSet" << Log::indent);
757 if (nest_count++ == 0 && LOGGING(2)) {
759 nested_trace =
memuse.start();
763 void end_apply()
override {
764 LOG1(
"finished CWS" << Log::unindent);
765 if (--nest_count == 0 && LOGGING(2)) {
766 memuse.stop(nested_trace);
772 std::shared_ptr<CachedLocs> cached_locs;
Definition frontends/p4/def_use.h:545
Definition alloc_trace.h:20
Definition frontends/p4/def_use.h:91
Definition frontends/p4/def_use.h:604
bool lhs
if true we are processing an expression on the lhs of an assignment
Definition frontends/p4/def_use.h:681
AllocTrace memuse
True if we are analyzing a virtual method.
Definition frontends/p4/def_use.h:685
Definitions * breakDefinitions
Definitions after exit statements.
Definition frontends/p4/def_use.h:677
Definitions * returnedDefinitions
Before statement currently processed.
Definition frontends/p4/def_use.h:675
ComputeWriteSet(const ComputeWriteSet *source, ProgramPoint context, Definitions *definitions, std::shared_ptr< CachedLocs > cached_locs)
Definition frontends/p4/def_use.h:691
void visitVirtualMethods(const IR::IndexedVector< IR::Declaration > &locals)
Statements and other control structures.
Definition frontends/p4/def_use.cpp:760
Definitions * exitDefinitions
Definitions after return statements.
Definition frontends/p4/def_use.h:676
Definitions * currentDefinitions
Result computed by this pass.
Definition frontends/p4/def_use.h:674
Definitions * continueDefinitions
Definitions at break statements.
Definition frontends/p4/def_use.h:678
ProgramPoint callingContext
Definitions at continue statements.
Definition frontends/p4/def_use.h:679
hvec_map< loc_t, const LocationSet * > writes
For each program location the location set it writes.
Definition frontends/p4/def_use.h:683
List of definers for each base storage (at a specific program point).
Definition frontends/p4/def_use.h:491
Definitions * writes(ProgramPoint point, const LocationSet &locations) const
Point writes the specified LocationSet.
Definition frontends/p4/def_use.cpp:369
Definition stringify.h:33
The Declaration interface, representing objects with names.
Definition declaration.h:17
virtual ID getName() const =0
Definition indexed_vector.h:31
Interface for locations that support an index operation.
Definition frontends/p4/def_use.h:163
Definition frontends/p4/def_use.h:243
const LocationSet * canonicalize() const
Definition frontends/p4/def_use.cpp:232
Indicates a statement in the program.
Definition frontends/p4/def_use.h:384
static ProgramPoint beforeStart
A point logically before the function/control/action start.
Definition frontends/p4/def_use.h:403
ProgramPoint after()
We use a nullptr to indicate a point after the previous context.
Definition frontends/p4/def_use.h:405
Definition frontends/p4/def_use.h:466
Class used to encode maps from paths to declarations.
Definition referenceMap.h:67
Definition frontends/p4/def_use.h:226
Abstraction for something that is has a left value (variable, parameter)
Definition frontends/p4/def_use.h:48
LocationSet getValidBits() const
Definition frontends/p4/def_use.cpp:154
LocationSet removeHeaders() const
Definition frontends/p4/def_use.cpp:106
LocationSet getLastIndexField() const
Definition frontends/p4/def_use.cpp:160
Maps a declaration to its associated storage.
Definition frontends/p4/def_use.h:348
Definition iterator_range.h:44
Base class for location sets that contain fields.
Definition frontends/p4/def_use.h:115
Definition ordered_set.h:32
TODO: this is not really specific to BMV2, it should reside somewhere else.
Definition applyOptionsPragmas.cpp:13
Definition frontends/p4/def_use.h:32