amdgpu: simplify evaluator

This commit is contained in:
DH 2025-12-02 19:26:31 +03:00
parent d361dfcaf0
commit de7c40d330
3 changed files with 401 additions and 544 deletions

View file

@ -453,20 +453,16 @@ Cache::ShaderResources::eval(ir::InstructionId instId,
case 8:
result = readPointer<std::uint64_t>(address);
break;
case 12:
result = readPointer<u32vec3>(address);
break;
case 16:
result = readPointer<u32vec4>(address);
break;
case 32:
result = readPointer<std::array<std::uint32_t, 8>>(address);
break;
case 64:
result = readPointer<std::array<std::uint32_t, 16>>(address);
break;
default:
rx::die("unexpected pointer load size {}", loadSize);
rx::dieIf(loadSize % sizeof(std::uint32_t), "unaligned load size {}",
loadSize);
for (std::int32_t offset = 0; offset < loadSize;
offset += sizeof(std::uint32_t)) {
result.add(readPointer<std::uint32_t>(address + offset));
}
break;
}
return result;
@ -2368,7 +2364,7 @@ Cache::Shader Cache::GraphicsTag::getShader(
std::bit_cast<std::uint32_t>(context.paClVports[slot.data].zScale);
break;
case gcn::ConfigType::PsInputVGpr:
if (slot.data > psVgprInput.size()) {
if (slot.data >= psVgprInput.size()) {
configPtr[index] = ~0;
} else {
configPtr[index] = std::bit_cast<std::uint32_t>(psVgprInput[slot.data]);

View file

@ -2,33 +2,156 @@
#include "Vector.hpp"
#include "ir/Value.hpp"
#include "rx/align.hpp"
#include "rx/die.hpp"
#include <algorithm>
#include <array>
#include <cstdint>
#include <variant>
#include <cstring>
#include <tuple>
#include <type_traits>
#include <utility>
namespace shader::eval {
struct Value {
using Storage = std::variant<
std::nullptr_t, std::int8_t, std::int16_t, std::int32_t, std::int64_t,
std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t, float16_t,
float32_t, float64_t, u8vec2, u8vec3, u8vec4, i8vec2, i8vec3, i8vec4,
u16vec2, u16vec3, u16vec4, i16vec2, i16vec3, i16vec4, u32vec2, u32vec3,
u32vec4, i32vec2, i32vec3, i32vec4, u64vec2, u64vec3, u64vec4, i64vec2,
i64vec3, i64vec4, f32vec2, f32vec3, f32vec4, f64vec2, f64vec3, f64vec4,
f16vec2, f16vec3, f16vec4, bool, bvec2, bvec3, bvec4,
std::array<uint32_t, 8>, std::array<std::uint32_t, 16>>;
static constexpr auto StorageSize = std::variant_size_v<Storage>;
Storage storage;
using Types = std::tuple<std::nullptr_t, bool, int8_t, int16_t, int32_t,
int64_t, uint8_t, uint16_t, uint32_t, uint64_t,
float16_t, float32_t, float64_t>;
static constexpr auto kMaxElementCount = 32;
static constexpr auto kMaxTypeAlignment = [] {
auto impl = []<std::size_t... I>(std::index_sequence<I...>) {
std::size_t result = 1;
((result = std::max(alignof(std::tuple_element_t<I, Types>), result)),
...);
return result;
};
return impl(std::make_index_sequence<std::tuple_size_v<Types>>{});
}();
static constexpr auto kMaxTypeSize = [] {
auto impl = []<std::size_t... I>(std::index_sequence<I...>) {
std::size_t result = 1;
((result = std::max(sizeof(std::tuple_element_t<I, Types>), result)),
...);
return result;
};
return rx::alignUp(
impl(std::make_index_sequence<std::tuple_size_v<Types>>{}),
kMaxTypeAlignment);
}();
template <typename T> static consteval std::size_t getTypeIndex() {
auto impl = []<std::size_t... I>(std::index_sequence<I...>) {
std::size_t result = -1;
((result =
std::is_same_v<T, std::tuple_element_t<I, Types>> ? I : result),
...);
return result;
};
return impl(std::make_index_sequence<std::tuple_size_v<Types>>{});
}
static constexpr auto StorageSize = std::tuple_size_v<Types>;
template <typename T, typename RT = std::invoke_result_t<
T, std::span<std::tuple_element_t<0, Types>>>>
RT visit(T &&cb) const {
static constexpr auto table = [] {
std::array<RT (*)(void *cb, const char *data, std::uint32_t count),
std::tuple_size_v<Types>>
result;
auto impl = [&]<std::size_t... I>(std::index_sequence<I...>) {
((result[I] = [](void *cb, const char *data,
std::uint32_t count) -> RT {
return (*reinterpret_cast<T *>(cb))(std::span(
reinterpret_cast<const std::tuple_element_t<I, Types> *>(data),
count));
}),
...);
};
impl(std::make_index_sequence<std::tuple_size_v<Types>>{});
return result;
}();
return table[mTypeIndex](&cb, mData, mCount);
}
[[nodiscard]] std::size_t getConstituentSize() const {
return visit([](auto values) -> std::size_t {
if constexpr (std::is_same_v<decltype(values[0]), std::nullptr_t>) {
return 0;
} else {
return sizeof(values[0]);
}
});
}
explicit operator bool() const { return !empty(); }
bool empty() const { return storage.index() == 0; }
[[nodiscard]] bool empty() const {
return mCount == 0 || mTypeIndex == getTypeIndex<std::nullptr_t>();
}
[[nodiscard]] std::size_t size() const { return mCount; }
Value() : storage(nullptr) {}
Value() = default;
template <typename FT, typename... T>
requires(getTypeIndex<FT>() < std::tuple_size_v<Types> &&
(std::is_same_v<FT, T> && ...))
Value(FT firstValue, T... value) {
add(firstValue);
(add(value), ...);
}
void add(const Value &value) {
if (value.mCount != 1) {
mTypeIndex = getTypeIndex<std::nullptr_t>();
mCount = 1;
return;
}
if (mCount == 0) {
mTypeIndex = value.mTypeIndex;
mCount = 1;
std::memcpy(mData, value.mData, kMaxTypeSize);
return;
}
rx::dieIf(mCount >= kMaxElementCount, "storage too small");
if (mTypeIndex != value.mTypeIndex) {
mTypeIndex = getTypeIndex<std::nullptr_t>();
mCount = 1;
return;
}
auto index = mCount++;
auto elemSize = getConstituentSize();
std::memcpy(mData + elemSize * index, value.mData, kMaxTypeSize);
}
template <typename T>
Value(T &&value)
requires requires { Storage(std::forward<T>(value)); }
: storage(std::forward<T>(value)) {}
requires(getTypeIndex<T>() < std::tuple_size_v<Types>)
void add(T value) {
if (mCount == 0) {
mTypeIndex = getTypeIndex<T>();
mCount = 1;
*getData<T>() = value;
return;
}
rx::dieIf(mCount >= kMaxElementCount, "storage too small");
if (mTypeIndex != getTypeIndex<T>()) {
mTypeIndex = getTypeIndex<std::nullptr_t>();
mCount = 1;
return;
}
getData<T>()[mCount++] = value;
}
static Value compositeConstruct(ir::Value type,
std::span<const Value> constituents);
@ -52,21 +175,43 @@ struct Value {
std::optional<std::int64_t> sExtScalar() const;
template <typename T>
requires requires { std::get<T>(storage); }
T get() const {
return std::get<T>(storage);
requires(getTypeIndex<T>() < std::tuple_size_v<Types>)
[[nodiscard]] const T &get(std::size_t index = 0) const {
rx::dieIf(mTypeIndex != getTypeIndex<T>(),
"eval::Value::get(): invalid type");
rx::dieIf(index >= std::tuple_size_v<Types>,
"eval::Value::get(): invalid index");
return getData<T>()[index];
}
template <auto I>
requires(I < std::tuple_size_v<Types>)
[[nodiscard]] const std::tuple_element_t<I, Types> &
get(std::size_t index = 0) const {
rx::dieIf(mTypeIndex != I, "eval::Value::get(): invalid type");
rx::dieIf(index >= std::tuple_size_v<Types>,
"eval::Value::get(): invalid index");
return getData<std::tuple_element_t<I, Types>>()[index];
}
template <typename T>
requires requires { std::get<T>(storage); }
std::optional<T> as() const {
if (auto result = std::get_if<T>(&storage)) {
return *result;
requires(getTypeIndex<T>() < std::tuple_size_v<Types>)
[[nodiscard]] std::optional<T> as(std::size_t index = 0) const {
rx::dieIf(index >= std::tuple_size_v<Types>,
"eval::Value::as(): invalid index");
if (mTypeIndex == getTypeIndex<T>()) {
return getData<T>()[index];
}
return std::nullopt;
}
template <typename T>
requires(getTypeIndex<T>() < std::tuple_size_v<Types>)
[[nodiscard]] bool is() const {
return mTypeIndex == getTypeIndex<T>();
}
Value operator+(const Value &rhs) const;
Value operator-(const Value &rhs) const;
Value operator*(const Value &rhs) const;
@ -89,5 +234,17 @@ struct Value {
Value operator-() const;
Value operator~() const;
Value operator!() const;
std::size_t index() const { return mTypeIndex; }
private:
template <typename T> T *getData() { return reinterpret_cast<T *>(mData); }
template <typename T> const T *getData() const {
return reinterpret_cast<const T *>(mData);
}
std::uint32_t mTypeIndex = 0;
std::uint32_t mCount = 0;
alignas(kMaxTypeAlignment) char mData[kMaxTypeSize * kMaxElementCount];
};
} // namespace shader::eval

View file

@ -2,132 +2,15 @@
#include "dialect.hpp"
#include "ir.hpp"
#include <cmath>
#include <concepts>
#include <type_traits>
using namespace shader;
template <typename Cond, typename... Args> consteval bool testVisitCond() {
if constexpr (std::is_same_v<Cond, void>) {
return true;
} else {
return Cond{}(std::remove_cvref_t<Args>{}...);
}
};
template <typename Cond, std::size_t U> consteval bool testVisitCond() {
if constexpr (U >= eval::Value::StorageSize) {
return false;
} else if constexpr (std::is_same_v<Cond, void>) {
return true;
} else {
return Cond{}(std::variant_alternative_t<U, eval::Value::Storage>{});
}
};
template <typename Cond = void, size_t I = 0>
constexpr eval::Value visitImpl(const eval::Value &variant, auto &&fn) {
#define DEFINE_CASE(N) \
case I + N: \
if constexpr (testVisitCond<Cond, I + N>()) { \
return std::forward<decltype(fn)>(fn)(std::get<I + N>(variant.storage)); \
} else { \
return {}; \
}
switch (variant.storage.index()) {
DEFINE_CASE(0);
DEFINE_CASE(1);
DEFINE_CASE(2);
DEFINE_CASE(3);
DEFINE_CASE(4);
DEFINE_CASE(5);
DEFINE_CASE(6);
DEFINE_CASE(7);
DEFINE_CASE(8);
DEFINE_CASE(9);
DEFINE_CASE(10);
DEFINE_CASE(11);
DEFINE_CASE(12);
DEFINE_CASE(13);
DEFINE_CASE(14);
DEFINE_CASE(15);
DEFINE_CASE(16);
DEFINE_CASE(17);
DEFINE_CASE(18);
DEFINE_CASE(19);
DEFINE_CASE(20);
DEFINE_CASE(21);
DEFINE_CASE(22);
DEFINE_CASE(23);
DEFINE_CASE(24);
DEFINE_CASE(25);
DEFINE_CASE(26);
DEFINE_CASE(27);
DEFINE_CASE(28);
DEFINE_CASE(29);
DEFINE_CASE(30);
DEFINE_CASE(31);
DEFINE_CASE(32);
DEFINE_CASE(33);
DEFINE_CASE(34);
DEFINE_CASE(35);
DEFINE_CASE(36);
DEFINE_CASE(37);
DEFINE_CASE(38);
DEFINE_CASE(39);
DEFINE_CASE(40);
DEFINE_CASE(41);
DEFINE_CASE(42);
DEFINE_CASE(43);
DEFINE_CASE(44);
DEFINE_CASE(45);
DEFINE_CASE(46);
DEFINE_CASE(47);
DEFINE_CASE(48);
DEFINE_CASE(49);
DEFINE_CASE(50);
DEFINE_CASE(51);
DEFINE_CASE(52);
DEFINE_CASE(53);
DEFINE_CASE(54);
DEFINE_CASE(55);
DEFINE_CASE(56);
DEFINE_CASE(57);
DEFINE_CASE(58);
DEFINE_CASE(59);
DEFINE_CASE(60);
DEFINE_CASE(61);
DEFINE_CASE(62);
DEFINE_CASE(63);
}
#undef DEFINE_CASE
constexpr auto NextIndex = I + 64;
if constexpr (NextIndex < eval::Value::StorageSize) {
return visitImpl<Cond, NextIndex>(std::forward<decltype(fn)>(fn),
std::forward<decltype(variant)>(variant));
}
return {};
}
template <typename Cond = void, typename Cb>
constexpr eval::Value visitScalarType(ir::Value type, Cb &&cb)
requires requires {
{ std::forward<Cb>(cb)(int{}) } -> std::same_as<eval::Value>;
}
{
auto invoke = [&](auto type) -> eval::Value {
if constexpr (testVisitCond<Cond, std::remove_cvref_t<decltype(type)>>()) {
return std::forward<Cb>(cb)(type);
}
return {};
};
template <typename T>
constexpr bool invokeWithType(ir::Value type, T &&invoke) {
if (type == ir::spv::OpTypeBool) {
return invoke(bool{});
invoke(bool{});
return true;
}
if (type == ir::spv::OpTypeInt) {
@ -136,213 +19,106 @@ constexpr eval::Value visitScalarType(ir::Value type, Cb &&cb)
switch (*type.getOperand(0).getAsInt32()) {
case 8:
if (isSigned) {
return invoke(std::int8_t{});
invoke(std::int8_t{});
return true;
}
return invoke(std::uint8_t{});
invoke(std::uint8_t{});
return true;
case 16:
if (isSigned) {
return invoke(std::int16_t{});
invoke(std::int16_t{});
return true;
}
return invoke(std::uint16_t{});
invoke(std::uint16_t{});
return true;
case 32:
if (isSigned) {
return invoke(std::int32_t{});
invoke(std::int32_t{});
return true;
}
return invoke(std::uint32_t{});
invoke(std::uint32_t{});
return true;
case 64:
if (isSigned) {
return invoke(std::int64_t{});
invoke(std::int64_t{});
return true;
}
return invoke(std::uint64_t{});
invoke(std::uint64_t{});
return true;
}
return {};
return false;
}
if (type == ir::spv::OpTypeFloat) {
switch (*type.getOperand(0).getAsInt32()) {
case 16:
return invoke(shader::float16_t{});
invoke(shader::float16_t{});
return true;
case 32:
return invoke(shader::float32_t{});
invoke(shader::float32_t{});
return true;
case 64:
return invoke(shader::float64_t{});
invoke(shader::float64_t{});
return true;
}
return {};
return false;
}
return {};
}
template <typename Cond = void, typename Cb>
constexpr eval::Value visitType(ir::Value type, Cb &&cb)
requires requires {
{ std::forward<Cb>(cb)(int{}) } -> std::same_as<eval::Value>;
}
{
if (type == ir::spv::OpTypeInt || type == ir::spv::OpTypeFloat ||
type == ir::spv::OpTypeBool) {
return visitScalarType<Cond>(type, cb);
}
auto invoke = [&](auto type) -> eval::Value {
if constexpr (testVisitCond<Cond, std::remove_cvref_t<decltype(type)>>()) {
return std::forward<Cb>(cb)(type);
} else {
return {};
}
};
if (type == ir::spv::OpTypeVector) {
switch (*type.getOperand(1).getAsInt32()) {
case 2:
return visitScalarType(
type.getOperand(0).getAsValue(),
[&]<typename T>(T) { return invoke(shader::Vector<T, 2>{}); });
case 3:
return visitScalarType(
type.getOperand(0).getAsValue(),
[&]<typename T>(T) { return invoke(shader::Vector<T, 3>{}); });
case 4:
return visitScalarType(
type.getOperand(0).getAsValue(),
[&]<typename T>(T) { return invoke(shader::Vector<T, 4>{}); });
}
return {};
return invokeWithType(type.getOperand(0).getAsValue(),
std::forward<T>(invoke));
}
return {};
return false;
}
template <typename Cond = void, typename Cb>
eval::Value visit(const eval::Value &value, Cb &&cb) {
using VisitCond = decltype([](auto &&storage) {
using T = std::remove_cvref_t<decltype(storage)>;
if constexpr (std::is_same_v<T, std::nullptr_t>) {
return false;
} else {
return testVisitCond<Cond, T>();
}
static constexpr std::size_t getIrTypeIndex(ir::Value type) {
std::size_t result = 0;
invokeWithType(type, [&](auto type) {
result = eval::Value::getTypeIndex<decltype(type)>();
});
return visitImpl<VisitCond>(value, std::forward<Cb>(cb));
return result;
}
template <typename Cb>
eval::Value visit2(auto &&cond, const eval::Value &value, Cb &&cb) {
if constexpr (cond()) {
return visitImpl(value, std::forward<Cb>(cb));
} else {
return {};
static constexpr std::size_t getIrTypeConstituents(ir::Value type) {
if (type == ir::spv::OpTypeVector) {
return *type.getOperand(1).getAsInt32();
}
return 1;
}
template <typename ValueCond = void, typename TypeVisitCond = void,
typename TypeValueVisitCond = void, typename Cb>
eval::Value visitWithType(const eval::Value &value, ir::Value type, Cb &&cb) {
using ValueVisitCond = decltype([](auto storage) {
if constexpr (std::is_same_v<decltype(storage), std::nullptr_t>) {
return false;
} else {
return testVisitCond<ValueCond, decltype(storage)>();
}
});
return visitImpl<ValueVisitCond>(value, [&](auto &&value) -> eval::Value {
return visitType<TypeVisitCond>(type, [&](auto type) -> eval::Value {
if constexpr (testVisitCond<TypeValueVisitCond, decltype(type),
decltype(value)>()) {
return std::forward<Cb>(cb)(type, value);
} else {
return {};
}
});
});
}
namespace {
template <typename T> struct ComponentTypeImpl {
using type = T;
};
template <typename T, std::size_t N> struct ComponentTypeImpl<Vector<T, N>> {
using type = T;
};
template <typename T, std::size_t N>
struct ComponentTypeImpl<std::array<T, N>> {
using type = T;
};
template <typename T> struct MakeSignedImpl {
using type = std::make_signed_t<T>;
};
template <typename T, std::size_t N> struct MakeSignedImpl<Vector<T, N>> {
using type = Vector<std::make_signed_t<T>, N>;
};
template <typename T> struct MakeUnsignedImpl {
using type = std::make_unsigned_t<T>;
};
template <typename T, std::size_t N> struct MakeUnsignedImpl<Vector<T, N>> {
using type = Vector<std::make_unsigned_t<T>, N>;
};
} // namespace
template <typename T> using ComponentType = typename ComponentTypeImpl<T>::type;
template <typename T> using MakeSigned = typename MakeSignedImpl<T>::type;
template <typename T> using MakeUnsigned = typename MakeUnsignedImpl<T>::type;
template <typename> constexpr std::size_t Components = 1;
template <typename T, std::size_t N>
constexpr std::size_t Components<Vector<T, N>> = N;
template <typename T, std::size_t N>
constexpr std::size_t Components<std::array<T, N>> = N;
template <typename> constexpr bool IsArray = false;
template <typename T, std::size_t N>
constexpr bool IsArray<std::array<T, N>> = true;
eval::Value
eval::Value::compositeConstruct(ir::Value type,
std::span<const eval::Value> constituents) {
using Cond =
decltype([](auto type) { return Components<decltype(type)> > 1; });
if (getIrTypeConstituents(type) != constituents.size()) {
return {};
}
return visitType<Cond>(type, [&](auto type) -> Value {
constexpr std::size_t N = Components<decltype(type)>;
if (N != constituents.size()) {
return {};
}
auto typeIndex = getIrTypeIndex(type);
decltype(type) result;
eval::Value result;
for (auto &elem : constituents) {
result.add(elem);
}
for (std::size_t i = 0; i < N; ++i) {
if (auto value = constituents[i].as<ComponentType<decltype(type)>>()) {
result[i] = *value;
} else {
return {};
}
}
if (result.index() != typeIndex) {
return {};
}
return result;
});
return result;
}
eval::Value eval::Value::compositeExtract(const Value &index) const {
using Cond =
decltype([](auto type) { return Components<decltype(type)> > 1; });
auto optIndexInt = index.zExtScalar();
if (!optIndexInt) {
return {};
@ -350,321 +126,249 @@ eval::Value eval::Value::compositeExtract(const Value &index) const {
auto indexInt = *optIndexInt;
return visit<Cond>(*this, [&](auto &&value) -> Value {
using ValueType = std::remove_cvref_t<decltype(value)>;
constexpr std::size_t N = Components<ValueType>;
if (indexInt >= size()) {
return {};
}
if (indexInt >= N) {
return {};
}
return value[indexInt];
});
eval::Value result;
result.mTypeIndex = mTypeIndex;
result.mCount = 1;
std::memcpy(result.mData, mData + indexInt * getConstituentSize(),
kMaxTypeSize);
return result;
}
eval::Value eval::Value::isNan() const {
using Cond = decltype([](auto type) {
return std::is_floating_point_v<ComponentType<decltype(type)>> &&
!IsArray<decltype(type)>;
});
return visit([](auto value) {
eval::Value result;
return visit<Cond>(*this, [](auto &&value) -> Value {
constexpr std::size_t N = Components<std::remove_cvref_t<decltype(value)>>;
if constexpr (N == 1) {
return std::isnan(value);
} else {
Vector<bool, N> result;
for (std::size_t i = 0; i < N; ++i) {
result[i] = std::isnan(value[i]);
if constexpr (std::is_floating_point_v<
typename decltype(value)::value_type>) {
for (std::size_t i = 0; i < value.size(); ++i) {
result.add(std::isnan(value[i]));
}
return result;
}
return result;
});
}
eval::Value eval::Value::isInf() const {
using Cond = decltype([](auto type) {
return std::is_floating_point_v<ComponentType<decltype(type)>> &&
!IsArray<decltype(type)>;
});
return visit([](auto value) {
eval::Value result;
return visit<Cond>(*this, [](auto &&value) -> Value {
constexpr std::size_t N = Components<std::remove_cvref_t<decltype(value)>>;
if constexpr (N == 1) {
return std::isinf(value);
} else {
Vector<bool, N> result;
for (std::size_t i = 0; i < N; ++i) {
result[i] = std::isinf(value[i]);
if constexpr (std::is_floating_point_v<
typename decltype(value)::value_type>) {
for (std::size_t i = 0; i < value.size(); ++i) {
result.add(std::isinf(value[i]));
}
return result;
}
return result;
});
}
eval::Value eval::Value::isFinite() const {
using Cond = decltype([](auto type) {
return std::is_floating_point_v<ComponentType<decltype(type)>>;
});
return visit([](auto value) {
eval::Value result;
return visit<Cond>(*this, [](auto &&value) -> Value {
constexpr std::size_t N = Components<std::remove_cvref_t<decltype(value)>>;
if constexpr (N == 1) {
return std::isfinite(value);
} else {
Vector<bool, N> result;
for (std::size_t i = 0; i < N; ++i) {
result[i] = std::isfinite(value[i]);
if constexpr (std::is_floating_point_v<
typename decltype(value)::value_type>) {
for (std::size_t i = 0; i < value.size(); ++i) {
result.add(std::isfinite(value[i]));
}
return result;
}
return result;
});
}
eval::Value eval::Value::makeUnsigned() const {
using Cond = decltype([](auto type) {
return std::is_integral_v<ComponentType<decltype(type)>> &&
!std::is_same_v<ComponentType<decltype(type)>, bool> &&
!IsArray<decltype(type)>;
});
return visit([](auto value) {
eval::Value result;
using value_type = typename decltype(value)::value_type;
return visit<Cond>(*this, [](auto &&value) -> Value {
constexpr std::size_t N = Components<std::remove_cvref_t<decltype(value)>>;
using T = std::make_unsigned_t<
ComponentType<std::remove_cvref_t<decltype(value)>>>;
if constexpr (N == 1) {
return static_cast<T>(value);
} else {
Vector<T, N> result;
for (std::size_t i = 0; i < N; ++i) {
result[i] = static_cast<T>(value[i]);
if constexpr (std::is_integral_v<value_type> &&
!std::is_same_v<value_type, bool>) {
for (std::size_t i = 0; i < value.size(); ++i) {
result.add(static_cast<std::make_unsigned_t<value_type>>(value[i]));
}
return result;
}
return result;
});
}
eval::Value eval::Value::makeSigned() const {
using Cond = decltype([](auto type) {
return std::is_integral_v<ComponentType<decltype(type)>> &&
!std::is_same_v<ComponentType<decltype(type)>, bool> &&
!IsArray<decltype(type)>;
});
return visit([](auto value) {
eval::Value result;
using value_type = typename decltype(value)::value_type;
return visit<Cond>(*this, [](auto &&value) -> Value {
constexpr std::size_t N = Components<std::remove_cvref_t<decltype(value)>>;
using T =
std::make_signed_t<ComponentType<std::remove_cvref_t<decltype(value)>>>;
if constexpr (N == 1) {
return static_cast<T>(value);
} else {
Vector<T, N> result;
for (std::size_t i = 0; i < N; ++i) {
result[i] = static_cast<T>(value[i]);
if constexpr (std::is_integral_v<value_type> &&
!std::is_same_v<value_type, bool>) {
for (std::size_t i = 0; i < value.size(); ++i) {
result.add(static_cast<std::make_signed_t<value_type>>(value[i]));
}
return result;
}
return result;
});
}
eval::Value eval::Value::all() const {
using Cond = decltype([](auto type) {
return std::is_same_v<ComponentType<decltype(type)>, bool> &&
(Components<decltype(type)> > 1) && !IsArray<decltype(type)>;
});
return visit<Cond>(*this, [](auto &&value) {
constexpr std::size_t N = Components<std::remove_cvref_t<decltype(value)>>;
for (std::size_t i = 0; i < N; ++i) {
if (!value[i]) {
return false;
return visit([](auto value) {
if constexpr (std::is_same_v<typename decltype(value)::value_type, bool>) {
for (std::size_t i = 0; i < value.size(); ++i) {
if (!value[i]) {
return eval::Value(false);
}
}
return eval::Value(true);
}
return true;
return eval::Value();
});
}
eval::Value eval::Value::any() const {
using Cond = decltype([](auto type) {
return std::is_same_v<ComponentType<decltype(type)>, bool> &&
(Components<decltype(type)> > 1) && !IsArray<decltype(type)>;
});
return visit<Cond>(*this, [](auto &&value) {
constexpr std::size_t N = Components<std::remove_cvref_t<decltype(value)>>;
for (std::size_t i = 0; i < N; ++i) {
if (value[i]) {
return true;
return visit([](auto value) {
if constexpr (std::is_same_v<typename decltype(value)::value_type, bool>) {
for (std::size_t i = 0; i < value.size(); ++i) {
if (value[i]) {
return eval::Value(true);
}
}
return eval::Value(false);
}
return false;
return eval::Value();
});
}
eval::Value eval::Value::select(const Value &trueValue,
const Value &falseValue) const {
using Cond = decltype([](auto type) consteval {
return std::is_same_v<ComponentType<decltype(type)>, bool> &&
!IsArray<decltype(type)>;
});
auto optCond = as<bool>();
if (!optCond) {
return {};
}
return visit<Cond>(*this, [&](auto &&cond) -> Value {
using CondType = std::remove_cvref_t<decltype(cond)>;
using TrueCond = decltype([](auto type) consteval {
return Components<decltype(type)> == Components<CondType>;
});
return visit<TrueCond>(trueValue, [&](auto &&trueValue) {
using TrueValue = std::remove_cvref_t<decltype(trueValue)>;
using FalseCond = decltype([](auto type) {
return std::is_same_v<TrueValue, std::remove_cvref_t<decltype(type)>>;
});
return visit(falseValue, [&](auto &&falseValue) -> Value {
if constexpr (std::is_same_v<TrueValue, std::remove_cvref_t<
decltype(falseValue)>>) {
constexpr std::size_t N = Components<CondType>;
if constexpr (N == 1) {
return cond ? trueValue : falseValue;
} else {
Vector<bool, N> result;
for (std::size_t i = 0; i < N; ++i) {
result[i] = cond[i] ? trueValue[i] : falseValue[i];
}
return result;
}
} else {
return {};
}
});
});
});
auto cond = *optCond;
return cond ? trueValue : falseValue;
}
eval::Value eval::Value::iConvert(ir::Value type, bool isSigned) const {
using Cond = decltype([](auto type) {
using Type = std::remove_cvref_t<decltype(type)>;
eval::Value result;
return std::is_integral_v<ComponentType<Type>> &&
!std::is_same_v<bool, ComponentType<Type>> &&
!IsArray<decltype(type)>;
});
using PairCond = decltype([](auto lhs, auto rhs) {
using Lhs = decltype(lhs);
using Rhs = decltype(rhs);
return !std::is_same_v<Lhs, Rhs> && Components<Lhs> == Components<Rhs>;
});
return visitWithType<Cond, Cond, PairCond>(
*this, type, [&](auto type, auto &&value) -> Value {
using Type = std::remove_cvref_t<decltype(type)>;
using ValueType = std::remove_cvref_t<decltype(value)>;
if (isSigned) {
return static_cast<Type>(static_cast<MakeSigned<ValueType>>(value));
} else {
return static_cast<Type>(static_cast<MakeUnsigned<ValueType>>(value));
(isSigned ? makeSigned() : makeUnsigned()).visit([&](auto value) {
invokeWithType(type, [&](auto type) {
if constexpr (std::is_integral_v<decltype(type)> &&
!std::is_same_v<bool, decltype(type)> &&
std::is_integral_v<typename decltype(value)::value_type> &&
!std::is_same_v<bool,
typename decltype(value)::value_type>) {
for (auto item : value) {
result.add(static_cast<decltype(type)>(item));
}
});
}
});
});
return result;
}
eval::Value eval::Value::fConvert(ir::Value type) const {
using Cond = decltype([](auto type) {
return std::is_floating_point_v<ComponentType<decltype(type)>> &&
!IsArray<decltype(type)>;
eval::Value result;
visit([&](auto value) {
invokeWithType(type, [&](auto type) {
if constexpr (std::is_floating_point_v<decltype(type)> &&
std::is_floating_point_v<
typename decltype(value)::value_type>) {
for (auto item : value) {
result.add(static_cast<decltype(type)>(item));
}
}
});
});
using PairCond = decltype([](auto lhs, auto rhs) {
using Lhs = decltype(lhs);
using Rhs = decltype(rhs);
return !std::is_same_v<Lhs, Rhs> && Components<Lhs> == Components<Rhs>;
});
return visitWithType<void, void, PairCond>(
*this, type, [&](auto type, auto &&value) -> Value {
using Type = std::remove_cvref_t<decltype(type)>;
return static_cast<Type>(value);
});
return result;
}
eval::Value eval::Value::bitcast(ir::Value type) const {
using Cond = decltype([](auto type, auto value) {
using Type = std::remove_cvref_t<decltype(type)>;
eval::Value result;
return sizeof(type) == sizeof(value);
auto resultTypeElemCount = getIrTypeConstituents(type);
visit([&](auto value) {
invokeWithType(type, [&](auto resultType) {
if (value.size_bytes() == sizeof(resultType) * resultTypeElemCount) {
result.mTypeIndex = getIrTypeIndex(type);
result.mCount = resultTypeElemCount;
std::memcpy(result.mData, value.data(), value.size_bytes());
}
});
});
return visitWithType<void, void, Cond>(
*this, type, [](auto type, auto &&value) -> Value {
return std::bit_cast<decltype(type)>(value);
});
return result;
}
std::optional<std::uint64_t> eval::Value::zExtScalar() const {
using Cond = decltype([](auto type) {
return std::is_integral_v<ComponentType<decltype(type)>> &&
!std::is_same_v<ComponentType<decltype(type)>, bool> &&
Components<decltype(type)> == 1 && !IsArray<decltype(type)>;
});
auto result = visit<Cond>(*this, [&](auto value) -> Value {
return static_cast<std::uint64_t>(
static_cast<MakeUnsigned<decltype(value)>>(value));
});
if (result) {
return result.as<std::uint64_t>();
if (empty() || size() != 1) {
return {};
}
return {};
return makeUnsigned().visit([](auto value) -> std::optional<std::uint64_t> {
if constexpr (std::is_integral_v<typename decltype(value)::value_type>) {
return static_cast<std::uint64_t>(value[0]);
} else {
return {};
}
});
}
std::optional<std::int64_t> eval::Value::sExtScalar() const {
using Cond = decltype([](auto type) {
return std::is_integral_v<ComponentType<decltype(type)>> &&
!std::is_same_v<ComponentType<decltype(type)>, bool> &&
Components<decltype(type)> == 1 && !IsArray<decltype(type)>;
});
auto result = visit<Cond>(*this, [&](auto value) -> Value {
return static_cast<std::int64_t>(
static_cast<MakeSigned<decltype(value)>>(value));
});
if (result) {
return result.as<std::int64_t>();
if (empty() || size() != 1) {
return {};
}
return {};
return makeSigned().visit([](auto value) -> std::optional<std::int64_t> {
if constexpr (std::is_integral_v<typename decltype(value)::value_type>) {
return static_cast<std::int64_t>(value[0]);
} else {
return {};
}
});
}
#define DEFINE_BINARY_OP(OP) \
eval::Value eval::Value::operator OP(const Value &rhs) const { \
using LhsCond = decltype([](auto &&lhs) { return true; }); \
return visit<LhsCond>(*this, [&]<typename Lhs>(Lhs &&lhs) -> Value { \
using RhsCond = decltype([](auto &&rhs) { \
return requires(Lhs lhs) { static_cast<Value>(lhs OP rhs); }; \
}); \
return visit<RhsCond>(rhs, [&](auto &&rhs) -> Value { \
return static_cast<Value>(lhs OP rhs); \
if (index() != rhs.index() || size() != rhs.size()) { \
return {}; \
} \
eval::Value result; \
visit([&](auto lhsValues) { \
rhs.visit([&](auto rhsValues) { \
if constexpr (requires { lhsValues[0] OP rhsValues[0]; }) { \
if constexpr (std::is_same_v<decltype(lhsValues[0]), \
decltype(rhsValues[0])>) { \
for (std::size_t i = 0; i < lhsValues.size(); ++i) { \
result.add(lhsValues[i] OP rhsValues[i]); \
} \
} \
} \
}); \
}); \
return result; \
}
#define DEFINE_UNARY_OP(OP) \
eval::Value eval::Value::operator OP() const { \
using Cond = decltype([](auto rhs) { \
return requires { static_cast<Value>(OP rhs); }; \
}); \
return visit<Cond>(*this, [&](auto &&rhs) -> Value { \
return static_cast<Value>(OP rhs); \
eval::Value result; \
visit([&](auto values) { \
if constexpr (requires { OP values[0]; }) { \
for (std::size_t i = 0; i < values.size(); ++i) { \
result.add(OP values[i]); \
} \
} \
}); \
return result; \
}
DEFINE_BINARY_OP(+);