Skip to content

Commit c246150

Browse files
kripkenradekdoulik
authored andcommitted
Implement table.copy (WebAssembly#6078)
Helps WebAssembly#5951
1 parent 9794963 commit c246150

29 files changed

+2545
-42
lines changed

scripts/gen-s-parser.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -548,10 +548,9 @@
548548
("table.size", "makeTableSize(s)"),
549549
("table.grow", "makeTableGrow(s)"),
550550
("table.fill", "makeTableFill(s)"),
551+
("table.copy", "makeTableCopy(s)"),
551552
# TODO:
552553
# table.init
553-
# table.fill
554-
# table.copy
555554
#
556555
# exception handling instructions
557556
("try", "makeTry(s)"),

src/gen-s-parser.inc

Lines changed: 9 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 'c':
3361+
if (op == "table.copy"sv) { return makeTableCopy(s); }
3362+
goto parse_error;
33603363
case 'f':
33613364
if (op == "table.fill"sv) { return makeTableFill(s); }
33623365
goto parse_error;
@@ -8619,6 +8622,12 @@ switch (buf[0]) {
86198622
switch (buf[1]) {
86208623
case 'a': {
86218624
switch (buf[6]) {
8625+
case 'c':
8626+
if (op == "table.copy"sv) {
8627+
CHECK_ERR(makeTableCopy(ctx, pos));
8628+
return Ok{};
8629+
}
8630+
goto parse_error;
86228631
case 'f':
86238632
if (op == "table.fill"sv) {
86248633
CHECK_ERR(makeTableFill(ctx, pos));

src/ir/ReFinalize.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ void ReFinalize::visitTableSet(TableSet* curr) { curr->finalize(); }
125125
void ReFinalize::visitTableSize(TableSize* curr) { curr->finalize(); }
126126
void ReFinalize::visitTableGrow(TableGrow* curr) { curr->finalize(); }
127127
void ReFinalize::visitTableFill(TableFill* curr) { curr->finalize(); }
128+
void ReFinalize::visitTableCopy(TableCopy* curr) { curr->finalize(); }
128129
void ReFinalize::visitTry(Try* curr) { curr->finalize(); }
129130
void ReFinalize::visitThrow(Throw* curr) { curr->finalize(); }
130131
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
@@ -577,6 +577,9 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
577577
CostType visitTableFill(TableFill* curr) {
578578
return 6 + visit(curr->dest) + visit(curr->value) + visit(curr->size);
579579
}
580+
CostType visitTableCopy(TableCopy* curr) {
581+
return 6 + visit(curr->dest) + visit(curr->source) + visit(curr->size);
582+
}
580583
CostType visitTry(Try* curr) {
581584
// We assume no exception will be thrown in most cases
582585
return visit(curr->body);

src/ir/effects.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,11 @@ class EffectAnalyzer {
696696
parent.writesTable = true;
697697
parent.implicitTrap = true;
698698
}
699+
void visitTableCopy(TableCopy* curr) {
700+
parent.readsTable = true;
701+
parent.writesTable = true;
702+
parent.implicitTrap = true;
703+
}
699704
void visitTry(Try* curr) {
700705
if (curr->delegateTarget.is()) {
701706
parent.delegateTargets.insert(curr->delegateTarget);

src/ir/possible-contents.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,7 @@ struct InfoCollector
662662
void visitTableSize(TableSize* curr) { addRoot(curr); }
663663
void visitTableGrow(TableGrow* curr) { addRoot(curr); }
664664
void visitTableFill(TableFill* curr) { addRoot(curr); }
665+
void visitTableCopy(TableCopy* curr) { addRoot(curr); }
665666

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

src/parser/parsers.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ template<typename Ctx> Result<> makeTableSet(Ctx&, Index);
118118
template<typename Ctx> Result<> makeTableSize(Ctx&, Index);
119119
template<typename Ctx> Result<> makeTableGrow(Ctx&, Index);
120120
template<typename Ctx> Result<> makeTableFill(Ctx&, Index);
121+
template<typename Ctx> Result<> makeTableCopy(Ctx&, Index);
121122
template<typename Ctx> Result<> makeTry(Ctx&, Index);
122123
template<typename Ctx>
123124
Result<> makeTryOrCatchBody(Ctx&, Index, Type type, bool isTry);
@@ -1264,6 +1265,10 @@ template<typename Ctx> Result<> makeTableFill(Ctx& ctx, Index pos) {
12641265
return ctx.in.err("unimplemented instruction");
12651266
}
12661267

1268+
template<typename Ctx> Result<> makeTableCopy(Ctx& ctx, Index pos) {
1269+
return ctx.in.err("unimplemented instruction");
1270+
}
1271+
12671272
template<typename Ctx> Result<> makeTry(Ctx& ctx, Index pos) {
12681273
return ctx.in.err("unimplemented instruction");
12691274
}

src/passes/Directize.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,9 @@ struct Directize : public Pass {
263263
void visitTableFill(TableFill* curr) {
264264
tablesWithSet.insert(curr->table);
265265
}
266+
void visitTableCopy(TableCopy* curr) {
267+
tablesWithSet.insert(curr->destTable);
268+
}
266269
};
267270

268271
Finder(tablesWithSet).walkFunction(func);

src/passes/Print.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1950,6 +1950,12 @@ struct PrintExpressionContents
19501950
printMedium(o, "table.fill ");
19511951
printName(curr->table, o);
19521952
}
1953+
void visitTableCopy(TableCopy* curr) {
1954+
printMedium(o, "table.copy ");
1955+
printName(curr->destTable, o);
1956+
o << ' ';
1957+
printName(curr->sourceTable, o);
1958+
}
19531959
void visitTry(Try* curr) {
19541960
printMedium(o, "try");
19551961
if (curr->name.is()) {

src/passes/Unsubtyping.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,10 @@ struct Unsubtyping
446446
void visitTableFill(TableFill* curr) {
447447
noteSubtype(curr->value->type, getModule()->getTable(curr->table)->type);
448448
}
449+
void visitTableCopy(TableCopy* curr) {
450+
noteSubtype(getModule()->getTable(curr->sourceTable)->type,
451+
getModule()->getTable(curr->destTable)->type);
452+
}
449453
void visitTry(Try* curr) {
450454
noteSubtype(curr->body->type, curr->type);
451455
for (auto* body : curr->catchBodies) {

src/wasm-binary.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,6 +1132,7 @@ enum ASTNodes {
11321132
TableGrow = 0x0f,
11331133
TableSize = 0x10,
11341134
TableFill = 0x11,
1135+
TableCopy = 0x0e,
11351136
RefNull = 0xd0,
11361137
RefIsNull = 0xd1,
11371138
RefFunc = 0xd2,
@@ -1844,6 +1845,7 @@ class WasmBinaryReader {
18441845
bool maybeVisitTableSize(Expression*& out, uint32_t code);
18451846
bool maybeVisitTableGrow(Expression*& out, uint32_t code);
18461847
bool maybeVisitTableFill(Expression*& out, uint32_t code);
1848+
bool maybeVisitTableCopy(Expression*& out, uint32_t code);
18471849
bool maybeVisitRefI31(Expression*& out, uint32_t code);
18481850
bool maybeVisitI31Get(Expression*& out, uint32_t code);
18491851
bool maybeVisitRefTest(Expression*& out, uint32_t code);

src/wasm-builder.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,20 @@ class Builder {
771771
ret->finalize();
772772
return ret;
773773
}
774+
TableCopy* makeTableCopy(Expression* dest,
775+
Expression* source,
776+
Expression* size,
777+
Name destTable,
778+
Name sourceTable) {
779+
auto* ret = wasm.allocator.alloc<TableCopy>();
780+
ret->dest = dest;
781+
ret->source = source;
782+
ret->size = size;
783+
ret->destTable = destTable;
784+
ret->sourceTable = sourceTable;
785+
ret->finalize();
786+
return ret;
787+
}
774788

775789
private:
776790
Try* makeTry(Name name,

src/wasm-delegations-fields.def

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,16 @@ switch (DELEGATE_ID) {
576576
DELEGATE_END(TableFill);
577577
break;
578578
}
579+
case Expression::Id::TableCopyId: {
580+
DELEGATE_START(TableCopy);
581+
DELEGATE_FIELD_CHILD(TableCopy, size);
582+
DELEGATE_FIELD_CHILD(TableCopy, source);
583+
DELEGATE_FIELD_CHILD(TableCopy, dest);
584+
DELEGATE_FIELD_NAME_KIND(TableCopy, sourceTable, ModuleItemKind::Table);
585+
DELEGATE_FIELD_NAME_KIND(TableCopy, destTable, ModuleItemKind::Table);
586+
DELEGATE_END(TableCopy);
587+
break;
588+
}
579589
case Expression::Id::TryId: {
580590
DELEGATE_START(Try);
581591
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
@@ -63,6 +63,7 @@ DELEGATE(TableSet);
6363
DELEGATE(TableSize);
6464
DELEGATE(TableGrow);
6565
DELEGATE(TableFill);
66+
DELEGATE(TableCopy);
6667
DELEGATE(Try);
6768
DELEGATE(Throw);
6869
DELEGATE(Rethrow);

src/wasm-interpreter.h

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1392,6 +1392,7 @@ class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
13921392
Flow visitTableSize(TableSize* curr) { WASM_UNREACHABLE("unimp"); }
13931393
Flow visitTableGrow(TableGrow* curr) { WASM_UNREACHABLE("unimp"); }
13941394
Flow visitTableFill(TableFill* curr) { WASM_UNREACHABLE("unimp"); }
1395+
Flow visitTableCopy(TableCopy* curr) { WASM_UNREACHABLE("unimp"); }
13951396
Flow visitTry(Try* curr) { WASM_UNREACHABLE("unimp"); }
13961397
Flow visitThrow(Throw* curr) {
13971398
NOTE_ENTER("Throw");
@@ -2210,6 +2211,10 @@ class ConstantExpressionRunner : public ExpressionRunner<SubType> {
22102211
NOTE_ENTER("TableFill");
22112212
return Flow(NONCONSTANT_FLOW);
22122213
}
2214+
Flow visitTableCopy(TableCopy* curr) {
2215+
NOTE_ENTER("TableCopy");
2216+
return Flow(NONCONSTANT_FLOW);
2217+
}
22132218
Flow visitLoad(Load* curr) {
22142219
NOTE_ENTER("Load");
22152220
return Flow(NONCONSTANT_FLOW);
@@ -3027,6 +3032,57 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
30273032
return Flow();
30283033
}
30293034

3035+
Flow visitTableCopy(TableCopy* curr) {
3036+
NOTE_ENTER("TableCopy");
3037+
Flow dest = self()->visit(curr->dest);
3038+
if (dest.breaking()) {
3039+
return dest;
3040+
}
3041+
Flow source = self()->visit(curr->source);
3042+
if (source.breaking()) {
3043+
return source;
3044+
}
3045+
Flow size = self()->visit(curr->size);
3046+
if (size.breaking()) {
3047+
return size;
3048+
}
3049+
NOTE_EVAL1(dest);
3050+
NOTE_EVAL1(source);
3051+
NOTE_EVAL1(size);
3052+
Address destVal(dest.getSingleValue().getUnsigned());
3053+
Address sourceVal(source.getSingleValue().getUnsigned());
3054+
Address sizeVal(size.getSingleValue().getUnsigned());
3055+
3056+
auto destInfo = getTableInterfaceInfo(curr->destTable);
3057+
auto sourceInfo = getTableInterfaceInfo(curr->sourceTable);
3058+
auto destTableSize = destInfo.interface->tableSize(destInfo.name);
3059+
auto sourceTableSize = sourceInfo.interface->tableSize(sourceInfo.name);
3060+
if (sourceVal + sizeVal > sourceTableSize ||
3061+
destVal + sizeVal > destTableSize ||
3062+
// FIXME: better/cheaper way to detect wrapping?
3063+
sourceVal + sizeVal < sourceVal || sourceVal + sizeVal < sizeVal ||
3064+
destVal + sizeVal < destVal || destVal + sizeVal < sizeVal) {
3065+
trap("out of bounds segment access in table.copy");
3066+
}
3067+
3068+
int64_t start = 0;
3069+
int64_t end = sizeVal;
3070+
int step = 1;
3071+
// Reverse direction if source is below dest
3072+
if (sourceVal < destVal) {
3073+
start = int64_t(sizeVal) - 1;
3074+
end = -1;
3075+
step = -1;
3076+
}
3077+
for (int64_t i = start; i != end; i += step) {
3078+
destInfo.interface->tableStore(
3079+
destInfo.name,
3080+
destVal + i,
3081+
sourceInfo.interface->tableLoad(sourceInfo.name, sourceVal + i));
3082+
}
3083+
return {};
3084+
}
3085+
30303086
Flow visitLocalGet(LocalGet* curr) {
30313087
NOTE_ENTER("LocalGet");
30323088
auto index = curr->index;

src/wasm-ir-builder.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
135135
// [[nodiscard]] Result<> makeTableSize();
136136
// [[nodiscard]] Result<> makeTableGrow();
137137
// [[nodiscard]] Result<> makeTableFill();
138+
// [[nodiscard]] Result<> makeTableCopy();
138139
// [[nodiscard]] Result<> makeTry();
139140
// [[nodiscard]] Result<> makeThrow();
140141
// [[nodiscard]] Result<> makeRethrow();

src/wasm-s-parser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ class SExpressionWasmBuilder {
282282
Expression* makeTableSize(Element& s);
283283
Expression* makeTableGrow(Element& s);
284284
Expression* makeTableFill(Element& s);
285+
Expression* makeTableCopy(Element& s);
285286
Expression* makeTry(Element& s);
286287
Expression* makeTryOrCatchBody(Element& s, Type type, bool isTry);
287288
Expression* makeThrow(Element& s);

src/wasm.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,7 @@ class Expression {
699699
TableSizeId,
700700
TableGrowId,
701701
TableFillId,
702+
TableCopyId,
702703
TryId,
703704
ThrowId,
704705
RethrowId,
@@ -1435,6 +1436,20 @@ class TableFill : public SpecificExpression<Expression::TableFillId> {
14351436
void finalize();
14361437
};
14371438

1439+
class TableCopy : public SpecificExpression<Expression::TableCopyId> {
1440+
public:
1441+
TableCopy() = default;
1442+
TableCopy(MixedArena& allocator) : TableCopy() {}
1443+
1444+
Expression* dest;
1445+
Expression* source;
1446+
Expression* size;
1447+
Name destTable;
1448+
Name sourceTable;
1449+
1450+
void finalize();
1451+
};
1452+
14381453
class Try : public SpecificExpression<Expression::TryId> {
14391454
public:
14401455
Try(MixedArena& allocator) : catchTags(allocator), catchBodies(allocator) {}

src/wasm/wasm-binary.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4062,6 +4062,9 @@ BinaryConsts::ASTNodes WasmBinaryReader::readExpression(Expression*& curr) {
40624062
if (maybeVisitTableFill(curr, opcode)) {
40634063
break;
40644064
}
4065+
if (maybeVisitTableCopy(curr, opcode)) {
4066+
break;
4067+
}
40654068
throwError("invalid code after misc prefix: " + std::to_string(opcode));
40664069
break;
40674070
}
@@ -5436,6 +5439,28 @@ bool WasmBinaryReader::maybeVisitTableFill(Expression*& out, uint32_t code) {
54365439
return true;
54375440
}
54385441

5442+
bool WasmBinaryReader::maybeVisitTableCopy(Expression*& out, uint32_t code) {
5443+
if (code != BinaryConsts::TableCopy) {
5444+
return false;
5445+
}
5446+
Index destTableIdx = getU32LEB();
5447+
if (destTableIdx >= wasm.tables.size()) {
5448+
throwError("bad table index");
5449+
}
5450+
Index sourceTableIdx = getU32LEB();
5451+
if (sourceTableIdx >= wasm.tables.size()) {
5452+
throwError("bad table index");
5453+
}
5454+
auto* size = popNonVoidExpression();
5455+
auto* source = popNonVoidExpression();
5456+
auto* dest = popNonVoidExpression();
5457+
auto* ret = Builder(wasm).makeTableCopy(dest, source, size, Name(), Name());
5458+
tableRefs[destTableIdx].push_back(&ret->destTable);
5459+
tableRefs[sourceTableIdx].push_back(&ret->sourceTable);
5460+
out = ret;
5461+
return true;
5462+
}
5463+
54395464
bool WasmBinaryReader::maybeVisitBinary(Expression*& out, uint8_t code) {
54405465
Binary* curr;
54415466
#define INT_TYPED_CODE(code) \

src/wasm/wasm-s-parser.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2706,6 +2706,24 @@ Expression* SExpressionWasmBuilder::makeTableFill(Element& s) {
27062706
return Builder(wasm).makeTableFill(tableName, dest, value, size);
27072707
}
27082708

2709+
Expression* SExpressionWasmBuilder::makeTableCopy(Element& s) {
2710+
auto destTableName = s[1]->str();
2711+
auto* destTable = wasm.getTableOrNull(destTableName);
2712+
if (!destTable) {
2713+
throw SParseException("invalid dest table name in table.copy", s);
2714+
}
2715+
auto sourceTableName = s[2]->str();
2716+
auto* sourceTable = wasm.getTableOrNull(sourceTableName);
2717+
if (!sourceTable) {
2718+
throw SParseException("invalid source table name in table.copy", s);
2719+
}
2720+
auto* dest = parseExpression(s[3]);
2721+
auto* source = parseExpression(s[4]);
2722+
auto* size = parseExpression(s[5]);
2723+
return Builder(wasm).makeTableCopy(
2724+
dest, source, size, destTableName, sourceTableName);
2725+
}
2726+
27092727
// try can be either in the form of try-catch or try-delegate.
27102728
// try-catch is written in the folded wast format as
27112729
// (try

src/wasm/wasm-stack.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1941,6 +1941,12 @@ void BinaryInstWriter::visitTableFill(TableFill* curr) {
19411941
o << U32LEB(parent.getTableIndex(curr->table));
19421942
}
19431943

1944+
void BinaryInstWriter::visitTableCopy(TableCopy* curr) {
1945+
o << int8_t(BinaryConsts::MiscPrefix) << U32LEB(BinaryConsts::TableCopy);
1946+
o << U32LEB(parent.getTableIndex(curr->destTable));
1947+
o << U32LEB(parent.getTableIndex(curr->sourceTable));
1948+
}
1949+
19441950
void BinaryInstWriter::visitTry(Try* curr) {
19451951
breakStack.push_back(curr->name);
19461952
o << int8_t(BinaryConsts::Try);

0 commit comments

Comments
 (0)