Skip to content

Commit 5fd0930

Browse files
committed
Implement table.fill
This instruction was standardized as part of the bulk memory proposal, but we never implemented it until now. Leave similar instructions like table.copy as future work. Fixes #5939.
1 parent 6dde22c commit 5fd0930

24 files changed

+411
-0
lines changed

scripts/gen-s-parser.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,7 @@
547547
("table.set", "makeTableSet(s)"),
548548
("table.size", "makeTableSize(s)"),
549549
("table.grow", "makeTableGrow(s)"),
550+
("table.fill", "makeTableFill(s)"),
550551
# TODO:
551552
# table.init
552553
# table.fill

src/gen-s-parser.inc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3357,6 +3357,9 @@ switch (buf[0]) {
33573357
switch (buf[1]) {
33583358
case 'a': {
33593359
switch (buf[6]) {
3360+
case 'f':
3361+
if (op == "table.fill"sv) { return makeTableFill(s); }
3362+
goto parse_error;
33603363
case 'g': {
33613364
switch (buf[7]) {
33623365
case 'e':
@@ -9190,6 +9193,13 @@ switch (buf[0]) {
91909193
switch (buf[1]) {
91919194
case 'a': {
91929195
switch (buf[6]) {
9196+
case 'f':
9197+
if (op == "table.fill"sv) {
9198+
auto ret = makeTableFill(ctx, pos);
9199+
CHECK_ERR(ret);
9200+
return *ret;
9201+
}
9202+
goto parse_error;
91939203
case 'g': {
91949204
switch (buf[7]) {
91959205
case 'e':

src/ir/ReFinalize.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ void ReFinalize::visitTableGet(TableGet* curr) { curr->finalize(); }
124124
void ReFinalize::visitTableSet(TableSet* curr) { curr->finalize(); }
125125
void ReFinalize::visitTableSize(TableSize* curr) { curr->finalize(); }
126126
void ReFinalize::visitTableGrow(TableGrow* curr) { curr->finalize(); }
127+
void ReFinalize::visitTableFill(TableFill* curr) { curr->finalize(); }
127128
void ReFinalize::visitTry(Try* curr) { curr->finalize(); }
128129
void ReFinalize::visitThrow(Throw* curr) { curr->finalize(); }
129130
void ReFinalize::visitRethrow(Rethrow* curr) { curr->finalize(); }

src/ir/cost.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,9 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
574574
CostType visitTableGrow(TableGrow* curr) {
575575
return Unacceptable + visit(curr->value) + visit(curr->delta);
576576
}
577+
CostType visitTableFill(TableFill* curr) {
578+
return 6 + visit(curr->dest) + visit(curr->value) + visit(curr->size);
579+
}
577580
CostType visitTry(Try* curr) {
578581
// We assume no exception will be thrown in most cases
579582
return visit(curr->body);

src/ir/effects.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,10 @@ class EffectAnalyzer {
692692
parent.readsTable = true;
693693
parent.writesTable = true;
694694
}
695+
void visitTableFill(TableFill* curr) {
696+
parent.writesTable = true;
697+
parent.implicitTrap = true;
698+
}
695699
void visitTry(Try* curr) {
696700
if (curr->delegateTarget.is()) {
697701
parent.delegateTargets.insert(curr->delegateTarget);

src/ir/possible-contents.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,7 @@ struct InfoCollector
661661
void visitTableSet(TableSet* curr) {}
662662
void visitTableSize(TableSize* curr) { addRoot(curr); }
663663
void visitTableGrow(TableGrow* curr) { addRoot(curr); }
664+
void visitTableFill(TableFill* curr) { addRoot(curr); }
664665

665666
void visitNop(Nop* curr) {}
666667
void visitUnreachable(Unreachable* curr) {}

src/passes/Print.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1925,6 +1925,10 @@ struct PrintExpressionContents
19251925
printMedium(o, "table.grow ");
19261926
printName(curr->table, o);
19271927
}
1928+
void visitTableFill(TableFill* curr) {
1929+
printMedium(o, "table.fill ");
1930+
printName(curr->table, o);
1931+
}
19281932
void visitTry(Try* curr) {
19291933
printMedium(o, "try");
19301934
if (curr->name.is()) {

src/wasm-binary.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,6 +1129,7 @@ enum ASTNodes {
11291129

11301130
TableGrow = 0x0f,
11311131
TableSize = 0x10,
1132+
TableFill = 0x11,
11321133
RefNull = 0xd0,
11331134
RefIsNull = 0xd1,
11341135
RefFunc = 0xd2,
@@ -1838,6 +1839,7 @@ class WasmBinaryReader {
18381839
bool maybeVisitMemoryFill(Expression*& out, uint32_t code);
18391840
bool maybeVisitTableSize(Expression*& out, uint32_t code);
18401841
bool maybeVisitTableGrow(Expression*& out, uint32_t code);
1842+
bool maybeVisitTableFill(Expression*& out, uint32_t code);
18411843
bool maybeVisitRefI31(Expression*& out, uint32_t code);
18421844
bool maybeVisitI31Get(Expression*& out, uint32_t code);
18431845
bool maybeVisitRefTest(Expression*& out, uint32_t code);

src/wasm-builder.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,18 @@ class Builder {
759759
ret->finalize();
760760
return ret;
761761
}
762+
TableFill* makeTableFill(Name table,
763+
Expression* dest,
764+
Expression* value,
765+
Expression* size) {
766+
auto* ret = wasm.allocator.alloc<TableFill>();
767+
ret->table = table;
768+
ret->dest = dest;
769+
ret->value = value;
770+
ret->size = size;
771+
ret->finalize();
772+
return ret;
773+
}
762774

763775
private:
764776
Try* makeTry(Name name,

src/wasm-delegations-fields.def

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,15 @@ switch (DELEGATE_ID) {
567567
DELEGATE_END(TableGrow);
568568
break;
569569
}
570+
case Expression::Id::TableFillId: {
571+
DELEGATE_START(TableFill);
572+
DELEGATE_FIELD_CHILD(TableFill, size);
573+
DELEGATE_FIELD_CHILD(TableFill, value);
574+
DELEGATE_FIELD_CHILD(TableFill, dest);
575+
DELEGATE_FIELD_NAME_KIND(TableFill, table, ModuleItemKind::Table);
576+
DELEGATE_END(TableFill);
577+
break;
578+
}
570579
case Expression::Id::TryId: {
571580
DELEGATE_START(Try);
572581
DELEGATE_FIELD_SCOPE_NAME_USE(Try, delegateTarget);

src/wasm-delegations.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ DELEGATE(TableGet);
6262
DELEGATE(TableSet);
6363
DELEGATE(TableSize);
6464
DELEGATE(TableGrow);
65+
DELEGATE(TableFill);
6566
DELEGATE(Try);
6667
DELEGATE(Throw);
6768
DELEGATE(Rethrow);

src/wasm-interpreter.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1391,6 +1391,7 @@ class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
13911391
Flow visitTableSet(TableSet* curr) { WASM_UNREACHABLE("unimp"); }
13921392
Flow visitTableSize(TableSize* curr) { WASM_UNREACHABLE("unimp"); }
13931393
Flow visitTableGrow(TableGrow* curr) { WASM_UNREACHABLE("unimp"); }
1394+
Flow visitTableFill(TableFill* curr) { WASM_UNREACHABLE("unimp"); }
13941395
Flow visitTry(Try* curr) { WASM_UNREACHABLE("unimp"); }
13951396
Flow visitThrow(Throw* curr) {
13961397
NOTE_ENTER("Throw");
@@ -2205,6 +2206,10 @@ class ConstantExpressionRunner : public ExpressionRunner<SubType> {
22052206
NOTE_ENTER("TableGrow");
22062207
return Flow(NONCONSTANT_FLOW);
22072208
}
2209+
Flow visitTableFill(TableFill* curr) {
2210+
NOTE_ENTER("TableFill");
2211+
return Flow(NONCONSTANT_FLOW);
2212+
}
22082213
Flow visitLoad(Load* curr) {
22092214
NOTE_ENTER("Load");
22102215
return Flow(NONCONSTANT_FLOW);
@@ -2989,6 +2994,38 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
29892994
return ret;
29902995
}
29912996

2997+
Flow visitTableFill(TableFill* curr) {
2998+
NOTE_ENTER("TableFill");
2999+
Flow destFlow = self()->visit(curr->dest);
3000+
if (destFlow.breaking()) {
3001+
return destFlow;
3002+
}
3003+
Flow valueFlow = self()->visit(curr->value);
3004+
if (valueFlow.breaking()) {
3005+
return valueFlow;
3006+
}
3007+
Flow sizeFlow = self()->visit(curr->size);
3008+
if (sizeFlow.breaking()) {
3009+
return sizeFlow;
3010+
}
3011+
Name tableName = curr->table;
3012+
auto info = getTableInterfaceInfo(tableName);
3013+
3014+
Index dest = destFlow.getSingleValue().geti32();
3015+
Literal value = valueFlow.getSingleValue();
3016+
Index size = sizeFlow.getSingleValue().geti32();
3017+
3018+
Index tableSize = info.interface->tableSize(tableName);
3019+
if (dest + size > tableSize) {
3020+
trap("out of bounds table access");
3021+
}
3022+
3023+
for (Index i = 0; i < size; ++i) {
3024+
info.interface->tableStore(info.name, dest + i, value);
3025+
}
3026+
return Flow();
3027+
}
3028+
29923029
Flow visitLocalGet(LocalGet* curr) {
29933030
NOTE_ENTER("LocalGet");
29943031
auto index = curr->index;

src/wasm-ir-builder.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
125125
// [[nodiscard]] Result<> makeTableSet();
126126
// [[nodiscard]] Result<> makeTableSize();
127127
// [[nodiscard]] Result<> makeTableGrow();
128+
// [[nodiscard]] Result<> makeTableFill();
128129
// [[nodiscard]] Result<> makeTry();
129130
// [[nodiscard]] Result<> makeThrow();
130131
// [[nodiscard]] Result<> makeRethrow();

src/wasm-s-parser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ class SExpressionWasmBuilder {
278278
Expression* makeTableSet(Element& s);
279279
Expression* makeTableSize(Element& s);
280280
Expression* makeTableGrow(Element& s);
281+
Expression* makeTableFill(Element& s);
281282
Expression* makeTry(Element& s);
282283
Expression* makeTryOrCatchBody(Element& s, Type type, bool isTry);
283284
Expression* makeThrow(Element& s);

src/wasm.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,7 @@ class Expression {
698698
TableSetId,
699699
TableSizeId,
700700
TableGrowId,
701+
TableFillId,
701702
TryId,
702703
ThrowId,
703704
RethrowId,
@@ -1421,6 +1422,19 @@ class TableGrow : public SpecificExpression<Expression::TableGrowId> {
14211422
void finalize();
14221423
};
14231424

1425+
class TableFill : public SpecificExpression<Expression::TableFillId> {
1426+
public:
1427+
TableFill() = default;
1428+
TableFill(MixedArena& allocator) : TableFill() {}
1429+
1430+
Name table;
1431+
Expression* dest;
1432+
Expression* value;
1433+
Expression* size;
1434+
1435+
void finalize();
1436+
};
1437+
14241438
class Try : public SpecificExpression<Expression::TryId> {
14251439
public:
14261440
Try(MixedArena& allocator) : catchTags(allocator), catchBodies(allocator) {}

src/wasm/wasm-binary.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4012,6 +4012,9 @@ BinaryConsts::ASTNodes WasmBinaryReader::readExpression(Expression*& curr) {
40124012
if (maybeVisitTableGrow(curr, opcode)) {
40134013
break;
40144014
}
4015+
if (maybeVisitTableFill(curr, opcode)) {
4016+
break;
4017+
}
40154018
throwError("invalid code after misc prefix: " + std::to_string(opcode));
40164019
break;
40174020
}
@@ -5369,6 +5372,23 @@ bool WasmBinaryReader::maybeVisitTableGrow(Expression*& out, uint32_t code) {
53695372
return true;
53705373
}
53715374

5375+
bool WasmBinaryReader::maybeVisitTableFill(Expression*& out, uint32_t code) {
5376+
if (code != BinaryConsts::TableFill) {
5377+
return false;
5378+
}
5379+
Index tableIdx = getU32LEB();
5380+
if (tableIdx >= wasm.tables.size()) {
5381+
throwError("bad table index");
5382+
}
5383+
auto* size = popNonVoidExpression();
5384+
auto* value = popNonVoidExpression();
5385+
auto* dest = popNonVoidExpression();
5386+
auto* ret = Builder(wasm).makeTableFill(Name(), dest, value, size);
5387+
tableRefs[tableIdx].push_back(&ret->table);
5388+
out = ret;
5389+
return true;
5390+
}
5391+
53725392
bool WasmBinaryReader::maybeVisitBinary(Expression*& out, uint8_t code) {
53735393
Binary* curr;
53745394
#define INT_TYPED_CODE(code) \

src/wasm/wasm-s-parser.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2677,6 +2677,18 @@ Expression* SExpressionWasmBuilder::makeTableGrow(Element& s) {
26772677
return Builder(wasm).makeTableGrow(tableName, value, delta);
26782678
}
26792679

2680+
Expression* SExpressionWasmBuilder::makeTableFill(Element& s) {
2681+
auto tableName = s[1]->str();
2682+
auto* table = wasm.getTableOrNull(tableName);
2683+
if (!table) {
2684+
throw ParseException("invalid table name in table.fill", s.line, s.col);
2685+
}
2686+
auto* dest = parseExpression(s[2]);
2687+
auto* value = parseExpression(s[3]);
2688+
auto* size = parseExpression(s[4]);
2689+
return Builder(wasm).makeTableFill(tableName, dest, value, size);
2690+
}
2691+
26802692
// try can be either in the form of try-catch or try-delegate.
26812693
// try-catch is written in the folded wast format as
26822694
// (try

src/wasm/wasm-stack.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1909,6 +1909,11 @@ void BinaryInstWriter::visitTableGrow(TableGrow* curr) {
19091909
o << U32LEB(parent.getTableIndex(curr->table));
19101910
}
19111911

1912+
void BinaryInstWriter::visitTableFill(TableFill* curr) {
1913+
o << int8_t(BinaryConsts::MiscPrefix) << U32LEB(BinaryConsts::TableFill);
1914+
o << U32LEB(parent.getTableIndex(curr->table));
1915+
}
1916+
19121917
void BinaryInstWriter::visitTry(Try* curr) {
19131918
breakStack.push_back(curr->name);
19141919
o << int8_t(BinaryConsts::Try);

src/wasm/wasm-validator.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,7 @@ struct FunctionValidator : public WalkerPass<PostWalker<FunctionValidator>> {
440440
void visitTableSet(TableSet* curr);
441441
void visitTableSize(TableSize* curr);
442442
void visitTableGrow(TableGrow* curr);
443+
void visitTableFill(TableFill* curr);
443444
void noteDelegate(Name name, Expression* curr);
444445
void noteRethrow(Name name, Expression* curr);
445446
void visitTry(Try* curr);
@@ -2294,6 +2295,24 @@ void FunctionValidator::visitTableGrow(TableGrow* curr) {
22942295
}
22952296
}
22962297

2298+
void FunctionValidator::visitTableFill(TableFill* curr) {
2299+
shouldBeTrue(
2300+
getModule()->features.hasReferenceTypes(),
2301+
curr,
2302+
"table.fill requires reference types [--enable-reference-types]");
2303+
auto* table = getModule()->getTableOrNull(curr->table);
2304+
if (shouldBeTrue(!!table, curr, "table.fill table must exist")) {
2305+
shouldBeSubType(curr->value->type,
2306+
table->type,
2307+
curr,
2308+
"table.fill value must have right type");
2309+
}
2310+
shouldBeEqualOrFirstIsUnreachable(
2311+
curr->dest->type, Type(Type::i32), curr, "table.fill dest must be i32");
2312+
shouldBeEqualOrFirstIsUnreachable(
2313+
curr->size->type, Type(Type::i32), curr, "table.fill size must be i32");
2314+
}
2315+
22972316
void FunctionValidator::noteDelegate(Name name, Expression* curr) {
22982317
if (name != DELEGATE_CALLER_TARGET) {
22992318
shouldBeTrue(delegateTargetNames.count(name) != 0,

src/wasm/wasm.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,15 @@ void TableGrow::finalize() {
855855
}
856856
}
857857

858+
void TableFill::finalize() {
859+
if (dest->type == Type::unreachable || value->type == Type::unreachable ||
860+
size->type == Type::unreachable) {
861+
type = Type::unreachable;
862+
} else {
863+
type = Type::none;
864+
}
865+
}
866+
858867
void Try::finalize() {
859868
// If none of the component bodies' type is a supertype of the others, assume
860869
// the current type is already correct. TODO: Calculate a proper LUB.

src/wasm/wat-parser.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1936,6 +1936,7 @@ template<typename Ctx> Result<typename Ctx::InstrT> makeTableGet(Ctx&, Index);
19361936
template<typename Ctx> Result<typename Ctx::InstrT> makeTableSet(Ctx&, Index);
19371937
template<typename Ctx> Result<typename Ctx::InstrT> makeTableSize(Ctx&, Index);
19381938
template<typename Ctx> Result<typename Ctx::InstrT> makeTableGrow(Ctx&, Index);
1939+
template<typename Ctx> Result<typename Ctx::InstrT> makeTableFill(Ctx&, Index);
19391940
template<typename Ctx> Result<typename Ctx::InstrT> makeTry(Ctx&, Index);
19401941
template<typename Ctx>
19411942
Result<typename Ctx::InstrT>
@@ -3005,6 +3006,11 @@ Result<typename Ctx::InstrT> makeTableGrow(Ctx& ctx, Index pos) {
30053006
return ctx.in.err("unimplemented instruction");
30063007
}
30073008

3009+
template<typename Ctx>
3010+
Result<typename Ctx::InstrT> makeTableFill(Ctx& ctx, Index pos) {
3011+
return ctx.in.err("unimplemented instruction");
3012+
}
3013+
30083014
template<typename Ctx>
30093015
Result<typename Ctx::InstrT> makeTry(Ctx& ctx, Index pos) {
30103016
return ctx.in.err("unimplemented instruction");

src/wasm2js.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2251,6 +2251,10 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m,
22512251
unimplemented(curr);
22522252
WASM_UNREACHABLE("unimp");
22532253
}
2254+
Ref visitTableFill(TableFill* curr) {
2255+
unimplemented(curr);
2256+
WASM_UNREACHABLE("unimp");
2257+
}
22542258
Ref visitTry(Try* curr) {
22552259
unimplemented(curr);
22562260
WASM_UNREACHABLE("unimp");

0 commit comments

Comments
 (0)