1 | /* |
2 | * Copyright 2016 WebAssembly Community Group participants |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | #ifndef wasm_wasm_builder_h |
18 | #define wasm_wasm_builder_h |
19 | |
20 | #include "ir/manipulation.h" |
21 | #include "parsing.h" |
22 | #include "wasm.h" |
23 | |
24 | namespace wasm { |
25 | |
26 | // Useful data structures |
27 | |
28 | struct NameType { |
29 | Name name; |
30 | Type type; |
31 | NameType() : name(nullptr), type(Type::none) {} |
32 | NameType(Name name, Type type) : name(name), type(type) {} |
33 | }; |
34 | |
35 | // General AST node builder |
36 | |
37 | class Builder { |
38 | Module& wasm; |
39 | |
40 | public: |
41 | Builder(Module& wasm) : wasm(wasm) {} |
42 | |
43 | // make* functions create an expression instance. |
44 | |
45 | static std::unique_ptr<Function> makeFunction(Name name, |
46 | HeapType type, |
47 | std::vector<Type>&& vars, |
48 | Expression* body = nullptr) { |
49 | assert(type.isSignature()); |
50 | auto func = std::make_unique<Function>(); |
51 | func->name = name; |
52 | func->type = type; |
53 | func->body = body; |
54 | func->vars.swap(vars); |
55 | return func; |
56 | } |
57 | |
58 | static std::unique_ptr<Function> makeFunction(Name name, |
59 | std::vector<NameType>&& params, |
60 | HeapType type, |
61 | std::vector<NameType>&& vars, |
62 | Expression* body = nullptr) { |
63 | assert(type.isSignature()); |
64 | auto func = std::make_unique<Function>(); |
65 | func->name = name; |
66 | func->type = type; |
67 | func->body = body; |
68 | for (size_t i = 0; i < params.size(); ++i) { |
69 | NameType& param = params[i]; |
70 | assert(func->getParams()[i] == param.type); |
71 | Index index = func->localNames.size(); |
72 | func->localIndices[param.name] = index; |
73 | func->localNames[index] = param.name; |
74 | } |
75 | for (auto& var : vars) { |
76 | func->vars.push_back(var.type); |
77 | Index index = func->localNames.size(); |
78 | func->localIndices[var.name] = index; |
79 | func->localNames[index] = var.name; |
80 | } |
81 | return func; |
82 | } |
83 | |
84 | static std::unique_ptr<Table> makeTable(Name name, |
85 | Type type = Type(HeapType::func, |
86 | Nullable), |
87 | Address initial = 0, |
88 | Address max = Table::kMaxSize) { |
89 | auto table = std::make_unique<Table>(); |
90 | table->name = name; |
91 | table->type = type; |
92 | table->initial = initial; |
93 | table->max = max; |
94 | return table; |
95 | } |
96 | |
97 | static std::unique_ptr<ElementSegment> |
98 | makeElementSegment(Name name, |
99 | Name table, |
100 | Expression* offset = nullptr, |
101 | Type type = Type(HeapType::func, Nullable)) { |
102 | auto seg = std::make_unique<ElementSegment>(); |
103 | seg->name = name; |
104 | seg->table = table; |
105 | seg->offset = offset; |
106 | seg->type = type; |
107 | return seg; |
108 | } |
109 | |
110 | static std::unique_ptr<Memory> makeMemory(Name name, |
111 | Address initial = 0, |
112 | Address max = Memory::kMaxSize32, |
113 | bool shared = false, |
114 | Type indexType = Type::i32) { |
115 | auto memory = std::make_unique<Memory>(); |
116 | memory->name = name; |
117 | memory->initial = initial; |
118 | memory->max = max; |
119 | memory->shared = shared; |
120 | memory->indexType = indexType; |
121 | return memory; |
122 | } |
123 | |
124 | static std::unique_ptr<DataSegment> |
125 | makeDataSegment(Name name = "" , |
126 | Name memory = "" , |
127 | bool isPassive = false, |
128 | Expression* offset = nullptr, |
129 | const char* init = "" , |
130 | Address size = 0) { |
131 | auto seg = std::make_unique<DataSegment>(); |
132 | seg->name = name; |
133 | seg->memory = memory; |
134 | seg->isPassive = isPassive; |
135 | seg->offset = offset; |
136 | seg->data.resize(size); |
137 | std::copy_n(init, size, seg->data.begin()); |
138 | return seg; |
139 | } |
140 | |
141 | static std::unique_ptr<Export> |
142 | makeExport(Name name, Name value, ExternalKind kind) { |
143 | auto export_ = std::make_unique<Export>(); |
144 | export_->name = name; |
145 | export_->value = value; |
146 | export_->kind = kind; |
147 | return export_; |
148 | } |
149 | |
150 | enum Mutability { Mutable, Immutable }; |
151 | |
152 | static std::unique_ptr<Global> |
153 | makeGlobal(Name name, Type type, Expression* init, Mutability mutable_) { |
154 | auto glob = std::make_unique<Global>(); |
155 | glob->name = name; |
156 | glob->type = type; |
157 | glob->init = init; |
158 | glob->mutable_ = mutable_ == Mutable; |
159 | return glob; |
160 | } |
161 | |
162 | static std::unique_ptr<Tag> makeTag(Name name, Signature sig) { |
163 | auto tag = std::make_unique<Tag>(); |
164 | tag->name = name; |
165 | tag->sig = sig; |
166 | return tag; |
167 | } |
168 | |
169 | // IR nodes |
170 | Nop* makeNop() { return wasm.allocator.alloc<Nop>(); } |
171 | Block* makeBlock(Expression* first = nullptr) { |
172 | auto* ret = wasm.allocator.alloc<Block>(); |
173 | if (first) { |
174 | ret->list.push_back(first); |
175 | ret->finalize(); |
176 | } |
177 | return ret; |
178 | } |
179 | Block* makeBlock(Name name, Expression* first = nullptr) { |
180 | auto* ret = makeBlock(first); |
181 | ret->name = name; |
182 | ret->finalize(); |
183 | return ret; |
184 | } |
185 | |
186 | template<typename T> |
187 | using bool_if_not_expr_t = |
188 | std::enable_if_t<std::negation_v<std::is_convertible<T, Expression*>>, |
189 | bool>; |
190 | |
191 | template<typename T, bool_if_not_expr_t<T> = true> |
192 | Block* makeBlock(const T& items) { |
193 | auto* ret = wasm.allocator.alloc<Block>(); |
194 | ret->list.set(items); |
195 | ret->finalize(); |
196 | return ret; |
197 | } |
198 | |
199 | template<typename T, bool_if_not_expr_t<T> = true> |
200 | Block* makeBlock(const T& items, Type type) { |
201 | auto* ret = wasm.allocator.alloc<Block>(); |
202 | ret->list.set(items); |
203 | ret->finalize(type); |
204 | return ret; |
205 | } |
206 | |
207 | template<typename T, bool_if_not_expr_t<T> = true> |
208 | Block* makeBlock(Name name, const T& items, Type type) { |
209 | auto* ret = wasm.allocator.alloc<Block>(); |
210 | ret->name = name; |
211 | ret->list.set(items); |
212 | ret->finalize(type); |
213 | return ret; |
214 | } |
215 | Block* makeBlock(std::initializer_list<Expression*>&& items) { |
216 | return makeBlock(items); |
217 | } |
218 | Block* makeBlock(std::initializer_list<Expression*>&& items, Type type) { |
219 | return makeBlock(items, type); |
220 | } |
221 | Block* |
222 | makeBlock(Name name, std::initializer_list<Expression*>&& items, Type type) { |
223 | return makeBlock(name, items, type); |
224 | } |
225 | |
226 | If* makeIf(Expression* condition, |
227 | Expression* ifTrue, |
228 | Expression* ifFalse = nullptr) { |
229 | auto* ret = wasm.allocator.alloc<If>(); |
230 | ret->condition = condition; |
231 | ret->ifTrue = ifTrue; |
232 | ret->ifFalse = ifFalse; |
233 | ret->finalize(); |
234 | return ret; |
235 | } |
236 | If* makeIf(Expression* condition, |
237 | Expression* ifTrue, |
238 | Expression* ifFalse, |
239 | Type type) { |
240 | auto* ret = wasm.allocator.alloc<If>(); |
241 | ret->condition = condition; |
242 | ret->ifTrue = ifTrue; |
243 | ret->ifFalse = ifFalse; |
244 | ret->finalize(type); |
245 | return ret; |
246 | } |
247 | Loop* makeLoop(Name name, Expression* body) { |
248 | auto* ret = wasm.allocator.alloc<Loop>(); |
249 | ret->name = name; |
250 | ret->body = body; |
251 | ret->finalize(); |
252 | return ret; |
253 | } |
254 | Loop* makeLoop(Name name, Expression* body, Type type) { |
255 | auto* ret = wasm.allocator.alloc<Loop>(); |
256 | ret->name = name; |
257 | ret->body = body; |
258 | ret->finalize(type); |
259 | return ret; |
260 | } |
261 | Break* makeBreak(Name name, |
262 | Expression* value = nullptr, |
263 | Expression* condition = nullptr) { |
264 | auto* ret = wasm.allocator.alloc<Break>(); |
265 | ret->name = name; |
266 | ret->value = value; |
267 | ret->condition = condition; |
268 | ret->finalize(); |
269 | return ret; |
270 | } |
271 | template<typename T> |
272 | Switch* makeSwitch(T& list, |
273 | Name default_, |
274 | Expression* condition, |
275 | Expression* value = nullptr) { |
276 | auto* ret = wasm.allocator.alloc<Switch>(); |
277 | ret->targets.set(list); |
278 | ret->default_ = default_; |
279 | ret->value = value; |
280 | ret->condition = condition; |
281 | return ret; |
282 | } |
283 | Call* makeCall(Name target, |
284 | const std::vector<Expression*>& args, |
285 | Type type, |
286 | bool isReturn = false) { |
287 | auto* call = wasm.allocator.alloc<Call>(); |
288 | // not all functions may exist yet, so type must be provided |
289 | call->type = type; |
290 | call->target = target; |
291 | call->operands.set(args); |
292 | call->isReturn = isReturn; |
293 | return call; |
294 | } |
295 | template<typename T> |
296 | Call* makeCall(Name target, const T& args, Type type, bool isReturn = false) { |
297 | auto* call = wasm.allocator.alloc<Call>(); |
298 | // not all functions may exist yet, so type must be provided |
299 | call->type = type; |
300 | call->target = target; |
301 | call->operands.set(args); |
302 | call->isReturn = isReturn; |
303 | call->finalize(); |
304 | return call; |
305 | } |
306 | template<typename T> |
307 | CallIndirect* makeCallIndirect(const Name table, |
308 | Expression* target, |
309 | const T& args, |
310 | HeapType heapType, |
311 | bool isReturn = false) { |
312 | assert(heapType.isSignature()); |
313 | auto* call = wasm.allocator.alloc<CallIndirect>(); |
314 | call->table = table; |
315 | call->heapType = heapType; |
316 | call->type = heapType.getSignature().results; |
317 | call->target = target; |
318 | call->operands.set(args); |
319 | call->isReturn = isReturn; |
320 | call->finalize(); |
321 | return call; |
322 | } |
323 | template<typename T> |
324 | CallRef* makeCallRef(Expression* target, |
325 | const T& args, |
326 | Type type, |
327 | bool isReturn = false) { |
328 | auto* call = wasm.allocator.alloc<CallRef>(); |
329 | call->type = type; |
330 | call->target = target; |
331 | call->operands.set(args); |
332 | call->isReturn = isReturn; |
333 | call->finalize(); |
334 | return call; |
335 | } |
336 | LocalGet* makeLocalGet(Index index, Type type) { |
337 | auto* ret = wasm.allocator.alloc<LocalGet>(); |
338 | ret->index = index; |
339 | ret->type = type; |
340 | return ret; |
341 | } |
342 | LocalSet* makeLocalSet(Index index, Expression* value) { |
343 | auto* ret = wasm.allocator.alloc<LocalSet>(); |
344 | ret->index = index; |
345 | ret->value = value; |
346 | ret->makeSet(); |
347 | ret->finalize(); |
348 | return ret; |
349 | } |
350 | LocalSet* makeLocalTee(Index index, Expression* value, Type type) { |
351 | auto* ret = wasm.allocator.alloc<LocalSet>(); |
352 | ret->index = index; |
353 | ret->value = value; |
354 | ret->makeTee(type); |
355 | return ret; |
356 | } |
357 | GlobalGet* makeGlobalGet(Name name, Type type) { |
358 | auto* ret = wasm.allocator.alloc<GlobalGet>(); |
359 | ret->name = name; |
360 | ret->type = type; |
361 | return ret; |
362 | } |
363 | GlobalSet* makeGlobalSet(Name name, Expression* value) { |
364 | auto* ret = wasm.allocator.alloc<GlobalSet>(); |
365 | ret->name = name; |
366 | ret->value = value; |
367 | ret->finalize(); |
368 | return ret; |
369 | } |
370 | Load* makeLoad(unsigned bytes, |
371 | bool signed_, |
372 | Address offset, |
373 | unsigned align, |
374 | Expression* ptr, |
375 | Type type, |
376 | Name memory) { |
377 | auto* ret = wasm.allocator.alloc<Load>(); |
378 | ret->isAtomic = false; |
379 | ret->bytes = bytes; |
380 | ret->signed_ = signed_; |
381 | ret->offset = offset; |
382 | ret->align = align; |
383 | ret->ptr = ptr; |
384 | ret->type = type; |
385 | ret->memory = memory; |
386 | return ret; |
387 | } |
388 | Load* makeAtomicLoad( |
389 | unsigned bytes, Address offset, Expression* ptr, Type type, Name memory) { |
390 | Load* load = makeLoad(bytes, signed_: false, offset, align: bytes, ptr, type, memory); |
391 | load->isAtomic = true; |
392 | return load; |
393 | } |
394 | AtomicWait* makeAtomicWait(Expression* ptr, |
395 | Expression* expected, |
396 | Expression* timeout, |
397 | Type expectedType, |
398 | Address offset, |
399 | Name memory) { |
400 | auto* wait = wasm.allocator.alloc<AtomicWait>(); |
401 | wait->offset = offset; |
402 | wait->ptr = ptr; |
403 | wait->expected = expected; |
404 | wait->timeout = timeout; |
405 | wait->expectedType = expectedType; |
406 | wait->finalize(); |
407 | wait->memory = memory; |
408 | return wait; |
409 | } |
410 | AtomicNotify* makeAtomicNotify(Expression* ptr, |
411 | Expression* notifyCount, |
412 | Address offset, |
413 | Name memory) { |
414 | auto* notify = wasm.allocator.alloc<AtomicNotify>(); |
415 | notify->offset = offset; |
416 | notify->ptr = ptr; |
417 | notify->notifyCount = notifyCount; |
418 | notify->finalize(); |
419 | notify->memory = memory; |
420 | return notify; |
421 | } |
422 | AtomicFence* makeAtomicFence() { return wasm.allocator.alloc<AtomicFence>(); } |
423 | Store* makeStore(unsigned bytes, |
424 | Address offset, |
425 | unsigned align, |
426 | Expression* ptr, |
427 | Expression* value, |
428 | Type type, |
429 | Name memory) { |
430 | auto* ret = wasm.allocator.alloc<Store>(); |
431 | ret->isAtomic = false; |
432 | ret->bytes = bytes; |
433 | ret->offset = offset; |
434 | ret->align = align; |
435 | ret->ptr = ptr; |
436 | ret->value = value; |
437 | ret->valueType = type; |
438 | ret->memory = memory; |
439 | ret->finalize(); |
440 | assert(ret->value->type.isConcrete() ? ret->value->type == type : true); |
441 | return ret; |
442 | } |
443 | Store* makeAtomicStore(unsigned bytes, |
444 | Address offset, |
445 | Expression* ptr, |
446 | Expression* value, |
447 | Type type, |
448 | Name memory) { |
449 | Store* store = makeStore(bytes, offset, align: bytes, ptr, value, type, memory); |
450 | store->isAtomic = true; |
451 | return store; |
452 | } |
453 | AtomicRMW* makeAtomicRMW(AtomicRMWOp op, |
454 | unsigned bytes, |
455 | Address offset, |
456 | Expression* ptr, |
457 | Expression* value, |
458 | Type type, |
459 | Name memory) { |
460 | auto* ret = wasm.allocator.alloc<AtomicRMW>(); |
461 | ret->op = op; |
462 | ret->bytes = bytes; |
463 | ret->offset = offset; |
464 | ret->ptr = ptr; |
465 | ret->value = value; |
466 | ret->type = type; |
467 | ret->finalize(); |
468 | ret->memory = memory; |
469 | return ret; |
470 | } |
471 | AtomicCmpxchg* makeAtomicCmpxchg(unsigned bytes, |
472 | Address offset, |
473 | Expression* ptr, |
474 | Expression* expected, |
475 | Expression* replacement, |
476 | Type type, |
477 | Name memory) { |
478 | auto* ret = wasm.allocator.alloc<AtomicCmpxchg>(); |
479 | ret->bytes = bytes; |
480 | ret->offset = offset; |
481 | ret->ptr = ptr; |
482 | ret->expected = expected; |
483 | ret->replacement = replacement; |
484 | ret->type = type; |
485 | ret->finalize(); |
486 | ret->memory = memory; |
487 | return ret; |
488 | } |
489 | SIMDExtract* |
490 | (SIMDExtractOp op, Expression* vec, uint8_t index) { |
491 | auto* ret = wasm.allocator.alloc<SIMDExtract>(); |
492 | ret->op = op; |
493 | ret->vec = vec; |
494 | ret->index = index; |
495 | ret->finalize(); |
496 | return ret; |
497 | } |
498 | SIMDReplace* makeSIMDReplace(SIMDReplaceOp op, |
499 | Expression* vec, |
500 | uint8_t index, |
501 | Expression* value) { |
502 | auto* ret = wasm.allocator.alloc<SIMDReplace>(); |
503 | ret->op = op; |
504 | ret->vec = vec; |
505 | ret->index = index; |
506 | ret->value = value; |
507 | ret->finalize(); |
508 | return ret; |
509 | } |
510 | SIMDShuffle* makeSIMDShuffle(Expression* left, |
511 | Expression* right, |
512 | const std::array<uint8_t, 16>& mask) { |
513 | auto* ret = wasm.allocator.alloc<SIMDShuffle>(); |
514 | ret->left = left; |
515 | ret->right = right; |
516 | ret->mask = mask; |
517 | ret->finalize(); |
518 | return ret; |
519 | } |
520 | SIMDTernary* makeSIMDTernary(SIMDTernaryOp op, |
521 | Expression* a, |
522 | Expression* b, |
523 | Expression* c) { |
524 | auto* ret = wasm.allocator.alloc<SIMDTernary>(); |
525 | ret->op = op; |
526 | ret->a = a; |
527 | ret->b = b; |
528 | ret->c = c; |
529 | ret->finalize(); |
530 | return ret; |
531 | } |
532 | SIMDShift* makeSIMDShift(SIMDShiftOp op, Expression* vec, Expression* shift) { |
533 | auto* ret = wasm.allocator.alloc<SIMDShift>(); |
534 | ret->op = op; |
535 | ret->vec = vec; |
536 | ret->shift = shift; |
537 | ret->finalize(); |
538 | return ret; |
539 | } |
540 | SIMDLoad* makeSIMDLoad(SIMDLoadOp op, |
541 | Address offset, |
542 | Address align, |
543 | Expression* ptr, |
544 | Name memory) { |
545 | auto* ret = wasm.allocator.alloc<SIMDLoad>(); |
546 | ret->op = op; |
547 | ret->offset = offset; |
548 | ret->align = align; |
549 | ret->ptr = ptr; |
550 | ret->memory = memory; |
551 | ret->finalize(); |
552 | return ret; |
553 | } |
554 | SIMDLoadStoreLane* makeSIMDLoadStoreLane(SIMDLoadStoreLaneOp op, |
555 | Address offset, |
556 | Address align, |
557 | uint8_t index, |
558 | Expression* ptr, |
559 | Expression* vec, |
560 | Name memory) { |
561 | auto* ret = wasm.allocator.alloc<SIMDLoadStoreLane>(); |
562 | ret->op = op; |
563 | ret->offset = offset; |
564 | ret->align = align; |
565 | ret->index = index; |
566 | ret->ptr = ptr; |
567 | ret->vec = vec; |
568 | ret->finalize(); |
569 | ret->memory = memory; |
570 | return ret; |
571 | } |
572 | MemoryInit* makeMemoryInit(Name segment, |
573 | Expression* dest, |
574 | Expression* offset, |
575 | Expression* size, |
576 | Name memory) { |
577 | auto* ret = wasm.allocator.alloc<MemoryInit>(); |
578 | ret->segment = segment; |
579 | ret->dest = dest; |
580 | ret->offset = offset; |
581 | ret->size = size; |
582 | ret->memory = memory; |
583 | ret->finalize(); |
584 | return ret; |
585 | } |
586 | DataDrop* makeDataDrop(Name segment) { |
587 | auto* ret = wasm.allocator.alloc<DataDrop>(); |
588 | ret->segment = segment; |
589 | ret->finalize(); |
590 | return ret; |
591 | } |
592 | MemoryCopy* makeMemoryCopy(Expression* dest, |
593 | Expression* source, |
594 | Expression* size, |
595 | Name destMemory, |
596 | Name sourceMemory) { |
597 | auto* ret = wasm.allocator.alloc<MemoryCopy>(); |
598 | ret->dest = dest; |
599 | ret->source = source; |
600 | ret->size = size; |
601 | ret->destMemory = destMemory; |
602 | ret->sourceMemory = sourceMemory; |
603 | ret->finalize(); |
604 | return ret; |
605 | } |
606 | MemoryFill* makeMemoryFill(Expression* dest, |
607 | Expression* value, |
608 | Expression* size, |
609 | Name memory) { |
610 | auto* ret = wasm.allocator.alloc<MemoryFill>(); |
611 | ret->dest = dest; |
612 | ret->value = value; |
613 | ret->size = size; |
614 | ret->memory = memory; |
615 | ret->finalize(); |
616 | return ret; |
617 | } |
618 | Const* makeConst(Literal value) { |
619 | assert(value.type.isNumber()); |
620 | auto* ret = wasm.allocator.alloc<Const>(); |
621 | ret->value = value; |
622 | ret->type = value.type; |
623 | return ret; |
624 | } |
625 | template<typename T> Const* makeConst(T x) { return makeConst(value: Literal(x)); } |
626 | Unary* makeUnary(UnaryOp op, Expression* value) { |
627 | auto* ret = wasm.allocator.alloc<Unary>(); |
628 | ret->op = op; |
629 | ret->value = value; |
630 | ret->finalize(); |
631 | return ret; |
632 | } |
633 | Const* makeConstPtr(uint64_t val, Type indexType) { |
634 | return makeConst(value: Literal::makeFromInt64(x: val, type: indexType)); |
635 | } |
636 | Binary* makeBinary(BinaryOp op, Expression* left, Expression* right) { |
637 | auto* ret = wasm.allocator.alloc<Binary>(); |
638 | ret->op = op; |
639 | ret->left = left; |
640 | ret->right = right; |
641 | ret->finalize(); |
642 | return ret; |
643 | } |
644 | Select* |
645 | makeSelect(Expression* condition, Expression* ifTrue, Expression* ifFalse) { |
646 | auto* ret = wasm.allocator.alloc<Select>(); |
647 | ret->condition = condition; |
648 | ret->ifTrue = ifTrue; |
649 | ret->ifFalse = ifFalse; |
650 | ret->finalize(); |
651 | return ret; |
652 | } |
653 | Select* makeSelect(Expression* condition, |
654 | Expression* ifTrue, |
655 | Expression* ifFalse, |
656 | Type type) { |
657 | auto* ret = wasm.allocator.alloc<Select>(); |
658 | ret->condition = condition; |
659 | ret->ifTrue = ifTrue; |
660 | ret->ifFalse = ifFalse; |
661 | ret->finalize(type); |
662 | return ret; |
663 | } |
664 | Return* makeReturn(Expression* value = nullptr) { |
665 | auto* ret = wasm.allocator.alloc<Return>(); |
666 | ret->value = value; |
667 | return ret; |
668 | } |
669 | |
670 | // Some APIs can be told if the memory is 32 or 64-bit. If they are not |
671 | // informed of that, then the memory they refer to is looked up and that |
672 | // information fetched from there. |
673 | enum MemoryInfo { Memory32, Memory64, Unspecified }; |
674 | |
675 | bool isMemory64(Name memoryName, MemoryInfo info) { |
676 | return info == MemoryInfo::Memory64 || (info == MemoryInfo::Unspecified && |
677 | wasm.getMemory(name: memoryName)->is64()); |
678 | } |
679 | |
680 | MemorySize* makeMemorySize(Name memoryName, |
681 | MemoryInfo info = MemoryInfo::Unspecified) { |
682 | auto* ret = wasm.allocator.alloc<MemorySize>(); |
683 | if (isMemory64(memoryName, info)) { |
684 | ret->make64(); |
685 | } |
686 | ret->memory = memoryName; |
687 | ret->finalize(); |
688 | return ret; |
689 | } |
690 | MemoryGrow* makeMemoryGrow(Expression* delta, |
691 | Name memoryName, |
692 | MemoryInfo info = MemoryInfo::Unspecified) { |
693 | auto* ret = wasm.allocator.alloc<MemoryGrow>(); |
694 | if (isMemory64(memoryName, info)) { |
695 | ret->make64(); |
696 | } |
697 | ret->delta = delta; |
698 | ret->memory = memoryName; |
699 | ret->finalize(); |
700 | return ret; |
701 | } |
702 | RefNull* makeRefNull(HeapType type) { |
703 | auto* ret = wasm.allocator.alloc<RefNull>(); |
704 | ret->finalize(Type(type.getBottom(), Nullable)); |
705 | return ret; |
706 | } |
707 | RefNull* makeRefNull(Type type) { |
708 | assert(type.isNullable() && type.isNull()); |
709 | auto* ret = wasm.allocator.alloc<RefNull>(); |
710 | ret->finalize(type); |
711 | return ret; |
712 | } |
713 | RefIsNull* makeRefIsNull(Expression* value) { |
714 | auto* ret = wasm.allocator.alloc<RefIsNull>(); |
715 | ret->value = value; |
716 | ret->finalize(); |
717 | return ret; |
718 | } |
719 | RefFunc* makeRefFunc(Name func, HeapType heapType) { |
720 | auto* ret = wasm.allocator.alloc<RefFunc>(); |
721 | ret->func = func; |
722 | ret->finalize(Type(heapType, NonNullable)); |
723 | return ret; |
724 | } |
725 | RefEq* makeRefEq(Expression* left, Expression* right) { |
726 | auto* ret = wasm.allocator.alloc<RefEq>(); |
727 | ret->left = left; |
728 | ret->right = right; |
729 | ret->finalize(); |
730 | return ret; |
731 | } |
732 | TableGet* makeTableGet(Name table, Expression* index, Type type) { |
733 | auto* ret = wasm.allocator.alloc<TableGet>(); |
734 | ret->table = table; |
735 | ret->index = index; |
736 | ret->type = type; |
737 | ret->finalize(); |
738 | return ret; |
739 | } |
740 | TableSet* makeTableSet(Name table, Expression* index, Expression* value) { |
741 | auto* ret = wasm.allocator.alloc<TableSet>(); |
742 | ret->table = table; |
743 | ret->index = index; |
744 | ret->value = value; |
745 | ret->finalize(); |
746 | return ret; |
747 | } |
748 | TableSize* makeTableSize(Name table) { |
749 | auto* ret = wasm.allocator.alloc<TableSize>(); |
750 | ret->table = table; |
751 | ret->finalize(); |
752 | return ret; |
753 | } |
754 | TableGrow* makeTableGrow(Name table, Expression* value, Expression* delta) { |
755 | auto* ret = wasm.allocator.alloc<TableGrow>(); |
756 | ret->table = table; |
757 | ret->value = value; |
758 | ret->delta = delta; |
759 | ret->finalize(); |
760 | return ret; |
761 | } |
762 | |
763 | private: |
764 | Try* makeTry(Name name, |
765 | Expression* body, |
766 | const std::vector<Name>& catchTags, |
767 | const std::vector<Expression*>& catchBodies, |
768 | Name delegateTarget, |
769 | Type type, |
770 | bool hasType) { // differentiate whether a type was passed in |
771 | auto* ret = wasm.allocator.alloc<Try>(); |
772 | ret->name = name; |
773 | ret->body = body; |
774 | ret->catchTags.set(catchTags); |
775 | ret->catchBodies.set(catchBodies); |
776 | if (hasType) { |
777 | ret->finalize(type); |
778 | } else { |
779 | ret->finalize(); |
780 | } |
781 | return ret; |
782 | } |
783 | |
784 | public: |
785 | Try* makeTry(Expression* body, |
786 | const std::vector<Name>& catchTags, |
787 | const std::vector<Expression*>& catchBodies) { |
788 | return makeTry( |
789 | Name(), body, catchTags, catchBodies, Name(), Type::none, false); |
790 | } |
791 | Try* makeTry(Expression* body, |
792 | const std::vector<Name>& catchTags, |
793 | const std::vector<Expression*>& catchBodies, |
794 | Type type) { |
795 | return makeTry(Name(), body, catchTags, catchBodies, Name(), type, true); |
796 | } |
797 | Try* makeTry(Name name, |
798 | Expression* body, |
799 | const std::vector<Name>& catchTags, |
800 | const std::vector<Expression*>& catchBodies) { |
801 | return makeTry( |
802 | name, body, catchTags, catchBodies, Name(), Type::none, false); |
803 | } |
804 | Try* makeTry(Name name, |
805 | Expression* body, |
806 | const std::vector<Name>& catchTags, |
807 | const std::vector<Expression*>& catchBodies, |
808 | Type type) { |
809 | return makeTry(name, body, catchTags, catchBodies, Name(), type, true); |
810 | } |
811 | Try* makeTry(Expression* body, Name delegateTarget) { |
812 | return makeTry(Name(), body, {}, {}, delegateTarget, Type::none, false); |
813 | } |
814 | Try* makeTry(Expression* body, Name delegateTarget, Type type) { |
815 | return makeTry(Name(), body, {}, {}, delegateTarget, type, true); |
816 | } |
817 | Try* makeTry(Name name, Expression* body, Name delegateTarget) { |
818 | return makeTry(name, body, {}, {}, delegateTarget, Type::none, false); |
819 | } |
820 | Try* makeTry(Name name, Expression* body, Name delegateTarget, Type type) { |
821 | return makeTry(name, body, {}, {}, delegateTarget, type, true); |
822 | } |
823 | Throw* makeThrow(Tag* tag, const std::vector<Expression*>& args) { |
824 | return makeThrow(tag->name, args); |
825 | } |
826 | Throw* makeThrow(Name tag, const std::vector<Expression*>& args) { |
827 | auto* ret = wasm.allocator.alloc<Throw>(); |
828 | ret->tag = tag; |
829 | ret->operands.set(args); |
830 | ret->finalize(); |
831 | return ret; |
832 | } |
833 | Rethrow* makeRethrow(Name target) { |
834 | auto* ret = wasm.allocator.alloc<Rethrow>(); |
835 | ret->target = target; |
836 | ret->finalize(); |
837 | return ret; |
838 | } |
839 | Unreachable* makeUnreachable() { return wasm.allocator.alloc<Unreachable>(); } |
840 | Pop* makePop(Type type) { |
841 | auto* ret = wasm.allocator.alloc<Pop>(); |
842 | ret->type = type; |
843 | ret->finalize(); |
844 | return ret; |
845 | } |
846 | template<typename ListType> TupleMake* makeTupleMake(ListType&& operands) { |
847 | auto* ret = wasm.allocator.alloc<TupleMake>(); |
848 | ret->operands.set(operands); |
849 | ret->finalize(); |
850 | return ret; |
851 | } |
852 | TupleExtract* (Expression* tuple, Index index) { |
853 | auto* ret = wasm.allocator.alloc<TupleExtract>(); |
854 | ret->tuple = tuple; |
855 | ret->index = index; |
856 | ret->finalize(); |
857 | return ret; |
858 | } |
859 | I31New* makeI31New(Expression* value) { |
860 | auto* ret = wasm.allocator.alloc<I31New>(); |
861 | ret->value = value; |
862 | ret->finalize(); |
863 | return ret; |
864 | } |
865 | I31Get* makeI31Get(Expression* i31, bool signed_) { |
866 | auto* ret = wasm.allocator.alloc<I31Get>(); |
867 | ret->i31 = i31; |
868 | ret->signed_ = signed_; |
869 | ret->finalize(); |
870 | return ret; |
871 | } |
872 | RefTest* makeRefTest(Expression* ref, Type castType) { |
873 | auto* ret = wasm.allocator.alloc<RefTest>(); |
874 | ret->ref = ref; |
875 | ret->castType = castType; |
876 | ret->finalize(); |
877 | return ret; |
878 | } |
879 | RefCast* makeRefCast(Expression* ref, Type type, RefCast::Safety safety) { |
880 | auto* ret = wasm.allocator.alloc<RefCast>(); |
881 | ret->ref = ref; |
882 | ret->type = type; |
883 | ret->safety = safety; |
884 | ret->finalize(); |
885 | return ret; |
886 | } |
887 | BrOn* |
888 | makeBrOn(BrOnOp op, Name name, Expression* ref, Type castType = Type::none) { |
889 | auto* ret = wasm.allocator.alloc<BrOn>(); |
890 | ret->op = op; |
891 | ret->name = name; |
892 | ret->ref = ref; |
893 | ret->castType = castType; |
894 | ret->finalize(); |
895 | return ret; |
896 | } |
897 | template<typename T> StructNew* makeStructNew(HeapType type, const T& args) { |
898 | auto* ret = wasm.allocator.alloc<StructNew>(); |
899 | ret->operands.set(args); |
900 | ret->type = Type(type, NonNullable); |
901 | ret->finalize(); |
902 | return ret; |
903 | } |
904 | StructGet* |
905 | makeStructGet(Index index, Expression* ref, Type type, bool signed_ = false) { |
906 | auto* ret = wasm.allocator.alloc<StructGet>(); |
907 | ret->index = index; |
908 | ret->ref = ref; |
909 | ret->type = type; |
910 | ret->signed_ = signed_; |
911 | ret->finalize(); |
912 | return ret; |
913 | } |
914 | StructSet* makeStructSet(Index index, Expression* ref, Expression* value) { |
915 | auto* ret = wasm.allocator.alloc<StructSet>(); |
916 | ret->index = index; |
917 | ret->ref = ref; |
918 | ret->value = value; |
919 | ret->finalize(); |
920 | return ret; |
921 | } |
922 | ArrayNew* |
923 | makeArrayNew(HeapType type, Expression* size, Expression* init = nullptr) { |
924 | auto* ret = wasm.allocator.alloc<ArrayNew>(); |
925 | ret->size = size; |
926 | ret->init = init; |
927 | ret->type = Type(type, NonNullable); |
928 | ret->finalize(); |
929 | return ret; |
930 | } |
931 | ArrayNewData* makeArrayNewData(HeapType type, |
932 | Name seg, |
933 | Expression* offset, |
934 | Expression* size) { |
935 | auto* ret = wasm.allocator.alloc<ArrayNewData>(); |
936 | ret->segment = seg; |
937 | ret->offset = offset; |
938 | ret->size = size; |
939 | ret->type = Type(type, NonNullable); |
940 | ret->finalize(); |
941 | return ret; |
942 | } |
943 | ArrayNewElem* makeArrayNewElem(HeapType type, |
944 | Name seg, |
945 | Expression* offset, |
946 | Expression* size) { |
947 | auto* ret = wasm.allocator.alloc<ArrayNewElem>(); |
948 | ret->segment = seg; |
949 | ret->offset = offset; |
950 | ret->size = size; |
951 | ret->type = Type(type, NonNullable); |
952 | ret->finalize(); |
953 | return ret; |
954 | } |
955 | ArrayNewFixed* makeArrayNewFixed(HeapType type, |
956 | const std::vector<Expression*>& values) { |
957 | auto* ret = wasm.allocator.alloc<ArrayNewFixed>(); |
958 | ret->values.set(values); |
959 | ret->type = Type(type, NonNullable); |
960 | ret->finalize(); |
961 | return ret; |
962 | } |
963 | ArrayGet* makeArrayGet(Expression* ref, |
964 | Expression* index, |
965 | Type type, |
966 | bool signed_ = false) { |
967 | auto* ret = wasm.allocator.alloc<ArrayGet>(); |
968 | ret->ref = ref; |
969 | ret->index = index; |
970 | ret->type = type; |
971 | ret->signed_ = signed_; |
972 | ret->finalize(); |
973 | return ret; |
974 | } |
975 | ArraySet* |
976 | makeArraySet(Expression* ref, Expression* index, Expression* value) { |
977 | auto* ret = wasm.allocator.alloc<ArraySet>(); |
978 | ret->ref = ref; |
979 | ret->index = index; |
980 | ret->value = value; |
981 | ret->finalize(); |
982 | return ret; |
983 | } |
984 | ArrayLen* makeArrayLen(Expression* ref) { |
985 | auto* ret = wasm.allocator.alloc<ArrayLen>(); |
986 | ret->ref = ref; |
987 | ret->finalize(); |
988 | return ret; |
989 | } |
990 | ArrayCopy* makeArrayCopy(Expression* destRef, |
991 | Expression* destIndex, |
992 | Expression* srcRef, |
993 | Expression* srcIndex, |
994 | Expression* length) { |
995 | auto* ret = wasm.allocator.alloc<ArrayCopy>(); |
996 | ret->destRef = destRef; |
997 | ret->destIndex = destIndex; |
998 | ret->srcRef = srcRef; |
999 | ret->srcIndex = srcIndex; |
1000 | ret->length = length; |
1001 | ret->finalize(); |
1002 | return ret; |
1003 | } |
1004 | ArrayFill* makeArrayFill(Expression* ref, |
1005 | Expression* index, |
1006 | Expression* value, |
1007 | Expression* size) { |
1008 | auto* ret = wasm.allocator.alloc<ArrayFill>(); |
1009 | ret->ref = ref; |
1010 | ret->index = index; |
1011 | ret->value = value; |
1012 | ret->size = size; |
1013 | ret->finalize(); |
1014 | return ret; |
1015 | } |
1016 | ArrayInitData* makeArrayInitData(Name seg, |
1017 | Expression* ref, |
1018 | Expression* index, |
1019 | Expression* offset, |
1020 | Expression* size) { |
1021 | auto* ret = wasm.allocator.alloc<ArrayInitData>(); |
1022 | ret->segment = seg; |
1023 | ret->ref = ref; |
1024 | ret->index = index; |
1025 | ret->offset = offset; |
1026 | ret->size = size; |
1027 | ret->finalize(); |
1028 | return ret; |
1029 | } |
1030 | ArrayInitElem* makeArrayInitElem(Name seg, |
1031 | Expression* ref, |
1032 | Expression* index, |
1033 | Expression* offset, |
1034 | Expression* size) { |
1035 | auto* ret = wasm.allocator.alloc<ArrayInitElem>(); |
1036 | ret->segment = seg; |
1037 | ret->ref = ref; |
1038 | ret->index = index; |
1039 | ret->offset = offset; |
1040 | ret->size = size; |
1041 | ret->finalize(); |
1042 | return ret; |
1043 | } |
1044 | RefAs* makeRefAs(RefAsOp op, Expression* value) { |
1045 | auto* ret = wasm.allocator.alloc<RefAs>(); |
1046 | ret->op = op; |
1047 | ret->value = value; |
1048 | ret->finalize(); |
1049 | return ret; |
1050 | } |
1051 | StringNew* makeStringNew(StringNewOp op, |
1052 | Expression* ptr, |
1053 | Expression* length, |
1054 | bool try_) { |
1055 | auto* ret = wasm.allocator.alloc<StringNew>(); |
1056 | ret->op = op; |
1057 | ret->ptr = ptr; |
1058 | ret->length = length; |
1059 | ret->try_ = try_; |
1060 | ret->finalize(); |
1061 | return ret; |
1062 | } |
1063 | StringNew* makeStringNew(StringNewOp op, |
1064 | Expression* ptr, |
1065 | Expression* start, |
1066 | Expression* end, |
1067 | bool try_) { |
1068 | auto* ret = wasm.allocator.alloc<StringNew>(); |
1069 | ret->op = op; |
1070 | ret->ptr = ptr; |
1071 | ret->start = start; |
1072 | ret->end = end; |
1073 | ret->try_ = try_; |
1074 | ret->finalize(); |
1075 | return ret; |
1076 | } |
1077 | StringConst* makeStringConst(Name string) { |
1078 | auto* ret = wasm.allocator.alloc<StringConst>(); |
1079 | ret->string = string; |
1080 | ret->finalize(); |
1081 | return ret; |
1082 | } |
1083 | StringMeasure* makeStringMeasure(StringMeasureOp op, Expression* ref) { |
1084 | auto* ret = wasm.allocator.alloc<StringMeasure>(); |
1085 | ret->op = op; |
1086 | ret->ref = ref; |
1087 | ret->finalize(); |
1088 | return ret; |
1089 | } |
1090 | StringEncode* makeStringEncode(StringEncodeOp op, |
1091 | Expression* ref, |
1092 | Expression* ptr, |
1093 | Expression* start = nullptr) { |
1094 | auto* ret = wasm.allocator.alloc<StringEncode>(); |
1095 | ret->op = op; |
1096 | ret->ref = ref; |
1097 | ret->ptr = ptr; |
1098 | ret->start = start; |
1099 | ret->finalize(); |
1100 | return ret; |
1101 | } |
1102 | StringConcat* makeStringConcat(Expression* left, Expression* right) { |
1103 | auto* ret = wasm.allocator.alloc<StringConcat>(); |
1104 | ret->left = left; |
1105 | ret->right = right; |
1106 | ret->finalize(); |
1107 | return ret; |
1108 | } |
1109 | StringEq* makeStringEq(StringEqOp op, Expression* left, Expression* right) { |
1110 | auto* ret = wasm.allocator.alloc<StringEq>(); |
1111 | ret->op = op; |
1112 | ret->left = left; |
1113 | ret->right = right; |
1114 | ret->finalize(); |
1115 | return ret; |
1116 | } |
1117 | StringAs* makeStringAs(StringAsOp op, Expression* ref) { |
1118 | auto* ret = wasm.allocator.alloc<StringAs>(); |
1119 | ret->op = op; |
1120 | ret->ref = ref; |
1121 | ret->finalize(); |
1122 | return ret; |
1123 | } |
1124 | StringWTF8Advance* |
1125 | makeStringWTF8Advance(Expression* ref, Expression* pos, Expression* bytes) { |
1126 | auto* ret = wasm.allocator.alloc<StringWTF8Advance>(); |
1127 | ret->ref = ref; |
1128 | ret->pos = pos; |
1129 | ret->bytes = bytes; |
1130 | ret->finalize(); |
1131 | return ret; |
1132 | } |
1133 | StringWTF16Get* makeStringWTF16Get(Expression* ref, Expression* pos) { |
1134 | auto* ret = wasm.allocator.alloc<StringWTF16Get>(); |
1135 | ret->ref = ref; |
1136 | ret->pos = pos; |
1137 | ret->finalize(); |
1138 | return ret; |
1139 | } |
1140 | StringIterNext* makeStringIterNext(Expression* ref) { |
1141 | auto* ret = wasm.allocator.alloc<StringIterNext>(); |
1142 | ret->ref = ref; |
1143 | ret->finalize(); |
1144 | return ret; |
1145 | } |
1146 | StringIterMove* |
1147 | makeStringIterMove(StringIterMoveOp op, Expression* ref, Expression* num) { |
1148 | auto* ret = wasm.allocator.alloc<StringIterMove>(); |
1149 | ret->op = op; |
1150 | ret->ref = ref; |
1151 | ret->num = num; |
1152 | ret->finalize(); |
1153 | return ret; |
1154 | } |
1155 | StringSliceWTF* makeStringSliceWTF(StringSliceWTFOp op, |
1156 | Expression* ref, |
1157 | Expression* start, |
1158 | Expression* end) { |
1159 | auto* ret = wasm.allocator.alloc<StringSliceWTF>(); |
1160 | ret->op = op; |
1161 | ret->ref = ref; |
1162 | ret->start = start; |
1163 | ret->end = end; |
1164 | ret->finalize(); |
1165 | return ret; |
1166 | } |
1167 | StringSliceIter* makeStringSliceIter(Expression* ref, Expression* num) { |
1168 | auto* ret = wasm.allocator.alloc<StringSliceIter>(); |
1169 | ret->ref = ref; |
1170 | ret->num = num; |
1171 | ret->finalize(); |
1172 | return ret; |
1173 | } |
1174 | |
1175 | // Additional helpers |
1176 | |
1177 | Drop* makeDrop(Expression* value) { |
1178 | auto* ret = wasm.allocator.alloc<Drop>(); |
1179 | ret->value = value; |
1180 | ret->finalize(); |
1181 | return ret; |
1182 | } |
1183 | |
1184 | // Make a constant expression. This might be a wasm Const, or something |
1185 | // else of constant value like ref.null. |
1186 | Expression* makeConstantExpression(Literal value) { |
1187 | auto type = value.type; |
1188 | if (type.isNumber()) { |
1189 | return makeConst(value); |
1190 | } |
1191 | if (value.isNull()) { |
1192 | return makeRefNull(type); |
1193 | } |
1194 | if (type.isFunction()) { |
1195 | return makeRefFunc(func: value.getFunc(), heapType: type.getHeapType()); |
1196 | } |
1197 | if (type.isRef() && type.getHeapType() == HeapType::i31) { |
1198 | return makeI31New(value: makeConst(x: value.geti31())); |
1199 | } |
1200 | if (type.isString()) { |
1201 | // TODO: more than ascii support |
1202 | std::string string; |
1203 | for (auto c : value.getGCData()->values) { |
1204 | string.push_back(c.getInteger()); |
1205 | } |
1206 | return makeStringConst(string: string); |
1207 | } |
1208 | if (type.isRef() && type.getHeapType() == HeapType::ext) { |
1209 | return makeRefAs(op: ExternExternalize, |
1210 | value: makeConstantExpression(value: value.internalize())); |
1211 | } |
1212 | TODO_SINGLE_COMPOUND(type); |
1213 | WASM_UNREACHABLE("unsupported constant expression" ); |
1214 | } |
1215 | |
1216 | Expression* makeConstantExpression(Literals values) { |
1217 | assert(values.size() > 0); |
1218 | if (values.size() == 1) { |
1219 | return makeConstantExpression(values[0]); |
1220 | } else { |
1221 | std::vector<Expression*> consts; |
1222 | for (auto value : values) { |
1223 | consts.push_back(makeConstantExpression(value)); |
1224 | } |
1225 | return makeTupleMake(consts); |
1226 | } |
1227 | } |
1228 | |
1229 | // Additional utility functions for building on top of nodes |
1230 | // Convenient to have these on Builder, as it has allocation built in |
1231 | |
1232 | static Index addParam(Function* func, Name name, Type type) { |
1233 | // only ok to add a param if no vars, otherwise indices are invalidated |
1234 | assert(func->localIndices.size() == func->getParams().size()); |
1235 | assert(name.is()); |
1236 | Signature sig = func->getSig(); |
1237 | std::vector<Type> params(sig.params.begin(), sig.params.end()); |
1238 | params.push_back(type); |
1239 | func->type = Signature(Type(params), sig.results); |
1240 | Index index = func->localNames.size(); |
1241 | func->localIndices[name] = index; |
1242 | func->localNames[index] = name; |
1243 | return index; |
1244 | } |
1245 | |
1246 | static Index addVar(Function* func, Name name, Type type) { |
1247 | // always ok to add a var, it does not affect other indices |
1248 | assert(type.isConcrete()); |
1249 | Index index = func->getNumLocals(); |
1250 | if (name.is()) { |
1251 | func->localIndices[name] = index; |
1252 | func->localNames[index] = name; |
1253 | } |
1254 | func->vars.emplace_back(type); |
1255 | return index; |
1256 | } |
1257 | |
1258 | static Index addVar(Function* func, Type type) { |
1259 | return addVar(func, name: Name(), type); |
1260 | } |
1261 | |
1262 | static void clearLocalNames(Function* func) { |
1263 | func->localNames.clear(); |
1264 | func->localIndices.clear(); |
1265 | } |
1266 | |
1267 | // ensure a node is a block, if it isn't already, and optionally append to the |
1268 | // block |
1269 | Block* blockify(Expression* any, Expression* append = nullptr) { |
1270 | Block* block = nullptr; |
1271 | if (any) { |
1272 | block = any->dynCast<Block>(); |
1273 | } |
1274 | if (!block) { |
1275 | block = makeBlock(first: any); |
1276 | } |
1277 | if (append) { |
1278 | block->list.push_back(append); |
1279 | block->finalize(); |
1280 | } |
1281 | return block; |
1282 | } |
1283 | |
1284 | template<typename... Ts> |
1285 | Block* blockify(Expression* any, Expression* append, Ts... args) { |
1286 | return blockify(blockify(any, append), args...); |
1287 | } |
1288 | |
1289 | // ensure a node is a block, if it isn't already, and optionally append to the |
1290 | // block this variant sets a name for the block, so it will not reuse a block |
1291 | // already named |
1292 | Block* |
1293 | blockifyWithName(Expression* any, Name name, Expression* append = nullptr) { |
1294 | Block* block = nullptr; |
1295 | if (any) { |
1296 | block = any->dynCast<Block>(); |
1297 | } |
1298 | if (!block || block->name.is()) { |
1299 | block = makeBlock(first: any); |
1300 | } |
1301 | block->name = name; |
1302 | if (append) { |
1303 | block->list.push_back(append); |
1304 | block->finalize(); |
1305 | } |
1306 | return block; |
1307 | } |
1308 | |
1309 | // a helper for the common pattern of a sequence of two expressions. Similar |
1310 | // to blockify, but does *not* reuse a block if the first is one. |
1311 | Block* makeSequence(Expression* left, Expression* right) { |
1312 | auto* block = makeBlock(first: left); |
1313 | block->list.push_back(right); |
1314 | block->finalize(); |
1315 | return block; |
1316 | } |
1317 | |
1318 | Block* makeSequence(Expression* left, Expression* right, Type type) { |
1319 | auto* block = makeBlock(first: left); |
1320 | block->list.push_back(right); |
1321 | block->finalize(type_: type); |
1322 | return block; |
1323 | } |
1324 | |
1325 | // Drop an expression if it has a concrete type |
1326 | Expression* dropIfConcretelyTyped(Expression* curr) { |
1327 | if (!curr->type.isConcrete()) { |
1328 | return curr; |
1329 | } |
1330 | return makeDrop(value: curr); |
1331 | } |
1332 | |
1333 | void flip(If* iff) { |
1334 | std::swap(iff->ifTrue, iff->ifFalse); |
1335 | iff->condition = makeUnary(op: EqZInt32, value: iff->condition); |
1336 | } |
1337 | |
1338 | // Returns a replacement with the precise same type, and with minimal contents |
1339 | // as best we can. As a replacement, this may reuse the input node. |
1340 | template<typename T> Expression* replaceWithIdenticalType(T* curr) { |
1341 | if (curr->type.isTuple() && curr->type.isDefaultable()) { |
1342 | return makeConstantExpression(Literal::makeZeros(type: curr->type)); |
1343 | } |
1344 | if (curr->type.isNullable() && curr->type.isNull()) { |
1345 | return ExpressionManipulator::refNull(curr, curr->type); |
1346 | } |
1347 | if (curr->type.isRef() && curr->type.getHeapType() == HeapType::i31) { |
1348 | Expression* ret = makeI31New(value: makeConst(x: 0)); |
1349 | if (curr->type.isNullable()) { |
1350 | // To keep the type identical, wrap it in a block that adds nullability. |
1351 | ret = makeBlock({ret}, curr->type); |
1352 | } |
1353 | return ret; |
1354 | } |
1355 | if (!curr->type.isBasic()) { |
1356 | // We can't do any better, keep the original. |
1357 | return curr; |
1358 | } |
1359 | Literal value; |
1360 | // TODO: reuse node conditionally when possible for literals |
1361 | switch (curr->type.getBasic()) { |
1362 | case Type::i32: |
1363 | value = Literal(int32_t(0)); |
1364 | break; |
1365 | case Type::i64: |
1366 | value = Literal(int64_t(0)); |
1367 | break; |
1368 | case Type::f32: |
1369 | value = Literal(float(0)); |
1370 | break; |
1371 | case Type::f64: |
1372 | value = Literal(double(0)); |
1373 | break; |
1374 | case Type::v128: { |
1375 | std::array<uint8_t, 16> bytes; |
1376 | bytes.fill(0); |
1377 | value = Literal(bytes.data()); |
1378 | break; |
1379 | } |
1380 | case Type::none: |
1381 | return ExpressionManipulator::nop(curr); |
1382 | case Type::unreachable: |
1383 | return ExpressionManipulator::unreachable(curr); |
1384 | } |
1385 | return makeConst(value); |
1386 | } |
1387 | }; |
1388 | |
1389 | } // namespace wasm |
1390 | |
1391 | #endif // wasm_wasm_builder_h |
1392 | |