Skip to content

Commit 3ed93d0

Browse files
authored
[Wasm GC] ArrayInit support (#4138)
array.init is like array.new_with_rtt except that it takes as arguments the values to initialize the array with (as opposed to a size and an optional initial value). Spec: https://docs.google.com/document/d/1afthjsL_B9UaMqCA5ekgVmOm75BVFu6duHNsN9-gnXw/edit#
1 parent 23e452a commit 3ed93d0

31 files changed

+308
-23
lines changed

scripts/gen-s-parser.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -559,19 +559,20 @@
559559
("struct.set", "makeStructSet(s)"),
560560
("array.new_with_rtt", "makeArrayNew(s, false)"),
561561
("array.new_default_with_rtt", "makeArrayNew(s, true)"),
562-
("array.get", "makeArrayGet(s)"),
563-
("array.get_s", "makeArrayGet(s, true)"),
564-
("array.get_u", "makeArrayGet(s, false)"),
565-
("array.set", "makeArraySet(s)"),
566-
("array.len", "makeArrayLen(s)"),
567-
("array.copy", "makeArrayCopy(s)"),
568-
("ref.is_func", "makeRefIs(s, RefIsFunc)"),
569-
("ref.is_data", "makeRefIs(s, RefIsData)"),
570-
("ref.is_i31", "makeRefIs(s, RefIsI31)"),
571-
("ref.as_non_null", "makeRefAs(s, RefAsNonNull)"),
572-
("ref.as_func", "makeRefAs(s, RefAsFunc)"),
573-
("ref.as_data", "makeRefAs(s, RefAsData)"),
574-
("ref.as_i31", "makeRefAs(s, RefAsI31)"),
562+
("array.init", "makeArrayInit(s)"),
563+
("array.get", "makeArrayGet(s)"),
564+
("array.get_s", "makeArrayGet(s, true)"),
565+
("array.get_u", "makeArrayGet(s, false)"),
566+
("array.set", "makeArraySet(s)"),
567+
("array.len", "makeArrayLen(s)"),
568+
("array.copy", "makeArrayCopy(s)"),
569+
("ref.is_func", "makeRefIs(s, RefIsFunc)"),
570+
("ref.is_data", "makeRefIs(s, RefIsData)"),
571+
("ref.is_i31", "makeRefIs(s, RefIsI31)"),
572+
("ref.as_non_null", "makeRefAs(s, RefAsNonNull)"),
573+
("ref.as_func", "makeRefAs(s, RefAsFunc)"),
574+
("ref.as_data", "makeRefAs(s, RefAsData)"),
575+
("ref.as_i31", "makeRefAs(s, RefAsI31)"),
575576
]
576577

577578

src/gen-s-parser.inc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ switch (op[0]) {
3333
default: goto parse_error;
3434
}
3535
}
36+
case 'i':
37+
if (strcmp(op, "array.init") == 0) { return makeArrayInit(s); }
38+
goto parse_error;
3639
case 'l':
3740
if (strcmp(op, "array.len") == 0) { return makeArrayLen(s); }
3841
goto parse_error;

src/ir/ReFinalize.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ void ReFinalize::visitStructNew(StructNew* curr) { curr->finalize(); }
162162
void ReFinalize::visitStructGet(StructGet* curr) { curr->finalize(); }
163163
void ReFinalize::visitStructSet(StructSet* curr) { curr->finalize(); }
164164
void ReFinalize::visitArrayNew(ArrayNew* curr) { curr->finalize(); }
165+
void ReFinalize::visitArrayInit(ArrayInit* curr) { curr->finalize(); }
165166
void ReFinalize::visitArrayGet(ArrayGet* curr) { curr->finalize(); }
166167
void ReFinalize::visitArraySet(ArraySet* curr) { curr->finalize(); }
167168
void ReFinalize::visitArrayLen(ArrayLen* curr) { curr->finalize(); }

