diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ir/properties.h | 3 | ||||
-rw-r--r-- | src/literal.h | 13 | ||||
-rw-r--r-- | src/passes/Precompute.cpp | 4 | ||||
-rw-r--r-- | src/tools/execution-results.h | 5 | ||||
-rw-r--r-- | src/wasm-builder.h | 8 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 52 | ||||
-rw-r--r-- | src/wasm-type.h | 3 | ||||
-rw-r--r-- | src/wasm/literal.cpp | 18 | ||||
-rw-r--r-- | src/wasm/wasm-type.cpp | 13 |
9 files changed, 104 insertions, 15 deletions
diff --git a/src/ir/properties.h b/src/ir/properties.h index e7e96507c..d47ee55a8 100644 --- a/src/ir/properties.h +++ b/src/ir/properties.h @@ -82,7 +82,8 @@ inline bool isNamedControlFlow(Expression* curr) { // runtime will be equal as well. TODO: combine this with // isValidInConstantExpression or find better names(#4845) inline bool isSingleConstantExpression(const Expression* curr) { - return curr->is<Const>() || curr->is<RefNull>() || curr->is<RefFunc>(); + return curr->is<Const>() || curr->is<RefNull>() || curr->is<RefFunc>() || + curr->is<StringConst>(); } inline bool isConstantExpression(const Expression* curr) { diff --git a/src/literal.h b/src/literal.h index 213713a1f..5e449c576 100644 --- a/src/literal.h +++ b/src/literal.h @@ -48,7 +48,9 @@ class Literal { // A reference to GC data, either a Struct or an Array. For both of those // we store the referred data as a Literals object (which is natural for an // Array, and for a Struct, is just the fields in order). The type is used - // to indicate whether this is a Struct or an Array, and of what type. + // to indicate whether this is a Struct or an Array, and of what type. We + // also use this to store String data, as it is similarly stored on the + // heap. std::shared_ptr<GCData> gcData; // TODO: Literals of type `anyref` can only be `null` currently but we // will need to represent external values eventually, to @@ -90,7 +92,10 @@ public: bool isConcrete() const { return type.isConcrete(); } bool isNone() const { return type == Type::none; } bool isFunction() const { return type.isFunction(); } + // Whether this is GC data, that is, something stored on the heap (aside from + // a null or i31). This includes structs, arrays, and also strings. bool isData() const { return type.isData(); } + bool isString() const { return type.isString(); } bool isNull() const { return type.isNull(); } @@ -709,10 +714,10 @@ public: std::ostream& operator<<(std::ostream& o, wasm::Literal literal); std::ostream& operator<<(std::ostream& o, wasm::Literals literals); -// A GC Struct or Array is a set of values with a type saying how it should be -// interpreted. +// A GC Struct, Array, or String is a set of values with a type saying how it +// should be interpreted. struct GCData { - // The type of this struct or array. + // The type of this struct, array, or string. HeapType type; // The element or field values. diff --git a/src/passes/Precompute.cpp b/src/passes/Precompute.cpp index c90fdf167..5fff7710b 100644 --- a/src/passes/Precompute.cpp +++ b/src/passes/Precompute.cpp @@ -509,6 +509,10 @@ private: if (type.isFunction()) { return true; } + // We can emit a StringConst for a string constant. + if (type.isString()) { + return true; + } // All other reference types cannot be precomputed. Even an immutable GC // reference is not currently something this pass can handle, as it will // evaluate and reevaluate code multiple times in e.g. propagateLocals, see diff --git a/src/tools/execution-results.h b/src/tools/execution-results.h index 925c9b6d8..d12c84d1e 100644 --- a/src/tools/execution-results.h +++ b/src/tools/execution-results.h @@ -116,12 +116,15 @@ struct ExecutionResults { if (values->size() > 0) { std::cout << "[fuzz-exec] note result: " << exp->name << " => "; auto resultType = func->getResults(); - if (resultType.isRef()) { + if (resultType.isRef() && !resultType.isString()) { // Don't print reference values, as funcref(N) contains an index // for example, which is not guaranteed to remain identical after // optimizations. std::cout << resultType << '\n'; } else { + // Non-references can be printed in full. So can strings, since we + // always know how to print them and there is just one string + // type. std::cout << *values << '\n'; } } diff --git a/src/wasm-builder.h b/src/wasm-builder.h index 324ed6fd2..5592273da 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -1153,6 +1153,14 @@ public: if (type.isRef() && type.getHeapType() == HeapType::i31) { return makeI31New(makeConst(value.geti31())); } + if (type.isString()) { + // TODO: more than ascii support + std::string string; + for (auto c : value.getGCData()->values) { + string.push_back(c.getInteger()); + } + return makeStringConst(string); + } TODO_SINGLE_COMPOUND(type); WASM_UNREACHABLE("unsupported constant expression"); } diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 6d25e4398..851c4013c 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -1797,8 +1797,54 @@ public: } WASM_UNREACHABLE("unimplemented ref.as_*"); } - Flow visitStringNew(StringNew* curr) { WASM_UNREACHABLE("unimp"); } - Flow visitStringConst(StringConst* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitStringNew(StringNew* curr) { + Flow ptr = visit(curr->ptr); + if (ptr.breaking()) { + return ptr; + } + switch (curr->op) { + case StringNewWTF16Array: { + Flow start = visit(curr->start); + if (start.breaking()) { + return start; + } + Flow end = visit(curr->end); + if (end.breaking()) { + return end; + } + auto ptrData = ptr.getSingleValue().getGCData(); + if (!ptrData) { + trap("null ref"); + } + const auto& ptrDataValues = ptrData->values; + size_t startVal = start.getSingleValue().getUnsigned(); + size_t endVal = end.getSingleValue().getUnsigned(); + if (endVal > ptrDataValues.size()) { + trap("array oob"); + } + Literals contents; + if (endVal > startVal) { + contents.reserve(endVal - startVal); + for (size_t i = startVal; i < endVal; i++) { + contents.push_back(ptrDataValues[i]); + } + } + auto heapType = curr->type.getHeapType(); + return Literal(std::make_shared<GCData>(heapType, contents), heapType); + } + default: + // TODO: others + return Flow(NONCONSTANT_FLOW); + } + } + Flow visitStringConst(StringConst* curr) { + Literals contents; + for (size_t i = 0; i < curr->string.size(); i++) { + contents.push_back(Literal(int32_t(curr->string[i]))); + } + auto heapType = curr->type.getHeapType(); + return Literal(std::make_shared<GCData>(heapType, contents), heapType); + } Flow visitStringMeasure(StringMeasure* curr) { WASM_UNREACHABLE("unimp"); } Flow visitStringEncode(StringEncode* curr) { WASM_UNREACHABLE("unimp"); } Flow visitStringConcat(StringConcat* curr) { WASM_UNREACHABLE("unimp"); } @@ -2121,8 +2167,6 @@ public: NOTE_ENTER("Rethrow"); return Flow(NONCONSTANT_FLOW); } - Flow visitStringNew(StringNew* curr) { return Flow(NONCONSTANT_FLOW); } - Flow visitStringConst(StringConst* curr) { return Flow(NONCONSTANT_FLOW); } Flow visitStringMeasure(StringMeasure* curr) { return Flow(NONCONSTANT_FLOW); } diff --git a/src/wasm-type.h b/src/wasm-type.h index a359ea794..67223705e 100644 --- a/src/wasm-type.h +++ b/src/wasm-type.h @@ -159,6 +159,7 @@ public: bool isSingle() const { return isConcrete() && !isTuple(); } bool isRef() const; bool isFunction() const; + // See literal.h. bool isData() const; // Checks whether a type is a reference and is nullable. This returns false // for a value that is not a reference, that is, for which nullability is @@ -173,6 +174,7 @@ public: bool isNull() const; bool isStruct() const; bool isArray() const; + bool isString() const; bool isDefaultable() const; Nullability getNullability() const; @@ -364,6 +366,7 @@ public: bool isSignature() const; bool isStruct() const; bool isArray() const; + bool isString() const; bool isBottom() const; Signature getSignature() const; diff --git a/src/wasm/literal.cpp b/src/wasm/literal.cpp index 34f3d250d..80ff47b17 100644 --- a/src/wasm/literal.cpp +++ b/src/wasm/literal.cpp @@ -71,7 +71,8 @@ Literal::Literal(const uint8_t init[16]) : type(Type::v128) { Literal::Literal(std::shared_ptr<GCData> gcData, HeapType type) : gcData(gcData), type(type, NonNullable) { - // The type must be a proper type for GC data. + // The type must be a proper type for GC data: either a struct, array, or + // string; or a null. assert((isData() && gcData) || (type.isBottom() && !gcData)); } @@ -577,7 +578,20 @@ std::ostream& operator<<(std::ostream& o, Literal literal) { case HeapType::struct_: case HeapType::array: WASM_UNREACHABLE("invalid type"); - case HeapType::string: + case HeapType::string: { + auto data = literal.getGCData(); + if (!data) { + o << "nullstring"; + } else { + o << "string(\""; + for (auto c : data->values) { + // TODO: more than ascii + o << char(c.getInteger()); + } + o << "\")"; + } + break; + } case HeapType::stringview_wtf8: case HeapType::stringview_wtf16: case HeapType::stringview_iter: diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp index 2c26f0e21..cebb1b489 100644 --- a/src/wasm/wasm-type.cpp +++ b/src/wasm/wasm-type.cpp @@ -897,7 +897,8 @@ bool Type::isFunction() const { bool Type::isData() const { if (isBasic()) { - return false; + // The only basic type that is considered data is a string. + return isString(); } else { auto* info = getTypeInfo(*this); return info->isRef() && info->ref.heapType.isData(); @@ -924,6 +925,8 @@ bool Type::isStruct() const { return isRef() && getHeapType().isStruct(); } bool Type::isArray() const { return isRef() && getHeapType().isArray(); } +bool Type::isString() const { return isRef() && getHeapType().isString(); } + bool Type::isDefaultable() const { // A variable can get a default value if its type is concrete (unreachable // and none have no values, hence no default), and if it's a reference, it @@ -1267,7 +1270,7 @@ bool HeapType::isFunction() const { bool HeapType::isData() const { if (isBasic()) { - return id == struct_ || id == array; + return id == struct_ || id == array || id == string; } else { return getHeapTypeInfo(*this)->isData(); } @@ -1297,6 +1300,8 @@ bool HeapType::isArray() const { } } +bool HeapType::isString() const { return *this == HeapType::string; } + bool HeapType::isBottom() const { if (isBasic()) { switch (getBasic()) { @@ -1672,7 +1677,9 @@ bool SubTyper::isSubType(HeapType a, HeapType b) { case HeapType::any: return a.getBottom() == HeapType::none; case HeapType::eq: - return a == HeapType::i31 || a == HeapType::none || a.isData(); + return a == HeapType::i31 || a == HeapType::none || + a == HeapType::struct_ || a == HeapType::array || a.isStruct() || + a.isArray(); case HeapType::i31: return a == HeapType::none; case HeapType::struct_: |