@@ -45,37 +45,107 @@ enum class VisitorStrategy {
4545 ByFunctionDeclaration,
4646};
4747
48+ // / @brief Auxiliary template to support nested visitor clients.
49+ // /
50+ // / It can be convenient to write visitor clients in a modular fashion and
51+ // / combine them together to use a single @ref Visitor. Each client module
52+ // / may use a different payload type.
53+ // /
54+ // / In this case, each nested client module's payload must be reachable from
55+ // / the top-level payload, and a specialization of this template must be
56+ // / provided that "projects" parent payload references to child payload
57+ // / references.
58+ // /
59+ // / In cases where the nested payload is a field of the parent payload, the
60+ // / @ref LLVM_DIALECTS_VISITOR_PAYLOAD_PROJECT_FIELD can be used instead.
61+ template <typename PayloadT, typename NestedPayloadT>
62+ struct VisitorPayloadProjection {
63+ // Template specializations must implement this static method:
64+ //
65+ // static NestedPayloadT &project(PayloadT &);
66+ };
67+
68+ // / Declare that `PayloadT` can be projected to a nested payload type via
69+ // / `field`.
70+ // /
71+ // / This creates a specialization of @ref VisitorPayloadProjection and must
72+ // / therefore typically be outside of any namespace. The nested type is derived
73+ // / automatically.
74+ #define LLVM_DIALECTS_VISITOR_PAYLOAD_PROJECT_FIELD (PayloadT, field ) \
75+ template <> \
76+ struct llvm_dialects ::detail::VisitorPayloadOffsetProjection< \
77+ PayloadT, \
78+ std::remove_reference_t <decltype (std::declval<PayloadT>().field)>> { \
79+ static constexpr bool useOffsetof = true ; \
80+ static constexpr std::size_t offset = offsetof(PayloadT, field); \
81+ };
82+
4883namespace detail {
4984
5085class VisitorBase ;
5186
5287using VisitorCallback = void (void *, void *, llvm::Instruction *);
53- using VisitorCase = std::tuple<const OpDescription *, void *, VisitorCallback *>;
88+ using PayloadProjectionCallback = void *(void *);
89+
90+ // / Apply first the byte offset and then the projection function. If projection
91+ // / is null, stop the projection sequence.
92+ struct PayloadProjection {
93+ std::size_t offset = 0 ;
94+ PayloadProjectionCallback *projection = nullptr ;
95+ };
96+
97+ struct VisitorCase {
98+ const OpDescription *description = nullptr ;
99+ VisitorCallback *callback = nullptr ;
100+ void *callbackData = nullptr ;
101+
102+ // If non-negative, a byte offset to apply to the payload. If negative,
103+ // a shifted index into the projections vector.
104+ ssize_t projection = 0 ;
105+ };
106+
107+ template <typename PayloadT, typename NestedPayloadT>
108+ struct VisitorPayloadOffsetProjection {
109+ static constexpr bool useOffsetof = false ;
110+ };
54111
55112class VisitorBuilderBase {
56113 friend class VisitorBase ;
57114public:
115+ VisitorBuilderBase () = default ;
116+ explicit VisitorBuilderBase (VisitorBuilderBase *parent) : m_parent(parent) {}
117+ ~VisitorBuilderBase ();
118+
58119 void setStrategy (VisitorStrategy strategy) { m_strategy = strategy; }
59120
60- protected:
61121 void add (const OpDescription &desc, void *extra, VisitorCallback *fn);
62122
123+ public:
124+ PayloadProjectionCallback *m_projection = nullptr ;
125+ size_t m_offsetProjection = 0 ;
126+
63127private:
128+ VisitorBuilderBase *m_parent = nullptr ;
64129 VisitorStrategy m_strategy = VisitorStrategy::ByFunctionDeclaration;
65130 llvm::SmallVector<VisitorCase> m_cases;
131+ llvm::SmallVector<PayloadProjection> m_projections;
66132};
67133
68134class VisitorBase {
69135protected:
70- VisitorBase (VisitorBuilderBase builder);
136+ VisitorBase (VisitorBuilderBase && builder);
71137
72138 void visit (void *payload, llvm::Instruction &inst) const ;
73139 void visit (void *payload, llvm::Function &fn) const ;
74140 void visit (void *payload, llvm::Module &module ) const ;
75141
76142private:
143+ void call (const VisitorCase &theCase, void *payload,
144+ llvm::Instruction &inst) const ;
145+
77146 VisitorStrategy m_strategy;
78147 llvm::SmallVector<VisitorCase> m_cases;
148+ llvm::SmallVector<PayloadProjection> m_projections;
79149};
80150
81151} // namespace detail
@@ -91,7 +161,7 @@ class VisitorBase {
91161template <typename PayloadT>
92162class Visitor : public detail ::VisitorBase {
93163public:
94- Visitor (detail::VisitorBuilderBase builder)
164+ Visitor (detail::VisitorBuilderBase && builder)
95165 : VisitorBase(std::move(builder)) {}
96166
97167 void visit (PayloadT &payload, llvm::Instruction &inst) const {
@@ -130,16 +200,20 @@ class Visitor : public detail::VisitorBase {
130200// / myVisitor(myPayload, module);
131201// / @endcode
132202template <typename PayloadT>
133- class VisitorBuilder : public detail ::VisitorBuilderBase {
203+ class VisitorBuilder : private detail ::VisitorBuilderBase {
204+ template <typename OtherT> friend class VisitorBuilder ;
205+
134206public:
135207 using Payload = PayloadT;
136208
209+ VisitorBuilder () = default ;
210+
137211 VisitorBuilder &setStrategy (VisitorStrategy strategy) {
138212 VisitorBuilderBase::setStrategy (strategy);
139213 return *this ;
140214 }
141215
142- Visitor<PayloadT> build () { return { std::move (*this )} ; }
216+ Visitor<PayloadT> build () { return std::move (*this ); }
143217
144218 template <typename OpT> VisitorBuilder &add (void (*fn)(PayloadT &, OpT &)) {
145219 VisitorBuilderBase::add (
@@ -151,6 +225,32 @@ class VisitorBuilder : public detail::VisitorBuilderBase {
151225 });
152226 return *this ;
153227 }
228+
229+ template <typename NestedPayloadT>
230+ VisitorBuilder &nest (void (*registration)(VisitorBuilder<NestedPayloadT> &)) {
231+ VisitorBuilder<NestedPayloadT> nested{this };
232+
233+ if constexpr (detail::VisitorPayloadOffsetProjection<
234+ PayloadT, NestedPayloadT>::useOffsetof) {
235+ nested.m_offsetProjection =
236+ detail::VisitorPayloadOffsetProjection<PayloadT,
237+ NestedPayloadT>::offset;
238+ } else {
239+ nested.m_projection = [](void *payload) -> void * {
240+ return static_cast <void *>(
241+ &VisitorPayloadProjection<PayloadT, NestedPayloadT>::project (
242+ *static_cast <PayloadT *>(payload)));
243+ };
244+ }
245+
246+ (*registration)(nested);
247+
248+ return *this ;
249+ }
250+
251+ private:
252+ explicit VisitorBuilder (VisitorBuilderBase *parent)
253+ : VisitorBuilderBase(parent) {}
154254};
155255
156256} // namespace llvm_dialects
0 commit comments