Skip to content

Commit 86f0e8a

Browse files
authored
[multibyte] Add multibyte array load instructions. (#8504)
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 ddb7026 commit 86f0e8a

30 files changed

+1060
-210
lines changed

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 visitArrayLoad(ArrayLoad* curr) { WASM_UNREACHABLE("TODO"); }
265266
Flow visitArrayStore(ArrayStore* curr) { WASM_UNREACHABLE("TODO"); }
266267
Flow visitArrayLen(ArrayLen* curr) { WASM_UNREACHABLE("TODO"); }
267268
Flow visitArrayCopy(ArrayCopy* 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::visitArrayLoad(ArrayLoad* curr) { curr->finalize(); }
174175
void ReFinalize::visitArrayStore(ArrayStore* curr) { curr->finalize(); }
175176
void ReFinalize::visitArrayLen(ArrayLen* curr) { curr->finalize(); }
176177
void ReFinalize::visitArrayCopy(ArrayCopy* curr) { curr->finalize(); }

src/ir/child-typer.h

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

1099+
void visitArrayLoad(ArrayLoad* curr,
1100+
std::optional<HeapType> ht = std::nullopt) {
1101+
if (!ht) {
1102+
if (!curr->ref->type.isRef()) {
1103+
self().noteUnknown();
1104+
return;
1105+
}
1106+
ht = curr->ref->type.getHeapType();
1107+
}
1108+
note(&curr->ref, Type(*ht, Nullable));
1109+
note(&curr->index, Type::i32);
1110+
}
1111+
10991112
void visitArrayStore(ArrayStore* curr,
11001113
std::optional<HeapType> ht = std::nullopt,
11011114
std::optional<Type> valueType = std::nullopt) {

src/ir/cost.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,9 @@ 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 visitArrayLoad(ArrayLoad* curr) {
757+
return 1 + nullCheckCost(curr->ref) + visit(curr->ref) + visit(curr->index);
758+
}
756759
CostType visitArrayStore(ArrayStore* curr) {
757760
return 2 + nullCheckCost(curr->ref) + visit(curr->ref) +
758761
visit(curr->index) + visit(curr->value);

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 visitArrayLoad(ArrayLoad* curr) {
1151+
if (trapOnNull(curr->ref)) {
1152+
return;
1153+
}
1154+
// Null refs and OOB access.
1155+
parent.implicitTrap = true;
1156+
readsArray(curr->ref->type.getHeapType(), MemoryOrder::Unordered);
1157+
}
1158+
11501159
void visitArrayStore(ArrayStore* curr) {
11511160
if (curr->ref->type.isNull()) {
11521161
parent.trap = true;

src/ir/possible-contents.cpp

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,13 @@ struct InfoCollector
11051105
addChildParentLink(curr->ref, curr);
11061106
addChildParentLink(curr->value, curr);
11071107
}
1108+
void visitArrayLoad(ArrayLoad* curr) {
1109+
if (!isRelevant(curr->ref)) {
1110+
addRoot(curr);
1111+
return;
1112+
}
1113+
addChildParentLink(curr->ref, curr);
1114+
}
11081115
void visitArrayStore(ArrayStore* curr) {
11091116
if (curr->ref->type == Type::unreachable) {
11101117
return;
@@ -1755,6 +1762,7 @@ void TNHOracle::scan(Function* func,
17551762
}
17561763
void visitArrayGet(ArrayGet* curr) { notePossibleTrap(curr->ref); }
17571764
void visitArraySet(ArraySet* curr) { notePossibleTrap(curr->ref); }
1765+
void visitArrayLoad(ArrayLoad* curr) { notePossibleTrap(curr->ref); }
17581766
void visitArrayStore(ArrayStore* curr) { notePossibleTrap(curr->ref); }
17591767
void visitArrayLen(ArrayLen* curr) { notePossibleTrap(curr->ref); }
17601768
void visitArrayCopy(ArrayCopy* curr) {
@@ -2865,6 +2873,9 @@ void Flower::flowAfterUpdate(LocationIndex locationIndex) {
28652873
} else if (auto* set = parent->dynCast<ArraySet>()) {
28662874
assert(set->ref == child || set->value == child);
28672875
writeToData(set->ref, set->value, 0);
2876+
} else if (auto* load = parent->dynCast<ArrayLoad>()) {
2877+
assert(load->ref == child);
2878+
readFromData(load->ref->type, 0, contents, load);
28682879
} else if (auto* store = parent->dynCast<ArrayStore>()) {
28692880
assert(store->ref == child || store->value == child);
28702881
// TODO: model the stored value, and handle different but equal values in
@@ -3108,15 +3119,25 @@ void Flower::filterPackedDataReads(PossibleContents& contents,
31083119
auto signed_ = false;
31093120
Expression* ref;
31103121
Index index;
3122+
unsigned bytes = 0;
3123+
Type resultType = Type::none;
31113124
if (auto* get = expr->dynCast<StructGet>()) {
31123125
signed_ = get->signed_;
31133126
ref = get->ref;
31143127
index = get->index;
3128+
resultType = get->type;
31153129
} else if (auto* get = expr->dynCast<ArrayGet>()) {
31163130
signed_ = get->signed_;
31173131
ref = get->ref;
31183132
// Arrays are treated as having a single field.
31193133
index = 0;
3134+
resultType = get->type;
3135+
} else if (auto* load = expr->dynCast<ArrayLoad>()) {
3136+
signed_ = load->signed_;
3137+
ref = load->ref;
3138+
index = 0;
3139+
bytes = load->bytes;
3140+
resultType = load->type;
31203141
} else {
31213142
WASM_UNREACHABLE("bad packed read");
31223143
}
@@ -3135,13 +3156,21 @@ void Flower::filterPackedDataReads(PossibleContents& contents,
31353156
assert(ref->type.isRef());
31363157
auto field = GCTypeUtils::getField(ref->type.getHeapType(), index);
31373158
assert(field);
3138-
if (!field->isPacked()) {
3139-
return;
3159+
if (!bytes) {
3160+
if (!field->isPacked()) {
3161+
return;
3162+
}
3163+
bytes = field->getByteSize();
31403164
}
31413165

31423166
if (contents.isLiteral()) {
31433167
// This is a constant. We can sign-extend it and use that value.
3144-
auto shifts = Literal(int32_t(32 - field->getByteSize() * 8));
3168+
unsigned bits = resultType.getByteSize() == 8 ? 64 : 32;
3169+
if (bits <= bytes * 8) {
3170+
// No need to sign-extend for full size.
3171+
return;
3172+
}
3173+
auto shifts = Literal(int32_t(bits - bytes * 8));
31453174
auto lit = contents.getLiteral();
31463175
lit = lit.shl(shifts);
31473176
lit = lit.shrS(shifts);

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 visitArrayLoad(ArrayLoad* curr) {}
415416
void visitArrayStore(ArrayStore* curr) {}
416417
void visitArrayLen(ArrayLen* curr) {}
417418
void visitArrayCopy(ArrayCopy* curr) {

src/parser/contexts.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,11 @@ struct NullInstrParserCtx {
570570
return Ok{};
571571
}
572572
template<typename HeapTypeT>
573+
Result<> makeArrayLoad(
574+
Index, const std::vector<Annotation>&, Type, int, bool, HeapTypeT) {
575+
return Ok{};
576+
}
577+
template<typename HeapTypeT>
573578
Result<>
574579
makeArrayStore(Index, const std::vector<Annotation>&, Type, int, HeapTypeT) {
575580
return Ok{};
@@ -2331,6 +2336,16 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx>, AnnotationParserCtx {
23312336
pos, irBuilder.makeStore(bytes, memarg.offset, memarg.align, type, *m));
23322337
}
23332338

2339+
Result<> makeArrayLoad(Index pos,
2340+
const std::vector<Annotation>& annotations,
2341+
Type type,
2342+
int bytes,
2343+
bool signed_,
2344+
HeapTypeT arrayType) {
2345+
return withLoc(pos,
2346+
irBuilder.makeArrayLoad(arrayType, bytes, signed_, type));
2347+
}
2348+
23342349
Result<> makeArrayStore(Index pos,
23352350
const std::vector<Annotation>& annotations,
23362351
Type type,

src/parser/parsers.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1783,6 +1783,18 @@ Result<> makeLoad(Ctx& ctx,
17831783
int bytes,
17841784
bool isAtomic) {
17851785

1786+
if (ctx.in.takeSExprStart("type"sv)) {
1787+
auto arrayType = typeidx(ctx);
1788+
CHECK_ERR(arrayType);
1789+
1790+
if (!ctx.in.takeRParen()) {
1791+
return ctx.in.err("expected end of type use");
1792+
}
1793+
1794+
return ctx.makeArrayLoad(
1795+
pos, annotations, type, bytes, signed_, *arrayType);
1796+
}
1797+
17861798
auto mem = maybeMemidx(ctx);
17871799
CHECK_ERR(mem);
17881800

src/passes/Precompute.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,10 @@ class PrecomputingExpressionRunner
221221
}
222222
// ArrayLen is not disallowed here as it is an immutable property.
223223
Flow visitArrayCopy(ArrayCopy* curr) { return Flow(NONCONSTANT_FLOW); }
224+
Flow visitArrayLoad(ArrayLoad* curr) {
225+
// TODO: We could optimize loads from immutable data, like ArrayGet.
226+
return Flow(NONCONSTANT_FLOW);
227+
}
224228
Flow visitArrayStore(ArrayStore* curr) { return Flow(NONCONSTANT_FLOW); }
225229

226230
// Generates heap info for a heap-allocating expression.

0 commit comments

Comments
 (0)