Skip to content

Commit dad5c60

Browse files
committed
Make local.tee's type its local's type
According to the current spec, `local.tee`'s return type should be the same as its local's type. (Discussions on whether we should change this rule is going on in WebAssembly/reference-types#55, but here I will assume this spec does not change. If this changes, we should change many parts of Binaryen transformation anyway...) But currently in Binaryen `local.tee`'s type is computed from its value's type. This didn't make any difference in the MVP, but after we have subtype relationship in WebAssembly#2451, this can become a problem. For example: ``` (func $test (result funcref) (local $0 anyref) (local.tee $0 (ref.func $test) ) ) ``` This shouldn't validate in the spec, but this will pass Binaryen validation with the current `local.tee` implementation. This makes `local.tee`'s type computed from the local's type, and makes `LocalSet::makeTee` get a type parameter, to which we should pass the its corresponding local's type. We don't embed the local type in the class `LocalSet` because it may increase memory size. This also fixes the type of `local.get` to be the local type where `local.get` and `local.set` pair is created from `local.tee`.
1 parent 65c334d commit dad5c60

26 files changed

+80
-57
lines changed

src/asm2wasm.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1907,7 +1907,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
19071907
auto ret = allocator.alloc<LocalSet>();
19081908
ret->index = function->getLocalIndex(assign->target());
19091909
ret->value = process(assign->value());
1910-
ret->setTee(false);
1910+
ret->makeSet();
19111911
ret->finalize();
19121912
return ret;
19131913
}
@@ -2160,7 +2160,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
21602160
auto set = allocator.alloc<LocalSet>();
21612161
set->index = function->getLocalIndex(I32_TEMP);
21622162
set->value = value;
2163-
set->setTee(false);
2163+
set->makeSet();
21642164
set->finalize();
21652165
auto get = [&]() {
21662166
auto ret = allocator.alloc<LocalGet>();
@@ -2266,7 +2266,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
22662266
view.bytes,
22672267
0,
22682268
processUnshifted(ast[2][1], view.bytes),
2269-
builder.makeLocalTee(temp, process(ast[2][2])),
2269+
builder.makeLocalTee(temp, process(ast[2][2]), type),
22702270
type),
22712271
builder.makeLocalGet(temp, type));
22722272
} else if (name == Atomics_exchange) {

src/binaryen-c.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1261,22 +1261,23 @@ BinaryenExpressionRef BinaryenLocalSet(BinaryenModuleRef module,
12611261

12621262
ret->index = index;
12631263
ret->value = (Expression*)value;
1264-
ret->setTee(false);
1264+
ret->makeSet();
12651265
ret->finalize();
12661266
return static_cast<Expression*>(ret);
12671267
}
12681268
BinaryenExpressionRef BinaryenLocalTee(BinaryenModuleRef module,
12691269
BinaryenIndex index,
1270-
BinaryenExpressionRef value) {
1270+
BinaryenExpressionRef value,
1271+
BinaryenType type) {
12711272
auto* ret = ((Module*)module)->allocator.alloc<LocalSet>();
12721273

12731274
if (tracing) {
1274-
traceExpression(ret, "BinaryenLocalTee", index, value);
1275+
traceExpression(ret, "BinaryenLocalTee", index, value, type);
12751276
}
12761277

12771278
ret->index = index;
12781279
ret->value = (Expression*)value;
1279-
ret->setTee(true);
1280+
ret->makeTee(Type(type));
12801281
ret->finalize();
12811282
return static_cast<Expression*>(ret);
12821283
}

src/binaryen-c.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -665,8 +665,10 @@ BINARYEN_API BinaryenExpressionRef BinaryenLocalGet(BinaryenModuleRef module,
665665
BinaryenType type);
666666
BINARYEN_API BinaryenExpressionRef BinaryenLocalSet(
667667
BinaryenModuleRef module, BinaryenIndex index, BinaryenExpressionRef value);
668-
BINARYEN_API BinaryenExpressionRef BinaryenLocalTee(
669-
BinaryenModuleRef module, BinaryenIndex index, BinaryenExpressionRef value);
668+
BINARYEN_API BinaryenExpressionRef BinaryenLocalTee(BinaryenModuleRef module,
669+
BinaryenIndex index,
670+
BinaryenExpressionRef value,
671+
BinaryenType type);
670672
BINARYEN_API BinaryenExpressionRef BinaryenGlobalGet(BinaryenModuleRef module,
671673
const char* name,
672674
BinaryenType type);

src/ir/ExpressionManipulator.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ flexibleCopy(Expression* original, Module& wasm, CustomCopier custom) {
9191
}
9292
Expression* visitLocalSet(LocalSet* curr) {
9393
if (curr->isTee()) {
94-
return builder.makeLocalTee(curr->index, copy(curr->value));
94+
return builder.makeLocalTee(curr->index, copy(curr->value), curr->type);
9595
} else {
9696
return builder.makeLocalSet(curr->index, copy(curr->value));
9797
}

src/ir/localize.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ struct Localizer {
3636
index = set->index;
3737
} else {
3838
index = Builder::addVar(func, expr->type);
39-
expr = Builder(*wasm).makeLocalTee(index, expr);
39+
expr = Builder(*wasm).makeLocalTee(index, expr, expr->type);
4040
}
4141
}
4242
};

