Skip to content

Commit ffdaad6

Browse files
authored
Avoid boilerplate code with a big universal delegation header (#3294)
To get an idea, this is a sample from the header: switch (DELEGATE_ID) { [..] case Expression::Id::BlockId: { DELEGATE_START(Block); DELEGATE_FIELD_CHILD_LIST(Block, list); DELEGATE_FIELD_SCOPE_NAME(Block, name); DELEGATE_END(Block); break; } A switch is defined (on some DELEGATE_ID that is provided). There is then a case for each expression type, calling a delegate for each field type. This allows a user to define the delegates it wants, then include this header. This uses the new header in the copying logic, where the rewrite also avoids recursion. This fixes a missing BrOnExn::sent scanning in the existing hashing/comparing code (which is what I used as a basis). When this is used there, it will fix that bug.
1 parent 47607d4 commit ffdaad6

File tree

2 files changed

+664
-295
lines changed

2 files changed

+664
-295
lines changed

src/ir/ExpressionManipulator.cpp

Lines changed: 80 additions & 295 deletions
Original file line numberDiff line numberDiff line change
@@ -23,306 +23,91 @@ namespace ExpressionManipulator {
2323

2424
Expression*
2525
flexibleCopy(Expression* original, Module& wasm, CustomCopier custom) {
26-
struct Copier : public OverriddenVisitor<Copier, Expression*> {
27-
Module& wasm;
28-
CustomCopier custom;
26+
// Perform the copy using a stack of tasks (avoiding recusion).
27+
struct CopyTask {
28+
// The thing to copy.
29+
Expression* original;
30+
// The location of the pointer to write the copy to.
31+
Expression** destPointer;
32+
};
33+
std::vector<CopyTask> tasks;
34+
Expression* ret;
35+
tasks.push_back({original, &ret});
36+
while (!tasks.empty()) {
37+
auto task = tasks.back();
38+
tasks.pop_back();
39+
// If the custom copier handled this one, we have nothing to do.
40+
auto* copy = custom(task.original);
41+
if (copy) {
42+
*task.destPointer = copy;
43+
continue;
44+
}
45+
// If the original is a null, just copy that. (This can happen for an
46+
// optional child.)
47+
auto* original = task.original;
48+
if (original == nullptr) {
49+
*task.destPointer = nullptr;
50+
continue;
51+
}
52+
// Allocate a new copy, and copy the fields.
2953

30-
Builder builder;
54+
#define DELEGATE_ID original->_id
3155

32-
Copier(Module& wasm, CustomCopier custom)
33-
: wasm(wasm), custom(custom), builder(wasm) {}
56+
// Allocate a new expression of the right type, and create cast versions of it
57+
// for later operations.
58+
#define DELEGATE_START(id) \
59+
copy = wasm.allocator.alloc<id>(); \
60+
auto* castOriginal = original->cast<id>(); \
61+
WASM_UNUSED(castOriginal); \
62+
auto* castCopy = copy->cast<id>(); \
63+
WASM_UNUSED(castCopy);
3464

35-
Expression* copy(Expression* curr) {
36-
if (!curr) {
37-
return nullptr;
38-
}
39-
auto* ret = custom(curr);
40-
if (ret) {
41-
return ret;
42-
}
43-
return OverriddenVisitor<Copier, Expression*>::visit(curr);
44-
}
65+
// Handle each type of field, copying it appropriately.
66+
#define DELEGATE_FIELD_CHILD(id, name) \
67+
tasks.push_back({castOriginal->name, &castCopy->name});
4568

46-
Expression* visitBlock(Block* curr) {
47-
ExpressionList list(wasm.allocator);
48-
for (Index i = 0; i < curr->list.size(); i++) {
49-
list.push_back(copy(curr->list[i]));
50-
}
51-
return builder.makeBlock(curr->name, list, curr->type);
52-
}
53-
Expression* visitIf(If* curr) {
54-
return builder.makeIf(copy(curr->condition),
55-
copy(curr->ifTrue),
56-
copy(curr->ifFalse),
57-
curr->type);
58-
}
59-
Expression* visitLoop(Loop* curr) {
60-
return builder.makeLoop(curr->name, copy(curr->body), curr->type);
61-
}
62-
Expression* visitBreak(Break* curr) {
63-
return builder.makeBreak(
64-
curr->name, copy(curr->value), copy(curr->condition));
65-
}
66-
Expression* visitSwitch(Switch* curr) {
67-
return builder.makeSwitch(curr->targets,
68-
curr->default_,
69-
copy(curr->condition),
70-
copy(curr->value));
71-
}
72-
Expression* visitCall(Call* curr) {
73-
auto* ret =
74-
builder.makeCall(curr->target, {}, curr->type, curr->isReturn);
75-
for (Index i = 0; i < curr->operands.size(); i++) {
76-
ret->operands.push_back(copy(curr->operands[i]));
77-
}
78-
return ret;
79-
}
80-
Expression* visitCallIndirect(CallIndirect* curr) {
81-
std::vector<Expression*> copiedOps;
82-
for (auto op : curr->operands) {
83-
copiedOps.push_back(copy(op));
84-
}
85-
return builder.makeCallIndirect(
86-
copy(curr->target), copiedOps, curr->sig, curr->isReturn);
87-
}
88-
Expression* visitLocalGet(LocalGet* curr) {
89-
return builder.makeLocalGet(curr->index, curr->type);
90-
}
91-
Expression* visitLocalSet(LocalSet* curr) {
92-
if (curr->isTee()) {
93-
return builder.makeLocalTee(curr->index, copy(curr->value), curr->type);
94-
} else {
95-
return builder.makeLocalSet(curr->index, copy(curr->value));
96-
}
97-
}
98-
Expression* visitGlobalGet(GlobalGet* curr) {
99-
return builder.makeGlobalGet(curr->name, curr->type);
100-
}
101-
Expression* visitGlobalSet(GlobalSet* curr) {
102-
return builder.makeGlobalSet(curr->name, copy(curr->value));
103-
}
104-
Expression* visitLoad(Load* curr) {
105-
if (curr->isAtomic) {
106-
return builder.makeAtomicLoad(
107-
curr->bytes, curr->offset, copy(curr->ptr), curr->type);
108-
}
109-
return builder.makeLoad(curr->bytes,
110-
LoadUtils::isSignRelevant(curr) ? curr->signed_
111-
: false,
112-
curr->offset,
113-
curr->align,
114-
copy(curr->ptr),
115-
curr->type);
116-
}
117-
Expression* visitStore(Store* curr) {
118-
if (curr->isAtomic) {
119-
return builder.makeAtomicStore(curr->bytes,
120-
curr->offset,
121-
copy(curr->ptr),
122-
copy(curr->value),
123-
curr->valueType);
124-
}
125-
return builder.makeStore(curr->bytes,
126-
curr->offset,
127-
curr->align,
128-
copy(curr->ptr),
129-
copy(curr->value),
130-
curr->valueType);
131-
}
132-
Expression* visitAtomicRMW(AtomicRMW* curr) {
133-
return builder.makeAtomicRMW(curr->op,
134-
curr->bytes,
135-
curr->offset,
136-
copy(curr->ptr),
137-
copy(curr->value),
138-
curr->type);
139-
}
140-
Expression* visitAtomicCmpxchg(AtomicCmpxchg* curr) {
141-
return builder.makeAtomicCmpxchg(curr->bytes,
142-
curr->offset,
143-
copy(curr->ptr),
144-
copy(curr->expected),
145-
copy(curr->replacement),
146-
curr->type);
147-
}
148-
Expression* visitAtomicWait(AtomicWait* curr) {
149-
return builder.makeAtomicWait(copy(curr->ptr),
150-
copy(curr->expected),
151-
copy(curr->timeout),
152-
curr->expectedType,
153-
curr->offset);
154-
}
155-
Expression* visitAtomicNotify(AtomicNotify* curr) {
156-
return builder.makeAtomicNotify(
157-
copy(curr->ptr), copy(curr->notifyCount), curr->offset);
158-
}
159-
Expression* visitAtomicFence(AtomicFence* curr) {
160-
return builder.makeAtomicFence();
161-
}
162-
Expression* visitSIMDExtract(SIMDExtract* curr) {
163-
return builder.makeSIMDExtract(curr->op, copy(curr->vec), curr->index);
164-
}
165-
Expression* visitSIMDReplace(SIMDReplace* curr) {
166-
return builder.makeSIMDReplace(
167-
curr->op, copy(curr->vec), curr->index, copy(curr->value));
168-
}
169-
Expression* visitSIMDShuffle(SIMDShuffle* curr) {
170-
return builder.makeSIMDShuffle(
171-
copy(curr->left), copy(curr->right), curr->mask);
172-
}
173-
Expression* visitSIMDTernary(SIMDTernary* curr) {
174-
return builder.makeSIMDTernary(
175-
curr->op, copy(curr->a), copy(curr->b), copy(curr->c));
176-
}
177-
Expression* visitSIMDShift(SIMDShift* curr) {
178-
return builder.makeSIMDShift(
179-
curr->op, copy(curr->vec), copy(curr->shift));
180-
}
181-
Expression* visitSIMDLoad(SIMDLoad* curr) {
182-
return builder.makeSIMDLoad(
183-
curr->op, curr->offset, curr->align, copy(curr->ptr));
184-
}
185-
Expression* visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {
186-
return builder.makeSIMDLoadStoreLane(curr->op,
187-
curr->offset,
188-
curr->align,
189-
curr->index,
190-
copy(curr->ptr),
191-
copy(curr->vec));
192-
}
193-
Expression* visitConst(Const* curr) {
194-
return builder.makeConst(curr->value);
195-
}
196-
Expression* visitMemoryInit(MemoryInit* curr) {
197-
return builder.makeMemoryInit(
198-
curr->segment, copy(curr->dest), copy(curr->offset), copy(curr->size));
199-
}
200-
Expression* visitDataDrop(DataDrop* curr) {
201-
return builder.makeDataDrop(curr->segment);
202-
}
203-
Expression* visitMemoryCopy(MemoryCopy* curr) {
204-
return builder.makeMemoryCopy(
205-
copy(curr->dest), copy(curr->source), copy(curr->size));
206-
}
207-
Expression* visitMemoryFill(MemoryFill* curr) {
208-
return builder.makeMemoryFill(
209-
copy(curr->dest), copy(curr->value), copy(curr->size));
210-
}
211-
Expression* visitUnary(Unary* curr) {
212-
return builder.makeUnary(curr->op, copy(curr->value));
213-
}
214-
Expression* visitBinary(Binary* curr) {
215-
return builder.makeBinary(curr->op, copy(curr->left), copy(curr->right));
216-
}
217-
Expression* visitSelect(Select* curr) {
218-
return builder.makeSelect(copy(curr->condition),
219-
copy(curr->ifTrue),
220-
copy(curr->ifFalse),
221-
curr->type);
222-
}
223-
Expression* visitDrop(Drop* curr) {
224-
return builder.makeDrop(copy(curr->value));
225-
}
226-
Expression* visitReturn(Return* curr) {
227-
return builder.makeReturn(copy(curr->value));
228-
}
229-
Expression* visitMemorySize(MemorySize* curr) {
230-
return builder.makeMemorySize();
231-
}
232-
Expression* visitMemoryGrow(MemoryGrow* curr) {
233-
return builder.makeMemoryGrow(copy(curr->delta));
234-
}
235-
Expression* visitRefNull(RefNull* curr) {
236-
return builder.makeRefNull(curr->type);
237-
}
238-
Expression* visitRefIsNull(RefIsNull* curr) {
239-
return builder.makeRefIsNull(copy(curr->value));
240-
}
241-
Expression* visitRefFunc(RefFunc* curr) {
242-
return builder.makeRefFunc(curr->func);
243-
}
244-
Expression* visitRefEq(RefEq* curr) {
245-
return builder.makeRefEq(copy(curr->left), copy(curr->right));
246-
}
247-
Expression* visitTry(Try* curr) {
248-
return builder.makeTry(
249-
copy(curr->body), copy(curr->catchBody), curr->type);
250-
}
251-
Expression* visitThrow(Throw* curr) {
252-
std::vector<Expression*> operands;
253-
for (Index i = 0; i < curr->operands.size(); i++) {
254-
operands.push_back(copy(curr->operands[i]));
255-
}
256-
return builder.makeThrow(curr->event, std::move(operands));
257-
}
258-
Expression* visitRethrow(Rethrow* curr) {
259-
return builder.makeRethrow(copy(curr->exnref));
260-
}
261-
Expression* visitBrOnExn(BrOnExn* curr) {
262-
return builder.makeBrOnExn(
263-
curr->name, curr->event, copy(curr->exnref), curr->sent);
264-
}
265-
Expression* visitNop(Nop* curr) { return builder.makeNop(); }
266-
Expression* visitUnreachable(Unreachable* curr) {
267-
return builder.makeUnreachable();
268-
}
269-
Expression* visitPop(Pop* curr) { return builder.makePop(curr->type); }
270-
Expression* visitTupleMake(TupleMake* curr) {
271-
std::vector<Expression*> operands;
272-
for (auto* op : curr->operands) {
273-
operands.push_back(copy(op));
274-
}
275-
return builder.makeTupleMake(std::move(operands));
276-
}
277-
Expression* visitTupleExtract(TupleExtract* curr) {
278-
return builder.makeTupleExtract(copy(curr->tuple), curr->index);
279-
}
280-
Expression* visitI31New(I31New* curr) {
281-
return builder.makeI31New(copy(curr->value));
282-
}
283-
Expression* visitI31Get(I31Get* curr) {
284-
return builder.makeI31Get(copy(curr->i31), curr->signed_);
285-
}
286-
Expression* visitRefTest(RefTest* curr) {
287-
WASM_UNREACHABLE("TODO (gc): ref.test");
288-
}
289-
Expression* visitRefCast(RefCast* curr) {
290-
WASM_UNREACHABLE("TODO (gc): ref.cast");
291-
}
292-
Expression* visitBrOnCast(BrOnCast* curr) {
293-
WASM_UNREACHABLE("TODO (gc): br_on_cast");
294-
}
295-
Expression* visitRttCanon(RttCanon* curr) {
296-
WASM_UNREACHABLE("TODO (gc): rtt.canon");
297-
}
298-
Expression* visitRttSub(RttSub* curr) {
299-
WASM_UNREACHABLE("TODO (gc): rtt.sub");
300-
}
301-
Expression* visitStructNew(StructNew* curr) {
302-
WASM_UNREACHABLE("TODO (gc): struct.new");
303-
}
304-
Expression* visitStructGet(StructGet* curr) {
305-
WASM_UNREACHABLE("TODO (gc): struct.get");
306-
}
307-
Expression* visitStructSet(StructSet* curr) {
308-
WASM_UNREACHABLE("TODO (gc): struct.set");
309-
}
310-
Expression* visitArrayNew(ArrayNew* curr) {
311-
WASM_UNREACHABLE("TODO (gc): array.new");
312-
}
313-
Expression* visitArrayGet(ArrayGet* curr) {
314-
WASM_UNREACHABLE("TODO (gc): array.get");
315-
}
316-
Expression* visitArraySet(ArraySet* curr) {
317-
WASM_UNREACHABLE("TODO (gc): array.set");
318-
}
319-
Expression* visitArrayLen(ArrayLen* curr) {
320-
WASM_UNREACHABLE("TODO (gc): array.len");
321-
}
322-
};
69+
#define DELEGATE_FIELD_CHILD_VECTOR(id, name) \
70+
castCopy->name.resize(castOriginal->name.size()); \
71+
for (Index i = 0; i < castOriginal->name.size(); i++) { \
72+
tasks.push_back({castOriginal->name[i], &castCopy->name[i]}); \
73+
}
74+
75+
#define COPY_FIELD(name) castCopy->name = castOriginal->name;
76+
77+
#define DELEGATE_FIELD_INT(id, name) COPY_FIELD(name)
78+
#define DELEGATE_FIELD_LITERAL(id, name) COPY_FIELD(name)
79+
#define DELEGATE_FIELD_NAME(id, name) COPY_FIELD(name)
80+
#define DELEGATE_FIELD_SCOPE_NAME(id, name) COPY_FIELD(name)
81+
#define DELEGATE_FIELD_SIGNATURE(id, name) COPY_FIELD(name)
82+
#define DELEGATE_FIELD_TYPE(id, name) COPY_FIELD(name)
83+
#define DELEGATE_FIELD_ADDRESS(id, name) COPY_FIELD(name)
32384

324-
Copier copier(wasm, custom);
325-
return copier.copy(original);
85+
#define COPY_FIELD_LIST(name) \
86+
for (Index i = 0; i < castOriginal->name.size(); i++) { \
87+
castCopy->name[i] = castOriginal->name[i]; \
88+
}
89+
90+
#define COPY_VECTOR(name) \
91+
castCopy->name.resize(castOriginal->name.size()); \
92+
COPY_FIELD_LIST(name)
93+
94+
#define COPY_ARRAY(name) \
95+
assert(castCopy->name.size() == castOriginal->name.size()); \
96+
COPY_FIELD_LIST(name)
97+
98+
#define DELEGATE_FIELD_SCOPE_NAME_VECTOR(id, name) COPY_VECTOR(name)
99+
100+
#define DELEGATE_FIELD_INT_ARRAY(id, name) COPY_ARRAY(name)
101+
102+
#include "wasm-delegations-fields.h"
103+
104+
// The type can be simply copied.
105+
copy->type = original->type;
106+
107+
// Write the copy to where it should be referred to.
108+
*task.destPointer = copy;
109+
}
110+
return ret;
326111
}
327112

328113
// Splice an item into the middle of a block's list

0 commit comments

Comments
 (0)