Skip to content
Merged
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 src/interpreter/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,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 visitArrayLoad(ArrayLoad* 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"); }
Expand Down
1 change: 1 addition & 0 deletions src/ir/ReFinalize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,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::visitArrayLoad(ArrayLoad* curr) { curr->finalize(); }
void ReFinalize::visitArrayStore(ArrayStore* curr) { curr->finalize(); }
void ReFinalize::visitArrayLen(ArrayLen* curr) { curr->finalize(); }
void ReFinalize::visitArrayCopy(ArrayCopy* curr) { curr->finalize(); }
Expand Down
13 changes: 13 additions & 0 deletions src/ir/child-typer.h
Original file line number Diff line number Diff line change
Expand Up @@ -1096,6 +1096,19 @@ template<typename Subtype> struct ChildTyper : OverriddenVisitor<Subtype> {
note(&curr->value, type);
}

void visitArrayLoad(ArrayLoad* 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);
}

void visitArrayStore(ArrayStore* curr,
std::optional<HeapType> ht = std::nullopt,
std::optional<Type> valueType = std::nullopt) {
Expand Down
3 changes: 3 additions & 0 deletions src/ir/cost.h
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,9 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
return 2 + nullCheckCost(curr->ref) + visit(curr->ref) +
visit(curr->index) + visit(curr->value);
}
CostType visitArrayLoad(ArrayLoad* curr) {
return 1 + nullCheckCost(curr->ref) + visit(curr->ref) + visit(curr->index);
}
CostType visitArrayStore(ArrayStore* curr) {
return 2 + nullCheckCost(curr->ref) + visit(curr->ref) +
visit(curr->index) + visit(curr->value);
Expand Down
9 changes: 9 additions & 0 deletions src/ir/effects.h
Original file line number Diff line number Diff line change
Expand Up @@ -1147,6 +1147,15 @@ class EffectAnalyzer {
parent.implicitTrap = true;
writesArray(curr->ref->type.getHeapType(), curr->order);
}
void visitArrayLoad(ArrayLoad* curr) {
if (trapOnNull(curr->ref)) {
return;
}
// Null refs and OOB access.
parent.implicitTrap = true;
readsArray(curr->ref->type.getHeapType(), MemoryOrder::Unordered);
}

void visitArrayStore(ArrayStore* curr) {
if (curr->ref->type.isNull()) {
parent.trap = true;
Expand Down
35 changes: 32 additions & 3 deletions src/ir/possible-contents.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1105,6 +1105,13 @@ struct InfoCollector
addChildParentLink(curr->ref, curr);
addChildParentLink(curr->value, curr);
}
void visitArrayLoad(ArrayLoad* curr) {
if (!isRelevant(curr->ref)) {
addRoot(curr);
return;
}
addChildParentLink(curr->ref, curr);
}
void visitArrayStore(ArrayStore* curr) {
if (curr->ref->type == Type::unreachable) {
return;
Expand Down Expand Up @@ -1755,6 +1762,7 @@ void TNHOracle::scan(Function* func,
}
void visitArrayGet(ArrayGet* curr) { notePossibleTrap(curr->ref); }
void visitArraySet(ArraySet* curr) { notePossibleTrap(curr->ref); }
void visitArrayLoad(ArrayLoad* curr) { notePossibleTrap(curr->ref); }
void visitArrayStore(ArrayStore* curr) { notePossibleTrap(curr->ref); }
void visitArrayLen(ArrayLen* curr) { notePossibleTrap(curr->ref); }
void visitArrayCopy(ArrayCopy* curr) {
Expand Down Expand Up @@ -2865,6 +2873,9 @@ void Flower::flowAfterUpdate(LocationIndex locationIndex) {
} else if (auto* set = parent->dynCast<ArraySet>()) {
assert(set->ref == child || set->value == child);
writeToData(set->ref, set->value, 0);
} else if (auto* load = parent->dynCast<ArrayLoad>()) {
assert(load->ref == child);
readFromData(load->ref->type, 0, contents, load);
} else if (auto* store = parent->dynCast<ArrayStore>()) {
assert(store->ref == child || store->value == child);
// TODO: model the stored value, and handle different but equal values in
Expand Down Expand Up @@ -3108,15 +3119,25 @@ void Flower::filterPackedDataReads(PossibleContents& contents,
auto signed_ = false;
Expression* ref;
Index index;
unsigned bytes = 0;
Type resultType = Type::none;
if (auto* get = expr->dynCast<StructGet>()) {
signed_ = get->signed_;
ref = get->ref;
index = get->index;
resultType = get->type;
} else if (auto* get = expr->dynCast<ArrayGet>()) {
signed_ = get->signed_;
ref = get->ref;
// Arrays are treated as having a single field.
index = 0;
resultType = get->type;
} else if (auto* load = expr->dynCast<ArrayLoad>()) {
signed_ = load->signed_;
ref = load->ref;
index = 0;
bytes = load->bytes;
resultType = load->type;
} else {
WASM_UNREACHABLE("bad packed read");
}
Expand All @@ -3135,13 +3156,21 @@ void Flower::filterPackedDataReads(PossibleContents& contents,
assert(ref->type.isRef());
auto field = GCTypeUtils::getField(ref->type.getHeapType(), index);
assert(field);
if (!field->isPacked()) {
return;
if (!bytes) {
if (!field->isPacked()) {
return;
}
bytes = field->getByteSize();
}

if (contents.isLiteral()) {
// This is a constant. We can sign-extend it and use that value.
auto shifts = Literal(int32_t(32 - field->getByteSize() * 8));
unsigned bits = resultType.getByteSize() == 8 ? 64 : 32;
if (bits <= bytes * 8) {
// No need to sign-extend for full size.
return;
}
auto shifts = Literal(int32_t(bits - bytes * 8));
auto lit = contents.getLiteral();
lit = lit.shl(shifts);
lit = lit.shrS(shifts);
Expand Down
1 change: 1 addition & 0 deletions src/ir/subtype-exprs.h
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,7 @@ struct SubtypingDiscoverer : public OverriddenVisitor<SubType> {
auto array = curr->ref->type.getHeapType().getArray();
self()->noteSubtype(curr->value, array.element.type);
}
void visitArrayLoad(ArrayLoad* curr) {}
void visitArrayStore(ArrayStore* curr) {}
void visitArrayLen(ArrayLen* curr) {}
void visitArrayCopy(ArrayCopy* curr) {
Expand Down
15 changes: 15 additions & 0 deletions src/parser/contexts.h
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,11 @@ struct NullInstrParserCtx {
return Ok{};
}
template<typename HeapTypeT>
Result<> makeArrayLoad(
Index, const std::vector<Annotation>&, Type, int, bool, HeapTypeT) {
return Ok{};
}
template<typename HeapTypeT>
Result<>
makeArrayStore(Index, const std::vector<Annotation>&, Type, int, HeapTypeT) {
return Ok{};
Expand Down Expand Up @@ -2331,6 +2336,16 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx>, AnnotationParserCtx {
pos, irBuilder.makeStore(bytes, memarg.offset, memarg.align, type, *m));
}

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

Result<> makeArrayStore(Index pos,
const std::vector<Annotation>& annotations,
Type type,
Expand Down
12 changes: 12 additions & 0 deletions src/parser/parsers.h
Original file line number Diff line number Diff line change
Expand Up @@ -1783,6 +1783,18 @@ Result<> makeLoad(Ctx& ctx,
int bytes,
bool isAtomic) {

if (ctx.in.takeSExprStart("type"sv)) {
auto arrayType = typeidx(ctx);
CHECK_ERR(arrayType);

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

return ctx.makeArrayLoad(
pos, annotations, type, bytes, signed_, *arrayType);
}

auto mem = maybeMemidx(ctx);
CHECK_ERR(mem);

Expand Down
4 changes: 4 additions & 0 deletions src/passes/Precompute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,10 @@ class PrecomputingExpressionRunner
}
// ArrayLen is not disallowed here as it is an immutable property.
Flow visitArrayCopy(ArrayCopy* curr) { return Flow(NONCONSTANT_FLOW); }
Flow visitArrayLoad(ArrayLoad* curr) {
// TODO: We could optimize loads from immutable data, like ArrayGet.
return Flow(NONCONSTANT_FLOW);
}
Flow visitArrayStore(ArrayStore* curr) { return Flow(NONCONSTANT_FLOW); }

// Generates heap info for a heap-allocating expression.
Expand Down
17 changes: 17 additions & 0 deletions src/passes/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2478,6 +2478,23 @@ struct PrintExpressionContents
o << ' ';
printHeapTypeName(curr->ref->type.getHeapType());
}
void visitArrayLoad(ArrayLoad* curr) {
prepareColor(o) << forceConcrete(curr->type);
o << ".load";
if (curr->type != Type::unreachable &&
curr->bytes < curr->type.getByteSize()) {
printMemoryPostfix(curr->bytes, curr->type);
o << (curr->signed_ ? "_s" : "_u");
}
o << " ";
restoreNormalColor(o);

o << '(';
printMinor(o, "type ");
printHeapTypeName(curr->ref->type.getHeapType());
o << ')';
}

void visitArrayStore(ArrayStore* curr) {
prepareColor(o) << forceConcrete(curr->value->type);
o << ".store";
Expand Down
1 change: 1 addition & 0 deletions src/passes/TypeGeneralizing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,7 @@ struct TransferFn : OverriddenVisitor<TransferFn> {
}
}

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

void visitArrayLen(ArrayLen* curr) {
Expand Down
1 change: 1 addition & 0 deletions src/wasm-binary.h
Original file line number Diff line number Diff line change
Expand Up @@ -1716,6 +1716,7 @@ class WasmBinaryReader {

void readExports();

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

// The strings in the strings section (which are referred to by StringConst).
Expand Down
15 changes: 15 additions & 0 deletions src/wasm-builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -1132,6 +1132,21 @@ class Builder {
ret->finalize();
return ret;
}
ArrayLoad* makeArrayLoad(unsigned bytes,
bool signed_,
Expression* ref,
Expression* index,
Type type) {
auto* ret = wasm.allocator.alloc<ArrayLoad>();
ret->bytes = bytes;
ret->signed_ = signed_;
ret->ref = ref;
ret->index = index;
ret->type = type;
ret->finalize();
return ret;
}

ArrayStore* makeArrayStore(unsigned bytes,
Expression* ref,
Expression* index,
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(ArrayLoad)
DELEGATE_FIELD_CHILD(ArrayLoad, index)
DELEGATE_FIELD_IMMEDIATE_TYPED_CHILD(ArrayLoad, ref)
DELEGATE_FIELD_INT(ArrayLoad, bytes)
DELEGATE_FIELD_INT(ArrayLoad, signed_)
DELEGATE_FIELD_TYPE(ArrayLoad, type)
DELEGATE_FIELD_CASE_END(ArrayLoad)

DELEGATE_FIELD_CASE_START(ArrayStore)
DELEGATE_FIELD_CHILD(ArrayStore, value)
DELEGATE_FIELD_CHILD(ArrayStore, index)
Expand Down
1 change: 1 addition & 0 deletions src/wasm-delegations.def
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ DELEGATE(ArrayNewElem);
DELEGATE(ArrayNewFixed);
DELEGATE(ArrayGet);
DELEGATE(ArraySet);
DELEGATE(ArrayLoad);
DELEGATE(ArrayStore);
DELEGATE(ArrayLen);
DELEGATE(ArrayCopy);
Expand Down
52 changes: 52 additions & 0 deletions src/wasm-interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -2382,6 +2382,58 @@ class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
data->values[i] = truncateForPacking(value.getSingleValue(), field);
return Flow();
}
Flow visitArrayLoad(ArrayLoad* curr) {
VISIT(ref, curr->ref)
VISIT(index, curr->index)
auto data = ref.getSingleValue().getGCData();
if (!data) {
trap("null ref");
}
Index i = index.getSingleValue().geti32();
size_t size = data->values.size();
if (i >= size || curr->bytes > (size - i)) {
trap("array oob");
}
uint64_t val = 0;
for (unsigned b = 0; b < curr->bytes; ++b) {
val |= static_cast<uint64_t>(data->values[i + b].geti32()) << (b * 8);
}
switch (curr->type.getBasic()) {
case Type::i32: {
int32_t sval = static_cast<int32_t>(val);
if (curr->signed_) {
if (curr->bytes == 1) {
sval = static_cast<int32_t>(static_cast<int8_t>(sval));
} else if (curr->bytes == 2) {
sval = static_cast<int32_t>(static_cast<int16_t>(sval));
}
}
return Literal(sval);
}
case Type::i64: {
int64_t sval = static_cast<int64_t>(val);
if (curr->signed_) {
if (curr->bytes == 1) {
sval = static_cast<int64_t>(static_cast<int8_t>(sval));
} else if (curr->bytes == 2) {
sval = static_cast<int64_t>(static_cast<int16_t>(sval));
} else if (curr->bytes == 4) {
sval = static_cast<int64_t>(static_cast<int32_t>(sval));
}
}
return Literal(sval);
}
case Type::f32: {
return Literal(bit_cast<float>(static_cast<int32_t>(val)));
}
case Type::f64: {
return Literal(bit_cast<double>(static_cast<int64_t>(val)));
}
default:
WASM_UNREACHABLE("invalid type");
}
}

Flow visitArrayStore(ArrayStore* curr) {
VISIT(ref, curr->ref)
VISIT(index, curr->index)
Expand Down
2 changes: 2 additions & 0 deletions src/wasm-ir-builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,8 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
Result<> makeArrayNewFixed(HeapType type, uint32_t arity);
Result<> makeArrayGet(HeapType type, bool signed_, MemoryOrder order);
Result<> makeArraySet(HeapType type, MemoryOrder order);
Result<>
makeArrayLoad(HeapType arrayType, unsigned bytes, bool signed_, Type type);
Result<> makeArrayStore(HeapType arrayType, unsigned bytes, Type type);
Result<> makeArrayLen();
Result<> makeArrayCopy(HeapType destType, HeapType srcType);
Expand Down
1 change: 1 addition & 0 deletions src/wasm-stack.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ class BinaryInstWriter : public OverriddenVisitor<BinaryInstWriter> {
MemoryOrder order,
bool isRMW,
BackingType backing = BackingType::Memory);
void emitLoadOpcode(unsigned bytes, bool signed_, Type type);
void emitStoreOpcode(uint8_t bytes, Type valueType);
int32_t getBreakIndex(Name name);

Expand Down
Loading
Loading