src/js/binaryen.js-post.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -541,8 +541,8 @@ function wrapModule(module, self) {
541541
'set': function(index, value) {
542542
return Module['_BinaryenLocalSet'](module, index, value);
543543
},
544-
'tee': function(index, value) {
545-
return Module['_BinaryenLocalTee'](module, index, value);
544+
'tee': function(index, value, type) {
545+
return Module['_BinaryenLocalTee'](module, index, value, type);
546546
}
547547
}
548548

src/passes/Flatten.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,10 @@ struct Flatten
172172
replaceCurrent(set->value); // trivial, no set happens
173173
} else {
174174
// use a set in a prelude + a get
175-
set->setTee(false);
175+
set->makeSet();
176176
ourPreludes.push_back(set);
177-
replaceCurrent(builder.makeLocalGet(set->index, set->value->type));
177+
replaceCurrent(builder.makeLocalGet(
178+
set->index, getFunction()->getLocalType(set->index)));
178179
}
179180
}
180181
} else if (auto* br = curr->dynCast<Break>()) {

src/passes/LocalCSE.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,9 @@ struct LocalCSE : public WalkerPass<LinearExecutionWalker<LocalCSE>> {
184184
if (iter != usables.end()) {
185185
// already exists in the table, this is good to reuse
186186
auto& info = iter->second;
187+
Type localType = getFunction()->getLocalType(info.index);
187188
set->value =
188-
Builder(*getModule()).makeLocalGet(info.index, value->type);
189+
Builder(*getModule()).makeLocalGet(info.index, localType);
189190
anotherPass = true;
190191
} else {
191192
// not in table, add this, maybe we can help others later

src/passes/MergeLocals.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ struct MergeLocals
8888
if (auto* get = curr->value->dynCast<LocalGet>()) {
8989
if (get->index != curr->index) {
9090
Builder builder(*getModule());
91-
auto* trivial = builder.makeLocalTee(get->index, get);
91+
auto* trivial = builder.makeLocalTee(get->index, get, get->type);
9292
curr->value = trivial;
9393
copies.push_back(curr);
9494
}

src/passes/RemoveUnusedBrs.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
287287
Expression* z;
288288
replaceCurrent(
289289
z = builder.makeIf(
290-
builder.makeLocalTee(temp, curr->condition),
290+
builder.makeLocalTee(temp, curr->condition, i32),
291291
builder.makeIf(builder.makeBinary(EqInt32,
292292
builder.makeLocalGet(temp, i32),
293293
builder.makeConst(Literal(int32_t(
@@ -1074,7 +1074,7 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
10741074
iff->finalize();
10751075
Expression* replacement = iff;
10761076
if (tee) {
1077-
set->setTee(false);
1077+
set->makeSet();
10781078
// We need a block too.
10791079
replacement = builder.makeSequence(iff,
10801080
get // reuse the get

src/passes/SSAify.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ struct SSAify : public Pass {
154154
if (set) {
155155
// a set exists, just add a tee of its value
156156
auto* value = set->value;
157-
auto* tee = builder.makeLocalTee(new_, value);
157+
auto* tee = builder.makeLocalTee(new_, value, get->type);
158158
set->value = tee;
159159
// the value may have been something we tracked the location
160160
// of. if so, update that, since we moved it into the tee

src/passes/SimplifyLocals.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ struct SimplifyLocals
256256
} else {
257257
this->replaceCurrent(set);
258258
assert(!set->isTee());
259-
set->setTee(true);
259+
set->makeTee(this->getFunction()->getLocalType(set->index));
260260
}
261261
// reuse the local.get that is dying
262262
*found->second.item = curr;
@@ -271,7 +271,7 @@ struct SimplifyLocals
271271
auto* set = curr->value->dynCast<LocalSet>();
272272
if (set) {
273273
assert(set->isTee());
274-
set->setTee(false);
274+
set->makeSet();
275275
this->replaceCurrent(set);
276276
}
277277
}
@@ -559,7 +559,7 @@ struct SimplifyLocals
559559
auto* set = (*breakLocalSetPointer)->template cast<LocalSet>();
560560
if (br->condition) {
561561
br->value = set;
562-
set->setTee(true);
562+
set->makeTee(this->getFunction()->getLocalType(set->index));
563563
*breakLocalSetPointer =
564564
this->getModule()->allocator.template alloc<Nop>();
565565
// in addition, as this is a conditional br that now has a value, it now
@@ -728,7 +728,8 @@ struct SimplifyLocals
728728
ifTrueBlock->finalize();
729729
assert(ifTrueBlock->type != none);
730730
// Update the ifFalse side.
731-
iff->ifFalse = builder.makeLocalGet(set->index, set->value->type);
731+
iff->ifFalse = builder.makeLocalGet(
732+
set->index, this->getFunction()->getLocalType(set->index));
732733
iff->finalize(); // update type
733734
// Update the get count.
734735
getCounter.num[set->index]++;

src/passes/Untee.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,10 @@ struct Untee : public WalkerPass<PostWalker<Untee>> {
4141
} else {
4242
// a normal tee. replace with set and get
4343
Builder builder(*getModule());
44-
replaceCurrent(builder.makeSequence(
45-
curr, builder.makeLocalGet(curr->index, curr->value->type)));
46-
curr->setTee(false);
44+
LocalGet* get = builder.makeLocalGet(
45+
curr->index, getFunction()->getLocalType(curr->index));
46+
replaceCurrent(builder.makeSequence(curr, get));
47+
curr->makeSet();
4748
}
4849
}
4950
}

src/passes/Vacuum.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> {
346346
// a drop of a tee is a set
347347
if (auto* set = curr->value->dynCast<LocalSet>()) {
348348
assert(set->isTee());
349-
set->setTee(false);
349+
set->makeSet();
350350
replaceCurrent(set);
351351
return;
352352
}

src/tools/fuzzing.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1282,7 +1282,7 @@ class TranslateToFuzzReader {
12821282
}
12831283
auto* value = make(valueType);
12841284
if (tee) {
1285-
return builder.makeLocalTee(pick(locals), value);
1285+
return builder.makeLocalTee(pick(locals), value, valueType);
12861286
} else {
12871287
return builder.makeLocalSet(pick(locals), value);
12881288
}

src/wasm-builder.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,14 +241,16 @@ class Builder {
241241
auto* ret = allocator.alloc<LocalSet>();
242242
ret->index = index;
243243
ret->value = value;
244+
ret->makeSet();
244245
ret->finalize();
245246
return ret;
246247
}
247-
LocalSet* makeLocalTee(Index index, Expression* value) {
248+
LocalSet* makeLocalTee(Index index, Expression* value, Type type) {
248249
auto* ret = allocator.alloc<LocalSet>();
249250
ret->index = index;
250251
ret->value = value;
251-
ret->setTee(true);
252+
ret->makeTee(type);
253+
ret->finalize();
252254
return ret;
253255
}
254256
GlobalGet* makeGlobalGet(Name name, Type type) {

src/wasm.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -725,8 +725,9 @@ class LocalSet : public SpecificExpression<Expression::LocalSetId> {
725725
Index index;
726726
Expression* value;
727727

728-
bool isTee();
729-
void setTee(bool is);
728+
bool isTee() const;
729+
void makeTee(Type type);
730+
void makeSet();
730731
};
731732

732733
class GlobalGet : public SpecificExpression<Expression::GlobalGetId> {

src/wasm/wasm-binary.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2527,8 +2527,11 @@ void WasmBinaryBuilder::visitLocalSet(LocalSet* curr, uint8_t code) {
25272527
throwError("bad local.set index");
25282528
}
25292529
curr->value = popNonVoidExpression();
2530-
curr->type = curr->value->type;
2531-
curr->setTee(code == BinaryConsts::LocalTee);
2530+
if (code == BinaryConsts::LocalTee) {
2531+
curr->makeTee(currFunction->getLocalType(curr->index));
2532+
} else {
2533+
curr->makeSet();
2534+
}
25322535
curr->finalize();
25332536
}
25342537

src/wasm/wasm-emscripten.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ inline Expression* stackBoundsCheck(Builder& builder,
113113
auto check =
114114
builder.makeIf(builder.makeBinary(
115115
BinaryOp::LtUInt32,
116-
builder.makeLocalTee(newSP, value),
116+
builder.makeLocalTee(newSP, value, stackPointer->type),
117117
builder.makeGlobalGet(stackLimit->name, stackLimit->type)),
118118
builder.makeCall(handler, {}, none));
119119
// (global.set $__stack_pointer (local.get $newSP))
@@ -173,7 +173,7 @@ void EmscriptenGlueGenerator::generateStackAllocFunction() {
173173
const static uint32_t bitMask = bitAlignment - 1;
174174
Const* subConst = builder.makeConst(Literal(~bitMask));
175175
Binary* maskedSub = builder.makeBinary(AndInt32, sub, subConst);
176-
LocalSet* teeStackLocal = builder.makeLocalTee(1, maskedSub);
176+
LocalSet* teeStackLocal = builder.makeLocalTee(1, maskedSub, i32);
177177
Expression* storeStack = generateStoreStackPointer(function, teeStackLocal);
178178

179179
Block* block = builder.makeBlock();

src/wasm/wasm-s-parser.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,7 +1040,7 @@ Expression* SExpressionWasmBuilder::makeLocalTee(Element& s) {
10401040
auto ret = allocator.alloc<LocalSet>();
10411041
ret->index = getLocalIndex(*s[1]);
10421042
ret->value = parseExpression(s[2]);
1043-
ret->setTee(true);
1043+
ret->makeTee(currFunction->getLocalType(ret->index));
10441044
ret->finalize();
10451045
return ret;
10461046
}
@@ -1049,7 +1049,7 @@ Expression* SExpressionWasmBuilder::makeLocalSet(Element& s) {
10491049
auto ret = allocator.alloc<LocalSet>();
10501050
ret->index = getLocalIndex(*s[1]);
10511051
ret->value = parseExpression(s[2]);
1052-
ret->setTee(false);
1052+
ret->makeSet();
10531053
ret->finalize();
10541054
return ret;
10551055
}

src/wasm/wasm-validator.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -720,7 +720,13 @@ void FunctionValidator::visitLocalSet(LocalSet* curr) {
720720
if (curr->value->type != unreachable) {
721721
if (curr->type != none) { // tee is ok anyhow
722722
shouldBeEqualOrFirstIsUnreachable(
723-
curr->value->type, curr->type, curr, "local.set type must be correct");
723+
curr->value->type,
724+
curr->type,
725+
curr,
726+
"local.set's value type must be correct");
727+
shouldBeTrue(curr->type == getFunction()->getLocalType(curr->index),
728+
curr,
729+
"local.set's tpye must be correct");
724730
}
725731
shouldBeEqual(getFunction()->getLocalType(curr->index),
726732
curr->value->type,

src/wasm/wasm.cpp

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -469,24 +469,23 @@ bool FunctionType::operator==(FunctionType& b) {
469469
}
470470
bool FunctionType::operator!=(FunctionType& b) { return !(*this == b); }
471471

472-
bool LocalSet::isTee() { return type != none; }
472+
bool LocalSet::isTee() const { return type != none; }
473473

474-
void LocalSet::setTee(bool is) {
475-
if (is) {
476-
type = value->type;
477-
} else {
478-
type = none;
479-
}
474+
// Changes to local.tee. The type of the local should be given.
475+
void LocalSet::makeTee(Type type_) {
476+
type = type_;
477+
finalize(); // type may need to be unreachable
478+
}
479+
480+
// Changes to local.set.
481+
void LocalSet::makeSet() {
482+
type = none;
480483
finalize(); // type may need to be unreachable
481484
}
482485

483486
void LocalSet::finalize() {
484487
if (value->type == unreachable) {
485488
type = unreachable;
486-
} else if (isTee()) {
487-
type = value->type;
488-
} else {
489-
type = none;
490489
}
491490
}
492491

test/binaryen.js/kitchen-sink.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ function test_core() {
469469
),
470470
module.drop(module.local.get(0, Binaryen.i32)),
471471
module.local.set(0, makeInt32(101)),
472-
module.drop(module.local.tee(0, makeInt32(102))),
472+
module.drop(module.local.tee(0, makeInt32(102), Binaryen.i32)),
473473
module.i32.load(0, 0, makeInt32(1)),
474474
module.i64.load16_s(2, 1, makeInt32(8)),
475475
module.f32.load(0, 0, makeInt32(2)),

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -642,7 +642,9 @@ void test_core() {
642642
module, makeInt32(module, 2449), callOperands4b, 4, "iiIfF")),
643643
BinaryenDrop(module, BinaryenLocalGet(module, 0, BinaryenTypeInt32())),
644644
BinaryenLocalSet(module, 0, makeInt32(module, 101)),
645-
BinaryenDrop(module, BinaryenLocalTee(module, 0, makeInt32(module, 102))),
645+
BinaryenDrop(
646+
module,
647+
BinaryenLocalTee(module, 0, makeInt32(module, 102), BinaryenTypeInt32())),
646648
BinaryenLoad(module, 4, 0, 0, 0, BinaryenTypeInt32(), makeInt32(module, 1)),
647649
BinaryenLoad(module, 2, 1, 2, 1, BinaryenTypeInt64(), makeInt32(module, 8)),
648650
BinaryenLoad(

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3835,7 +3835,7 @@ int main() {
38353835
expressions[747] = BinaryenConst(the_module, BinaryenLiteralInt32(101));
38363836
expressions[748] = BinaryenLocalSet(the_module, 0, expressions[747]);
38373837
expressions[749] = BinaryenConst(the_module, BinaryenLiteralInt32(102));
3838-
expressions[750] = BinaryenLocalTee(the_module, 0, expressions[749]);
3838+
expressions[750] = BinaryenLocalTee(the_module, 0, expressions[749], 2);
38393839
expressions[751] = BinaryenDrop(the_module, expressions[750]);
38403840
expressions[752] = BinaryenConst(the_module, BinaryenLiteralInt32(1));
38413841
expressions[753] = BinaryenLoad(the_module, 4, 0, 0, 0, 2, expressions[752]);

0 commit comments

Comments
 (0)