src/ir/cost.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,13 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
606606
CostType visitArrayNew(ArrayNew* curr) {
607607
return 4 + visit(curr->rtt) + visit(curr->size) + maybeVisit(curr->init);
608608
}
609+
CostType visitArrayInit(ArrayInit* curr) {
610+
CostType ret = 4 + visit(curr->rtt);
611+
for (auto* child : curr->values) {
612+
ret += visit(child);
613+
}
614+
return ret;
615+
}
609616
CostType visitArrayGet(ArrayGet* curr) {
610617
return 1 + nullCheckCost(curr->ref) + visit(curr->ref) + visit(curr->index);
611618
}

src/ir/effects.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,7 @@ class EffectAnalyzer {
642642
}
643643
}
644644
void visitArrayNew(ArrayNew* curr) {}
645+
void visitArrayInit(ArrayInit* curr) {}
645646
void visitArrayGet(ArrayGet* curr) {
646647
parent.readsArray = true;
647648
// traps when the arg is null or the index out of bounds

src/ir/global-utils.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ inline bool canInitializeGlobal(Expression* curr) {
6565
}
6666
if (Properties::isSingleConstantExpression(curr) || curr->is<GlobalGet>() ||
6767
curr->is<RttCanon>() || curr->is<RttSub>() || curr->is<StructNew>() ||
68-
curr->is<ArrayNew>() || curr->is<I31New>()) {
68+
curr->is<ArrayNew>() || curr->is<ArrayInit>() || curr->is<I31New>()) {
6969
for (auto* child : ChildIterator(curr)) {
7070
if (!canInitializeGlobal(child)) {
7171
return false;

src/ir/properties.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ bool isGenerative(Expression* curr, FeatureSet features) {
3232
bool generative = false;
3333
void visitStructNew(StructNew* curr) { generative = true; }
3434
void visitArrayNew(ArrayNew* curr) { generative = true; }
35+
void visitArrayInit(ArrayInit* curr) { generative = true; }
3536
} scanner;
3637
scanner.walk(curr);
3738
return scanner.generative;

src/js/binaryen.js-post.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ function initializeConstants() {
109109
'StructGet',
110110
'StructSet',
111111
'ArrayNew',
112+
'ArrayInit',
112113
'ArrayGet',
113114
'ArraySet',
114115
'ArrayLen'

src/passes/Precompute.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ class PrecomputingExpressionRunner
9494
Flow visitStructNew(StructNew* curr) { return Flow(NONCONSTANT_FLOW); }
9595
Flow visitStructGet(StructGet* curr) { return Flow(NONCONSTANT_FLOW); }
9696
Flow visitArrayNew(ArrayNew* curr) { return Flow(NONCONSTANT_FLOW); }
97+
Flow visitArrayInit(ArrayInit* curr) { return Flow(NONCONSTANT_FLOW); }
9798
Flow visitArrayGet(ArrayGet* curr) { return Flow(NONCONSTANT_FLOW); }
9899
Flow visitArrayLen(ArrayLen* curr) { return Flow(NONCONSTANT_FLOW); }
99100
Flow visitArrayCopy(ArrayCopy* curr) { return Flow(NONCONSTANT_FLOW); }

src/passes/Print.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2006,6 +2006,10 @@ struct PrintExpressionContents
20062006
o << "with_rtt ";
20072007
TypeNamePrinter(o, wasm).print(curr->rtt->type.getHeapType());
20082008
}
2009+
void visitArrayInit(ArrayInit* curr) {
2010+
printMedium(o, "array.init ");
2011+
TypeNamePrinter(o, wasm).print(curr->rtt->type.getHeapType());
2012+
}
20092013
void visitArrayGet(ArrayGet* curr) {
20102014
if (printUnreachableReplacement(curr->ref)) {
20112015
return;

src/wasm-binary.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,6 +1056,7 @@ enum ASTNodes {
10561056
ArraySet = 0x16,
10571057
ArrayLen = 0x17,
10581058
ArrayCopy = 0x18,
1059+
ArrayInit = 0x19,
10591060
I31New = 0x20,
10601061
I31GetS = 0x21,
10611062
I31GetU = 0x22,
@@ -1626,6 +1627,7 @@ class WasmBinaryBuilder {
16261627
bool maybeVisitStructGet(Expression*& out, uint32_t code);
16271628
bool maybeVisitStructSet(Expression*& out, uint32_t code);
16281629
bool maybeVisitArrayNew(Expression*& out, uint32_t code);
1630+
bool maybeVisitArrayInit(Expression*& out, uint32_t code);
16291631
bool maybeVisitArrayGet(Expression*& out, uint32_t code);
16301632
bool maybeVisitArraySet(Expression*& out, uint32_t code);
16311633
bool maybeVisitArrayLen(Expression*& out, uint32_t code);

src/wasm-builder.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,14 @@ class Builder {
840840
ret->finalize();
841841
return ret;
842842
}
843+
ArrayInit* makeArrayInit(Expression* rtt,
844+
const std::vector<Expression*>& values) {
845+
auto* ret = wasm.allocator.alloc<ArrayInit>();
846+
ret->rtt = rtt;
847+
ret->values.set(values);
848+
ret->finalize();
849+
return ret;
850+
}
843851
ArrayGet*
844852
makeArrayGet(Expression* ref, Expression* index, bool signed_ = false) {
845853
auto* ret = wasm.allocator.alloc<ArrayGet>();

src/wasm-delegations-fields.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,13 @@ switch (DELEGATE_ID) {
634634
DELEGATE_END(ArrayNew);
635635
break;
636636
}
637+
case Expression::Id::ArrayInitId: {
638+
DELEGATE_START(ArrayInit);
639+
DELEGATE_FIELD_CHILD(ArrayInit, rtt);
640+
DELEGATE_FIELD_CHILD_VECTOR(ArrayInit, values);
641+
DELEGATE_END(ArrayInit);
642+
break;
643+
}
637644
case Expression::Id::ArrayGetId: {
638645
DELEGATE_START(ArrayGet);
639646
DELEGATE_FIELD_CHILD(ArrayGet, index);

src/wasm-delegations.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ DELEGATE(StructNew);
7575
DELEGATE(StructGet);
7676
DELEGATE(StructSet);
7777
DELEGATE(ArrayNew);
78+
DELEGATE(ArrayInit);
7879
DELEGATE(ArrayGet);
7980
DELEGATE(ArraySet);
8081
DELEGATE(ArrayLen);

src/wasm-interpreter.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1702,6 +1702,27 @@ class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
17021702
return Flow(Literal(std::make_shared<GCData>(rtt.getSingleValue(), data),
17031703
curr->type));
17041704
}
1705+
Flow visitArrayInit(ArrayInit* curr) {
1706+
NOTE_ENTER("ArrayInit");
1707+
auto rtt = this->visit(curr->rtt);
1708+
if (rtt.breaking()) {
1709+
return rtt;
1710+
}
1711+
Index num = curr->values.size();
1712+
if (num >= ArrayLimit) {
1713+
hostLimit("allocation failure");
1714+
}
1715+
Literals data(num);
1716+
for (Index i = 0; i < num; i++) {
1717+
auto value = this->visit(curr->values[i]);
1718+
if (value.breaking()) {
1719+
return value;
1720+
}
1721+
data[i] = value.getSingleValue();
1722+
}
1723+
return Flow(Literal(std::make_shared<GCData>(rtt.getSingleValue(), data),
1724+
curr->type));
1725+
}
17051726
Flow visitArrayGet(ArrayGet* curr) {
17061727
NOTE_ENTER("ArrayGet");
17071728
Flow ref = this->visit(curr->ref);

src/wasm-s-parser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ class SExpressionWasmBuilder {
284284
Expression* makeStructGet(Element& s, bool signed_ = false);
285285
Expression* makeStructSet(Element& s);
286286
Expression* makeArrayNew(Element& s, bool default_);
287+
Expression* makeArrayInit(Element& s);
287288
Expression* makeArrayGet(Element& s, bool signed_ = false);
288289
Expression* makeArraySet(Element& s);
289290
Expression* makeArrayLen(Element& s);

src/wasm.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,7 @@ class Expression {
641641
StructGetId,
642642
StructSetId,
643643
ArrayNewId,
644+
ArrayInitId,
644645
ArrayGetId,
645646
ArraySetId,
646647
ArrayLenId,
@@ -1468,6 +1469,16 @@ class ArrayNew : public SpecificExpression<Expression::ArrayNewId> {
14681469
void finalize();
14691470
};
14701471

1472+
class ArrayInit : public SpecificExpression<Expression::ArrayInitId> {
1473+
public:
1474+
ArrayInit(MixedArena& allocator) : values(allocator) {}
1475+
1476+
ExpressionList values;
1477+
Expression* rtt;
1478+
1479+
void finalize();
1480+
};
1481+
14711482
class ArrayGet : public SpecificExpression<Expression::ArrayGetId> {
14721483
public:
14731484
ArrayGet(MixedArena& allocator) {}

src/wasm/wasm-binary.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3574,6 +3574,9 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) {
35743574
if (maybeVisitArrayNew(curr, opcode)) {
35753575
break;
35763576
}
3577+
if (maybeVisitArrayInit(curr, opcode)) {
3578+
break;
3579+
}
35773580
if (maybeVisitArrayGet(curr, opcode)) {
35783581
break;
35793582
}
@@ -6508,6 +6511,22 @@ bool WasmBinaryBuilder::maybeVisitArrayNew(Expression*& out, uint32_t code) {
65086511
return true;
65096512
}
65106513

6514+
bool WasmBinaryBuilder::maybeVisitArrayInit(Expression*& out, uint32_t code) {
6515+
if (code != BinaryConsts::ArrayInit) {
6516+
return false;
6517+
}
6518+
auto heapType = getIndexedHeapType();
6519+
auto size = getU32LEB();
6520+
auto* rtt = popNonVoidExpression();
6521+
validateHeapTypeUsingChild(rtt, heapType);
6522+
std::vector<Expression*> values(size);
6523+
for (size_t i = 0; i < size; i++) {
6524+
values[size - i - 1] = popNonVoidExpression();
6525+
}
6526+
out = Builder(wasm).makeArrayInit(rtt, values);
6527+
return true;
6528+
}
6529+
65116530
bool WasmBinaryBuilder::maybeVisitArrayGet(Expression*& out, uint32_t code) {
65126531
bool signed_ = false;
65136532
switch (code) {

src/wasm/wasm-s-parser.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2680,6 +2680,18 @@ Expression* SExpressionWasmBuilder::makeArrayNew(Element& s, bool default_) {
26802680
return Builder(wasm).makeArrayNew(rtt, size, init);
26812681
}
26822682

2683+
Expression* SExpressionWasmBuilder::makeArrayInit(Element& s) {
2684+
auto heapType = parseHeapType(*s[1]);
2685+
size_t i = 2;
2686+
std::vector<Expression*> values;
2687+
while (i < s.size() - 1) {
2688+
values.push_back(parseExpression(*s[i++]));
2689+
}
2690+
auto* rtt = parseExpression(*s[i++]);
2691+
validateHeapTypeUsingChild(rtt, heapType, s);
2692+
return Builder(wasm).makeArrayInit(rtt, values);
2693+
}
2694+
26832695
Expression* SExpressionWasmBuilder::makeArrayGet(Element& s, bool signed_) {
26842696
auto heapType = parseHeapType(*s[1]);
26852697
auto ref = parseExpression(*s[2]);

src/wasm/wasm-stack.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2040,6 +2040,12 @@ void BinaryInstWriter::visitArrayNew(ArrayNew* curr) {
20402040
parent.writeIndexedHeapType(curr->rtt->type.getHeapType());
20412041
}
20422042

2043+
void BinaryInstWriter::visitArrayInit(ArrayInit* curr) {
2044+
o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::ArrayInit);
2045+
parent.writeIndexedHeapType(curr->rtt->type.getHeapType());
2046+
o << U32LEB(curr->values.size());
2047+
}
2048+
20432049
void BinaryInstWriter::visitArrayGet(ArrayGet* curr) {
20442050
auto heapType = curr->ref->type.getHeapType();
20452051
const auto& field = heapType.getArray().element;

src/wasm/wasm-validator.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@ struct FunctionValidator : public WalkerPass<PostWalker<FunctionValidator>> {
382382
void visitStructGet(StructGet* curr);
383383
void visitStructSet(StructSet* curr);
384384
void visitArrayNew(ArrayNew* curr);
385+
void visitArrayInit(ArrayInit* curr);
385386
void visitArrayGet(ArrayGet* curr);
386387
void visitArraySet(ArraySet* curr);
387388
void visitArrayLen(ArrayLen* curr);
@@ -2416,6 +2417,31 @@ void FunctionValidator::visitArrayNew(ArrayNew* curr) {
24162417
}
24172418
}
24182419

2420+
void FunctionValidator::visitArrayInit(ArrayInit* curr) {
2421+
shouldBeTrue(getModule()->features.hasGC(),
2422+
curr,
2423+
"array.init requires gc to be enabled");
2424+
if (curr->type == Type::unreachable) {
2425+
return;
2426+
}
2427+
if (!shouldBeTrue(
2428+
curr->rtt->type.isRtt(), curr, "array.init rtt must be rtt")) {
2429+
return;
2430+
}
2431+
auto heapType = curr->rtt->type.getHeapType();
2432+
if (!shouldBeTrue(
2433+
heapType.isArray(), curr, "array.init heap type must be array")) {
2434+
return;
2435+
}
2436+
const auto& element = heapType.getArray().element;
2437+
for (auto* value : curr->values) {
2438+
shouldBeSubType(value->type,
2439+
element.type,
2440+
curr,
2441+
"array.init value must have proper type");
2442+
}
2443+
}
2444+
24192445
void FunctionValidator::visitArrayGet(ArrayGet* curr) {
24202446
shouldBeTrue(
24212447
getModule()->features.hasGC(), curr, "array.get requires gc to be enabled");

src/wasm/wasm.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,20 @@ void ArrayNew::finalize() {
10351035
type = Type(rtt->type.getHeapType(), NonNullable);
10361036
}
10371037

1038+
void ArrayInit::finalize() {
1039+
if (rtt->type == Type::unreachable) {
1040+
type = Type::unreachable;
1041+
return;
1042+
}
1043+
for (auto* value : values) {
1044+
if (value->type == Type::unreachable) {
1045+
type = Type::unreachable;
1046+
return;
1047+
}
1048+
}
1049+
type = Type(rtt->type.getHeapType(), NonNullable);
1050+
}
1051+
10381052
void ArrayGet::finalize() {
10391053
if (ref->type == Type::unreachable || index->type == Type::unreachable) {
10401054
type = Type::unreachable;

src/wasm2js.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2239,6 +2239,10 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m,
22392239
unimplemented(curr);
22402240
WASM_UNREACHABLE("unimp");
22412241
}
2242+
Ref visitArrayInit(ArrayInit* curr) {
2243+
unimplemented(curr);
2244+
WASM_UNREACHABLE("unimp");
2245+
}
22422246
Ref visitArrayGet(ArrayGet* curr) {
22432247
unimplemented(curr);
22442248
WASM_UNREACHABLE("unimp");

test/binaryen.js/kitchen-sink.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ function test_ids() {
180180
console.log("StructGetId: " + binaryen.StructGetId);
181181
console.log("StructSetId: " + binaryen.StructSetId);
182182
console.log("ArrayNewId: " + binaryen.ArrayNewId);
183+
console.log("ArrayInitId: " + binaryen.ArrayInitId);
183184
console.log("ArrayGetId: " + binaryen.ArrayGetId);
184185
console.log("ArraySetId: " + binaryen.ArraySetId);
185186
console.log("ArrayLenId: " + binaryen.ArrayLenId);

test/binaryen.js/kitchen-sink.js.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,10 @@ StructNewId: 58
104104
StructGetId: 59
105105
StructSetId: 60
106106
ArrayNewId: 61
107-
ArrayGetId: 62
108-
ArraySetId: 63
109-
ArrayLenId: 64
107+
ArrayInitId: 62
108+
ArrayGetId: 63
109+
ArraySetId: 64
110+
ArrayLenId: 65
110111
getExpressionInfo={"id":15,"type":4,"op":6}
111112
(f32.neg
112113
(f32.const -33.61199951171875)

0 commit comments

Comments
 (0)