shader: own control flow on IR blocks with direct targets

This commit is contained in:
nmzik
2026-10-09 01:59:41 +02:00
parent 8abf99c56e
commit 3df456675d
13 changed files with 477 additions and 472 deletions
@@ -83,44 +83,31 @@ void StoreDispatcherPhiEdge(ValueEmitContext& ctx, const DispatcherFunctionState
}
}
const IR::Block* TargetBlock(const IR::Program& program, uint32_t id) {
const auto found = std::ranges::find_if(
program.block_info, [&](const IR::BlockInfo& info) { return info.id == id; });
if (found == program.block_info.end()) {
return nullptr;
}
return program.blocks[static_cast<size_t>(found - program.block_info.begin())];
}
void EmitReturn(ValueEmitContext& ctx) {
EmitKillIfPixelValidMaskInactive(ctx.state);
ctx.state.builder.AddFunction(spv::OpReturn);
}
void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block,
const IR::BlockInfo& info) {
const auto& program = ctx.state.program;
const auto& term = info.terminator;
void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block) {
const auto& term = block->terminator;
const auto emit_merge = [&]() {
if (term.loop_header) {
const auto* merge = TargetBlock(program, term.merge_block);
const auto* cont = TargetBlock(program, term.continue_block);
const auto* merge = term.merge_block;
const auto* cont = term.continue_block;
if (merge != nullptr && cont != nullptr) {
ctx.state.builder.AddFunction(spv::OpLoopMerge, merge->Definition(),
cont->Definition(), spv::LoopControlMaskNone);
}
} else if (term.kind == CFG::TerminatorKind::ConditionalBranch &&
term.merge_block != UINT32_MAX) {
if (const auto* merge = TargetBlock(program, term.merge_block); merge != nullptr) {
ctx.state.builder.AddFunction(spv::OpSelectionMerge, merge->Definition(),
spv::SelectionControlMaskNone);
}
term.merge_block != nullptr) {
ctx.state.builder.AddFunction(spv::OpSelectionMerge, term.merge_block->Definition(),
spv::SelectionControlMaskNone);
}
};
switch (term.kind) {
case CFG::TerminatorKind::Branch: {
const auto* target = TargetBlock(program, term.true_block);
const auto* target = term.true_block;
if (target == nullptr) {
EmitReturn(ctx);
return;
@@ -130,13 +117,13 @@ void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block,
return;
}
case CFG::TerminatorKind::ConditionalBranch: {
const auto* true_block = TargetBlock(program, term.true_block);
const auto* false_block = TargetBlock(program, term.false_block);
if (true_block == nullptr || false_block == nullptr || info.condition.IsEmpty()) {
const auto* true_block = term.true_block;
const auto* false_block = term.false_block;
if (true_block == nullptr || false_block == nullptr || block->condition.IsEmpty()) {
EmitReturn(ctx);
return;
}
const auto condition = ctx.Def(info.condition);
const auto condition = ctx.Def(block->condition);
emit_merge();
ctx.state.builder.AddFunction(spv::OpBranchConditional, condition,
true_block->Definition(), false_block->Definition());
@@ -147,8 +134,7 @@ void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block,
}
void EmitDispatcherTarget(ValueEmitContext& ctx, const DispatcherFunctionState& dispatcher,
const IR::Block* from, uint32_t target) {
const auto* block = TargetBlock(ctx.state.program, target);
const IR::Block* from, const IR::Block* block) {
if (block != nullptr) {
StoreDispatcherPhiEdge(ctx, dispatcher, from, block);
if (ctx.other_half != nullptr) {
@@ -158,41 +144,37 @@ void EmitDispatcherTarget(ValueEmitContext& ctx, const DispatcherFunctionState&
}
uint32_t EmitDispatcherNextPc(ValueEmitContext& ctx, const DispatcherFunctionState& dispatcher,
const IR::Block* block, const IR::BlockInfo& info) {
const auto& term = info.terminator;
const IR::Block* block) {
const auto& term = block->terminator;
switch (term.kind) {
case CFG::TerminatorKind::Branch:
EmitDispatcherTarget(ctx, dispatcher, block, term.true_block);
return ConstantU32(ctx.state, term.true_block);
return ConstantU32(ctx.state, term.true_block->id);
case CFG::TerminatorKind::ConditionalBranch: {
EmitDispatcherTarget(ctx, dispatcher, block, term.true_block);
EmitDispatcherTarget(ctx, dispatcher, block, term.false_block);
const auto selected = ctx.state.builder.AllocateId();
ctx.state.builder.AddFunction(
spv::OpSelect, TypeU32(ctx.state), selected, ctx.Def(info.condition),
ConstantU32(ctx.state, term.true_block), ConstantU32(ctx.state, term.false_block));
ctx.state.builder.AddFunction(spv::OpSelect, TypeU32(ctx.state), selected,
ctx.Def(block->condition),
ConstantU32(ctx.state, term.true_block->id),
ConstantU32(ctx.state, term.false_block->id));
return selected;
}
case CFG::TerminatorKind::IndirectBranch: {
for (const auto target: term.indirect_targets) {
for (const auto* target: block->ImmSuccessors()) {
EmitDispatcherTarget(ctx, dispatcher, block, target);
}
uint32_t selected = ConstantU32(ctx.state, UINT32_MAX);
if (!info.indirect_target.IsEmpty()) {
const auto selector = ctx.Def(info.indirect_target);
const auto& values = term.indirect_selector_code != UINT32_MAX
? term.indirect_selector_values
: term.indirect_target_pcs;
const auto& targets = term.indirect_selector_code != UINT32_MAX
? term.indirect_selector_targets
: term.indirect_targets;
for (size_t index = 0; index < std::min(values.size(), targets.size()); index++) {
if (!block->indirect_target.IsEmpty()) {
const auto selector = ctx.Def(block->indirect_target);
for (const auto& branch: term.cases) {
const auto match = ctx.state.builder.AllocateId();
const auto next = ctx.state.builder.AllocateId();
ctx.state.builder.AddFunction(spv::OpIEqual, TypeBool(ctx.state), match,
selector, ConstantU32(ctx.state, values[index]));
selector, ConstantU32(ctx.state, branch.value));
ctx.state.builder.AddFunction(spv::OpSelect, TypeU32(ctx.state), next, match,
ConstantU32(ctx.state, targets[index]), selected);
ConstantU32(ctx.state, branch.target->id),
selected);
selected = next;
}
}
@@ -349,7 +331,7 @@ void EmitStructuredFunction(ValueEmitContext& ctx) {
EmitStructuredInstruction(lane, structured, inst);
});
structured.block_exit_labels.emplace(block, ctx.state.current_label);
EmitStructuredTerminator(ctx, block, program.block_info[index]);
EmitStructuredTerminator(ctx, block);
}
PatchStructuredPhis(ctx, structured);
}
@@ -361,8 +343,7 @@ void EmitDispatcherFunction(ValueEmitContext& ctx, const DispatcherFunctionState
EmitBlock(ctx, entry, [&](ValueEmitContext& lane, const IR::Inst& inst) {
EmitDispatcherInstruction(lane, dispatcher, inst);
});
const auto initial_pc =
EmitDispatcherNextPc(ctx, dispatcher, entry, state.program.block_info.front());
const auto initial_pc = EmitDispatcherNextPc(ctx, dispatcher, entry);
const auto initial_parent = state.current_label;
state.builder.AddFunction(spv::OpBranch, dispatcher.header_label);
@@ -384,7 +365,7 @@ void EmitDispatcherFunction(ValueEmitContext& ctx, const DispatcherFunctionState
spv::SelectionControlMaskNone);
std::vector<uint32_t> words {spv::OpSwitch, pc, dispatcher.after_switch_label};
for (size_t index = 1; index < state.program.blocks.size(); index++) {
words.push_back(state.program.block_info[index].id);
words.push_back(state.program.blocks[index]->id);
words.push_back(state.program.blocks[index]->Definition());
}
state.builder.AddFunction(words);
@@ -396,8 +377,7 @@ void EmitDispatcherFunction(ValueEmitContext& ctx, const DispatcherFunctionState
[&](ValueEmitContext& lane, const IR::Inst& inst) {
EmitDispatcherInstruction(lane, dispatcher, inst);
});
const auto selected = EmitDispatcherNextPc(ctx, dispatcher, state.program.blocks[index],
state.program.block_info[index]);
const auto selected = EmitDispatcherNextPc(ctx, dispatcher, state.program.blocks[index]);
next_pc_words.push_back(selected);
next_pc_words.push_back(state.current_label);
state.builder.AddFunction(spv::OpBranch, dispatcher.after_switch_label);
@@ -668,8 +648,8 @@ void EmitProgram(EmitterState& state) {
}
}
for (size_t index = 0; index < program.blocks.size(); index++) {
mark_cross_block(program.block_info[index].condition, program.blocks[index]);
mark_cross_block(program.block_info[index].indirect_target, program.blocks[index]);
mark_cross_block(program.blocks[index]->condition, program.blocks[index]);
mark_cross_block(program.blocks[index]->indirect_target, program.blocks[index]);
}
dispatch.header_label = state.builder.AllocateId();
dispatch.select_label = state.builder.AllocateId();
@@ -735,7 +735,7 @@ void Translator::WriteCompareResult(const Decoder::Operand& operand, IR::U1 valu
}
void Translator::AddBranchCondition(const CFG::Graph& graph, const CFG::BasicBlock& source,
IR::BlockInfo& info) {
IR::Block& info) {
const auto native_condition = [&](CFG::BranchCondition kind) -> IR::U1 {
IR::U1 condition;
switch (kind) {
@@ -1023,45 +1023,67 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph&
result.block_storage.reserve(cfg.blocks.size() + 1u);
result.blocks.reserve(cfg.blocks.size() + 1u);
result.block_info.reserve(cfg.blocks.size() + 1u);
const auto max_id = std::ranges::max_element(cfg.blocks, {}, [](const CFG::BasicBlock& block) {
return block.id;
})->id;
if (max_id == UINT32_MAX) {
EXIT("cannot allocate a typed entry block id");
}
CFG::Terminator terminator;
terminator.kind = CFG::TerminatorKind::Branch;
terminator.true_block = cfg.blocks.front().id;
result.block_storage.push_back(std::make_unique<IR::Block>());
result.blocks.push_back(result.block_storage.back().get());
result.block_info.push_back({max_id + 1u, cfg.blocks.front().start_pc,
cfg.blocks.front().start_pc, std::move(terminator)});
auto& entry = *result.blocks.front();
entry.id = max_id + 1u;
entry.start_pc = entry.end_pc = cfg.blocks.front().start_pc;
entry.terminator.kind = CFG::TerminatorKind::Branch;
std::unordered_map<uint32_t, size_t> block_indices;
block_indices.reserve(cfg.blocks.size());
for (const auto& source_block: cfg.blocks) {
if (!block_indices.emplace(source_block.id, result.blocks.size()).second) {
EXIT("CFG contains duplicate block id %u", source_block.id);
std::unordered_map<uint32_t, IR::Block*> blocks_by_id;
blocks_by_id.reserve(cfg.blocks.size());
for (const auto& source: cfg.blocks) {
auto& block = *result.block_storage.emplace_back(std::make_unique<IR::Block>());
if (!blocks_by_id.emplace(source.id, &block).second) {
EXIT("CFG contains duplicate block id %u", source.id);
}
result.block_storage.push_back(std::make_unique<IR::Block>());
result.blocks.push_back(result.block_storage.back().get());
result.block_info.push_back(
{source_block.id, source_block.start_pc, source_block.end_pc, source_block.terminator});
result.blocks.push_back(&block);
block.id = source.id;
block.start_pc = source.start_pc;
block.end_pc = source.end_pc;
}
for (const auto& source_block: cfg.blocks) {
const auto source_index = block_indices.at(source_block.id);
for (const auto successor: source_block.successors) {
const auto target = block_indices.find(successor);
if (target == block_indices.end()) {
EXIT("CFG block %u has unknown successor %u", source_block.id, successor);
const auto target_block = [&](uint32_t id) -> IR::Block* {
return id == UINT32_MAX ? nullptr : blocks_by_id.at(id);
};
entry.terminator.true_block = target_block(cfg.blocks.front().id);
for (const auto& source: cfg.blocks) {
auto& block = *blocks_by_id.at(source.id);
const auto& term = source.terminator;
block.terminator.kind = term.kind;
block.terminator.true_block = target_block(term.true_block);
block.terminator.false_block = target_block(term.false_block);
block.terminator.merge_block = target_block(term.merge_block);
block.terminator.continue_block = target_block(term.continue_block);
block.terminator.loop_header = term.loop_header;
block.terminator.indexed = term.indirect_selector_code != UINT32_MAX;
const auto& values =
block.terminator.indexed ? term.indirect_selector_values : term.indirect_target_pcs;
const auto& targets =
block.terminator.indexed ? term.indirect_selector_targets : term.indirect_targets;
if (values.size() != targets.size()) {
EXIT("CFG block %u has inconsistent indirect targets", source.id);
}
block.terminator.cases.reserve(values.size());
for (size_t index = 0; index < values.size(); ++index) {
block.terminator.cases.push_back({values[index], target_block(targets[index])});
}
for (const auto successor: source.successors) {
const auto target = blocks_by_id.find(successor);
if (target == blocks_by_id.end()) {
EXIT("CFG block %u has unknown successor %u", source.id, successor);
}
result.blocks[source_index]->AddBranch(result.blocks[target->second]);
block.AddBranch(target->second);
}
}
{
result.blocks.front()->AddBranch(result.blocks.at(block_indices.at(cfg.blocks.front().id)));
IR::IREmitter entry_ir(result.blocks.front());
entry.AddBranch(entry.terminator.true_block);
IR::IREmitter entry_ir(&entry);
const auto builtin = [&](IR::StageInputKind kind, uint32_t component = 0u) {
return IR::U32(
entry_ir.Emit(IR::ValueOpcode::GetBuiltin,
@@ -1331,10 +1353,10 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph&
}
}
const bool flush_f32_inputs = options.stage == ShaderType::Compute &&
(options.input_info.compute->float_mode & 0x10u) == 0;
(options.input_info.compute->float_mode & 0x10u) == 0;
for (const auto& cfg_block: cfg.blocks) {
const auto typed_index = block_indices.at(cfg_block.id);
Translator translator(result, result.blocks[typed_index], vector_limit, flush_f32_inputs);
auto* block = blocks_by_id.at(cfg_block.id);
Translator translator(result, block, vector_limit, flush_f32_inputs);
for (uint32_t index = cfg_block.inst_begin; index < cfg_block.inst_end; index++) {
const auto& instruction = decoded.instructions[index];
if (IsCodeTableLoad(cfg, instruction.pc)) {
@@ -1357,7 +1379,7 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph&
}
translator.TranslateInstruction(instruction);
}
translator.AddBranchCondition(cfg, cfg_block, result.block_info[typed_index]);
translator.AddBranchCondition(cfg, cfg_block, *block);
}
IR::ValidateProgram(result, false);
return result;
@@ -17,7 +17,8 @@ public:
void TranslateInstruction(const Decoder::Instruction& inst);
void TranslateEmbeddedFetch(const Decoder::Instruction& inst, uint32_t attribute,
uint32_t component_count, const ShaderBufferResource& resource);
void AddBranchCondition(const CFG::Graph& graph, const CFG::BasicBlock& source, IR::BlockInfo& info);
void AddBranchCondition(const CFG::Graph& graph, const CFG::BasicBlock& source,
IR::Block& info);
private:
const Decoder::Operand& SourceAt(const Decoder::Instruction& inst, uint32_t index);
+22
View File
@@ -1,5 +1,6 @@
#pragma once
#include "graphics/shader/recompiler/frontend/cfg/ShaderCFG.h"
#include "graphics/shader/recompiler/ir/Reg.h"
#include "graphics/shader/recompiler/ir/Value.h"
@@ -49,6 +50,27 @@ public:
const_iterator end() const;
bool empty() const;
struct SwitchCase {
uint32_t value;
Block* target;
};
struct Terminator {
CFG::TerminatorKind kind = CFG::TerminatorKind::Return;
Block* true_block = nullptr;
Block* false_block = nullptr;
Block* merge_block = nullptr;
Block* continue_block = nullptr;
std::vector<SwitchCase> cases;
bool indexed = false;
bool loop_header = false;
};
uint32_t id = 0;
uint32_t start_pc = 0;
uint32_t end_pc = 0;
Terminator terminator;
Value condition;
Value indirect_target;
std::array<Value, NumScalarRegs> ssa_sreg_values {};
std::array<Value, NumScalarRegs> ssa_thread_bit_sreg_values {};
std::array<Value, NumScalarRegs> ssa_sreg_mask_tags {};
+26 -40
View File
@@ -230,12 +230,11 @@ bool HasShaderMemoryWrites(const Program& program) {
}
void ValidateProgram(const Program& program, bool require_ssa) {
if (program.blocks.size() != program.block_info.size() ||
program.blocks.size() != program.block_storage.size()) {
if (program.blocks.size() != program.block_storage.size()) {
return Fail("value IR block storage is inconsistent");
}
std::unordered_map<const Block*, size_t> block_indices;
std::unordered_map<uint32_t, const Block*> blocks_by_id;
std::unordered_set<uint32_t> block_ids;
std::unordered_map<const Inst*, size_t> instruction_positions;
for (size_t block_index = 0; block_index < program.blocks.size(); block_index++) {
const auto* block = program.blocks[block_index];
@@ -246,10 +245,10 @@ void ValidateProgram(const Program& program, bool require_ssa) {
if (!block_indices.emplace(block, block_index).second) {
return Fail("value IR block pointer is duplicated");
}
if (program.block_info[block_index].id == UINT32_MAX) {
if (block->id == UINT32_MAX) {
return Fail("value IR block uses the reserved exit id");
}
if (!blocks_by_id.emplace(program.block_info[block_index].id, block).second) {
if (!block_ids.insert(block->id).second) {
return Fail("value IR block id is duplicated");
}
size_t position = 0;
@@ -294,15 +293,14 @@ void ValidateProgram(const Program& program, bool require_ssa) {
}
std::unordered_set<const Block*> expected_successors;
const auto add_target = [&](uint32_t id) {
const auto found = blocks_by_id.find(id);
if (found == blocks_by_id.end()) {
const auto add_target = [&](const Block* target) {
if (!block_indices.contains(target)) {
return false;
}
expected_successors.insert(found->second);
expected_successors.insert(target);
return true;
};
const auto& terminator = program.block_info[block_index].terminator;
const auto& terminator = block->terminator;
const auto validate_control_value = [&](Value value, Type type) {
if (value.IsEmpty() || value.GetType() != type) {
return false;
@@ -320,44 +318,32 @@ void ValidateProgram(const Program& program, bool require_ssa) {
if (!add_target(terminator.true_block) || !add_target(terminator.false_block)) {
return Fail("value IR conditional branch target is missing");
}
if (!validate_control_value(program.block_info[block_index].condition, Type::U1)) {
if (!validate_control_value(block->condition, Type::U1)) {
return Fail("value IR conditional branch condition is invalid");
}
break;
case CFG::TerminatorKind::IndirectBranch: {
if (!validate_control_value(program.block_info[block_index].indirect_target,
Type::U32)) {
if (!validate_control_value(block->indirect_target, Type::U32)) {
return Fail("value IR indirect branch selector is invalid");
}
std::unordered_set<uint32_t> indirect_targets;
for (const auto target: terminator.indirect_targets) {
if (!indirect_targets.insert(target).second) {
return Fail("value IR indirect branch target is duplicated");
std::unordered_set<uint32_t> values;
for (const auto& branch: terminator.cases) {
if (!values.insert(branch.value).second) {
return Fail("value IR indirect selector value is duplicated");
}
if (!add_target(target)) {
if (!add_target(branch.target)) {
return Fail("value IR indirect branch target is missing");
}
}
if (terminator.indirect_selector_values.size() !=
terminator.indirect_selector_targets.size()) {
return Fail("value IR indirect selector table is inconsistent");
}
for (const auto target: terminator.indirect_selector_targets) {
const auto found = blocks_by_id.find(target);
if (found == blocks_by_id.end() ||
!expected_successors.contains(found->second)) {
return Fail("value IR indirect selector target is not a CFG successor");
}
}
break;
}
case CFG::TerminatorKind::Return:
case CFG::TerminatorKind::Unsupported: break;
}
if ((terminator.merge_block != UINT32_MAX &&
!blocks_by_id.contains(terminator.merge_block)) ||
(terminator.continue_block != UINT32_MAX &&
!blocks_by_id.contains(terminator.continue_block))) {
if ((terminator.merge_block != nullptr &&
!block_indices.contains(terminator.merge_block)) ||
(terminator.continue_block != nullptr &&
!block_indices.contains(terminator.continue_block))) {
return Fail("value IR structured control target is missing");
}
if (successors != expected_successors) {
@@ -664,7 +650,7 @@ void ValidateProgram(const Program& program, bool require_ssa) {
}
}
}
const auto& info = program.block_info[block_index];
const auto& info = *program.blocks[block_index];
if (!info.condition.IsEmpty() && !control_dominates(info.condition, block)) {
return Fail("value IR branch condition definition does not dominate its use");
}
@@ -675,9 +661,9 @@ void ValidateProgram(const Program& program, bool require_ssa) {
}
void ResolveControlFlowIdentities(Program& program) {
for (auto& info: program.block_info) {
info.condition = info.condition.Resolve();
info.indirect_target = info.indirect_target.Resolve();
for (auto* block: program.blocks) {
block->condition = block->condition.Resolve();
block->indirect_target = block->indirect_target.Resolve();
}
}
@@ -712,9 +698,9 @@ std::string ProgramToString(const Program& program) {
std::string text;
for (size_t block_index = 0; block_index < program.blocks.size(); block_index++) {
text += fmt::format("Block ${} pc=0x{:08x}..0x{:08x}\n", block_index,
program.block_info[block_index].start_pc,
program.block_info[block_index].end_pc);
text +=
fmt::format("Block ${} pc=0x{:08x}..0x{:08x}\n", block_index,
program.blocks[block_index]->start_pc, program.blocks[block_index]->end_pc);
for (const auto& inst: *program.blocks[block_index]) {
const auto type = inst.GetType();
if (type != Type::Void) {
@@ -493,15 +493,6 @@ struct ShaderInfo {
bool operator==(const ShaderInfo& other) const = default;
};
struct BlockInfo {
uint32_t id = 0;
uint32_t start_pc = 0;
uint32_t end_pc = 0;
CFG::Terminator terminator;
Value condition;
Value indirect_target;
};
struct DescriptorSource {
struct IndirectDescriptor {
struct SelectorRead {
@@ -631,7 +622,6 @@ struct Program: ResourcePlan {
bool dispatcher_fallback = false;
CFG::FailureKind cfg_failure_kind = CFG::FailureKind::None;
std::string fallback_reason;
std::vector<BlockInfo> block_info;
struct ScalarWrite { uint32_t pc; ScalarReg reg; };
std::vector<ScalarWrite> scalar_writes;
// Typed memory and export instructions reference shader-local metadata by dense index.
@@ -680,30 +680,29 @@ bool BuildSamplerPlan(const ShaderInfo& base, SamplerPlan& plan) {
template <typename Predicate>
static std::vector<ResourceBlock> ResourceControlFlow(const Program& program, const Predicate& predicate) {
// The renderer checks captured scalar reads against final buffer and image write ranges.
if (program.blocks.size() != program.block_info.size() || program.has_address_writes) {
if (program.has_address_writes) {
return {};
}
std::unordered_map<uint32_t, uint32_t> indices;
for (uint32_t i = 0; i < program.block_info.size(); i++) {
if (!indices.emplace(program.block_info[i].id, i).second) {
std::unordered_map<const Block*, uint32_t> indices;
for (uint32_t i = 0; i < program.blocks.size(); i++) {
if (!indices.emplace(program.blocks[i], i).second) {
return {};
}
}
std::vector<ResourceBlock> blocks(program.blocks.size());
for (uint32_t i = 0; i < blocks.size(); i++) {
auto& block = blocks[i];
const auto& info = program.block_info[i];
const auto& info = *program.blocks[i];
const auto& terminator = info.terminator;
std::vector<uint32_t> successors;
const std::array targets {terminator.true_block, terminator.false_block};
std::span<Block* const> successors;
switch (terminator.kind) {
case CFG::TerminatorKind::Branch: successors.push_back(terminator.true_block); break;
case CFG::TerminatorKind::Branch: successors = std::span(targets).first(1); break;
case CFG::TerminatorKind::ConditionalBranch:
successors = {terminator.true_block, terminator.false_block};
successors = targets;
block.condition = info.condition;
break;
case CFG::TerminatorKind::IndirectBranch:
successors = terminator.indirect_targets;
break;
case CFG::TerminatorKind::IndirectBranch: successors = info.ImmSuccessors(); break;
case CFG::TerminatorKind::Return: break;
default: return {};
}
@@ -824,17 +823,16 @@ static std::optional<std::array<uint64_t, 3>> FillIndex(Value value, uint32_t ax
}
static UniformFillPlan AnalyzeUniformFill(const Program& program) {
if (program.stage != ShaderType::Compute || program.blocks.empty() ||
program.blocks.size() != program.block_info.size() || program.info.uses_dma ||
if (program.stage != ShaderType::Compute || program.blocks.empty() || program.info.uses_dma ||
!program.info.samplers.empty()) {
return {};
}
std::unordered_set<uint32_t> visited;
uint32_t index = 0;
const Inst* store = nullptr;
std::unordered_set<const Block*> visited;
const auto* block = program.blocks.front();
const Inst* store = nullptr;
for (;;) {
if (!visited.insert(index).second) return {};
for (const auto& inst: *program.blocks[index]) {
if (block == nullptr || !visited.insert(block).second) return {};
for (const auto& inst: *block) {
if (AddressOpcodeInfoOf(inst.GetOpcode()).access != AddressAccess::None) return {};
if (!inst.MayHaveSideEffects()) continue;
if (store != nullptr || (BufferAccessOf(inst.GetOpcode()) != BufferAccess::Write &&
@@ -842,12 +840,10 @@ static UniformFillPlan AnalyzeUniformFill(const Program& program) {
return {};
store = &inst;
}
const auto& term = program.block_info[index].terminator;
const auto& term = block->terminator;
if (term.kind == CFG::TerminatorKind::Return) break;
if (term.kind != CFG::TerminatorKind::Branch) return {};
const auto next = std::ranges::find(program.block_info, term.true_block, &BlockInfo::id);
if (next == program.block_info.end()) return {};
index = static_cast<uint32_t>(next - program.block_info.begin());
block = term.true_block;
}
if (store == nullptr || visited.size() != program.blocks.size()) return {};
for (const auto& buffer: program.info.buffers) {
@@ -613,8 +613,7 @@ private:
value = value.Resolve();
const auto* phi = value.TryInstruction();
if (m_shader_writes || phi == nullptr || phi->GetOpcode() != ValueOpcode::Phi ||
phi->NumArgs() != 2u || phi->NumPhiBlocks() != 2u || phi->GetType() != Type::U32 ||
m_program.blocks.size() != m_program.block_info.size()) {
phi->NumArgs() != 2u || phi->NumPhiBlocks() != 2u || phi->GetType() != Type::U32) {
return value;
}
const auto* merge = phi->Parent();
@@ -637,7 +636,7 @@ private:
if (branch == merge || branch->ImmSuccessors().size() != 2u) {
return value;
}
std::array<uint32_t, 2> target_ids;
std::array<const Block*, 2> targets;
for (uint32_t arm = 0; arm < 2; arm++) {
const auto* incoming = phi->PhiBlock(arm);
if (incoming == merge ||
@@ -648,29 +647,20 @@ private:
incoming->ImmSuccessors()[0] != merge))) {
return value;
}
const auto* target = incoming == branch ? merge : incoming;
const auto it = std::ranges::find(m_program.blocks, target);
if (it == m_program.blocks.end()) {
return value;
}
target_ids[arm] = m_program.block_info[it - m_program.blocks.begin()].id;
targets[arm] = incoming == branch ? merge : incoming;
}
const auto branch_it = std::ranges::find(m_program.blocks, branch);
if (branch_it == m_program.blocks.end()) {
return value;
}
const auto& info = m_program.block_info[branch_it - m_program.blocks.begin()];
const auto& info = *branch;
const auto& term = info.terminator;
if (term.kind != CFG::TerminatorKind::ConditionalBranch ||
!((term.true_block == target_ids[0] && term.false_block == target_ids[1]) ||
(term.false_block == target_ids[0] && term.true_block == target_ids[1])) ||
!((term.true_block == targets[0] && term.false_block == targets[1]) ||
(term.false_block == targets[0] && term.true_block == targets[1])) ||
!ValidateRuntimeValue(m_program, info.condition, RuntimeValueType::Integer) ||
!ValidateRuntimeValue(m_program, phi->Arg(0)) ||
!ValidateRuntimeValue(m_program, phi->Arg(1))) {
return value;
}
// Retain a host expression; replacing the GPU Phi would break SSA dominance.
const auto true_arg = term.true_block == target_ids[0] ? 0u : 1u;
const auto true_arg = term.true_block == targets[0] ? 0u : 1u;
auto& selected = m_program.value_storage.emplace_back(ValueOpcode::SelectU32);
selected.SetArg(0, info.condition);
selected.SetArg(1, phi->Arg(true_arg));
@@ -852,9 +842,9 @@ private:
if (entry.source.dwords[word].Resolve() == Value(read))
entry.source.dwords[word] = flat;
}
for (auto& info: m_program.block_info) {
if (info.condition.Resolve() == Value(read)) info.condition = flat;
if (info.indirect_target.Resolve() == Value(read)) info.indirect_target = flat;
for (auto* block: m_program.blocks) {
if (block->condition.Resolve() == Value(read)) block->condition = flat;
if (block->indirect_target.Resolve() == Value(read)) block->indirect_target = flat;
}
if (keep) {
if (resolved->planning_handle.IsEmpty()) {
@@ -1022,9 +1012,6 @@ private:
};
bool NonzeroOnEntry(Value value, const Block* block) const {
if (m_program.blocks.size() != m_program.block_info.size()) {
return false;
}
// Each unique predecessor must execute before this use. Stop at joins: an
// unrelated comparison is not a bound on FindILsb's zero-input sentinel.
for (size_t depth = 0; block != nullptr && depth < m_program.blocks.size(); ++depth) {
@@ -1098,15 +1085,11 @@ private:
}
std::optional<EdgePredicate> ConditionalEdge(const Block* from, const Block* to) const {
const auto position = std::ranges::find(m_program.blocks, from);
const auto target = std::ranges::find(m_program.blocks, to);
if (position == m_program.blocks.end() || target == m_program.blocks.end()) return {};
const auto& info = m_program.block_info[position - m_program.blocks.begin()];
const auto& term = info.terminator;
const auto id = m_program.block_info[target - m_program.blocks.begin()].id;
const auto& term = from->terminator;
if (term.kind != CFG::TerminatorKind::ConditionalBranch ||
(term.true_block == id) == (term.false_block == id)) return {};
EdgePredicate edge {info.condition, term.true_block == id};
(term.true_block == to) == (term.false_block == to))
return {};
EdgePredicate edge {from->condition, term.true_block == to};
while (const auto* inst = edge.condition.Resolve().TryInstruction()) {
if (inst->GetOpcode() == ValueOpcode::LogicalNot) {
edge.positive = !edge.positive;
@@ -1470,9 +1453,9 @@ private:
const Inst* BoundedLoop(Value key, const Block* use, const auto& accepts_bound) const {
const auto* phi = key.Resolve().TryInstruction();
if (phi == nullptr || phi->GetOpcode() != ValueOpcode::Phi ||
phi->GetType() != Type::U32 || phi->NumArgs() != 2u || phi->NumPhiBlocks() != 2u ||
m_program.blocks.size() != m_program.block_info.size()) return {};
if (phi == nullptr || phi->GetOpcode() != ValueOpcode::Phi || phi->GetType() != Type::U32 ||
phi->NumArgs() != 2u || phi->NumPhiBlocks() != 2u)
return {};
const Block* increment_block = nullptr;
uint32_t initial_arm = 0;
for (uint32_t initial = 0; initial < 2u; ++initial) {
@@ -1904,22 +1887,13 @@ private:
}
bool ImpossibleSwitchEdge(const Block* from, const Block* to) const {
const auto from_it = std::ranges::find(m_program.blocks, from);
const auto to_it = std::ranges::find(m_program.blocks, to);
if (from_it == m_program.blocks.end() || to_it == m_program.blocks.end() ||
m_program.block_info.size() != m_program.blocks.size()) return false;
const auto& info = m_program.block_info[from_it - m_program.blocks.begin()];
const auto& term = info.terminator;
if (term.kind != CFG::TerminatorKind::IndirectBranch ||
term.indirect_selector_code == UINT32_MAX ||
term.indirect_selector_values.empty() ||
term.indirect_selector_values.size() != term.indirect_selector_targets.size()) return false;
const auto target = m_program.block_info[to_it - m_program.blocks.begin()].id;
const auto maximum = SelectorMaximum(info.indirect_target);
const auto& term = from->terminator;
if (term.kind != CFG::TerminatorKind::IndirectBranch || !term.indexed) return false;
const auto maximum = SelectorMaximum(from->indirect_target);
bool found = false;
for (size_t i = 0; i < term.indirect_selector_values.size(); ++i) {
if (term.indirect_selector_targets[i] != target) continue;
if (term.indirect_selector_values[i] <= maximum) return false;
for (const auto& branch: term.cases) {
if (branch.target != to) continue;
if (branch.value <= maximum) return false;
found = true;
}
return found;
+9 -9
View File
@@ -27,7 +27,7 @@ AddValueBlock(Libs::Graphics::ShaderRecompiler::IR::Program &program) {
auto block = std::make_unique<Block>();
auto *result = block.get();
program.blocks.push_back(result);
program.block_info.push_back({.id = 0});
result->id = static_cast<uint32_t>(program.blocks.size() - 1u);
program.block_storage.push_back(std::move(block));
return *result;
}
@@ -462,16 +462,16 @@ void TestWrittenDescriptorUsesStrictReaderOnce() {
{Value(&offset), Value(4u)});
auto &store_block = AddValueBlock(program);
AddValueBlock(program);
program.block_info[0].condition = Value(&condition);
program.block_info[0].terminator.kind =
program.blocks[0]->condition = Value(&condition);
program.blocks[0]->terminator.kind =
Libs::Graphics::ShaderRecompiler::CFG::TerminatorKind::ConditionalBranch;
program.block_info[0].terminator.true_block = 1;
program.block_info[0].terminator.false_block = 2;
program.block_info[1].id = 1;
program.block_info[1].terminator.kind =
program.blocks[0]->terminator.true_block = program.blocks[1];
program.blocks[0]->terminator.false_block = program.blocks[2];
program.blocks[1]->id = 1;
program.blocks[1]->terminator.kind =
Libs::Graphics::ShaderRecompiler::CFG::TerminatorKind::Return;
program.block_info[2].id = 2;
program.block_info[2].terminator.kind =
program.blocks[2]->id = 2;
program.blocks[2]->terminator.kind =
Libs::Graphics::ShaderRecompiler::CFG::TerminatorKind::Return;
program.memory_info.push_back({.kind = ResourceKind::Buffer, .resource = 0});
auto &output = store_block.AppendNewInst(ValueOpcode::GetBufferResource,
+190 -141
View File
@@ -58,8 +58,8 @@ struct Fixture {
auto *result = storage.get();
program.block_storage.push_back(std::move(storage));
program.blocks.push_back(result);
program.block_info.push_back(
{.id = static_cast<uint32_t>(program.block_info.size())});
program.blocks.back()->id =
static_cast<uint32_t>(program.blocks.size() - 1u);
return result;
}
@@ -126,8 +126,8 @@ struct Fixture {
}
void PlanAndTrack() {
for (size_t index = 0; index < program.block_info.size(); ++index) {
const auto condition = program.block_info[index].condition;
for (size_t index = 0; index < program.blocks.size(); ++index) {
const auto condition = program.blocks[index]->condition;
if (!condition.IsEmpty())
Emit(ValueOpcode::Reference, {condition}, 0, program.blocks[index]);
}
@@ -475,9 +475,11 @@ void TestWaterfallImageTable() {
{guard, fixture->Emit(ValueOpcode::IEqual32, {image_words[word], local})});
}
}
fixture->program.block_info[0].condition = guard;
fixture->program.block_info[0].terminator = {
.kind = CFG::TerminatorKind::ConditionalBranch, .true_block = 1u, .false_block = 2u};
fixture->program.blocks[0]->condition = guard;
fixture->program.blocks[0]->terminator = {
.kind = CFG::TerminatorKind::ConditionalBranch,
.true_block = fixture->program.blocks[1u],
.false_block = fixture->program.blocks[2u]};
const auto sample = [&](Value image, Block *block) {
const auto sampler = fixture->Sampler({Value(0u), Value(0u), Value(0u), Value(0u)});
MemoryInfo memory;
@@ -589,19 +591,20 @@ void TestGuardedDirectImageTable() {
if (guard != Guard::Plain && guard != Guard::SccNonZero) {
condition = fixture.Emit(ValueOpcode::ConditionRef, {condition}, kind);
}
fixture.program.block_info[0].condition = condition;
fixture.program.block_info[0].terminator = {
fixture.program.blocks[0]->condition = condition;
fixture.program.blocks[0]->terminator = {
.kind = CFG::TerminatorKind::ConditionalBranch,
.true_block = guard == Guard::Zero ? 1u : 4u,
.false_block = guard == Guard::Zero ? 4u : 1u};
.true_block = fixture.program.blocks[guard == Guard::Zero ? 1u : 4u],
.false_block = fixture.program.blocks[guard == Guard::Zero ? 4u : 1u]};
for (uint32_t block = 1; block < 4; ++block) {
fixture.program.block_info[block].terminator = {
.kind = CFG::TerminatorKind::Branch, .true_block = block + 1u};
fixture.program.blocks[block]->terminator = {
.kind = CFG::TerminatorKind::Branch,
.true_block = fixture.program.blocks[block + 1u]};
}
fixture.program.block_info[4].terminator = {
fixture.program.blocks[4]->terminator = {
.kind = guard == Guard::Bypass ? CFG::TerminatorKind::Branch
: CFG::TerminatorKind::Return,
.true_block = 3u};
: CFG::TerminatorKind::Return,
.true_block = fixture.program.blocks[3u]};
const auto srt = fixture.Address(fixture.UserData(0), fixture.UserData(1));
std::array<Value, 2> pointer;
for (uint32_t word = 0; word < pointer.size(); ++word) {
@@ -767,27 +770,40 @@ void TestBoundedComputeImageLoop() {
if (variant == Variant::ExitBypass) exit->AddBranch(body);
if (variant == Variant::IncrementBypass || variant == Variant::PreviousBound)
exit->AddBranch(latch);
fixture.program.block_info[0].terminator = {
.kind = variant == Variant::EntryBypass ? CFG::TerminatorKind::ConditionalBranch
: CFG::TerminatorKind::Branch,
.true_block = 1u, .false_block = 2u};
fixture.program.block_info[1].terminator = {
fixture.program.blocks[0]->terminator = {
.kind = variant == Variant::EntryBypass
? CFG::TerminatorKind::ConditionalBranch
: CFG::TerminatorKind::Branch,
.true_block = fixture.program.blocks[1u],
.false_block = fixture.program.blocks[2u]};
fixture.program.blocks[1]->terminator = {
.kind = CFG::TerminatorKind::ConditionalBranch,
.true_block = variant == Variant::TrueEdge ? 2u : 4u,
.false_block = variant == Variant::TrueEdge ? 4u : 2u};
fixture.program.block_info[2].terminator = {
.kind = CFG::TerminatorKind::Branch, .true_block = 3u};
fixture.program.block_info[3].terminator = {
.kind = variant == Variant::PreviousBound ? CFG::TerminatorKind::ConditionalBranch
: CFG::TerminatorKind::Branch,
.true_block = 1u, .false_block = 5u};
fixture.program.block_info[4].terminator = {
.true_block =
fixture.program.blocks[variant == Variant::TrueEdge ? 2u : 4u],
.false_block =
fixture.program.blocks[variant == Variant::TrueEdge ? 4u : 2u]};
fixture.program.blocks[2]->terminator = {
.kind = CFG::TerminatorKind::Branch,
.true_block = fixture.program.blocks[3u]};
fixture.program.blocks[3]->terminator = {
.kind = variant == Variant::PreviousBound
? CFG::TerminatorKind::ConditionalBranch
: CFG::TerminatorKind::Branch,
.true_block = fixture.program.blocks[1u],
.false_block = final_exit};
fixture.program.blocks[4]->terminator = {
.kind = final_exit != exit || variant == Variant::ExitBypass ||
variant == Variant::IncrementBypass
? CFG::TerminatorKind::Branch : CFG::TerminatorKind::Return,
.true_block = final_exit != exit || variant == Variant::IncrementBypass ? 3u : 2u};
? CFG::TerminatorKind::Branch
: CFG::TerminatorKind::Return,
.true_block =
fixture.program.blocks[final_exit != exit ||
variant == Variant::IncrementBypass
? 3u
: 2u]};
if (final_exit != exit)
fixture.program.block_info[5].terminator.kind = CFG::TerminatorKind::Return;
fixture.program.blocks[5]->terminator.kind =
CFG::TerminatorKind::Return;
}
auto &phi = header->AppendNewInst(ValueOpcode::Phi, {},
@@ -859,14 +875,16 @@ void TestBoundedComputeImageLoop() {
block->AddBranch(fixture.program.blocks[yes]);
block->AddBranch(fixture.program.blocks[no]);
const auto inverse = fixture.Emit(ValueOpcode::LogicalNot, {predicate}, 0, block);
fixture.program.block_info[index].condition =
fixture.program.blocks[index]->condition =
fixture.Emit(ValueOpcode::ConditionRef, {inverse}, kind, block);
fixture.program.block_info[index].terminator = {
fixture.program.blocks[index]->terminator = {
.kind = CFG::TerminatorKind::ConditionalBranch,
.true_block = yes, .false_block = no};
.true_block = fixture.program.blocks[yes],
.false_block = fixture.program.blocks[no]};
};
fixture.program.block_info[0].terminator = {
.kind = CFG::TerminatorKind::Branch, .true_block = 1u};
fixture.program.blocks[0]->terminator = {
.kind = CFG::TerminatorKind::Branch,
.true_block = fixture.program.blocks[1u]};
branch(1u, 4u, 5u, active, CFG::BranchCondition::ExecZero);
branch(5u, variant == Variant::GuardedDiamondBypass ? 7u : 4u, 6u,
diamond ? execute : mask,
@@ -877,13 +895,15 @@ void TestBoundedComputeImageLoop() {
fixture.Emit(ValueOpcode::IEqual32, {local, Value(1u)}, 0, guard)}, 0, guard);
branch(6u, 3u, 2u, image_guard, CFG::BranchCondition::ExecZero);
body->AddBranch(latch);
fixture.program.block_info[2].terminator = {
.kind = CFG::TerminatorKind::Branch, .true_block = 3u};
fixture.program.blocks[2]->terminator = {
.kind = CFG::TerminatorKind::Branch,
.true_block = fixture.program.blocks[3u]};
branch(3u, 4u, 7u, next_active, CFG::BranchCondition::ExecZero);
increment->AddBranch(header);
fixture.program.block_info[7].terminator = {
.kind = CFG::TerminatorKind::Branch, .true_block = 1u};
fixture.program.block_info[4].terminator.kind = CFG::TerminatorKind::Return;
fixture.program.blocks[7]->terminator = {
.kind = CFG::TerminatorKind::Branch,
.true_block = fixture.program.blocks[1u]};
fixture.program.blocks[4]->terminator.kind = CFG::TerminatorKind::Return;
} else {
const auto active = initial_active;
const auto allowed = fixture.Emit(variant == Variant::Disjunction
@@ -899,14 +919,15 @@ void TestBoundedComputeImageLoop() {
: CFG::BranchCondition::ExecZero, header);
if (variant == Variant::Nonzero || variant == Variant::WrongPolarity)
condition = fixture.Emit(ValueOpcode::LogicalNot, {condition}, 0, header);
fixture.program.block_info[1].condition = condition;
fixture.program.blocks[1]->condition = condition;
if (variant == Variant::PreviousBound) {
previous->AddPhiOperand(entry, Value(false));
previous->AddPhiOperand(latch, in_range);
const auto continuing = fixture.Emit(ValueOpcode::LogicalOr,
{in_range, Value(previous)}, 0, latch);
fixture.program.block_info[3].condition = fixture.Emit(ValueOpcode::ConditionRef,
{continuing}, CFG::BranchCondition::SccNonZero, latch);
fixture.program.blocks[3]->condition =
fixture.Emit(ValueOpcode::ConditionRef, {continuing},
CFG::BranchCondition::SccNonZero, latch);
}
}
const auto step = fixture.Emit(ValueOpcode::IAdd32,
@@ -1067,21 +1088,27 @@ void TestUniformizedMaterialImageKeys() {
choose->AddBranch(done);
if (first_lane) {
sample_entry->AddBranch(first_lane_loop ? sample_header : sample);
fixture.program.block_info[sample_entry_id].terminator = {
.kind = CFG::TerminatorKind::Branch, .true_block = first_lane_loop ? 9u : 7u};
fixture.program.blocks[sample_entry_id]->terminator = {
.kind = CFG::TerminatorKind::Branch,
.true_block = fixture.program.blocks[first_lane_loop ? 9u : 7u]};
}
if (first_lane_loop) {
sample_header->AddBranch(sample);
sample->AddBranch(sample_header);
fixture.program.block_info[9].terminator = {
.kind = CFG::TerminatorKind::Branch, .true_block = 7u,
.merge_block = 8u, .continue_block = 7u, .loop_header = true};
fixture.program.blocks[9]->terminator = {
.kind = CFG::TerminatorKind::Branch,
.true_block = fixture.program.blocks[7u],
.merge_block = fixture.program.blocks[8u],
.continue_block = fixture.program.blocks[7u],
.loop_header = true};
}
sample->AddBranch(done);
fixture.program.block_info[0].terminator = {
.kind = CFG::TerminatorKind::Branch, .true_block = 1u};
fixture.program.block_info[1].terminator = {
.kind = CFG::TerminatorKind::Branch, .true_block = 2u};
fixture.program.blocks[0]->terminator = {
.kind = CFG::TerminatorKind::Branch,
.true_block = fixture.program.blocks[1u]};
fixture.program.blocks[1]->terminator = {
.kind = CFG::TerminatorKind::Branch,
.true_block = fixture.program.blocks[2u]};
const auto enabled = fixture.Emit(
ValueOpcode::INotEqual32, {fixture.UserData(5u), Value(0u)}, 0, entry);
const auto local = fixture.Emit(ValueOpcode::GetBuiltin,
@@ -1097,23 +1124,26 @@ void TestUniformizedMaterialImageKeys() {
ValueOpcode::Phi, {}, static_cast<uint64_t>(Type::U32));
const auto mask = Value(&mask_phi);
const auto active = Value(&active_phi);
fixture.program.block_info[2].condition = branch(
fixture.Emit(ValueOpcode::LogicalNot, {active}, 0, inactive),
variant == Variant::WrongExit ? CFG::BranchCondition::ExecNonZero
: CFG::BranchCondition::ExecZero, inactive);
fixture.program.block_info[2].terminator = {
fixture.program.blocks[2]->condition =
branch(fixture.Emit(ValueOpcode::LogicalNot, {active}, 0, inactive),
variant == Variant::WrongExit ? CFG::BranchCondition::ExecNonZero
: CFG::BranchCondition::ExecZero,
inactive);
fixture.program.blocks[2]->terminator = {
.kind = CFG::TerminatorKind::ConditionalBranch,
.true_block = 5u, .false_block = 3u};
.true_block = fixture.program.blocks[5u],
.false_block = fixture.program.blocks[3u]};
const auto nonzero = fixture.Emit(
ValueOpcode::INotEqual32, {Value(0u), mask}, 0, sentinel);
const auto bit_guard = fixture.Emit(
ValueOpcode::LogicalAnd, {active, nonzero}, 0, sentinel);
fixture.program.block_info[3].condition = branch(
fixture.Emit(ValueOpcode::LogicalNot, {bit_guard}, 0, sentinel),
CFG::BranchCondition::ExecZero, sentinel);
fixture.program.block_info[3].terminator = {
fixture.program.blocks[3]->condition =
branch(fixture.Emit(ValueOpcode::LogicalNot, {bit_guard}, 0, sentinel),
CFG::BranchCondition::ExecZero, sentinel);
fixture.program.blocks[3]->terminator = {
.kind = CFG::TerminatorKind::ConditionalBranch,
.true_block = 5u, .false_block = 4u};
.true_block = fixture.program.blocks[5u],
.false_block = fixture.program.blocks[4u]};
const auto first = fixture.Emit(ValueOpcode::FindILsb32, {mask}, 0, bit);
const auto position = fixture.Emit(
ValueOpcode::BitwiseAnd32, {first, Value(31u)}, 0, bit);
@@ -1130,11 +1160,12 @@ void TestUniformizedMaterialImageKeys() {
{mask, removed}, 0, bit);
const auto continuation = fixture.Emit(
ValueOpcode::LogicalAnd, {bit_guard, active_on_entry}, 0, bit);
fixture.program.block_info[4].condition = branch(
continuation, CFG::BranchCondition::ExecNonZero, bit);
fixture.program.block_info[4].terminator = {
fixture.program.blocks[4]->condition =
branch(continuation, CFG::BranchCondition::ExecNonZero, bit);
fixture.program.blocks[4]->terminator = {
.kind = CFG::TerminatorKind::ConditionalBranch,
.true_block = 1u, .false_block = 5u};
.true_block = fixture.program.blocks[1u],
.false_block = fixture.program.blocks[5u]};
active_phi.AddPhiOperand(entry, active_on_entry);
active_phi.AddPhiOperand(bit, continuation);
mask_phi.AddPhiOperand(entry, fixture.UserData(4u));
@@ -1159,8 +1190,9 @@ void TestUniformizedMaterialImageKeys() {
ValueOpcode::SGreaterThan32, {Value(32u), index}, 0, merge);
const auto material_guard = fixture.Emit(
ValueOpcode::LogicalAnd, {active_on_entry, below}, 0, merge);
fixture.program.block_info[5].terminator = {
.kind = CFG::TerminatorKind::Branch, .true_block = 6u};
fixture.program.blocks[5]->terminator = {
.kind = CFG::TerminatorKind::Branch,
.true_block = fixture.program.blocks[6u]};
const auto scaled = fixture.Emit(
ValueOpcode::ShiftLeftLogical32, {index, Value(4u)}, 0, choose);
const auto selected_scale = fixture.Emit(
@@ -1202,12 +1234,13 @@ void TestUniformizedMaterialImageKeys() {
sample_active_phi->AddPhiOperand(sample_entry, initial);
sample_active = Value(sample_active_phi);
}
fixture.program.block_info[6].condition = branch(
variant == Variant::EmptyFirstEntry ? enabled : initial,
CFG::BranchCondition::ExecNonZero, choose);
fixture.program.block_info[6].terminator = {
fixture.program.blocks[6]->condition =
branch(variant == Variant::EmptyFirstEntry ? enabled : initial,
CFG::BranchCondition::ExecNonZero, choose);
fixture.program.blocks[6]->terminator = {
.kind = CFG::TerminatorKind::ConditionalBranch,
.true_block = sample_entry_id, .false_block = 8u};
.true_block = fixture.program.blocks[sample_entry_id],
.false_block = fixture.program.blocks[8u]};
}
auto *key_block = first_lane ? sample : choose;
const auto key = first_lane
@@ -1219,12 +1252,13 @@ void TestUniformizedMaterialImageKeys() {
const auto sample_guard = fixture.Emit(
ValueOpcode::LogicalAnd, {sample_active, compared}, 0, key_block);
if (!first_lane) {
fixture.program.block_info[6].condition = branch(
fixture.program.blocks[6]->condition = branch(
fixture.Emit(ValueOpcode::LogicalNot, {sample_guard}, 0, choose),
CFG::BranchCondition::ExecZero, choose);
fixture.program.block_info[6].terminator = {
fixture.program.blocks[6]->terminator = {
.kind = CFG::TerminatorKind::ConditionalBranch,
.true_block = 8u, .false_block = 7u};
.true_block = fixture.program.blocks[8u],
.false_block = fixture.program.blocks[7u]};
}
const auto table_offset = fixture.Emit(
ValueOpcode::IAdd32,
@@ -1272,17 +1306,19 @@ void TestUniformizedMaterialImageKeys() {
if (variant == Variant::WideningFirstBackedge)
remaining = fixture.Emit(ValueOpcode::LogicalOr, {remaining, enabled}, 0, sample);
sample_active_phi->AddPhiOperand(sample, remaining);
fixture.program.block_info[7].condition = branch(
fixture.program.blocks[7]->condition = branch(
variant == Variant::WrongFirstBackedge ? sample_guard : remaining,
CFG::BranchCondition::ExecNonZero, sample);
fixture.program.block_info[7].terminator = {
fixture.program.blocks[7]->terminator = {
.kind = CFG::TerminatorKind::ConditionalBranch,
.true_block = 9u, .false_block = 8u};
.true_block = fixture.program.blocks[9u],
.false_block = fixture.program.blocks[8u]};
} else {
fixture.program.block_info[7].terminator = {
.kind = CFG::TerminatorKind::Branch, .true_block = 8u};
fixture.program.blocks[7]->terminator = {
.kind = CFG::TerminatorKind::Branch,
.true_block = fixture.program.blocks[8u]};
}
fixture.program.block_info[8].terminator.kind = CFG::TerminatorKind::Return;
fixture.program.blocks[8]->terminator.kind = CFG::TerminatorKind::Return;
const auto output = fixture.Emit(
ValueOpcode::GetBufferResource,
{fixture.UserData(8u), fixture.UserData(9u),
@@ -1556,10 +1592,10 @@ void TestComputeBufferFill() {
};
const auto Run = [](Options options) {
Fixture fixture;
fixture.program.block_info[0].terminator.kind =
fixture.program.blocks[0]->terminator.kind =
Libs::Graphics::ShaderRecompiler::CFG::TerminatorKind::Return;
if (options.branch) {
fixture.program.block_info[0].terminator.kind = Libs::Graphics::
fixture.program.blocks[0]->terminator.kind = Libs::Graphics::
ShaderRecompiler::CFG::TerminatorKind::ConditionalBranch;
}
const auto buffer =
@@ -2369,16 +2405,20 @@ ResourcePlan ConditionalSamplerPlan(bool diamond, bool reverse, bool reverse_phi
alternate->AddBranch(merge);
if (diamond) {
initial->AddBranch(merge);
fixture.program.block_info[1].terminator = {
.kind = CFG::TerminatorKind::Branch, .true_block = merge_id};
fixture.program.blocks[1]->terminator = {
.kind = CFG::TerminatorKind::Branch,
.true_block = fixture.program.blocks[merge_id]};
}
fixture.program.block_info[0].terminator = {
fixture.program.blocks[0]->terminator = {
.kind = CFG::TerminatorKind::ConditionalBranch,
.true_block = reverse ? alternate_id : initial_target,
.false_block = reverse ? initial_target : alternate_id};
fixture.program.block_info[alternate_id].terminator = {
.kind = CFG::TerminatorKind::Branch, .true_block = merge_id};
fixture.program.block_info[merge_id].terminator.kind =
.true_block =
fixture.program.blocks[reverse ? alternate_id : initial_target],
.false_block =
fixture.program.blocks[reverse ? initial_target : alternate_id]};
fixture.program.blocks[alternate_id]->terminator = {
.kind = CFG::TerminatorKind::Branch,
.true_block = fixture.program.blocks[merge_id]};
fixture.program.blocks[merge_id]->terminator.kind =
CFG::TerminatorKind::Return;
const auto control =
fixture.Buffer({Value(0x2000u), Value(0u), Value(200u), Value(0u)});
@@ -2389,7 +2429,7 @@ ResourcePlan ConditionalSamplerPlan(bool diamond, bool reverse, bool reverse_phi
if (nonuniform) {
flag = fixture.Emit(ValueOpcode::LaneId);
}
fixture.program.block_info[0].condition =
fixture.program.blocks[0]->condition =
fixture.Emit(ValueOpcode::SGreaterThanEqual32, {flag, Value(0u)});
if (writable) {
MemoryInfo memory;
@@ -2590,22 +2630,21 @@ void TestFiniteImageBitScanSentinel() {
{Value(uint32_t(StageInputKind::LocalInvocationIndex)), Value(0u)});
const auto condition = fixture.Emit(nonzero ? ValueOpcode::INotEqual32 : ValueOpcode::IEqual32,
{mask, Value(0u)});
auto &guard = fixture.program.block_info[0];
auto &guard = *fixture.program.blocks[0];
guard.condition = condition;
guard.terminator.kind = CFG::TerminatorKind::ConditionalBranch;
guard.terminator.true_block = 1;
guard.terminator.false_block = 4;
guard.terminator.true_block = dispatch;
guard.terminator.false_block = exit;
fixture.block = dispatch;
const auto first = fixture.Emit(ValueOpcode::FindILsb32, {mask});
const auto minimum = fixture.Emit(ValueOpcode::UMin32, {first, Value(32u)});
const auto selector = fixture.Emit(ValueOpcode::ShiftLeftLogical32, {minimum, Value(2u)});
fixture.Emit(ValueOpcode::ReferenceU32, {selector});
auto &table = fixture.program.block_info[1];
auto &table = *fixture.program.blocks[1];
table.indirect_target = selector;
table.terminator.kind = CFG::TerminatorKind::IndirectBranch;
table.terminator.indirect_selector_code = 0;
table.terminator.indirect_selector_values = {0u, 128u};
table.terminator.indirect_selector_targets = {2u, 3u};
table.terminator.indexed = true;
table.terminator.cases = {{0u, load}, {128u, merge}};
std::array<Value, 8> words;
for (uint32_t word = 0; word < words.size(); ++word) {
auto &phi = merge->AppendNewInst(ValueOpcode::Phi, {}, uint64_t(Type::U32));
@@ -2743,21 +2782,25 @@ void TestBoundedRelativeRegisterWrites() {
header->AddBranch(body);
header->AddBranch(exit);
body->AddBranch(header);
fixture.program.block_info[0].terminator = {
.kind = variant == Variant::EntryBypass ? CFG::TerminatorKind::ConditionalBranch
: CFG::TerminatorKind::Branch,
.true_block = 1u, .false_block = 2u};
fixture.program.block_info[1].terminator = {
fixture.program.blocks[0]->terminator = {
.kind = variant == Variant::EntryBypass
? CFG::TerminatorKind::ConditionalBranch
: CFG::TerminatorKind::Branch,
.true_block = fixture.program.blocks[1u],
.false_block = fixture.program.blocks[2u]};
fixture.program.blocks[1]->terminator = {
.kind = CFG::TerminatorKind::ConditionalBranch,
.true_block = 2u, .false_block = 3u};
fixture.program.block_info[2].terminator = {
.kind = CFG::TerminatorKind::Branch, .true_block = 1u};
fixture.program.block_info[3].terminator.kind = CFG::TerminatorKind::Return;
.true_block = fixture.program.blocks[2u],
.false_block = fixture.program.blocks[3u]};
fixture.program.blocks[2]->terminator = {
.kind = CFG::TerminatorKind::Branch,
.true_block = fixture.program.blocks[1u]};
fixture.program.blocks[3]->terminator.kind = CFG::TerminatorKind::Return;
const auto lane = fixture.Emit(ValueOpcode::GetBuiltin,
{Value(static_cast<uint32_t>(StageInputKind::LocalInvocationId)), Value(0u)});
const auto enabled = fixture.Emit(ValueOpcode::ULessThan32, {lane, Value(32u)});
fixture.program.block_info[0].condition = enabled;
fixture.program.blocks[0]->condition = enabled;
const auto chunk = fixture.Emit(ValueOpcode::SelectU32,
{enabled, Value(64u), Value(512u)});
const auto initial_bound = fixture.Emit(ValueOpcode::ShiftRightLogical32,
@@ -2769,8 +2812,9 @@ void TestBoundedRelativeRegisterWrites() {
auto &records = header->AppendNewInst(ValueOpcode::Phi, {}, uint64_t(Type::U32));
const auto in_range = fixture.Emit(ValueOpcode::ULessThan32,
{Value(&counter), Value(&bound)}, 0, header);
fixture.program.block_info[1].condition = fixture.Emit(
ValueOpcode::ConditionRef, {in_range}, CFG::BranchCondition::VccNonZero, header);
fixture.program.blocks[1]->condition =
fixture.Emit(ValueOpcode::ConditionRef, {in_range},
CFG::BranchCondition::VccNonZero, header);
const auto m0 = fixture.Emit(ValueOpcode::BitwiseAnd32,
{fixture.Emit(ValueOpcode::ShiftLeftLogical32,
{Value(&counter), Value(1u)}, 0, body), Value(255u)}, 0, body);
@@ -2953,26 +2997,27 @@ ResourcePlan ConditionalBufferPlan(ConditionalBufferUse use) {
auto *done = fixture.AddBlock();
auto *condition_block = entry;
uint32_t condition_index = 0;
fixture.program.block_info[0].id = 11;
fixture.program.block_info[1].id = 27;
fixture.program.block_info[2].id = 42;
fixture.program.blocks[0]->id = 11;
fixture.program.blocks[1]->id = 27;
fixture.program.blocks[2]->id = 42;
if (use == ConditionalBufferUse::Loop) {
condition_block = fixture.AddBlock();
condition_index = 3;
fixture.program.block_info[3].id = 55;
fixture.program.block_info[0].terminator = {
.kind = CFG::TerminatorKind::Branch, .true_block = 55};
fixture.program.blocks[3]->id = 55;
fixture.program.blocks[0]->terminator = {
.kind = CFG::TerminatorKind::Branch, .true_block = condition_block};
entry->AddBranch(condition_block);
}
condition_block->AddBranch(optional);
condition_block->AddBranch(done);
optional->AddBranch(use == ConditionalBufferUse::Loop ? condition_block : done);
fixture.program.block_info[condition_index].terminator = {
fixture.program.blocks[condition_index]->terminator = {
.kind = CFG::TerminatorKind::ConditionalBranch,
.true_block = 27, .false_block = 42};
fixture.program.block_info[1].terminator = {
.true_block = optional,
.false_block = done};
fixture.program.blocks[1]->terminator = {
.kind = CFG::TerminatorKind::Branch,
.true_block = use == ConditionalBufferUse::Loop ? 55u : 42u};
.true_block = use == ConditionalBufferUse::Loop ? condition_block : done};
const auto control = fixture.Buffer(
{fixture.UserData(0), fixture.UserData(1), fixture.UserData(2),
@@ -2988,8 +3033,8 @@ ResourcePlan ConditionalBufferPlan(ConditionalBufferUse use) {
phi.AddPhiOperand(optional, Value(1u));
flag = Value(&phi);
}
fixture.program.block_info[condition_index].condition =
fixture.Emit(ValueOpcode::INotEqual32, {flag, Value(0u)}, 0, condition_block);
fixture.program.blocks[condition_index]->condition = fixture.Emit(
ValueOpcode::INotEqual32, {flag, Value(0u)}, 0, condition_block);
const auto payload = fixture.Buffer(
{fixture.UserData(4), fixture.UserData(5), fixture.UserData(6),
@@ -3067,11 +3112,14 @@ void TestGuardedScalarDescriptorReads() {
entry->AddBranch(optional);
entry->AddBranch(done);
optional->AddBranch(done);
fixture.program.block_info[0].terminator = {
.kind = CFG::TerminatorKind::ConditionalBranch, .true_block = 1, .false_block = 2};
fixture.program.block_info[1].terminator = {
.kind = CFG::TerminatorKind::Branch, .true_block = 2};
fixture.program.block_info[2].terminator.kind = CFG::TerminatorKind::Return;
fixture.program.blocks[0]->terminator = {
.kind = CFG::TerminatorKind::ConditionalBranch,
.true_block = fixture.program.blocks[1],
.false_block = fixture.program.blocks[2]};
fixture.program.blocks[1]->terminator = {
.kind = CFG::TerminatorKind::Branch,
.true_block = fixture.program.blocks[2]};
fixture.program.blocks[2]->terminator.kind = CFG::TerminatorKind::Return;
const auto control = fixture.Buffer({fixture.UserData(0), fixture.UserData(1),
fixture.UserData(2), fixture.UserData(3)});
MemoryInfo scalar;
@@ -3083,7 +3131,7 @@ void TestGuardedScalarDescriptorReads() {
{Value(static_cast<uint32_t>(StageInputKind::LocalInvocationId)), Value(0u)});
const auto varying = fixture.Emit(ValueOpcode::INotEqual32, {lane, Value(0u)});
const auto enabled = fixture.Emit(ValueOpcode::INotEqual32, {flag, Value(0u)});
fixture.program.block_info[0].condition =
fixture.program.blocks[0]->condition =
fixture.Emit(ValueOpcode::LogicalAnd, {varying, enabled});
const auto root = fixture.Address(fixture.UserData(4), Value(0u));
const auto Load = [&](Block *block) {
@@ -3192,15 +3240,16 @@ void TestConditionalIndirectImageMaterialization() {
body->AddBranch(done);
const auto flag = fixture->Emit(ValueOpcode::GetUserData,
{Value(static_cast<ScalarReg>(8))}, 0, entry);
fixture->program.block_info[1].condition = fixture->Emit(
ValueOpcode::INotEqual32, {flag, Value(0u)}, 0, entry);
fixture->program.block_info[1].terminator = {
fixture->program.blocks[1]->condition =
fixture->Emit(ValueOpcode::INotEqual32, {flag, Value(0u)}, 0, entry);
fixture->program.blocks[1]->terminator = {
.kind = CFG::TerminatorKind::ConditionalBranch,
.true_block = 0, .false_block = 2};
fixture->program.block_info[0].terminator = {
.kind = CFG::TerminatorKind::Branch, .true_block = 2};
.true_block = fixture->program.blocks[0],
.false_block = fixture->program.blocks[2]};
fixture->program.blocks[0]->terminator = {.kind = CFG::TerminatorKind::Branch,
.true_block =
fixture->program.blocks[2]};
std::swap(fixture->program.blocks[0], fixture->program.blocks[1]);
std::swap(fixture->program.block_info[0], fixture->program.block_info[1]);
fixture->PlanAndTrack();
auto plan = ExtractResourcePlan(fixture->program);
std::array<uint32_t, 9> user_data{
+6 -6
View File
@@ -46,7 +46,7 @@ struct Fixture {
program.block_storage.push_back(std::make_unique<Block>());
auto *block = program.block_storage.back().get();
program.blocks.push_back(block);
program.block_info.push_back({.id = index});
program.blocks.back()->id = index;
}
}
@@ -624,14 +624,14 @@ void TestControlFlowValueSurvivesReadLaneFolding() {
entry->AddBranch(taken);
entry->AddBranch(other);
auto &entry_info = fixture.program.block_info[0];
auto &entry_info = *fixture.program.blocks[0];
entry_info.terminator.kind =
Libs::Graphics::ShaderRecompiler::CFG::TerminatorKind::ConditionalBranch;
entry_info.terminator.true_block = 1;
entry_info.terminator.false_block = 2;
fixture.program.block_info[1].terminator.kind =
entry_info.terminator.true_block = taken;
entry_info.terminator.false_block = other;
fixture.program.blocks[1]->terminator.kind =
Libs::Graphics::ShaderRecompiler::CFG::TerminatorKind::Return;
fixture.program.block_info[2].terminator.kind =
fixture.program.blocks[2]->terminator.kind =
Libs::Graphics::ShaderRecompiler::CFG::TerminatorKind::Return;
const auto undef = fixture.Emit(ValueOpcode::UndefU32);
-2
View File
@@ -34523,7 +34523,6 @@ void CheckIndirectBufferStore(VulkanHarness &vulkan) {
program.block_storage.push_back(std::make_unique<Block>());
auto *block = program.block_storage.back().get();
program.blocks.push_back(block);
program.block_info.push_back({.id = 0});
auto &lane = block->AppendNewInst(ValueOpcode::LaneId);
auto &selector = block->AppendNewInst(ValueOpcode::BitwiseAnd32,
@@ -34629,7 +34628,6 @@ void CheckIndirectImageKeySwitch(VulkanHarness &vulkan) {
program.block_storage.push_back(std::make_unique<Block>());
auto *block = program.block_storage.back().get();
program.blocks.push_back(block);
program.block_info.push_back({.id = 0});
auto &key = block->AppendNewInst(ValueOpcode::LaneId);
auto &image =
+97 -110
View File
@@ -1929,13 +1929,14 @@ void TestSopkCompareImmediateExtension() {
};
auto translated =
TranslateProgram(shader, MakeCompileOptions(ShaderType::Vertex));
const auto branch = std::ranges::find_if(
translated.program.block_info, [](const auto &block) {
return block.terminator.kind == CFG::TerminatorKind::ConditionalBranch;
const auto branch =
std::ranges::find_if(translated.program.blocks, [](const auto *block) {
return block->terminator.kind ==
CFG::TerminatorKind::ConditionalBranch;
});
Check(branch != translated.program.block_info.end(),
Check(branch != translated.program.blocks.end(),
"captured LUT parameter export branch was lost");
const auto condition = branch->condition.Resolve();
const auto condition = (*branch)->condition.Resolve();
Check(condition.IsImmediate() && condition.U1() == (counts != 6u),
"captured LUT unsigned compare suppressed a live parameter export");
}
@@ -11083,14 +11084,12 @@ void TestNewShaderRecompilerSetpcJumpTable() {
Check((result.ir_dump.find("mode=dispatcher") != std::string::npos),
"S_SETPC_B64 jump table did not select dispatcher fallback");
const auto jump = std::find_if(
result.program.block_info.begin(),
result.program.block_info.end(), [](const auto &block) {
return !block.terminator.indirect_targets.empty();
});
Check(jump != result.program.block_info.end() &&
jump->terminator.indirect_targets.size() == 2,
result.program.blocks.begin(), result.program.blocks.end(),
[](const auto *block) { return !block->terminator.cases.empty(); });
Check(jump != result.program.blocks.end() &&
(*jump)->ImmSuccessors().size() == 2,
"S_SETPC_B64 jump table did not retain both targets");
Check(jump->terminator.indirect_selector_values.size() == 2,
Check((*jump)->terminator.cases.size() == 2,
"S_SETPC_B64 jump table did not retain selector mapping");
Check(
(result.ir_dump.find("SLoadDword") == std::string::npos),
@@ -11187,31 +11186,21 @@ void TestNewShaderRecompilerSetpcDwordJumpTable() {
Check((result.ir_dump.find("mode=dispatcher") != std::string::npos),
"subtractive S_SETPC_B64 table did not select dispatcher fallback");
const auto jump = std::find_if(
result.program.block_info.begin(),
result.program.block_info.end(), [](const auto &block) {
return !block.terminator.indirect_targets.empty();
});
const auto block_pc = [&](uint32_t id) {
const auto block =
std::find_if(result.program.block_info.begin(),
result.program.block_info.end(),
[=](const auto &info) { return info.id == id; });
return block != result.program.block_info.end() ? block->start_pc
: UINT32_MAX;
};
Check(jump != result.program.block_info.end() &&
jump->terminator.indirect_targets.size() == 2 &&
jump->terminator.indirect_target_pcs ==
std::vector<uint32_t>({0x38u, 0x40u}) &&
jump->terminator.indirect_selector_values ==
std::vector<uint32_t>({0u, 4u, 8u}) &&
jump->terminator.indirect_selector_targets.size() == 3 &&
block_pc(jump->terminator.indirect_selector_targets[0]) == 0x38u &&
block_pc(jump->terminator.indirect_selector_targets[1]) == 0x40u &&
block_pc(jump->terminator.indirect_selector_targets[2]) == 0x38u &&
result.program.blocks.begin(), result.program.blocks.end(),
[](const auto *block) { return !block->terminator.cases.empty(); });
Check(jump != result.program.blocks.end() &&
(*jump)->ImmSuccessors().size() == 2 &&
(*jump)->terminator.indexed &&
(*jump)->terminator.cases.size() == 3 &&
(*jump)->terminator.cases[0].value == 0u &&
(*jump)->terminator.cases[0].target->start_pc == 0x38u &&
(*jump)->terminator.cases[1].value == 4u &&
(*jump)->terminator.cases[1].target->start_pc == 0x40u &&
(*jump)->terminator.cases[2].value == 8u &&
(*jump)->terminator.cases[2].target->start_pc == 0x38u &&
std::ranges::none_of(
result.program.block_info,
[](const auto &info) { return info.start_pc == 0x04u; }),
result.program.blocks,
[](const auto *block) { return block->start_pc == 0x04u; }),
"subtractive S_SETPC_B64 table targets or selector mapping changed");
Check((result.ir_dump.find("SLoadDword") == std::string::npos),
"subtractive S_SETPC_B64 table load reached normal IR");
@@ -11802,7 +11791,6 @@ void TestFinalSsaRejectsRegisterStatePseudos() {
Program program;
program.block_storage.push_back(std::make_unique<Block>());
program.blocks.push_back(program.block_storage.back().get());
program.block_info.emplace_back();
IREmitter ir(program.blocks.front());
if (setter) {
ir.SetScalarReg(ScalarReg{0}, U32(Value(1u)));
@@ -11842,7 +11830,7 @@ void TestValuePhiValidation() {
program.block_storage.push_back(std::make_unique<Block>());
auto *block = program.block_storage.back().get();
program.blocks.push_back(block);
program.block_info.push_back({.id = id});
program.blocks.back()->id = id;
return block;
};
@@ -11850,17 +11838,17 @@ void TestValuePhiValidation() {
auto *left = add_block(1);
auto *right = add_block(2);
auto *join = add_block(3);
program.block_info[0].terminator.kind =
program.blocks[0]->terminator.kind =
ShaderRecompiler::CFG::TerminatorKind::ConditionalBranch;
program.block_info[0].terminator.true_block = 1;
program.block_info[0].terminator.false_block = 2;
program.block_info[0].condition = Value(true);
program.block_info[1].terminator.kind =
program.blocks[0]->terminator.true_block = program.blocks[1];
program.blocks[0]->terminator.false_block = program.blocks[2];
program.blocks[0]->condition = Value(true);
program.blocks[1]->terminator.kind =
ShaderRecompiler::CFG::TerminatorKind::Branch;
program.block_info[1].terminator.true_block = 3;
program.block_info[2].terminator.kind =
program.blocks[1]->terminator.true_block = program.blocks[3];
program.blocks[2]->terminator.kind =
ShaderRecompiler::CFG::TerminatorKind::Branch;
program.block_info[2].terminator.true_block = 3;
program.blocks[2]->terminator.true_block = program.blocks[3];
entry->AddBranch(left);
entry->AddBranch(right);
left->AddBranch(join);
@@ -11910,12 +11898,12 @@ void TestValuePhiValidation() {
program.block_storage.push_back(std::make_unique<Block>());
auto *block = program.block_storage.back().get();
program.blocks.push_back(block);
program.block_info.push_back({.id = duplicate_id ? 0u : id});
program.blocks.back()->id = duplicate_id ? 0u : id;
}
if (!duplicate_id) {
program.block_info[0].terminator.kind =
program.blocks[0]->terminator.kind =
ShaderRecompiler::CFG::TerminatorKind::Branch;
program.block_info[0].terminator.true_block = 1;
program.blocks[0]->terminator.true_block = program.blocks[1];
}
expect_invalid(program,
"malformed Value CFG did not terminate IR validation");
@@ -11928,14 +11916,14 @@ void TestValuePhiValidation() {
for (uint32_t id = 0; id < 2; id++) {
program.block_storage.push_back(std::make_unique<Block>());
program.blocks.push_back(program.block_storage.back().get());
program.block_info.push_back({.id = id});
program.blocks.back()->id = id;
}
program.block_info[0].terminator.kind =
program.blocks[0]->terminator.kind =
ShaderRecompiler::CFG::TerminatorKind::Branch;
program.block_info[0].terminator.true_block = 1;
program.block_info[1].terminator.kind =
program.blocks[0]->terminator.true_block = program.blocks[1];
program.blocks[1]->terminator.kind =
ShaderRecompiler::CFG::TerminatorKind::Branch;
program.block_info[1].terminator.true_block = 0;
program.blocks[1]->terminator.true_block = program.blocks[0];
program.blocks[0]->AddBranch(program.blocks[1]);
program.blocks[1]->AddBranch(program.blocks[0]);
expect_invalid(program,
@@ -11946,17 +11934,15 @@ void TestValuePhiValidation() {
for (uint32_t id = 0; id < 3; id++) {
program.block_storage.push_back(std::make_unique<Block>());
program.blocks.push_back(program.block_storage.back().get());
program.block_info.push_back({.id = id});
program.blocks.back()->id = id;
}
program.block_info[0].terminator.kind =
program.blocks[0]->terminator.kind =
ShaderRecompiler::CFG::TerminatorKind::Branch;
program.block_info[0].terminator.true_block = 1;
auto &indirect = program.block_info[1];
program.blocks[0]->terminator.true_block = program.blocks[1];
auto &indirect = *program.blocks[1];
indirect.terminator.kind =
ShaderRecompiler::CFG::TerminatorKind::IndirectBranch;
indirect.terminator.indirect_targets = {1};
indirect.terminator.indirect_selector_values = {0};
indirect.terminator.indirect_selector_targets = {2};
indirect.terminator.cases = {{0, program.blocks[2]}};
indirect.indirect_target = Value(0u);
program.blocks[0]->AddBranch(program.blocks[1]);
program.blocks[1]->AddBranch(program.blocks[1]);
@@ -11968,7 +11954,7 @@ void TestValuePhiValidation() {
Program program;
program.block_storage.push_back(std::make_unique<Block>());
program.blocks.push_back(program.block_storage.back().get());
program.block_info.push_back({.id = UINT32_MAX});
program.blocks.back()->id = UINT32_MAX;
expect_invalid(program,
"reserved exit block id did not terminate IR validation");
}
@@ -11977,27 +11963,29 @@ void TestValuePhiValidation() {
for (uint32_t id = 0; id < 2; id++) {
program.block_storage.push_back(std::make_unique<Block>());
program.blocks.push_back(program.block_storage.back().get());
program.block_info.push_back({.id = id});
program.blocks.back()->id = id;
}
program.block_info[0].terminator.kind =
program.blocks[0]->terminator.kind =
ShaderRecompiler::CFG::TerminatorKind::Branch;
program.block_info[0].terminator.true_block = 1;
auto &indirect = program.block_info[1];
program.blocks[0]->terminator.true_block = program.blocks[1];
auto &indirect = *program.blocks[1];
indirect.terminator.kind =
ShaderRecompiler::CFG::TerminatorKind::IndirectBranch;
indirect.terminator.indirect_targets = {1, 1};
indirect.terminator.cases = {{0, program.blocks[1]},
{0, program.blocks[1]}};
indirect.indirect_target = Value(0u);
program.blocks[0]->AddBranch(program.blocks[1]);
program.blocks[1]->AddBranch(program.blocks[1]);
expect_invalid(program,
"duplicate indirect targets did not terminate IR validation");
expect_invalid(
program,
"duplicate indirect selector values did not terminate IR validation");
}
{
Program program;
for (uint32_t id = 0; id < 2; id++) {
program.block_storage.push_back(std::make_unique<Block>());
program.blocks.push_back(program.block_storage.back().get());
program.block_info.push_back({.id = id});
program.blocks.back()->id = id;
}
expect_invalid(program,
"unreachable block did not terminate IR validation");
@@ -12007,7 +11995,6 @@ void TestValuePhiValidation() {
program.block_storage.push_back(std::make_unique<Block>());
auto *block = program.block_storage.back().get();
program.blocks.push_back(block);
program.block_info.push_back({.id = 0});
auto &use =
block->AppendNewInst(ValueOpcode::IAdd32, {Value(1u), Value(2u)});
auto &definition =
@@ -12021,17 +12008,17 @@ void TestValuePhiValidation() {
for (uint32_t id = 0; id < 4; id++) {
program.block_storage.push_back(std::make_unique<Block>());
program.blocks.push_back(program.block_storage.back().get());
program.block_info.push_back({.id = id});
program.blocks.back()->id = id;
}
program.block_info[0].terminator.kind =
program.blocks[0]->terminator.kind =
ShaderRecompiler::CFG::TerminatorKind::ConditionalBranch;
program.block_info[0].terminator.true_block = 1;
program.block_info[0].terminator.false_block = 2;
program.block_info[0].condition = Value(true);
program.blocks[0]->terminator.true_block = program.blocks[1];
program.blocks[0]->terminator.false_block = program.blocks[2];
program.blocks[0]->condition = Value(true);
for (uint32_t id : {1u, 2u}) {
program.block_info[id].terminator.kind =
program.blocks[id]->terminator.kind =
ShaderRecompiler::CFG::TerminatorKind::Branch;
program.block_info[id].terminator.true_block = 3;
program.blocks[id]->terminator.true_block = program.blocks[3];
}
program.blocks[0]->AddBranch(program.blocks[1]);
program.blocks[0]->AddBranch(program.blocks[2]);
@@ -12049,17 +12036,17 @@ void TestValuePhiValidation() {
for (uint32_t id = 0; id < 4; id++) {
program.block_storage.push_back(std::make_unique<Block>());
program.blocks.push_back(program.block_storage.back().get());
program.block_info.push_back({.id = id});
program.blocks.back()->id = id;
}
program.block_info[0].terminator.kind =
program.blocks[0]->terminator.kind =
ShaderRecompiler::CFG::TerminatorKind::ConditionalBranch;
program.block_info[0].terminator.true_block = 1;
program.block_info[0].terminator.false_block = 2;
program.block_info[0].condition = Value(true);
program.blocks[0]->terminator.true_block = program.blocks[1];
program.blocks[0]->terminator.false_block = program.blocks[2];
program.blocks[0]->condition = Value(true);
for (uint32_t id : {1u, 2u}) {
program.block_info[id].terminator.kind =
program.blocks[id]->terminator.kind =
ShaderRecompiler::CFG::TerminatorKind::Branch;
program.block_info[id].terminator.true_block = 3;
program.blocks[id]->terminator.true_block = program.blocks[3];
}
program.blocks[0]->AddBranch(program.blocks[1]);
program.blocks[0]->AddBranch(program.blocks[2]);
@@ -12080,23 +12067,23 @@ void TestValuePhiValidation() {
for (uint32_t id = 0; id < 5; id++) {
program.block_storage.push_back(std::make_unique<Block>());
program.blocks.push_back(program.block_storage.back().get());
program.block_info.push_back({.id = id});
program.blocks.back()->id = id;
}
program.block_info[0].terminator.kind =
program.blocks[0]->terminator.kind =
ShaderRecompiler::CFG::TerminatorKind::ConditionalBranch;
program.block_info[0].terminator.true_block = 1;
program.block_info[0].terminator.false_block = 2;
program.block_info[0].condition = Value(true);
program.block_info[1].terminator.kind =
program.blocks[0]->terminator.true_block = program.blocks[1];
program.blocks[0]->terminator.false_block = program.blocks[2];
program.blocks[0]->condition = Value(true);
program.blocks[1]->terminator.kind =
ShaderRecompiler::CFG::TerminatorKind::Branch;
program.block_info[1].terminator.true_block = 3;
program.block_info[2].terminator.kind =
program.blocks[1]->terminator.true_block = program.blocks[3];
program.blocks[2]->terminator.kind =
ShaderRecompiler::CFG::TerminatorKind::ConditionalBranch;
program.block_info[2].terminator.true_block = 3;
program.block_info[2].terminator.false_block = 4;
program.block_info[3].terminator.kind =
program.blocks[2]->terminator.true_block = program.blocks[3];
program.blocks[2]->terminator.false_block = program.blocks[4];
program.blocks[3]->terminator.kind =
ShaderRecompiler::CFG::TerminatorKind::Branch;
program.block_info[3].terminator.true_block = 4;
program.blocks[3]->terminator.true_block = program.blocks[4];
program.blocks[0]->AddBranch(program.blocks[1]);
program.blocks[0]->AddBranch(program.blocks[2]);
program.blocks[1]->AddBranch(program.blocks[3]);
@@ -12105,7 +12092,7 @@ void TestValuePhiValidation() {
program.blocks[3]->AddBranch(program.blocks[4]);
auto &condition = program.blocks[1]->AppendNewInst(ValueOpcode::IEqual32,
{Value(1u), Value(2u)});
program.block_info[2].condition = Value(&condition);
program.blocks[2]->condition = Value(&condition);
expect_invalid(program,
"non-dominating branch condition did not terminate IR "
"validation");
@@ -12122,19 +12109,22 @@ void TestWave32MaskProjection() {
for (uint32_t id = 0; id < 3; ++id) {
program.block_storage.push_back(std::make_unique<Block>());
program.blocks.push_back(program.block_storage.back().get());
program.block_info.push_back({.id = id});
program.blocks.back()->id = id;
}
auto *entry = program.blocks[0];
auto *loop = program.blocks[1];
entry->AddBranch(loop);
loop->AddBranch(loop);
loop->AddBranch(program.blocks[2]);
program.block_info[0].terminator.kind = ShaderRecompiler::CFG::TerminatorKind::Branch;
program.block_info[0].terminator.true_block = 1;
program.block_info[1].terminator.kind = ShaderRecompiler::CFG::TerminatorKind::ConditionalBranch;
program.block_info[1].terminator.true_block = 1;
program.block_info[1].terminator.false_block = 2;
program.block_info[2].terminator.kind = ShaderRecompiler::CFG::TerminatorKind::Return;
program.blocks[0]->terminator.kind =
ShaderRecompiler::CFG::TerminatorKind::Branch;
program.blocks[0]->terminator.true_block = program.blocks[1];
program.blocks[1]->terminator.kind =
ShaderRecompiler::CFG::TerminatorKind::ConditionalBranch;
program.blocks[1]->terminator.true_block = program.blocks[1];
program.blocks[1]->terminator.false_block = program.blocks[2];
program.blocks[2]->terminator.kind =
ShaderRecompiler::CFG::TerminatorKind::Return;
IREmitter e(entry);
const auto zero = U32(Value(0u));
const auto raw = e.GetUserData(static_cast<ScalarReg>(0));
@@ -12188,7 +12178,7 @@ void TestWave32MaskProjection() {
mask.AddPhiOperand(loop, remaining);
predicate.AddPhiOperand(entry, initial_predicate);
predicate.AddPhiOperand(loop, remaining_predicate);
program.block_info[1].condition = remaining_predicate;
program.blocks[1]->condition = remaining_predicate;
ConstantPropagationPass(program.blocks, wave_size);
if (wave_size == 32u) {
@@ -12237,7 +12227,6 @@ void TestU64ShiftConstantPropagation() {
Program shifts;
shifts.block_storage.push_back(std::make_unique<Block>());
shifts.blocks.push_back(shifts.block_storage.back().get());
shifts.block_info.emplace_back();
IREmitter shift_ir(shifts.blocks.front());
struct ShiftCase {
Value value;
@@ -12309,9 +12298,7 @@ void TestNativeWideValueValidation() {
Program program;
program.block_storage.push_back(std::make_unique<Block>());
program.blocks.push_back(program.block_storage.back().get());
program.block_info.emplace_back();
program.block_info.front().id = 0;
program.block_info.front().terminator.kind =
program.blocks.front()->terminator.kind =
ShaderRecompiler::CFG::TerminatorKind::Return;
return program;
};