Skip to content

Commit 29ef3a4

Browse files
authored
[multibyte] Add multibyte array store instructions. (#8059)
Prototype implementation of the new multibyte array proposal that reuses the existing memory instructions. https://github.com/WebAssembly/multibyte-array-access/blob/944d79230b336442b1f0b3978da1908d54b9f230/proposals/multibyte-array-access/Overview.md
1 parent ca7ac96 commit 29ef3a4

46 files changed

Lines changed: 1495 additions & 184 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

scripts/fuzz_opt.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
# V8 does not support shared memories when running with
6666
# shared-everything enabled, so do not fuzz shared-everything
6767
# for now. The remaining features are not yet implemented in v8.
68-
DISALLOWED_FEATURES_IN_V8 = ['shared-everything', 'strings', 'stack-switching', 'relaxed-atomics']
68+
DISALLOWED_FEATURES_IN_V8 = ['shared-everything', 'strings', 'stack-switching', 'relaxed-atomics', 'multibyte']
6969

7070

7171
# utilities

src/binaryen-c.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,9 @@ BinaryenFeatures BinaryenFeatureCallIndirectOverlong(void) {
502502
BinaryenFeatures BinaryenFeatureRelaxedAtomics(void) {
503503
return static_cast<BinaryenFeatures>(FeatureSet::RelaxedAtomics);
504504
}
505+
BinaryenFeatures BinaryenFeatureMultibyte(void) {
506+
return static_cast<BinaryenFeatures>(FeatureSet::Multibyte);
507+
}
505508
BinaryenFeatures BinaryenFeatureCustomPageSizes(void) {
506509
return static_cast<BinaryenFeatures>(FeatureSet::CustomPageSizes);
507510
}

src/binaryen-c.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ BINARYEN_API BinaryenFeatures BinaryenFeatureFP16(void);
244244
BINARYEN_API BinaryenFeatures BinaryenFeatureBulkMemoryOpt(void);
245245
BINARYEN_API BinaryenFeatures BinaryenFeatureCallIndirectOverlong(void);
246246
BINARYEN_API BinaryenFeatures BinaryenFeatureRelaxedAtomics(void);
247+
BINARYEN_API BinaryenFeatures BinaryenFeatureMultibyte(void);
247248
BINARYEN_API BinaryenFeatures BinaryenFeatureCustomPageSizes(void);
248249
BINARYEN_API BinaryenFeatures BinaryenFeatureAll(void);
249250

src/interpreter/interpreter.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ struct ExpressionInterpreter : OverriddenVisitor<ExpressionInterpreter, Flow> {
262262
Flow visitArrayNewFixed(ArrayNewFixed* curr) { WASM_UNREACHABLE("TODO"); }
263263
Flow visitArrayGet(ArrayGet* curr) { WASM_UNREACHABLE("TODO"); }
264264
Flow visitArraySet(ArraySet* curr) { WASM_UNREACHABLE("TODO"); }
265+
Flow visitArrayStore(ArrayStore* curr) { WASM_UNREACHABLE("TODO"); }
265266
Flow visitArrayLen(ArrayLen* curr) { WASM_UNREACHABLE("TODO"); }
266267
Flow visitArrayCopy(ArrayCopy* curr) { WASM_UNREACHABLE("TODO"); }
267268
Flow visitArrayFill(ArrayFill* curr) { WASM_UNREACHABLE("TODO"); }

src/ir/ReFinalize.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ void ReFinalize::visitArrayNewElem(ArrayNewElem* curr) { curr->finalize(); }
171171
void ReFinalize::visitArrayNewFixed(ArrayNewFixed* curr) { curr->finalize(); }
172172
void ReFinalize::visitArrayGet(ArrayGet* curr) { curr->finalize(); }
173173
void ReFinalize::visitArraySet(ArraySet* curr) { curr->finalize(); }
174+
void ReFinalize::visitArrayStore(ArrayStore* curr) { curr->finalize(); }
174175
void ReFinalize::visitArrayLen(ArrayLen* curr) { curr->finalize(); }
175176
void ReFinalize::visitArrayCopy(ArrayCopy* curr) { curr->finalize(); }
176177
void ReFinalize::visitArrayFill(ArrayFill* curr) { curr->finalize(); }

src/ir/child-typer.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,6 +1096,26 @@ template<typename Subtype> struct ChildTyper : OverriddenVisitor<Subtype> {
10961096
note(&curr->value, type);
10971097
}
10981098

1099+
void visitArrayStore(ArrayStore* curr,
1100+
std::optional<HeapType> ht = std::nullopt,
1101+
std::optional<Type> valueType = std::nullopt) {
1102+
if (!ht) {
1103+
if (!curr->ref->type.isRef()) {
1104+
self().noteUnknown();
1105+
return;
1106+
}
1107+
ht = curr->ref->type.getHeapType();
1108+
}
1109+
auto actualValueType = valueType ? *valueType : curr->value->type;
1110+
if (actualValueType == Type::unreachable) {
1111+
self().noteUnknown();
1112+
return;
1113+
}
1114+
note(&curr->ref, Type(*ht, Nullable));
1115+
note(&curr->index, Type::i32);
1116+
note(&curr->value, actualValueType);
1117+
}
1118+
10991119
void visitArrayLen(ArrayLen* curr) {
11001120
note(&curr->ref, Type(HeapType::array, Nullable));
11011121
}

src/ir/cost.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,10 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
753753
return 2 + nullCheckCost(curr->ref) + visit(curr->ref) +
754754
visit(curr->index) + visit(curr->value);
755755
}
756+
CostType visitArrayStore(ArrayStore* curr) {
757+
return 2 + nullCheckCost(curr->ref) + visit(curr->ref) +
758+
visit(curr->index) + visit(curr->value);
759+
}
756760
CostType visitArrayLen(ArrayLen* curr) {
757761
return 1 + nullCheckCost(curr->ref) + visit(curr->ref);
758762
}

src/ir/effects.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,6 +1147,15 @@ class EffectAnalyzer {
11471147
parent.implicitTrap = true;
11481148
writesArray(curr->ref->type.getHeapType(), curr->order);
11491149
}
1150+
void visitArrayStore(ArrayStore* curr) {
1151+
if (curr->ref->type.isNull()) {
1152+
parent.trap = true;
1153+
return;
1154+
}
1155+
parent.writesArray = true;
1156+
// traps when the arg is null or the index out of bounds
1157+
parent.implicitTrap = true;
1158+
}
11501159
void visitArrayLen(ArrayLen* curr) {
11511160
trapOnNull(curr->ref);
11521161
// No need to model this as reading the array since the length cannot be

src/ir/possible-contents.cpp

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,13 @@ struct InfoCollector
11021102
addChildParentLink(curr->ref, curr);
11031103
addChildParentLink(curr->value, curr);
11041104
}
1105+
void visitArrayStore(ArrayStore* curr) {
1106+
if (curr->ref->type == Type::unreachable) {
1107+
return;
1108+
}
1109+
addChildParentLink(curr->ref, curr);
1110+
addChildParentLink(curr->value, curr);
1111+
}
11051112

11061113
void visitArrayLen(ArrayLen* curr) {
11071114
// TODO: optimize when possible (perhaps we can infer a Literal for the
@@ -1742,6 +1749,7 @@ void TNHOracle::scan(Function* func,
17421749
}
17431750
void visitArrayGet(ArrayGet* curr) { notePossibleTrap(curr->ref); }
17441751
void visitArraySet(ArraySet* curr) { notePossibleTrap(curr->ref); }
1752+
void visitArrayStore(ArrayStore* curr) { notePossibleTrap(curr->ref); }
17451753
void visitArrayLen(ArrayLen* curr) { notePossibleTrap(curr->ref); }
17461754
void visitArrayCopy(ArrayCopy* curr) {
17471755
notePossibleTrap(curr->srcRef);
@@ -2239,7 +2247,10 @@ struct Flower {
22392247
Expression* read);
22402248

22412249
// Similar to readFromData, but does a write for a struct.set or array.set.
2242-
void writeToData(Expression* ref, Expression* value, Index fieldIndex);
2250+
void writeToData(Expression* ref,
2251+
Expression* value,
2252+
Index fieldIndex,
2253+
bool multibyte = false);
22432254

22442255
// We will need subtypes during the flow, so compute them once ahead of time.
22452256
std::unique_ptr<SubTypes> subTypes;
@@ -2576,7 +2587,7 @@ Flower::Flower(Module& wasm, const PassOptions& options)
25762587
for (const auto& [location, value] : roots) {
25772588
#if defined(POSSIBLE_CONTENTS_DEBUG) && POSSIBLE_CONTENTS_DEBUG >= 2
25782589
std::cout << " init root\n";
2579-
dump(location);
2590+
dump(getLocation(location));
25802591
value.dump(std::cout, &wasm);
25812592
std::cout << '\n';
25822593
#endif
@@ -2743,6 +2754,11 @@ bool Flower::updateContents(LocationIndex locationIndex,
27432754
} else if (auto* globalLoc = std::get_if<GlobalLocation>(&location)) {
27442755
filterGlobalContents(contents, *globalLoc);
27452756
filtered = true;
2757+
} else if (auto* dataLoc = std::get_if<DataLocation>(&location)) {
2758+
// Multibyte array stores with differing widths can merge to Many, so
2759+
// filter again afterwards to fall back to the declared type limit.
2760+
filterDataContents(contents, *dataLoc);
2761+
filtered = true;
27462762
}
27472763

27482764
// Check if anything changed after filtering, if we did so.
@@ -2829,6 +2845,9 @@ void Flower::flowAfterUpdate(LocationIndex locationIndex) {
28292845
} else if (auto* set = parent->dynCast<ArraySet>()) {
28302846
assert(set->ref == child || set->value == child);
28312847
writeToData(set->ref, set->value, 0);
2848+
} else if (auto* store = parent->dynCast<ArrayStore>()) {
2849+
assert(store->ref == child || store->value == child);
2850+
writeToData(store->ref, store->value, 0, /*multibyte*/ true);
28322851
} else if (auto* get = parent->dynCast<RefGetDesc>()) {
28332852
// Similar to struct.get.
28342853
assert(get->ref == child);
@@ -3006,6 +3025,13 @@ void Flower::filterDataContents(PossibleContents& contents,
30063025
contents = PossibleContents::none();
30073026
return;
30083027
}
3028+
if (contents.isMany()) {
3029+
// An unknown state (e.g. from combining writes of different types like in
3030+
// multibyte array stores) translates into the most generic bounded type.
3031+
// TODO: We could optimize these, as e.g. a write of i16 0x1212 does not
3032+
// actually conflict with a write of i8 0x12.
3033+
contents = PossibleContents::fromType(field->type);
3034+
}
30093035

30103036
if (field->isPacked()) {
30113037
// We must handle packed fields carefully.
@@ -3188,7 +3214,10 @@ void Flower::readFromData(Type declaredType,
31883214
connectDuringFlow(coneReadLocation, ExpressionLocation{read, 0});
31893215
}
31903216

3191-
void Flower::writeToData(Expression* ref, Expression* value, Index fieldIndex) {
3217+
void Flower::writeToData(Expression* ref,
3218+
Expression* value,
3219+
Index fieldIndex,
3220+
bool multibyte) {
31923221
#if defined(POSSIBLE_CONTENTS_DEBUG) && POSSIBLE_CONTENTS_DEBUG >= 2
31933222
std::cout << " add special writes\n";
31943223
#endif
@@ -3236,6 +3265,9 @@ void Flower::writeToData(Expression* ref, Expression* value, Index fieldIndex) {
32363265
// As in readFromData, normalize to the proper cone.
32373266
auto cone = refContents.getCone();
32383267
auto normalizedDepth = getNormalizedConeDepth(cone.type, cone.depth);
3268+
if (multibyte) {
3269+
valueContents = PossibleContents::fromType(value->type);
3270+
}
32393271

32403272
subTypes->iterSubTypes(
32413273
cone.type.getHeapType(), normalizedDepth, [&](HeapType type, Index depth) {

src/ir/subtype-exprs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,7 @@ struct SubtypingDiscoverer : public OverriddenVisitor<SubType> {
412412
auto array = curr->ref->type.getHeapType().getArray();
413413
self()->noteSubtype(curr->value, array.element.type);
414414
}
415+
void visitArrayStore(ArrayStore* curr) {}
415416
void visitArrayLen(ArrayLen* curr) {}
416417
void visitArrayCopy(ArrayCopy* curr) {
417418
if (!curr->srcRef->type.isArray() || !curr->destRef->type.isArray()) {

0 commit comments

Comments
 (0)