Skip to content

Commit 0532093

Browse files
authored
Expressions should not appear twice in the ast (#1191)
1 parent 8f2f6a1 commit 0532093

6 files changed

Lines changed: 307 additions & 249 deletions

File tree

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,14 @@ There are a few differences between Binaryen IR and the WebAssembly language:
4646
* Blocks without names may not be branch targets.
4747
* Names are required to be unique. (Reading wast files with duplicate names is supported; the names are modified when the IR is constructed).
4848
* As an optimization, a block that is the child of a loop (or if arm, or function toplevel) and which has no branches targeting it will not be emitted when generating wasm. Instead its list of operands will be directly used in the containing node. Such a block is sometimes called an "implicit block".
49-
50-
51-
5249

5350
As a result, you might notice that round-trip conversions (wasm => Binaryen IR => wasm) change code a little in some corner cases.
5451

52+
Notes when working with Binaryen IR:
53+
54+
* As mentioned above, Binaryen IR has a tree structure. As a result, each expression should have exactly one parent - you should not "reuse" a node by having it appear more than once in the tree. The motivation for this limitation is that when we optimize we modify nodes, so if they appear more than once in the tree, a change in one place can appear in another incorrectly.
55+
* For similar reasons, nodes should not appear in more than one functions.
56+
5557
## Tools
5658

5759
This repository contains code that builds the following tools in `bin/`:

src/wasm-emscripten.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,8 @@ void generateStackAllocFunction(LinkerObject& linker) {
129129
Block* block = builder.makeBlock();
130130
block->list.push_back(setStackLocal);
131131
block->list.push_back(storeStack);
132-
block->list.push_back(getStackLocal);
132+
GetLocal* getStackLocal2 = builder.makeGetLocal(1, i32);
133+
block->list.push_back(getStackLocal2);
133134
block->type = i32;
134135
function->body = block;
135136

src/wasm-validator.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939

4040
#include <set>
4141
#include <sstream>
42+
#include <unordered_set>
4243

4344
#include "wasm.h"
4445
#include "wasm-printing.h"
@@ -83,6 +84,8 @@ struct WasmValidator : public PostWalker<WasmValidator> {
8384

8485
std::set<Name> labelNames; // Binaryen IR requires that label names must be unique - IR generators must ensure that
8586

87+
std::unordered_set<Expression*> seenExpressions; // expressions must not appear twice
88+
8689
void noteLabelName(Name name);
8790

8891
public:

src/wasm/wasm-validator.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,24 @@ void WasmValidator::visitFunction(Function *curr) {
559559
shouldBeTrue(breakTargets.empty(), curr->body, "all named break targets must exist");
560560
returnType = unreachable;
561561
labelNames.clear();
562+
// expressions must not be seen more than once
563+
struct Walker : public PostWalker<Walker, UnifiedExpressionVisitor<Walker>> {
564+
std::unordered_set<Expression*>& seen;
565+
std::vector<Expression*> dupes;
566+
567+
Walker(std::unordered_set<Expression*>& seen) : seen(seen) {}
568+
569+
void visitExpression(Expression* curr) {
570+
bool inserted;
571+
std::tie(std::ignore, inserted) = seen.insert(curr);
572+
if (!inserted) dupes.push_back(curr);
573+
}
574+
};
575+
Walker walker(seenExpressions);
576+
walker.walk(curr->body);
577+
for (auto* bad : walker.dupes) {
578+
fail("expression seen more than once in the tree", bad);
579+
}
562580
}
563581

564582
static bool checkOffset(Expression* curr, Address add, Address max) {

test/example/c-api-kitchen-sink.c

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ void test_core() {
9898

9999
BinaryenExpressionRef callOperands2[] = { makeInt32(module, 13), makeFloat64(module, 3.7) };
100100
BinaryenExpressionRef callOperands4[] = { makeInt32(module, 13), makeInt64(module, 37), makeFloat32(module, 1.3f), makeFloat64(module, 3.7) };
101+
BinaryenExpressionRef callOperands4b[] = { makeInt32(module, 13), makeInt64(module, 37), makeFloat32(module, 1.3f), makeFloat64(module, 3.7) };
101102

102103
BinaryenType params[4] = { BinaryenInt32(), BinaryenInt64(), BinaryenFloat32(), BinaryenFloat64() };
103104
BinaryenFunctionTypeRef iiIfF = BinaryenAddFunctionType(module, "iiIfF", BinaryenInt32(), params, 4);
@@ -203,7 +204,7 @@ void test_core() {
203204
)
204205
),
205206
BinaryenUnary(module, BinaryenEqZInt32(), // check the output type of the call node
206-
BinaryenCallIndirect(module, makeInt32(module, 2449), callOperands4, 4, "iiIfF")
207+
BinaryenCallIndirect(module, makeInt32(module, 2449), callOperands4b, 4, "iiIfF")
207208
),
208209
BinaryenDrop(module, BinaryenGetLocal(module, 0, BinaryenInt32())),
209210
BinaryenSetLocal(module, 0, makeInt32(module, 101)),
@@ -545,18 +546,36 @@ void test_interpret() {
545546

546547
void test_nonvalid() {
547548
// create a module that fails to validate
548-
BinaryenModuleRef module = BinaryenModuleCreate();
549+
{
550+
BinaryenModuleRef module = BinaryenModuleCreate();
549551

550-
BinaryenFunctionTypeRef v = BinaryenAddFunctionType(module, "v", BinaryenNone(), NULL, 0);
551-
BinaryenType localTypes[] = { BinaryenInt32() };
552-
BinaryenFunctionRef func = BinaryenAddFunction(module, "func", v, localTypes, 1,
553-
BinaryenSetLocal(module, 0, makeInt64(module, 1234)) // wrong type!
554-
);
552+
BinaryenFunctionTypeRef v = BinaryenAddFunctionType(module, "v", BinaryenNone(), NULL, 0);
553+
BinaryenType localTypes[] = { BinaryenInt32() };
554+
BinaryenFunctionRef func = BinaryenAddFunction(module, "func", v, localTypes, 1,
555+
BinaryenSetLocal(module, 0, makeInt64(module, 1234)) // wrong type!
556+
);
555557

556-
BinaryenModulePrint(module);
557-
printf("validation: %d\n", BinaryenModuleValidate(module));
558+
BinaryenModulePrint(module);
559+
printf("validation: %d\n", BinaryenModuleValidate(module));
558560

559-
BinaryenModuleDispose(module);
561+
BinaryenModuleDispose(module);
562+
}
563+
// validation failure due to duplicate nodes
564+
{
565+
BinaryenModuleRef module = BinaryenModuleCreate();
566+
567+
BinaryenFunctionTypeRef v = BinaryenAddFunctionType(module, "i", BinaryenInt32(), NULL, 0);
568+
BinaryenType localTypes[] = { };
569+
BinaryenExpressionRef num = makeInt32(module, 1234);
570+
BinaryenFunctionRef func = BinaryenAddFunction(module, "func", v, NULL, 0,
571+
BinaryenBinary(module, BinaryenInt32(), num, num) // incorrectly use num twice
572+
);
573+
574+
BinaryenModulePrint(module);
575+
printf("validation: %d\n", BinaryenModuleValidate(module));
576+
577+
BinaryenModuleDispose(module);
578+
}
560579
}
561580

562581
void test_tracing() {

0 commit comments

Comments
 (0)