Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
11 changes: 8 additions & 3 deletions src/wasm-type.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@
namespace wasm {

class Type {
uint32_t id;
// enough for the limit of 1000 function arguments
static constexpr unsigned sizeBits = 10;
Copy link
Member

Choose a reason for hiding this comment

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

perhaps SizeBits (capitalize?) or SIZE_BITS? we seem to use both of those, and to leave lowerCaps style names for actual variables.

static constexpr unsigned unknownSize = (1 << sizeBits) - 1;
unsigned id : 32 - sizeBits;
unsigned _size : sizeBits;
void init(const std::vector<Type>&);

public:
Expand All @@ -50,16 +54,17 @@ class Type {
Type() = default;

// ValueType can be implicitly upgraded to Type
constexpr Type(ValueType id) : id(id){};
constexpr Type(ValueType id) : id(id), _size(id == none ? 0 : 1){};

// But converting raw uint32_t is more dangerous, so make it explicit
constexpr explicit Type(uint32_t id) : id(id){};
constexpr explicit Type(uint32_t id) : id(id), _size(unknownSize){};
Copy link
Member

Choose a reason for hiding this comment

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

why is the size unknown here, mustn't the id be valid? if so shouldn't we get the size from the caller too?

Copy link
Member Author

Choose a reason for hiding this comment

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

I wanted to avoid taking the lock to look up the size, but idk how important that is. I'll investigate further.

Copy link
Member Author

Choose a reason for hiding this comment

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

It turns out that this constructor is primarily used in the C API to convert from BinaryenType aka uint32_t to a Type. Right now BinaryenType values are type IDs, but we could pack the size into them as well. The downside of that would be that it would make the conversion from BinaryenType to Type even more dangerous and error prone and generally add a maintenance burden to the C API. I think it's probably better to take the lock and look up the size in the constructor.


// Construct from lists of elementary types
Type(std::initializer_list<Type> types);
explicit Type(const std::vector<Type>& types);

// Accessors
size_t size();
size_t size() const;
const std::vector<Type>& expand() const;

Expand Down
73 changes: 50 additions & 23 deletions src/wasm/wasm-type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ void Type::init(const std::vector<Type>& types) {
}
#endif

if (types.size() >= unknownSize) {
WASM_UNREACHABLE("Type too large");
}
_size = types.size();

auto lookup = [&]() {
auto indexIt = indices.find(types);
if (indexIt != indices.end()) {
Expand All @@ -115,6 +120,9 @@ void Type::init(const std::vector<Type>& types) {
if (lookup()) {
return;
}
if (typeLists.size() >= (1 << (32 - sizeBits))) {
WASM_UNREACHABLE("Too many types!");
}
id = typeLists.size();
typeLists.push_back(std::make_unique<std::vector<Type>>(types));
indices[types] = id;
Expand All @@ -125,7 +133,19 @@ Type::Type(std::initializer_list<Type> types) { init(types); }

Type::Type(const std::vector<Type>& types) { init(types); }

size_t Type::size() const { return expand().size(); }
size_t Type::size() {
if (_size == unknownSize) {
_size = expand().size();
}
return _size;
}

size_t Type::size() const {
if (_size == unknownSize) {
return expand().size();
Copy link
Member

Choose a reason for hiding this comment

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

this is the bad old path? i hope we can avoid it as mentioned in another comment, by requiring the size to always be known.

Copy link
Member

Choose a reason for hiding this comment

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

After discussion, this seems ok for now but we should figure this out later.

}
return _size;
}

const std::vector<Type>& Type::expand() const {
std::shared_lock<std::shared_timed_mutex> lock(mutex);
Expand All @@ -146,28 +166,34 @@ bool Type::operator<(const Type& other) const {

unsigned Type::getByteSize() const {
// TODO: alignment?
unsigned size = 0;
for (auto t : expand()) {
auto getSingleByteSize = [](Type t) {
switch (t.getSingle()) {
case Type::i32:
case Type::f32:
size += 4;
break;
return 4;
case Type::i64:
case Type::f64:
size += 8;
break;
return 8;
case Type::v128:
size += 16;
break;
return 16;
case Type::funcref:
case Type::anyref:
case Type::nullref:
case Type::exnref:
case Type::none:
case Type::unreachable:
WASM_UNREACHABLE("invalid type");
break;
}
WASM_UNREACHABLE("invalid type");
};

if (isSingle()) {
return getSingleByteSize(*this);
}

unsigned size = 0;
for (auto t : expand()) {
size += getSingleByteSize(t);
}
return size;
}
Expand Down Expand Up @@ -197,25 +223,26 @@ Type Type::reinterpret() const {
}

FeatureSet Type::getFeatures() const {
FeatureSet feats = FeatureSet::MVP;
const auto& elements = expand();
if (elements.size() > 1) {
feats = FeatureSet::Multivalue;
}
for (Type t : elements) {
auto getSingleFeatures = [](Type t) {
switch (t.getSingle()) {
case Type::v128:
feats |= FeatureSet::SIMD;
break;
return FeatureSet::SIMD;
case Type::anyref:
feats |= FeatureSet::ReferenceTypes;
break;
return FeatureSet::ReferenceTypes;
case Type::exnref:
feats |= FeatureSet::ExceptionHandling;
break;
return FeatureSet::ExceptionHandling;
default:
break;
return FeatureSet::MVP;
}
};

if (isSingle()) {
return getSingleFeatures(*this);
}

FeatureSet feats = FeatureSet::Multivalue;
for (Type t : expand()) {
feats |= getSingleFeatures(t);
}
return feats;
}
Expand Down
8 changes: 4 additions & 4 deletions src/wasm/wasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -992,11 +992,11 @@ Index Function::getLocalIndex(Name name) {
Index Function::getVarIndexBase() { return sig.params.size(); }

Type Function::getLocalType(Index index) {
const std::vector<Type>& params = sig.params.expand();
if (index < params.size()) {
return params[index];
auto numParams = sig.params.size();
if (index < numParams) {
return sig.params.expand()[index];
Copy link
Member

Choose a reason for hiding this comment

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

this also seems like a bad path that we should avoid, as each expand takes a lock?

Copy link
Member Author

Choose a reason for hiding this comment

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

There's no way to avoid this right now because fetching an element out of a Type requires accessing the underlying vector. Thread-local caches could help, but that would be future work.

Copy link
Member

Choose a reason for hiding this comment

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

Sounds good.

} else if (isVar(index)) {
return vars[index - params.size()];
return vars[index - numParams];
} else {
WASM_UNREACHABLE("invalid local index");
}
Expand Down