Skip to content

Commit 3858d65

Browse files
committed
Visitor: Add convenient way of using member functions
Using member functions of the payload type seems likely to be a very common pattern, especially for converting code that uses the llvm::InstVisitor. This complicates the VisitorHandler setup a bit. At some point, one has to wonder if it wouldn't be better to just use std::function. The current system still has some performance advantages: * It is able to handle generic projections while still handling the common case of projections to fields cheaply, with a single, perfectly predictable branch. * VisitorHandler is 32 bytes; so is std::function, so wrapping std::function would be more expensive in terms of cache pressure. And we specifically do not want the full power of std::function. Users of this facility should be strongly encouraged to use the pattern where a visitor is constructed once as a static variable, so that the cost of building hash maps is amortized.
1 parent d1e1959 commit 3858d65

File tree

3 files changed

+70
-18
lines changed

3 files changed

+70
-18
lines changed

example/ExampleMain.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ struct VisitorInnermost {
146146
struct VisitorNest {
147147
raw_ostream *out = nullptr;
148148
VisitorInnermost inner;
149+
150+
void visitBinaryOperator(BinaryOperator &inst) {
151+
*out << "visiting BinaryOperator: " << inst << '\n';
152+
}
149153
};
150154

151155
struct VisitorContainer {
@@ -179,9 +183,7 @@ template <bool rpot> const Visitor<VisitorContainer> &getExampleVisitor() {
179183
[](VisitorNest &self, UnaryInstruction &inst) {
180184
*self.out << "visiting UnaryInstruction: " << inst << '\n';
181185
});
182-
b.add<BinaryOperator>([](VisitorNest &self, BinaryOperator &inst) {
183-
*self.out << "visiting BinaryOperator: " << inst << '\n';
184-
});
186+
b.add(&VisitorNest::visitBinaryOperator);
185187
b.nest<raw_ostream>([](VisitorBuilder<raw_ostream> &b) {
186188
b.add<xd::WriteOp>([](raw_ostream &out, xd::WriteOp &op) {
187189
out << "visiting WriteOp: " << op << '\n';

include/llvm-dialects/Dialect/Visitor.h

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -123,16 +123,31 @@ class VisitorKey {
123123
unsigned m_intrinsicId = 0;
124124
};
125125

126-
using VisitorCallback = void (void *, void *, llvm::Instruction *);
126+
struct VisitorCallbackData {
127+
private:
128+
static constexpr size_t SizeofMemberFunctionPointer =
129+
sizeof(void(VisitorCallbackData::*)());
130+
static constexpr size_t SizeofFunctionPointer = sizeof(void (*)());
131+
132+
public:
133+
// Size is 16 bytes in the 64-bit Itanium ABI.
134+
static constexpr size_t Size =
135+
std::max(SizeofFunctionPointer, SizeofMemberFunctionPointer);
136+
137+
char data[Size];
138+
};
139+
140+
using VisitorCallback = void(const VisitorCallbackData &, void *,
141+
llvm::Instruction *);
127142
using PayloadProjectionCallback = void *(void *);
128143

129144
struct VisitorHandler {
130-
VisitorCallback *callback = nullptr;
131-
void *callbackData = nullptr;
132-
133145
// If non-negative, a byte offset to apply to the payload. If negative,
134146
// a shifted index into the payload projections vector.
135147
ssize_t projection = 0;
148+
149+
VisitorCallback *callback = nullptr;
150+
VisitorCallbackData data;
136151
};
137152

138153
/// Apply first the byte offset and then the projection function. If projection
@@ -157,7 +172,7 @@ class VisitorTemplate {
157172

158173
public:
159174
void setStrategy(VisitorStrategy strategy);
160-
void add(VisitorKey key, void *extra, VisitorCallback *fn,
175+
void add(VisitorKey key, VisitorCallback *fn, VisitorCallbackData data,
161176
ssize_t projection);
162177

163178
private:
@@ -200,7 +215,7 @@ class VisitorBuilderBase {
200215

201216
void setStrategy(VisitorStrategy strategy);
202217

203-
void add(VisitorKey key, void *extra, VisitorCallback *fn);
218+
void add(VisitorKey key, VisitorCallback *fn, VisitorCallbackData data);
204219

205220
VisitorBase build();
206221

@@ -316,12 +331,23 @@ class VisitorBuilder : private detail::VisitorBuilderBase {
316331
return *this;
317332
}
318333

334+
template <typename OpT> VisitorBuilder &add(void (PayloadT::*fn)(OpT &)) {
335+
addMemberFnCase<OpT>(detail::VisitorKey::op<OpT>(), fn);
336+
return *this;
337+
}
338+
319339
VisitorBuilder &addIntrinsic(unsigned id,
320340
void (*fn)(PayloadT &, llvm::IntrinsicInst &)) {
321341
addCase<llvm::IntrinsicInst>(detail::VisitorKey::intrinsic(id), fn);
322342
return *this;
323343
}
324344

345+
VisitorBuilder &addIntrinsic(unsigned id,
346+
void (PayloadT::*fn)(llvm::IntrinsicInst &)) {
347+
addMemberFnCase<llvm::IntrinsicInst>(detail::VisitorKey::intrinsic(id), fn);
348+
return *this;
349+
}
350+
325351
template <typename NestedPayloadT>
326352
VisitorBuilder &nest(void (*registration)(VisitorBuilder<NestedPayloadT> &)) {
327353
if constexpr (detail::VisitorPayloadOffsetProjection<
@@ -354,14 +380,36 @@ class VisitorBuilder : private detail::VisitorBuilderBase {
354380

355381
template <typename OpT>
356382
void addCase(detail::VisitorKey key, void (*fn)(PayloadT &, OpT &)) {
357-
VisitorBuilderBase::add(key, (void *)fn, &VisitorBuilder::forwarder<OpT>);
383+
detail::VisitorCallbackData data{};
384+
static_assert(sizeof(fn) <= sizeof(data.data));
385+
memcpy(&data.data, &fn, sizeof(fn));
386+
VisitorBuilderBase::add(key, &VisitorBuilder::forwarder<OpT>, data);
358387
}
359388

360389
template <typename OpT>
361-
static void forwarder(void *extra, void *payload, llvm::Instruction *op) {
362-
auto fn = (void (*)(PayloadT &, OpT &))extra;
390+
void addMemberFnCase(detail::VisitorKey key, void (PayloadT::*fn)(OpT &)) {
391+
detail::VisitorCallbackData data{};
392+
static_assert(sizeof(fn) <= sizeof(data.data));
393+
memcpy(&data.data, &fn, sizeof(fn));
394+
VisitorBuilderBase::add(key, &VisitorBuilder::memberFnForwarder<OpT>, data);
395+
}
396+
397+
template <typename OpT>
398+
static void forwarder(const detail::VisitorCallbackData &data, void *payload,
399+
llvm::Instruction *op) {
400+
void (*fn)(PayloadT &, OpT &);
401+
memcpy(&fn, &data.data, sizeof(fn));
363402
fn(*static_cast<PayloadT *>(payload), *llvm::cast<OpT>(op));
364403
}
404+
405+
template <typename OpT>
406+
static void memberFnForwarder(const detail::VisitorCallbackData &data,
407+
void *payload, llvm::Instruction *op) {
408+
void (PayloadT::*fn)(OpT &);
409+
memcpy(&fn, &data.data, sizeof(fn));
410+
PayloadT *self = static_cast<PayloadT *>(payload);
411+
(self->*fn)(*llvm::cast<OpT>(op));
412+
}
365413
};
366414

367415
} // namespace llvm_dialects

lib/Dialect/Visitor.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ using namespace llvm;
3030

3131
using llvm_dialects::detail::VisitorBase;
3232
using llvm_dialects::detail::VisitorBuilderBase;
33+
using llvm_dialects::detail::VisitorCallbackData;
3334
using llvm_dialects::detail::VisitorHandler;
3435
using llvm_dialects::detail::VisitorKey;
3536
using llvm_dialects::detail::VisitorTemplate;
@@ -43,11 +44,11 @@ void VisitorTemplate::setStrategy(VisitorStrategy strategy) {
4344
m_strategy = strategy;
4445
}
4546

46-
void VisitorTemplate::add(VisitorKey key, void *extra, VisitorCallback *fn,
47-
ssize_t projection) {
47+
void VisitorTemplate::add(VisitorKey key, VisitorCallback *fn,
48+
VisitorCallbackData data, ssize_t projection) {
4849
VisitorHandler handler;
4950
handler.callback = fn;
50-
handler.callbackData = extra;
51+
handler.data = data;
5152
handler.projection = projection;
5253
m_handlers.emplace_back(handler);
5354

@@ -140,8 +141,9 @@ void VisitorBuilderBase::setStrategy(VisitorStrategy strategy) {
140141
m_template->setStrategy(strategy);
141142
}
142143

143-
void VisitorBuilderBase::add(VisitorKey key, void *extra, VisitorCallback *fn) {
144-
m_template->add(key, extra, fn, m_projection);
144+
void VisitorBuilderBase::add(VisitorKey key, VisitorCallback *fn,
145+
VisitorCallbackData data) {
146+
m_template->add(key, fn, data, m_projection);
145147
}
146148

147149
VisitorBase VisitorBuilderBase::build() {
@@ -220,7 +222,7 @@ void VisitorBase::call(const VisitorHandler &handler, void *payload,
220222
payload = m_projections[idx].projection(payload);
221223
}
222224
}
223-
handler.callback(handler.callbackData, payload, &inst);
225+
handler.callback(handler.data, payload, &inst);
224226
}
225227

226228
void VisitorBase::visit(void *payload, Instruction &inst) const {

0 commit comments

Comments
 (0)