summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ir/possible-contents.cpp69
-rw-r--r--src/ir/possible-contents.h5
2 files changed, 73 insertions, 1 deletions
diff --git a/src/ir/possible-contents.cpp b/src/ir/possible-contents.cpp
index d004ef70e..7a3cdc505 100644
--- a/src/ir/possible-contents.cpp
+++ b/src/ir/possible-contents.cpp
@@ -1999,6 +1999,8 @@ private:
const GlobalLocation& globalLoc);
void filterDataContents(PossibleContents& contents,
const DataLocation& dataLoc);
+ void filterPackedDataReads(PossibleContents& contents,
+ const ExpressionLocation& exprLoc);
// Reads from GC data: a struct.get or array.get. This is given the type of
// the read operation, the field that is read on that type, the known contents
@@ -2301,10 +2303,24 @@ bool Flower::updateContents(LocationIndex locationIndex,
if (auto* dataLoc = std::get_if<DataLocation>(&location)) {
filterDataContents(newContents, *dataLoc);
#if defined(POSSIBLE_CONTENTS_DEBUG) && POSSIBLE_CONTENTS_DEBUG >= 2
- std::cout << " pre-filtered contents:\n";
+ std::cout << " pre-filtered data contents:\n";
newContents.dump(std::cout, &wasm);
std::cout << '\n';
#endif
+ } else if (auto* exprLoc = std::get_if<ExpressionLocation>(&location)) {
+ if (exprLoc->expr->is<StructGet>() || exprLoc->expr->is<ArrayGet>()) {
+ // Packed data reads must be filtered before the combine() operation, as
+ // we must only combine the filtered contents (e.g. if 0xff arrives which
+ // as a signed read is truly 0xffffffff then we cannot first combine the
+ // existing 0xffffffff with the new 0xff, as they are different, and the
+ // result will no longer be a constant).
+ filterPackedDataReads(newContents, *exprLoc);
+#if defined(POSSIBLE_CONTENTS_DEBUG) && POSSIBLE_CONTENTS_DEBUG >= 2
+ std::cout << " pre-filtered packed read contents:\n";
+ newContents.dump(std::cout, &wasm);
+ std::cout << '\n';
+#endif
+ }
}
contents.combine(newContents);
@@ -2633,6 +2649,57 @@ void Flower::filterDataContents(PossibleContents& contents,
}
}
+void Flower::filterPackedDataReads(PossibleContents& contents,
+ const ExpressionLocation& exprLoc) {
+ auto* expr = exprLoc.expr;
+
+ // Packed fields are stored as the truncated bits (see comment on
+ // DataLocation; the actual truncation is done in filterDataContents), which
+ // means that unsigned gets just work but signed ones need fixing (and we only
+ // know how to do that here, when we reach the get and see if it is signed).
+ auto signed_ = false;
+ Expression* ref;
+ Index index;
+ if (auto* get = expr->dynCast<StructGet>()) {
+ signed_ = get->signed_;
+ ref = get->ref;
+ index = get->index;
+ } else if (auto* get = expr->dynCast<ArrayGet>()) {
+ signed_ = get->signed_;
+ ref = get->ref;
+ // Arrays are treated as having a single field.
+ index = 0;
+ } else {
+ WASM_UNREACHABLE("bad packed read");
+ }
+ if (!signed_) {
+ return;
+ }
+
+ // We are reading data here, so the reference must be a valid struct or
+ // array, otherwise we would never have gotten here.
+ assert(ref->type.isRef());
+ auto field = GCTypeUtils::getField(ref->type.getHeapType(), index);
+ assert(field);
+ if (!field->isPacked()) {
+ return;
+ }
+
+ 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));
+ auto lit = contents.getLiteral();
+ lit = lit.shl(shifts);
+ lit = lit.shrS(shifts);
+ contents = PossibleContents::literal(lit);
+ } else {
+ // This is not a constant. As in filterDataContents, give up and leave
+ // only the type, since we have no way to track the sign-extension on
+ // top of whatever this is.
+ contents = PossibleContents::fromType(contents.getType());
+ }
+}
+
void Flower::readFromData(Type declaredType,
Index fieldIndex,
const PossibleContents& refContents,
diff --git a/src/ir/possible-contents.h b/src/ir/possible-contents.h
index 7ff0f1a07..5ec4f758f 100644
--- a/src/ir/possible-contents.h
+++ b/src/ir/possible-contents.h
@@ -446,6 +446,11 @@ struct SignatureResultLocation {
// The location of contents in a struct or array (i.e., things that can fit in a
// dataref). Note that this is specific to this type - it does not include data
// about subtypes or supertypes.
+//
+// We store the truncated bits here when the field is packed. That is, if -1 is
+// written to an i8 then the value here will be 0xff. StructGet/ArrayGet
+// operations that read a signed value must then perform a sign-extend
+// operation.
struct DataLocation {
HeapType type;
// The index of the field in a struct, or 0 for an array (where we do not