Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions scripts/clusterfuzz/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
'--disable-strings',
'--disable-stack-switching',
'--disable-relaxed-atomics',
'--disable-multibyte',
]


Expand Down
3 changes: 3 additions & 0 deletions src/binaryen-c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,9 @@ BinaryenFeatures BinaryenFeatureCallIndirectOverlong(void) {
BinaryenFeatures BinaryenFeatureRelaxedAtomics(void) {
return static_cast<BinaryenFeatures>(FeatureSet::RelaxedAtomics);
}
BinaryenFeatures BinaryenFeatureMultibyte(void) {
return static_cast<BinaryenFeatures>(FeatureSet::Multibyte);
}
BinaryenFeatures BinaryenFeatureAll(void) {
return static_cast<BinaryenFeatures>(FeatureSet::All);
}
Expand Down
1 change: 1 addition & 0 deletions src/binaryen-c.h
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ BINARYEN_API BinaryenFeatures BinaryenFeatureFP16(void);
BINARYEN_API BinaryenFeatures BinaryenFeatureBulkMemoryOpt(void);
BINARYEN_API BinaryenFeatures BinaryenFeatureCallIndirectOverlong(void);
BINARYEN_API BinaryenFeatures BinaryenFeatureRelaxedAtomics(void);
BINARYEN_API BinaryenFeatures BinaryenFeatureMultibyte(void);
BINARYEN_API BinaryenFeatures BinaryenFeatureAll(void);

// Modules
Expand Down
1 change: 1 addition & 0 deletions src/interpreter/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ struct ExpressionInterpreter : OverriddenVisitor<ExpressionInterpreter, Flow> {
Flow visitArrayNewFixed(ArrayNewFixed* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitArrayGet(ArrayGet* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitArraySet(ArraySet* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitArrayStore(ArrayStore* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitArrayLen(ArrayLen* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitArrayCopy(ArrayCopy* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitArrayFill(ArrayFill* curr) { WASM_UNREACHABLE("TODO"); }
Expand Down
1 change: 1 addition & 0 deletions src/ir/ReFinalize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ void ReFinalize::visitArrayNewElem(ArrayNewElem* curr) { curr->finalize(); }
void ReFinalize::visitArrayNewFixed(ArrayNewFixed* curr) { curr->finalize(); }
void ReFinalize::visitArrayGet(ArrayGet* curr) { curr->finalize(); }
void ReFinalize::visitArraySet(ArraySet* curr) { curr->finalize(); }
void ReFinalize::visitArrayStore(ArrayStore* curr) { curr->finalize(); }
void ReFinalize::visitArrayLen(ArrayLen* curr) { curr->finalize(); }
void ReFinalize::visitArrayCopy(ArrayCopy* curr) { curr->finalize(); }
void ReFinalize::visitArrayFill(ArrayFill* curr) { curr->finalize(); }
Expand Down
14 changes: 14 additions & 0 deletions src/ir/child-typer.h
Original file line number Diff line number Diff line change
Expand Up @@ -1067,6 +1067,20 @@ template<typename Subtype> struct ChildTyper : OverriddenVisitor<Subtype> {
note(&curr->value, type);
}

void visitArrayStore(ArrayStore* curr,
std::optional<HeapType> ht = std::nullopt) {
if (!ht) {
if (!curr->ref->type.isRef()) {
self().noteUnknown();
return;
}
ht = curr->ref->type.getHeapType();
}
note(&curr->ref, Type(*ht, Nullable));
note(&curr->index, Type::i32);
note(&curr->value, curr->valueType);
}

void visitArrayLen(ArrayLen* curr) {
note(&curr->ref, Type(HeapType::array, Nullable));
}
Expand Down
4 changes: 4 additions & 0 deletions src/ir/cost.h
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,10 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
return 2 + nullCheckCost(curr->ref) + visit(curr->ref) +
visit(curr->index) + visit(curr->value);
}
CostType visitArrayStore(ArrayStore* curr) {
return 2 + nullCheckCost(curr->ref) + visit(curr->ref) +
visit(curr->index) + visit(curr->value);
}
CostType visitArrayLen(ArrayLen* curr) {
return 1 + nullCheckCost(curr->ref) + visit(curr->ref);
}
Expand Down
9 changes: 9 additions & 0 deletions src/ir/effects.h
Original file line number Diff line number Diff line change
Expand Up @@ -979,6 +979,15 @@ class EffectAnalyzer {
// traps when the arg is null or the index out of bounds
parent.implicitTrap = true;
}
void visitArrayStore(ArrayStore* curr) {
if (curr->ref->type.isNull()) {
parent.trap = true;
return;
}
parent.writesArray = true;
// traps when the arg is null or the index out of bounds
parent.implicitTrap = true;
}
void visitArrayLen(ArrayLen* curr) {
if (curr->ref->type.isNull()) {
parent.trap = true;
Expand Down
8 changes: 8 additions & 0 deletions src/ir/possible-contents.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1095,6 +1095,13 @@ struct InfoCollector
addChildParentLink(curr->ref, curr);
addChildParentLink(curr->value, curr);
}
void visitArrayStore(ArrayStore* curr) {
if (curr->ref->type == Type::unreachable) {
return;
}
addChildParentLink(curr->ref, curr);
addChildParentLink(curr->value, curr);
}

void visitArrayLen(ArrayLen* curr) {
// TODO: optimize when possible (perhaps we can infer a Literal for the
Expand Down Expand Up @@ -1732,6 +1739,7 @@ void TNHOracle::scan(Function* func,
}
void visitArrayGet(ArrayGet* curr) { notePossibleTrap(curr->ref); }
void visitArraySet(ArraySet* curr) { notePossibleTrap(curr->ref); }
void visitArrayStore(ArrayStore* curr) { notePossibleTrap(curr->ref); }
void visitArrayLen(ArrayLen* curr) { notePossibleTrap(curr->ref); }
void visitArrayCopy(ArrayCopy* curr) {
notePossibleTrap(curr->srcRef);
Expand Down
7 changes: 7 additions & 0 deletions src/ir/subtype-exprs.h
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,13 @@ struct SubtypingDiscoverer : public OverriddenVisitor<SubType> {
auto array = curr->ref->type.getHeapType().getArray();
self()->noteSubtype(curr->value, array.element.type);
}
void visitArrayStore(ArrayStore* curr) {
if (!curr->ref->type.isArray()) {
return;
}
auto array = curr->ref->type.getHeapType().getArray();
self()->noteSubtype(curr->value, array.element.type);
}
void visitArrayLen(ArrayLen* curr) {}
void visitArrayCopy(ArrayCopy* curr) {
if (!curr->srcRef->type.isArray() || !curr->destRef->type.isArray()) {
Expand Down
16 changes: 16 additions & 0 deletions src/parser/contexts.h
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,14 @@ struct NullInstrParserCtx {
MemoryOrder) {
return Ok{};
}
template<typename HeapTypeT>
Result<> makeArrayStore(Index,
const std::vector<Annotation>&,
Type,
int,
HeapTypeT) {
return Ok{};
}
Result<> makeAtomicRMW(Index,
const std::vector<Annotation>&,
AtomicRMWOp,
Expand Down Expand Up @@ -2302,6 +2310,14 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx>, AnnotationParserCtx {
pos, irBuilder.makeStore(bytes, memarg.offset, memarg.align, type, *m));
}

Result<> makeArrayStore(Index pos,
const std::vector<Annotation>& annotations,
Type type,
int bytes,
HeapTypeT arrayType) {
return withLoc(pos, irBuilder.makeArrayStore(arrayType, bytes, type));
}

Result<> makeAtomicRMW(Index pos,
const std::vector<Annotation>& annotations,
AtomicRMWOp op,
Expand Down
12 changes: 12 additions & 0 deletions src/parser/parsers.h
Original file line number Diff line number Diff line change
Expand Up @@ -1786,6 +1786,18 @@ Result<> makeStore(Ctx& ctx,
Type type,
int bytes,
bool isAtomic) {
if (ctx.in.takeSExprStart("type"sv)) {
std::optional<typename Ctx::HeapTypeT> arrayType;
auto x = typeidx(ctx);
CHECK_ERR(x);

if (!ctx.in.takeRParen()) {
return ctx.in.err("expected end of type use");
}

arrayType = *x;
return ctx.makeArrayStore(pos, annotations, type, bytes, *arrayType);
}
auto mem = maybeMemidx(ctx);
CHECK_ERR(mem);

Expand Down
47 changes: 32 additions & 15 deletions src/passes/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,25 @@ struct PrintExpressionContents
return parent.printBlockType(sig);
}

std::ostream& printStorePostfix(uint8_t bytes, Type valueType) {
if (bytes < 4 || (valueType == Type::i64 && bytes < 8)) {
if (bytes == 1) {
o << '8';
} else if (bytes == 2) {
if (valueType == Type::f32) {
o << "_f16";
} else {
o << "16";
}
} else if (bytes == 4) {
o << "32";
} else {
abort();
}
}
return o;
}

void visitBlock(Block* curr) {
printMedium(o, "block");
if (curr->name.is()) {
Expand Down Expand Up @@ -588,21 +607,7 @@ struct PrintExpressionContents
o << ".atomic";
}
o << ".store";
if (curr->bytes < 4 || (curr->valueType == Type::i64 && curr->bytes < 8)) {
if (curr->bytes == 1) {
o << '8';
} else if (curr->bytes == 2) {
if (curr->valueType == Type::f32) {
o << "_f16";
} else {
o << "16";
}
} else if (curr->bytes == 4) {
o << "32";
} else {
abort();
}
}
printStorePostfix(curr->bytes, curr->valueType);
restoreNormalColor(o);
printMemoryName(curr->memory, o, wasm);
printMemoryOrder(curr->order);
Expand Down Expand Up @@ -2462,6 +2467,18 @@ struct PrintExpressionContents
o << ' ';
printHeapTypeName(curr->ref->type.getHeapType());
}
void visitArrayStore(ArrayStore* curr) {
prepareColor(o) << forceConcrete(curr->valueType);
o << ".store";
printStorePostfix(curr->bytes, curr->valueType);
o << " ";
restoreNormalColor(o);

o << '(';
printMinor(o, "type ");
printHeapTypeName(curr->ref->type.getHeapType());
o << ')';
}
void visitArrayLen(ArrayLen* curr) { printMedium(o, "array.len"); }
void visitArrayCopy(ArrayCopy* curr) {
printMedium(o, "array.copy ");
Expand Down
2 changes: 2 additions & 0 deletions src/passes/TypeGeneralizing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,8 @@ struct TransferFn : OverriddenVisitor<TransferFn> {
}
}

void visitArrayStore(ArrayStore* curr) { WASM_UNREACHABLE("TODO"); }

void visitArrayLen(ArrayLen* curr) {
// The input must be an array.
push(Type(HeapType::array, Nullable));
Expand Down
1 change: 1 addition & 0 deletions src/tools/fuzzing.h
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,7 @@ class TranslateToFuzzReader {
Expression* makeStructSet(Type type);
Expression* makeArrayGet(Type type);
Expression* makeArraySet(Type type);
Expression* makeArrayStore(Type type);
// Use a single method for the misc array operations, to not give them too
// much representation (e.g. compared to struct operations, which only include
// get/set).
Expand Down
43 changes: 43 additions & 0 deletions src/tools/fuzzing/fuzzing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2531,6 +2531,8 @@ Expression* TranslateToFuzzReader::_makenone() {
.add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeCallRef)
.add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeStructSet)
.add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeArraySet)
.add(FeatureSet::ReferenceTypes | FeatureSet::GC | FeatureSet::Multibyte,
&Self::makeArrayStore)
.add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeBrOn)
.add(FeatureSet::ReferenceTypes | FeatureSet::GC,
&Self::makeArrayBulkMemoryOp);
Expand Down Expand Up @@ -5463,6 +5465,47 @@ Expression* TranslateToFuzzReader::makeArraySet(Type type) {
return builder.makeIf(check.condition, set);
}

Expression* TranslateToFuzzReader::makeArrayStore(Type type) {
assert(type == Type::none);
std::vector<HeapType> i8Arrays;
for (auto array : mutableArrays) {
if (array.getArray().element.packedType == Field::i8) {
i8Arrays.push_back(array);
}
}
if (i8Arrays.empty()) {
return makeTrivial(type);
}
auto arrayType = pick(i8Arrays);
auto* ref = makeTrappingRefUse(arrayType);
auto* index = make(Type::i32);
auto field = arrayType.getArray().element;
// TODO: non-default lanes
uint8_t lanes = 1;
auto bytes = field.type.getByteSize() * lanes;
if (!bytes) {
return makeTrivial(Type::none);
}
auto valueType = field.type;
if (valueType.isRef()) {
// ArrayStore only works for non-ref types.
return makeTrivial(Type::none);
}
// We can only store things that fit in the field.
// We also don't want to store a value that is too large for the field, to
// avoid truncation.
// For now, just use the same type as the field.
auto* value = make(valueType);
if (allowOOB && oneIn(10)) {
return builder.makeArrayStore(bytes, valueType, ref, index, value);
}
auto check = makeArrayBoundsCheck(ref, index, funcContext->func, builder);
auto* store = builder.makeArrayStore(bytes, valueType, check.getRef, check.getIndex, value);
return builder.makeIf(check.condition, store, builder.makeNop());
}



Expression* TranslateToFuzzReader::makeArrayBulkMemoryOp(Type type) {
assert(type == Type::none);
if (mutableArrays.empty()) {
Expand Down
1 change: 1 addition & 0 deletions src/tools/tool-options.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ struct ToolOptions : public Options {
.addFeature(FeatureSet::FP16, "float 16 operations")
.addFeature(FeatureSet::CustomDescriptors,
"custom descriptors (RTTs) and exact references")
.addFeature(FeatureSet::Multibyte, "multibyte array loads and stores")
.addFeature(FeatureSet::RelaxedAtomics,
"acquire/release atomic memory operations")
.add("--enable-typed-function-references",
Expand Down
8 changes: 6 additions & 2 deletions src/wasm-binary.h
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ enum BrOnCastFlag {

constexpr uint32_t ExactImport = 1 << 5;

constexpr uint32_t HasBackingArrayMask = 1 << 4;
constexpr uint32_t HasMemoryOrderMask = 1 << 5;
constexpr uint32_t HasMemoryIndexMask = 1 << 6;

Expand Down Expand Up @@ -462,6 +463,7 @@ extern const char* BulkMemoryOptFeature;
extern const char* CallIndirectOverlongFeature;
extern const char* CustomDescriptorsFeature;
extern const char* RelaxedAtomicsFeature;
extern const char* MultibyteFeature;

enum Subsection {
NameModule = 0,
Expand Down Expand Up @@ -1693,6 +1695,8 @@ class WasmBinaryReader {

void readExports();

Result<> readStore(unsigned bytes, Type type);

// The strings in the strings section (which are referred to by StringConst).
std::vector<Name> strings;
void readStrings();
Expand Down Expand Up @@ -1739,11 +1743,11 @@ class WasmBinaryReader {
void readRemovableIfUnusedHints(size_t payloadLen);
void readJSCalledHints(size_t payloadLen);

std::tuple<Address, Address, Index, MemoryOrder>
std::tuple<Address, Address, Index, MemoryOrder, BackingType>
readMemoryAccess(bool isAtomic, bool isRMW);
std::tuple<Name, Address, Address, MemoryOrder> getAtomicMemarg();
std::tuple<Name, Address, Address, MemoryOrder> getRMWMemarg();
std::tuple<Name, Address, Address> getMemarg();
std::tuple<Name, Address, Address, BackingType> getMemarg();
MemoryOrder getMemoryOrder(bool isRMW = false);

[[noreturn]] void throwError(std::string text) {
Expand Down
14 changes: 14 additions & 0 deletions src/wasm-builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -1129,6 +1129,20 @@ class Builder {
ret->finalize();
return ret;
}
ArrayStore* makeArrayStore(unsigned bytes,
Type valueType,
Expression* ref,
Expression* index,
Expression* value) {
auto* ret = wasm.allocator.alloc<ArrayStore>();
ret->bytes = bytes;
ret->valueType = valueType;
ret->ref = ref;
ret->index = index;
ret->value = value;
ret->finalize();
return ret;
}
ArrayLen* makeArrayLen(Expression* ref) {
auto* ret = wasm.allocator.alloc<ArrayLen>();
ret->ref = ref;
Expand Down
8 changes: 8 additions & 0 deletions src/wasm-delegations-fields.def
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,14 @@ DELEGATE_FIELD_IMMEDIATE_TYPED_CHILD(ArraySet, ref)
DELEGATE_FIELD_INT(ArraySet, order)
DELEGATE_FIELD_CASE_END(ArraySet)

DELEGATE_FIELD_CASE_START(ArrayStore)
DELEGATE_FIELD_CHILD(ArrayStore, value)
DELEGATE_FIELD_CHILD(ArrayStore, index)
DELEGATE_FIELD_IMMEDIATE_TYPED_CHILD(ArrayStore, ref)
DELEGATE_FIELD_INT(ArrayStore, bytes)
DELEGATE_FIELD_TYPE(ArrayStore, valueType)
DELEGATE_FIELD_CASE_END(ArrayStore)

DELEGATE_FIELD_CASE_START(ArrayLen)
DELEGATE_FIELD_CHILD(ArrayLen, ref)
DELEGATE_FIELD_CASE_END(ArrayLen)
Expand Down
Loading
Loading