/*
* Copyright 2024 WebAssembly Community Group participants
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef wasm_wasm_type_shape_h
#define wasm_wasm_type_shape_h
#include <functional>
#include <list>
#include <unordered_map>
#include <vector>
#include "wasm-features.h"
#include "wasm-type.h"
namespace wasm {
// Provides hashing and equality comparison for a sequence of types. The hashing
// and equality differentiate the top-level structure of each type in the
// sequence and the equality of referenced heap types that are not in the
// recursion group, but for references to types that are in the recursion group,
// it considers only the index of the referenced type within the group. That
// means that recursion groups containing different types can compare and hash
// as equal as long as their internal structure and external references are the
// same.
struct RecGroupShape {
const std::vector<HeapType>& types;
// Depending on the feature set, some types may be generalized when they are
// written out. Take the features into account to ensure our comparisons
// account for the rec groups that will ultimately be written.
const FeatureSet features;
RecGroupShape(const std::vector<HeapType>& types, const FeatureSet features)
: types(types), features(features) {}
bool operator==(const RecGroupShape& other) const;
bool operator!=(const RecGroupShape& other) const {
return !(*this == other);
}
};
// Extends `RecGroupShape` with ordered comparison of rec group structures.
// Requires the user to supply a global ordering on heap types to be able to
// compare differing references to external types.
// TODO: This can all be upgraded to use C++20 three-way comparisons.
struct ComparableRecGroupShape : RecGroupShape {
std::function<bool(HeapType, HeapType)> less;
ComparableRecGroupShape(const std::vector<HeapType>& types,
FeatureSet features,
std::function<bool(HeapType, HeapType)> less)
: RecGroupShape(types, features), less(less) {}
bool operator<(const RecGroupShape& other) const;
bool operator>(const RecGroupShape& other) const;
bool operator<=(const RecGroupShape& other) const { return !(*this > other); }
bool operator>=(const RecGroupShape& other) const { return !(*this < other); }
};
} // namespace wasm
namespace std {
template<> class hash<wasm::RecGroupShape> {
public:
size_t operator()(const wasm::RecGroupShape& shape) const;
};
} // namespace std
namespace wasm {
// Provides an infinite sequence of possible brand types, prioritizing those
// with the most compact encoding.
struct BrandTypeIterator {
static constexpr Index optionCount = 18;
static constexpr std::array<Field, optionCount> fieldOptions = {{
Field(Field::i8, Mutable),
Field(Field::i16, Mutable),
Field(Type::i32, Mutable),
Field(Type::i64, Mutable),
Field(Type::f32, Mutable),
Field(Type::f64, Mutable),
Field(Type(HeapType::any, Nullable), Mutable),
Field(Type(HeapType::func, Nullable), Mutable),
Field(Type(HeapType::ext, Nullable), Mutable),
Field(Type(HeapType::none, Nullable), Mutable),
Field(Type(HeapType::nofunc, Nullable), Mutable),
Field(Type(HeapType::noext, Nullable), Mutable),
Field(Type(HeapType::any, NonNullable), Mutable),
Field(Type(HeapType::func, NonNullable), Mutable),
Field(Type(HeapType::ext, NonNullable), Mutable),
Field(Type(HeapType::none, NonNullable), Mutable),
Field(Type(HeapType::nofunc, NonNullable), Mutable),
Field(Type(HeapType::noext, NonNullable), Mutable),
}};
struct FieldInfo {
uint8_t index = 0;
bool immutable = false;
operator Field() const {
auto field = fieldOptions[index];
if (immutable) {
field.mutable_ = Immutable;
}
return field;
}
bool advance() {
if (!immutable) {
immutable = true;
return true;
}
immutable = false;
index = (index + 1) % optionCount;
return index != 0;
}
};
bool useArray = false;
std::vector<FieldInfo> fields;
HeapType operator*() const {
if (useArray) {
return Array(fields[0]);
}
return Struct(std::vector<Field>(fields.begin(), fields.end()));
}
BrandTypeIterator& operator++() {
for (Index i = fields.size(); i > 0; --i) {
if (fields[i - 1].advance()) {
return *this;
}
}
if (useArray) {
useArray = false;
return *this;
}
fields.emplace_back();
useArray = fields.size() == 1;
return *this;
}
};
// A set of unique rec group shapes. Upon inserting a new group of types, if it
// has the same shape as a previously inserted group, the types will be rebuilt
// with an extra brand type at the end of the group that differentiates it from
// previous group.
struct UniqueRecGroups {
using Groups = std::list<std::vector<HeapType>>;
Groups groups;
std::unordered_map<RecGroupShape, RecGroup> shapes;
FeatureSet features;
UniqueRecGroups(FeatureSet features) : features(features) {}
// Insert a rec group. If it is already unique, return the original group.
// Otherwise rebuild the group to make it unique and return the rebuilt group.
RecGroup insert(RecGroup group);
// If the group is unique, insert it and return the types. Otherwise, return
// the types that already have this shape.
RecGroup insertOrGet(RecGroup group);
};
} // namespace wasm
#endif // wasm_wasm_type_shape_h