Skip to content

Commit dd3fbea

Browse files
committed
Store primitive GC arrays as raw byte buffers
GCData previously represented all allocations using a vector of Literals. Storing numeric array elements this way introduces unnecessary memory overhead and prevents efficient byte-level operations. Represent primitive numeric GC arrays using a raw byte buffer in GCData while preserving Literals storage for reference arrays and structs.
1 parent 876015e commit dd3fbea

6 files changed

Lines changed: 249 additions & 167 deletions

File tree

src/literal.h

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include <array>
2121
#include <iostream>
22+
#include <variant>
2223

2324
#include "support/bits.h"
2425
#include "support/hash.h"
@@ -212,7 +213,7 @@ class Literal {
212213
}
213214
}
214215

215-
static Literal makeFromMemory(void* p, Type type);
216+
static Literal makeFromMemory(const void* p, Type type);
216217

217218
static Literal makeSignedMin(Type type) {
218219
switch (type.getBasic()) {
@@ -784,28 +785,63 @@ std::ostream& operator<<(std::ostream& o, wasm::Literals literals);
784785
// A GC Struct, Array, or String is a set of values with a type saying how it
785786
// should be interpreted.
786787
struct GCData {
787-
// The element or field values.
788-
Literals values;
788+
Type type;
789+
790+
// The element or field values. Primitive numeric arrays use raw byte buffers
791+
// (std::vector<uint8_t>), while reference arrays, structs, strings, and other
792+
// reference allocations use Literals.
793+
std::variant<std::vector<uint8_t>, Literals> storage;
789794

790795
// The descriptor, if it exists, or null.
791796
Literal desc;
792797

793-
GCData(Literals&& values,
798+
GCData(Type type,
799+
Literals&& values,
800+
const Literal& desc = Literal::makeNull(HeapType::none))
801+
: type(type), storage(std::move(values)), desc(desc) {}
802+
803+
GCData(Type type,
804+
std::vector<uint8_t>&& data,
794805
const Literal& desc = Literal::makeNull(HeapType::none))
795-
: values(std::move(values)), desc(desc) {}
806+
: type(type), storage(std::move(data)), desc(desc) {}
807+
808+
bool isRawBytes() const {
809+
return std::holds_alternative<std::vector<uint8_t>>(storage);
810+
}
811+
812+
const std::vector<uint8_t>& getRawBytes() const {
813+
return std::get<std::vector<uint8_t>>(storage);
814+
}
815+
816+
std::vector<uint8_t>& getRawBytes() {
817+
return std::get<std::vector<uint8_t>>(storage);
818+
}
819+
820+
const Literals& getLiterals() const { return std::get<Literals>(storage); }
821+
822+
Literals& getLiterals() { return std::get<Literals>(storage); }
823+
824+
size_t getNumElements() const;
825+
Literal getElement(size_t index, bool signed_ = false) const;
826+
void setElement(size_t index, Literal value);
827+
828+
static void writeField(void* p, const Field& field, Literal value);
829+
static Literal
830+
readField(const void* p, const Field& field, bool signed_ = false);
796831
};
797832

798833
inline bool Literal::hasExternPayload() const {
799834
if (isNull()) {
800835
return false;
801836
}
802837
assert(type.getHeapType().isMaybeShared(HeapType::ext));
803-
return gcData->values[0].type == Type::i32;
838+
return !gcData->getLiterals().empty() &&
839+
gcData->getLiterals()[0].type == Type::i32;
804840
}
805841

806842
inline int32_t Literal::getExternPayload() const {
807843
assert(hasExternPayload());
808-
return gcData->values[0].geti32();
844+
return gcData->getLiterals()[0].geti32();
809845
}
810846

811847
} // namespace wasm
@@ -869,7 +905,7 @@ template<> struct hash<wasm::Literal> {
869905
return digest;
870906
}
871907
if (a.type.isString()) {
872-
auto& values = a.getGCData()->values;
908+
auto& values = a.getGCData()->getLiterals();
873909
wasm::rehash(digest, values.size());
874910
for (auto c : values) {
875911
wasm::rehash(digest, c.getInteger());

src/passes/Precompute.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1103,7 +1103,7 @@ struct Precompute
11031103
// string.
11041104
bool isValidUTF16Literal(const Literal& value) {
11051105
bool expectLowSurrogate = false;
1106-
for (auto& v : value.getGCData()->values) {
1106+
for (auto& v : value.getGCData()->getLiterals()) {
11071107
auto c = v.getInteger();
11081108
if (c >= 0xDC00 && c <= 0xDFFF) {
11091109
if (expectLowSurrogate) {

src/tools/wasm-ctor-eval.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -912,7 +912,6 @@ struct CtorEvalExternalInterface : EvallingModuleRunner::ExternalInterface {
912912
} else {
913913
// This is the first usage of this data. Generate a struct.new /
914914
// array.new for it.
915-
auto& values = data->values;
916915
std::vector<Expression*> args;
917916

918917
// The initial values for this allocation may themselves be GC
@@ -934,8 +933,8 @@ struct CtorEvalExternalInterface : EvallingModuleRunner::ExternalInterface {
934933
definingGlobals[data] = DefiningGlobalInfo{definingGlobalName, type};
935934
}
936935

937-
for (auto& value : values) {
938-
auto* serialized = getSerialization(value);
936+
for (size_t i = 0; i < data->getNumElements(); i++) {
937+
auto* serialized = getSerialization(data->getElement(i));
939938
if (!serialized) {
940939
return nullptr;
941940
}

src/wasm-builder.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1504,7 +1504,7 @@ class Builder {
15041504
// The string is already WTF-16, but we need to convert from `Literals` to
15051505
// actual string.
15061506
std::stringstream wtf16;
1507-
for (auto c : value.getGCData()->values) {
1507+
for (auto c : value.getGCData()->getLiterals()) {
15081508
auto u = c.getInteger();
15091509
assert(u < 0x10000);
15101510
wtf16 << uint8_t(u & 0xFF);

0 commit comments

Comments
 (0)