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 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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the 1 for here? Can we name this constant?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use lots of unnamed constants for base costs, so I think considering this separately would be fine.

}
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 (curr->ref->type.isNull()) {
parent.trap = true;
return;
}
Comment on lines +1151 to +1154
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can use

      if (trapOnNull(curr->ref)) {
        return;
      }

which also handles the unreachable case. I guess the below statement setting implicitTrap is still needed because OOB access is always possible?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to add a comment about the possibility of OOB access here.

parent.implicitTrap = true;
readsArray(curr->ref->type.getHeapType(), MemoryOrder::Unordered);
}

void visitArrayStore(ArrayStore* curr) {
if (curr->ref->type.isNull()) {
parent.trap = true;
Expand Down
34 changes: 31 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,20 @@ 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) {
return; // No need to sign-extend for full size
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We generally avoid inline comments.

Suggested change
return; // No need to sign-extend for full size
// 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
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
45 changes: 45 additions & 0 deletions src/wasm-interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -2378,6 +2378,51 @@ 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 = int32_t(int8_t(sval));
else if (curr->bytes == 2) sval = int32_t(int16_t(sval));
Comment on lines +2401 to +2402
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We always use brackets around if bodies, even for one-liners. (Here and below.)

}
return Literal(sval);
}
case Type::i64: {
int64_t sval = static_cast<int64_t>(val);
if (curr->signed_) {
if (curr->bytes == 1) sval = int64_t(int8_t(sval));
else if (curr->bytes == 2) sval = int64_t(int16_t(sval));
else if (curr->bytes == 4) sval = int64_t(int32_t(sval));
Comment on lines +2409 to +2411
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use C++ style casts or bit operators instead of C-style casts? e.g. sval & 0xFF

}
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
14 changes: 14 additions & 0 deletions src/wasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,7 @@ class Expression {
ArrayNewFixedId,
ArrayGetId,
ArraySetId,
ArrayLoadId,
ArrayStoreId,
ArrayLenId,
ArrayCopyId,
Expand Down Expand Up @@ -1872,6 +1873,19 @@ class ArraySet : public SpecificExpression<Expression::ArraySetId> {
void finalize();
};

class ArrayLoad : public SpecificExpression<Expression::ArrayLoadId> {
public:
ArrayLoad() = default;
ArrayLoad(MixedArena& allocator) {}

uint8_t bytes;
bool signed_ = false;
Expression* ref;
Expression* index;

void finalize();
};

class ArrayStore : public SpecificExpression<Expression::ArrayStoreId> {
public:
ArrayStore() = default;
Expand Down
Loading
Loading