gpu: extend resource evaluation rules to handle relative resources

implement evaluation of x16 dword loads
fixed evaluation of binary expressions
This commit is contained in:
DH 2024-11-14 00:06:52 +03:00
parent b85c6e6acc
commit f4994f6717
7 changed files with 419 additions and 116 deletions

View file

@ -146,6 +146,13 @@ static ConverterFn *getPrimConverterFn(gnm::PrimitiveType primType,
static_cast<unsigned>(primType));
}
}
shader::eval::Value Cache::ShaderResources::eval(shader::ir::Value op) {
if (op == ir::sop2::ADD_U32 || op == ir::sop2::ADDC_U32) {
return eval(op.getOperand(1)) + eval(op.getOperand(2));
}
return Evaluator::eval(op);
}
void Cache::ShaderResources::loadResources(
gcn::Resources &res, std::span<const std::uint32_t> userSgprs) {
@ -454,8 +461,11 @@ Cache::ShaderResources::eval(ir::InstructionId instId,
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");
rx::die("unexpected pointer load size %u", loadSize);
}
return result;
@ -1275,9 +1285,9 @@ Cache::Shader Cache::Tag::getShader(const ShaderKey &key,
// deserialized.print(std::cerr, context.ns);
converted = gcn::convertToSpv(context, deserialized,
mParent->mDevice->gcnSemanticModuleInfo,
key.stage, env);
converted = gcn::convertToSpv(
context, deserialized, mParent->mDevice->gcnSemantic,
mParent->mDevice->gcnSemanticModuleInfo, key.stage, env);
if (!converted) {
return {};
}

View file

@ -211,7 +211,7 @@ struct ConvertedShader {
std::optional<ConvertedShader>
convertToSpv(Context &context, ir::Region body,
const SemanticInfo &semanticInfo,
const SemanticModuleInfo &semanticModule, Stage stage,
const Environment &state);
} // namespace shader::gcn

View file

@ -191,7 +191,8 @@ CFG buildCFG(ir::Instruction firstInstruction,
MemorySSA buildMemorySSA(CFG &cfg, ModuleInfo *moduleInfo = nullptr);
MemorySSA buildMemorySSA(CFG &cfg, const SemanticInfo &instructionSemantic,
std::function<ir::Value(int)> getRegisterVarCb);
std::function<ir::Value(int)> getRegisterVarCb,
ModuleInfo *moduleInfo = nullptr);
bool dominates(ir::Instruction a, ir::Instruction b, bool isPostDom,
graph::DomTree<ir::Value> &domTree);
@ -385,10 +386,8 @@ struct Construct {
CFG &getCfg() {
return analysis.get<CFG>([this] {
if (parent != nullptr) {
return parent->getCfg().buildView(
header,
&parent->getPostDomTree(),
{header, merge});
return parent->getCfg().buildView(header, &parent->getPostDomTree(),
{header, merge});
}
return buildCFG(header);
@ -402,10 +401,8 @@ struct Construct {
return analysis.get<Tag<CFG, kWithoutContinue>>([this] {
if (parent != nullptr) {
return parent->getCfg().buildView(
header,
&parent->getPostDomTree(),
{header, merge}, loopContinue);
return parent->getCfg().buildView(header, &parent->getPostDomTree(),
{header, merge}, loopContinue);
}
return buildCFG(header, {}, loopContinue);

View file

@ -2,9 +2,9 @@
#include "Vector.hpp"
#include "ir/Value.hpp"
#include <array>
#include <cstdint>
#include <variant>
#include <array>
namespace shader::eval {
struct Value {
@ -15,7 +15,8 @@ struct Value {
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>>;
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;

View file

@ -71,6 +71,10 @@ inline int stageToDescriptorSet(gcn::Stage stage) {
static void printFlat(std::ostream &os, ir::Instruction inst,
ir::NameStorage &ns) {
if (inst == nullptr) {
os << "null";
return;
}
os << ir::getInstructionName(inst.getKind(), inst.getOp());
os << '(';
@ -91,29 +95,165 @@ static void printFlat(std::ostream &os, ir::Instruction inst,
os << ')';
}
static bool isEqual(ir::Value lhs, ir::Value rhs);
static bool isEqual(const ir::Operand &lhs, const ir::Operand &rhs) {
return std::visit(
[]<typename Lhs, typename Rhs>(const Lhs &lhs, const Rhs &rhs) {
if constexpr (std::is_same_v<Lhs, Rhs>) {
if constexpr (std::is_same_v<Lhs, ir::Value>) {
return isEqual(lhs, rhs);
} else {
return lhs == rhs;
}
} else {
return false;
}
},
lhs.value, rhs.value);
}
static bool isEqual(ir::Value lhs, ir::Value rhs) {
if (lhs == rhs) {
return true;
}
if ((lhs == nullptr) != (rhs == nullptr)) {
return false;
}
if (lhs.getInstId() != rhs.getInstId()) {
return false;
}
if (lhs.getOperandCount() != rhs.getOperandCount()) {
return false;
}
for (std::size_t i = 0, end = lhs.getOperandCount(); i < end; ++i) {
if (!isEqual(lhs.getOperand(i), rhs.getOperand(i))) {
return false;
}
}
return true;
}
struct ResourcesBuilder {
gcn::Resources resources;
ir::NameStorage *ns;
void addPointer(gcn::Resources::Pointer p) {
int addPointer(gcn::Resources::Pointer p) {
for (auto &pointer : resources.pointers) {
if (pointer.size != p.size) {
continue;
}
if (!isEqual(pointer.base, p.base)) {
continue;
}
if (!isEqual(pointer.offset, p.offset)) {
continue;
}
return pointer.resourceSlot;
}
p.resourceSlot = resources.slots++;
resources.pointers.push_back(p);
return p.resourceSlot;
}
void addTexture(gcn::Resources::Texture p) {
int addTexture(gcn::Resources::Texture p) {
for (auto &texture : resources.textures) {
bool equal = true;
for (std::size_t i = 0; i < std::size(texture.words); ++i) {
if (!isEqual(texture.words[i], p.words[i])) {
equal = false;
break;
}
}
if (!equal) {
continue;
}
texture.access |= p.access;
return texture.resourceSlot;
}
p.resourceSlot = resources.slots++;
resources.textures.push_back(p);
return p.resourceSlot;
}
void addImageBuffer(gcn::Resources::ImageBuffer p) {
int addImageBuffer(gcn::Resources::ImageBuffer p) {
for (auto &buffer : resources.imageBuffers) {
bool equal = true;
for (std::size_t i = 0; i < std::size(buffer.words); ++i) {
if (!isEqual(buffer.words[i], p.words[i])) {
equal = false;
break;
}
}
if (!equal) {
continue;
}
buffer.access |= p.access;
return buffer.resourceSlot;
}
p.resourceSlot = resources.slots++;
resources.imageBuffers.push_back(p);
return p.resourceSlot;
}
void addBuffer(gcn::Resources::Buffer p) {
int addBuffer(gcn::Resources::Buffer p) {
for (auto &buffer : resources.buffers) {
bool equal = true;
for (std::size_t i = 0; i < std::size(buffer.words); ++i) {
if (!isEqual(buffer.words[i], p.words[i])) {
equal = false;
break;
}
}
if (!equal) {
continue;
}
buffer.access |= p.access;
return buffer.resourceSlot;
}
p.resourceSlot = resources.slots++;
resources.buffers.push_back(p);
return p.resourceSlot;
}
void addSampler(gcn::Resources::Sampler p) {
int addSampler(gcn::Resources::Sampler p) {
for (auto &sampler : resources.samplers) {
bool equal = true;
for (std::size_t i = 0; i < std::size(sampler.words); ++i) {
if (!isEqual(sampler.words[i], p.words[i])) {
equal = false;
break;
}
}
if (!equal) {
continue;
}
return sampler.resourceSlot;
}
p.resourceSlot = resources.slots++;
resources.samplers.push_back(p);
return p.resourceSlot;
}
ir::Value unpackFunctionCall(MemorySSA &memorySSA, spv::Import &importer,
@ -163,6 +303,30 @@ struct ResourcesBuilder {
return nullptr;
}
ir::Instruction unpackInstruction(MemorySSA &memorySSA, spv::Import &importer,
ir::Instruction inst) {
for (auto &op : inst.getOperands()) {
auto value = op.getAsValue();
if (!value) {
continue;
}
if (value == ir::spv::OpVariable || value == ir::spv::OpAccessChain) {
auto varDef = memorySSA.getDefInst(inst, value);
if (varDef == ir::spv::OpStore) {
varDef = varDef.getOperand(1).getAsValue();
}
if (varDef == ir::amdgpu::POINTER) {
return importIR(memorySSA, importer, varDef).staticCast<ir::Value>();
}
} else if (value == ir::amdgpu::POINTER) {
return importIR(memorySSA, importer, value).staticCast<ir::Value>();
}
}
return inst;
}
ir::Instruction unpackResourceDef(MemorySSA &memorySSA, spv::Import &importer,
ir::memssa::Def def) {
if (def == nullptr) {
@ -180,6 +344,11 @@ struct ResourcesBuilder {
defInst.staticCast<ir::Value>());
}
if (defInst.getKind() != ir::Kind::Spv &&
defInst.getKind() != ir::Kind::AmdGpu) {
return unpackInstruction(memorySSA, importer, defInst);
}
return importIR(memorySSA, importer, defInst);
}
@ -194,6 +363,7 @@ struct ResourcesBuilder {
phi.getOperand(i + 1).getAsValue().staticCast<ir::memssa::Def>();
auto inst = unpackResourceDef(memorySSA, importer, def);
if (inst == nullptr) {
resources.hasUnknown = true;
}
@ -205,6 +375,7 @@ struct ResourcesBuilder {
resourcePhi.addOperand(value);
} else {
auto block = resources.context.create<ir::Block>(inst.getLocation());
inst.erase();
block.addChild(inst);
resourcePhi.addOperand(block);
}
@ -216,6 +387,20 @@ struct ResourcesBuilder {
return importIR(memorySSA, importer, def.getLinkedInst());
}
ir::Value toValue(ir::Instruction inst) {
if (inst == nullptr) {
return {};
}
if (auto value = inst.cast<ir::Value>()) {
return value;
}
auto block = resources.context.create<ir::Block>(inst.getLocation());
block.addChild(inst);
return block;
}
ir::Instruction importIR(MemorySSA &memorySSA, spv::Import &importer,
ir::Instruction resource) {
auto result = ir::clone(resource, resources.context, importer);
@ -240,16 +425,15 @@ struct ResourcesBuilder {
continue;
}
if (auto value = resourceInst.cast<ir::Value>()) {
cloned.staticCast<ir::Value>().replaceAllUsesWith(value);
} else {
auto block =
resources.context.create<ir::Block>(resourceInst.getLocation());
block.addChild(resourceInst);
cloned.staticCast<ir::Value>().replaceAllUsesWith(block);
}
if (resourceInst != cloned) {
if (resource == inst) {
result = resourceInst;
}
continue;
cloned.staticCast<ir::Value>().replaceAllUsesWith(
toValue(resourceInst));
continue;
}
}
if (inst == ir::spv::OpFunctionCall) {
@ -258,8 +442,55 @@ struct ResourcesBuilder {
if (unpacked) {
cloned.staticCast<ir::Value>().replaceAllUsesWith(unpacked);
if (resource == inst) {
result = unpacked;
}
if (visited.insert(unpacked).second) {
workList.push_back(unpacked);
workList.emplace_back(unpacked);
}
continue;
}
}
if (inst.getKind() != ir::Kind::Spv &&
inst.getKind() != ir::Kind::AmdGpu) {
auto unpacked = unpackInstruction(memorySSA, importer, inst);
if (unpacked) {
if (unpacked != inst) {
cloned.staticCast<ir::Value>().replaceAllUsesWith(
toValue(unpacked));
if (resource == inst) {
result = unpacked;
}
if (visited.insert(unpacked).second) {
workList.emplace_back(unpacked);
}
continue;
}
// FIXME: pass read only parameters as value and remove this
// workaround
for (std::size_t i = 0, end = cloned.getOperandCount(); i < end;
++i) {
auto valueOp = cloned.getOperand(i).getAsValue();
if (valueOp == nullptr) {
continue;
}
if (valueOp != ir::spv::OpVariable) {
continue;
}
auto def = memorySSA.getDef(inst, inst.getOperand(i).getAsValue());
if (auto resourceInst =
unpackResourceDef(memorySSA, importer, def)) {
cloned.replaceOperand(i, toValue(resourceInst));
}
}
continue;
@ -268,8 +499,13 @@ struct ResourcesBuilder {
for (auto &operand : inst.getOperands()) {
if (auto value = operand.getAsValue()) {
if (value.getKind() == ir::Kind::Spv &&
ir::spv::isTypeOp(value.getOp())) {
continue;
}
if (visited.insert(value).second) {
workList.push_back(value);
workList.emplace_back(value);
}
}
}
@ -286,9 +522,13 @@ struct ResourcesBuilder {
int slot = -1;
if (resourceSet.size() == 1 && resourceSet[0] != nullptr) {
slot = resources.slots;
}
auto trackResource = [&](int resourceSlot) {
if (slot == -1) {
slot = resourceSlot;
} else if (slot != resourceSlot) {
slot = -2;
}
};
for (auto inst : resourceSet) {
if (inst == ir::amdgpu::POINTER) {
@ -296,11 +536,11 @@ struct ResourcesBuilder {
auto base = inst.getOperand(2).getAsValue();
auto offset = inst.getOperand(3).getAsValue();
addPointer({
trackResource(addPointer({
.size = loadSize,
.base = base,
.offset = offset,
});
}));
continue;
}
@ -309,11 +549,11 @@ struct ResourcesBuilder {
auto access = static_cast<Access>(*inst.getOperand(1).getAsInt32());
auto words = inst.getOperands().subspan(2);
addBuffer({
trackResource(addBuffer({
.access = access,
.words = {words[0].getAsValue(), words[1].getAsValue(),
words[2].getAsValue(), words[3].getAsValue()},
});
}));
continue;
}
@ -322,19 +562,19 @@ struct ResourcesBuilder {
auto access = static_cast<Access>(*inst.getOperand(1).getAsInt32());
auto words = inst.getOperands().subspan(2);
if (words.size() > 4) {
addTexture({
trackResource(addTexture({
.access = access,
.words = {words[0].getAsValue(), words[1].getAsValue(),
words[2].getAsValue(), words[3].getAsValue(),
words[4].getAsValue(), words[5].getAsValue(),
words[6].getAsValue(), words[7].getAsValue()},
});
}));
} else {
addTexture({
trackResource(addTexture({
.access = access,
.words = {words[0].getAsValue(), words[1].getAsValue(),
words[2].getAsValue(), words[3].getAsValue()},
});
}));
}
continue;
}
@ -343,19 +583,19 @@ struct ResourcesBuilder {
auto access = static_cast<Access>(*inst.getOperand(1).getAsInt32());
auto words = inst.getOperands().subspan(2);
if (words.size() > 4) {
addImageBuffer({
trackResource(addImageBuffer({
.access = access,
.words = {words[0].getAsValue(), words[1].getAsValue(),
words[2].getAsValue(), words[3].getAsValue(),
words[4].getAsValue(), words[5].getAsValue(),
words[6].getAsValue(), words[7].getAsValue()},
});
}));
} else {
addImageBuffer({
trackResource(addImageBuffer({
.access = access,
.words = {words[0].getAsValue(), words[1].getAsValue(),
words[2].getAsValue(), words[3].getAsValue()},
});
}));
}
continue;
}
@ -363,11 +603,11 @@ struct ResourcesBuilder {
if (inst == ir::amdgpu::SAMPLER) {
auto words = inst.getOperands().subspan(1);
auto unorm = *inst.getOperand(5).getAsBool();
addSampler({
trackResource(addSampler({
.unorm = unorm,
.words = {words[0].getAsValue(), words[1].getAsValue(),
words[2].getAsValue(), words[3].getAsValue()},
});
}));
continue;
}
@ -877,6 +1117,7 @@ static void expToSpv(GcnConverter &converter, gcn::Stage stage,
static void instructionsToSpv(GcnConverter &converter, gcn::Import &importer,
gcn::Stage stage, const gcn::Environment &env,
const SemanticInfo &semanticInfo,
const SemanticModuleInfo &semanticModuleInfo,
gcn::ShaderInfo &info, ir::Region body) {
auto &context = converter.gcnContext;
@ -927,10 +1168,6 @@ static void instructionsToSpv(GcnConverter &converter, gcn::Import &importer,
}
for (auto inst : body.children()) {
if (inst.getKind() == ir::Kind::Spv) {
continue;
}
if (inst == ir::exp::EXP) {
expToSpv(converter, stage, info, inst);
inst.remove();
@ -941,6 +1178,57 @@ static void instructionsToSpv(GcnConverter &converter, gcn::Import &importer,
inst == ir::amdgpu::SAMPLER || inst == ir::amdgpu::TBUFFER ||
inst == ir::amdgpu::IMAGE_BUFFER) {
toAnalyze.push_back(inst.staticCast<ir::Value>());
}
}
if (!toAnalyze.empty()) {
auto &cfg =
context.analysis.get<CFG>([&] { return buildCFG(body.getFirst()); });
ModuleInfo moduleInfo;
collectModuleInfo(moduleInfo, context.layout);
auto memorySSA = buildMemorySSA(
cfg, semanticInfo,
[&](int regId) {
return context.getOrCreateRegisterVariable(gcn::RegId(regId));
},
&moduleInfo);
spv::Import resourceImporter;
// memorySSA.print(std::cerr, body, context.ns);
ResourcesBuilder resourcesBuilder;
std::map<ir::Value, std::int32_t> resourceConfigSlots;
resourcesBuilder.ns = &context.ns;
for (auto inst : toAnalyze) {
std::uint32_t configSlot = -1;
int resourceSlot =
resourcesBuilder.importResource(memorySSA, resourceImporter, inst);
if (resourceSlot >= 0) {
configSlot = info.create(gcn::ConfigType::ResourceSlot, resourceSlot);
}
resourceConfigSlots[inst] = configSlot;
}
for (auto [inst, slot] : resourceConfigSlots) {
auto builder = gcn::Builder::createInsertBefore(context, inst);
if (slot >= 0) {
auto value = converter.createReadConfig(stage, builder, slot);
value = builder.createSpvBitcast(inst.getLocation(),
context.getTypeSInt32(), value);
inst.replaceAllUsesWith(value);
} else {
inst.replaceAllUsesWith(context.simm32(-1));
}
inst.remove();
}
info.resources = std::move(resourcesBuilder.resources);
}
for (auto inst : body.children()) {
if (inst.getKind() == ir::Kind::Spv) {
continue;
}
@ -1290,47 +1578,6 @@ static void instructionsToSpv(GcnConverter &converter, gcn::Import &importer,
inst.remove();
}
if (!toAnalyze.empty()) {
auto &cfg =
context.analysis.get<CFG>([&] { return buildCFG(body.getFirst()); });
ModuleInfo moduleInfo;
collectModuleInfo(moduleInfo, context.layout);
auto memorySSA = buildMemorySSA(cfg, &moduleInfo);
spv::Import resourceImporter;
// memorySSA.print(std::cerr, body, context.ns);
ResourcesBuilder resourcesBuilder;
std::map<ir::Value, std::int32_t> resourceConfigSlots;
resourcesBuilder.ns = &context.ns;
for (auto inst : toAnalyze) {
std::uint32_t configSlot = -1;
int resourceSlot =
resourcesBuilder.importResource(memorySSA, resourceImporter, inst);
if (resourceSlot >= 0) {
configSlot = info.create(gcn::ConfigType::ResourceSlot, resourceSlot);
}
resourceConfigSlots[inst] = configSlot;
}
for (auto [inst, slot] : resourceConfigSlots) {
auto builder = gcn::Builder::createInsertBefore(context, inst);
if (slot >= 0) {
auto value = converter.createReadConfig(stage, builder, slot);
value = builder.createSpvBitcast(inst.getLocation(),
context.getTypeSInt32(), value);
inst.replaceAllUsesWith(value);
} else {
inst.replaceAllUsesWith(context.simm32(-1));
}
inst.remove();
}
info.resources = std::move(resourcesBuilder.resources);
}
for (auto inst : body.children()) {
if (inst.getKind() == ir::Kind::Spv) {
continue;
@ -1704,15 +1951,16 @@ static void createInitialValues(GcnConverter &converter,
std::optional<gcn::ConvertedShader>
gcn::convertToSpv(Context &context, ir::Region body,
const SemanticModuleInfo &semanticInfo, Stage stage,
const SemanticInfo &semanticInfo,
const SemanticModuleInfo &semanticModule, Stage stage,
const Environment &env) {
gcn::ConvertedShader result;
GcnConverter converter{context};
gcn::Import importer;
createInitialValues(converter, env, stage, result.info, body);
instructionsToSpv(converter, importer, stage, env, semanticInfo, result.info,
body);
instructionsToSpv(converter, importer, stage, env, semanticInfo,
semanticModule, result.info, body);
if (stage != gcn::Stage::Cs) {
replaceVariableWithConstant(
context.getOrCreateRegisterVariable(gcn::RegId::ThreadId),

View file

@ -964,13 +964,12 @@ MemorySSA MemorySSABuilder::build(CFG &cfg, auto &&handleInst) {
return std::move(memSSA);
}
MemorySSA
shader::buildMemorySSA(CFG &cfg, const SemanticInfo &instructionSemantic,
std::function<ir::Value(int)> getRegisterVarCb) {
return MemorySSABuilder{}.build(cfg, [&](MemorySSABuilder &builder,
ir::memssa::Scope scope,
ir::Instruction inst) {
using IRBuilder = MemorySSABuilder::IRBuilder;
MemorySSA shader::buildMemorySSA(CFG &cfg,
const SemanticInfo &instructionSemantic,
std::function<ir::Value(int)> getRegisterVarCb,
ModuleInfo *moduleInfo) {
auto handleSemantic = [&](MemorySSABuilder &builder, ir::memssa::Scope scope,
ir::Instruction inst) {
auto semantic = instructionSemantic.findSemantic(inst.getInstId());
if (semantic == nullptr) {
return false;
@ -1006,6 +1005,53 @@ shader::buildMemorySSA(CFG &cfg, const SemanticInfo &instructionSemantic,
}
return true;
};
auto handleModuleInfo = [&](MemorySSABuilder &builder,
ir::memssa::Scope scope, ir::Instruction inst) {
if (inst != ir::spv::OpFunctionCall) {
return false;
}
auto callee = inst.getOperand(1).getAsValue();
auto it = moduleInfo->functions.find(callee);
auto fnInfo = it == moduleInfo->functions.end() ? nullptr : &it->second;
if (fnInfo == nullptr) {
return false;
}
for (auto [variable, access] : fnInfo->variables) {
builder.createPointerAccess(inst, scope, variable, VarSearchType::Root,
access);
}
auto args = inst.getOperands();
args = args.subspan(args.size() - fnInfo->parameters.size());
for (std::size_t i = 0; i < args.size(); ++i) {
auto arg = args[i].getAsValue();
auto param = fnInfo->parameters[i];
if (param.access == Access::None) {
continue;
}
builder.createPointerAccess(inst, scope, arg, VarSearchType::Root,
param.access);
}
return true;
};
return MemorySSABuilder{}.build(cfg, [&](MemorySSABuilder &builder,
ir::memssa::Scope scope,
ir::Instruction inst) {
if (handleSemantic(builder, scope, inst)) {
return true;
}
return moduleInfo != nullptr && handleModuleInfo(builder, scope, inst);
});
}
@ -1013,8 +1059,6 @@ MemorySSA shader::buildMemorySSA(CFG &cfg, ModuleInfo *moduleInfo) {
return MemorySSABuilder{}.build(cfg, [&](MemorySSABuilder &builder,
ir::memssa::Scope scope,
ir::Instruction inst) {
using IRBuilder = MemorySSABuilder::IRBuilder;
if (moduleInfo == nullptr) {
return false;
}

View file

@ -364,7 +364,8 @@ eval::Value eval::Value::compositeExtract(const Value &index) const {
eval::Value eval::Value::isNan() const {
using Cond = decltype([](auto type) {
return std::is_floating_point_v<ComponentType<decltype(type)>> && !IsArray<decltype(type)>;
return std::is_floating_point_v<ComponentType<decltype(type)>> &&
!IsArray<decltype(type)>;
});
return visit<Cond>(*this, [](auto &&value) -> Value {
@ -384,7 +385,8 @@ eval::Value eval::Value::isNan() const {
eval::Value eval::Value::isInf() const {
using Cond = decltype([](auto type) {
return std::is_floating_point_v<ComponentType<decltype(type)>> && !IsArray<decltype(type)>;
return std::is_floating_point_v<ComponentType<decltype(type)>> &&
!IsArray<decltype(type)>;
});
return visit<Cond>(*this, [](auto &&value) -> Value {
@ -472,7 +474,7 @@ eval::Value eval::Value::makeSigned() const {
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)>;
(Components<decltype(type)> > 1) && !IsArray<decltype(type)>;
});
return visit<Cond>(*this, [](auto &&value) {
@ -506,7 +508,8 @@ eval::Value eval::Value::any() const {
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)>;
return std::is_same_v<ComponentType<decltype(type)>, bool> &&
!IsArray<decltype(type)>;
});
return visit<Cond>(*this, [&](auto &&cond) -> Value {
@ -548,7 +551,8 @@ eval::Value eval::Value::iConvert(ir::Value type, bool isSigned) const {
using Type = std::remove_cvref_t<decltype(type)>;
return std::is_integral_v<ComponentType<Type>> &&
!std::is_same_v<bool, ComponentType<Type>> && !IsArray<decltype(type)>;
!std::is_same_v<bool, ComponentType<Type>> &&
!IsArray<decltype(type)>;
});
using PairCond = decltype([](auto lhs, auto rhs) {
@ -571,7 +575,8 @@ eval::Value eval::Value::iConvert(ir::Value type, bool isSigned) const {
}
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)>;
return std::is_floating_point_v<ComponentType<decltype(type)>> &&
!IsArray<decltype(type)>;
});
using PairCond = decltype([](auto lhs, auto rhs) {
@ -640,10 +645,8 @@ std::optional<std::int64_t> eval::Value::sExtScalar() const {
}
#define DEFINE_BINARY_OP(OP) \
eval::Value eval::Value::operator OP(const Value & rhs) const { \
using LhsCond = decltype([](auto &&lhs) { \
return requires { static_cast<Value>(lhs OP rhs); }; \
}); \
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); }; \