mirror of
https://github.com/RPCSX/rpcsx.git
synced 2025-12-06 07:12:14 +01:00
2230 lines
68 KiB
C++
2230 lines
68 KiB
C++
#include <gtest/gtest.h>
|
|
#include <memory>
|
|
|
|
// Include shader framework for CFG testing
|
|
#include "shader/SpvConverter.hpp"
|
|
#include "shader/analyze.hpp"
|
|
#include "shader/dialect.hpp"
|
|
#include "shader/ir.hpp"
|
|
#include "shader/ir/Context.hpp"
|
|
#include "shader/spv.hpp"
|
|
#include "shader/transform.hpp"
|
|
|
|
using namespace shader;
|
|
using Builder = ir::Builder<ir::spv::Builder>;
|
|
|
|
class GcnShaderTest : public ::testing::Test {
|
|
protected:
|
|
void SetUp() override {
|
|
// Setup SPIR-V context for CFG testing
|
|
context = std::make_unique<spv::Context>();
|
|
loc = context->getUnknownLocation();
|
|
trueV = context->getTrue();
|
|
falseV = context->getFalse();
|
|
}
|
|
|
|
void TearDown() override { context.reset(); }
|
|
|
|
ir::Value createLabel(const std::string &name) {
|
|
auto builder = Builder::createAppend(
|
|
*context, context->layout.getOrCreateFunctions(*context));
|
|
auto label = builder.createSpvLabel(loc);
|
|
context->ns.setNameOf(label, name);
|
|
return label;
|
|
}
|
|
|
|
void createBranch(ir::Value from, ir::Value to) {
|
|
Builder::createInsertAfter(*context, from).createSpvBranch(loc, to);
|
|
}
|
|
|
|
void createConditionalBranch(ir::Value from, ir::Value a, ir::Value b) {
|
|
Builder::createInsertAfter(*context, from)
|
|
.createSpvBranchConditional(loc, trueV, a, b);
|
|
}
|
|
|
|
void createReturn(ir::Value from) {
|
|
Builder::createInsertAfter(*context, from).createSpvReturn(loc);
|
|
}
|
|
|
|
void createSwitch(ir::Value from, std::span<const ir::Value> cases) {
|
|
auto globals = Builder::createAppend(
|
|
*context, context->layout.getOrCreateGlobals(*context));
|
|
auto globalVariable = globals.createSpvVariable(
|
|
loc, context->getTypeUInt32(), ir::spv::StorageClass::Private,
|
|
context->imm32(0));
|
|
|
|
auto switchOp = Builder::createInsertAfter(*context, from)
|
|
.createSpvSwitch(loc, globalVariable, cases[0]);
|
|
|
|
std::uint32_t i = 0;
|
|
for (auto c : cases.subspan(1)) {
|
|
switchOp.addOperand(i++);
|
|
switchOp.addOperand(c);
|
|
}
|
|
}
|
|
|
|
void createSwitchBranch(ir::Value from, ir::Value defaultTarget,
|
|
const std::vector<std::pair<std::uint32_t, ir::Value>>& cases) {
|
|
// Create a switch value (use a constant for testing)
|
|
auto type = context->getTypeUInt32();
|
|
auto globals = Builder::createAppend(
|
|
*context, context->layout.getOrCreateGlobals(*context));
|
|
auto globalVariable = globals.createSpvConstant(
|
|
loc, type, 0);
|
|
|
|
auto builder = Builder::createInsertAfter(*context, from);
|
|
auto switchInst =
|
|
builder.createSpvSwitch(loc, globalVariable, defaultTarget);
|
|
|
|
// Add each case
|
|
for (const auto& [value, target] : cases) {
|
|
switchInst.addOperand(value);
|
|
switchInst.addOperand(target);
|
|
}
|
|
}
|
|
|
|
bool testStructurization() {
|
|
auto region = context->layout.getOrCreateFunctions(*context);
|
|
context->layout.regions[spv::BinaryLayout::kFunctions] = {};
|
|
auto functions = context->layout.getOrCreateFunctions(*context);
|
|
|
|
structurizeCfg(*context, region);
|
|
|
|
{
|
|
auto debugs = Builder::createAppend(
|
|
*context, context->layout.getOrCreateDebugs(*context));
|
|
|
|
for (auto inst : region.children()) {
|
|
if (auto value = inst.cast<ir::Value>()) {
|
|
if (auto name = context->ns.tryGetNameOf(value); !name.empty()) {
|
|
debugs.createSpvName(loc, value, std::string(name));
|
|
}
|
|
}
|
|
|
|
inst.erase();
|
|
functions.addChild(inst);
|
|
}
|
|
}
|
|
region = functions;
|
|
|
|
auto entryLabel = region.getFirst().cast<ir::Value>();
|
|
|
|
auto memModel = Builder::createAppend(
|
|
*context, context->layout.getOrCreateMemoryModels(*context));
|
|
auto capabilities = Builder::createAppend(
|
|
*context, context->layout.getOrCreateCapabilities(*context));
|
|
|
|
capabilities.createSpvCapability(loc, ir::spv::Capability::Shader);
|
|
|
|
memModel.createSpvMemoryModel(loc, ir::spv::AddressingModel::Logical,
|
|
ir::spv::MemoryModel::GLSL450);
|
|
|
|
auto mainReturnT = context->getTypeVoid();
|
|
auto mainFnT = context->getTypeFunction(mainReturnT, {});
|
|
|
|
auto builder = Builder::createPrepend(*context, region);
|
|
auto mainFn = builder.createSpvFunction(
|
|
loc, mainReturnT, ir::spv::FunctionControl::None, mainFnT);
|
|
|
|
builder.createSpvLabel(loc);
|
|
builder.createSpvBranch(loc, entryLabel);
|
|
|
|
Builder::createAppend(*context, region).createSpvFunctionEnd(loc);
|
|
|
|
auto entryPoints = Builder::createAppend(
|
|
*context, context->layout.getOrCreateEntryPoints(*context));
|
|
|
|
auto executionModes = Builder::createAppend(
|
|
*context, context->layout.getOrCreateExecutionModes(*context));
|
|
|
|
executionModes.createSpvExecutionMode(
|
|
mainFn.getLocation(), mainFn,
|
|
ir::spv::ExecutionMode::LocalSize(1, 1, 1));
|
|
|
|
entryPoints.createSpvEntryPoint(mainFn.getLocation(),
|
|
ir::spv::ExecutionModel::GLCompute, mainFn,
|
|
"main", {});
|
|
|
|
auto spv = shader::spv::serialize(context->layout.merge(*context));
|
|
if (shader::spv::validate(spv)) {
|
|
return true;
|
|
}
|
|
|
|
shader::spv::dump(spv, true);
|
|
return false;
|
|
}
|
|
|
|
protected:
|
|
std::unique_ptr<spv::Context> context;
|
|
ir::Location loc;
|
|
ir::Value trueV;
|
|
ir::Value falseV;
|
|
};
|
|
|
|
TEST_F(GcnShaderTest, ProjectDivaTest1) {
|
|
auto _1 = createLabel("1");
|
|
auto _2 = createLabel("2");
|
|
auto _3 = createLabel("3");
|
|
auto _4 = createLabel("4");
|
|
auto _5 = createLabel("5");
|
|
auto _6 = createLabel("6");
|
|
auto _7 = createLabel("7");
|
|
auto _8 = createLabel("8");
|
|
auto _9 = createLabel("9");
|
|
auto _10 = createLabel("10");
|
|
auto _11 = createLabel("11");
|
|
auto _12 = createLabel("12");
|
|
auto _13 = createLabel("13");
|
|
createBranch(_1, _2);
|
|
createConditionalBranch(_2, _4, _3);
|
|
createConditionalBranch(_3, _12, _11);
|
|
createConditionalBranch(_4, _6, _5);
|
|
createConditionalBranch(_5, _9, _8);
|
|
createConditionalBranch(_6, _7, _8);
|
|
createBranch(_7, _6);
|
|
createBranch(_8, _3);
|
|
createBranch(_9, _10);
|
|
createBranch(_10, _7);
|
|
createBranch(_11, _12);
|
|
createBranch(_12, _13);
|
|
createReturn(_13);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
TEST_F(GcnShaderTest, BatmanReturnToArkham1) {
|
|
auto _1 = createLabel("1");
|
|
auto _2 = createLabel("2");
|
|
auto _3 = createLabel("3");
|
|
auto _4 = createLabel("4");
|
|
auto _5 = createLabel("5");
|
|
auto _6 = createLabel("6");
|
|
auto _7 = createLabel("7");
|
|
auto _8 = createLabel("8");
|
|
auto _9 = createLabel("9");
|
|
auto _10 = createLabel("10");
|
|
auto _11 = createLabel("11");
|
|
auto _12 = createLabel("12");
|
|
auto _13 = createLabel("13");
|
|
auto _14 = createLabel("14");
|
|
auto _15 = createLabel("15");
|
|
auto _16 = createLabel("16");
|
|
auto _17 = createLabel("17");
|
|
auto _18 = createLabel("18");
|
|
auto _19 = createLabel("19");
|
|
auto _20 = createLabel("20");
|
|
auto _21 = createLabel("21");
|
|
auto _22 = createLabel("22");
|
|
auto _23 = createLabel("23");
|
|
auto _24 = createLabel("24");
|
|
auto _25 = createLabel("25");
|
|
createBranch(_1, _2);
|
|
createConditionalBranch(_2, _4, _3);
|
|
createConditionalBranch(_3, _6, _5);
|
|
createBranch(_4, _3);
|
|
createConditionalBranch(_5, _8, _7);
|
|
createBranch(_6, _5);
|
|
createConditionalBranch(_7, _10, _9);
|
|
createBranch(_8, _7);
|
|
createConditionalBranch(_9, _12, _11);
|
|
createBranch(_10, _9);
|
|
createConditionalBranch(_11, _14, _13);
|
|
createBranch(_12, _11);
|
|
createConditionalBranch(_13, _16, _15);
|
|
createBranch(_14, _13);
|
|
createBranch(_15, _25);
|
|
createConditionalBranch(_16, _18, _17);
|
|
createBranch(_17, _18);
|
|
createConditionalBranch(_18, _20, _19);
|
|
createBranch(_19, _20);
|
|
createConditionalBranch(_20, _22, _21);
|
|
createBranch(_21, _22);
|
|
createConditionalBranch(_22, _24, _23);
|
|
createBranch(_23, _24);
|
|
createBranch(_24, _15);
|
|
createReturn(_25);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
TEST_F(GcnShaderTest, Shadow1) {
|
|
auto _1 = createLabel("1");
|
|
auto _2 = createLabel("2");
|
|
auto _3 = createLabel("3");
|
|
auto _4 = createLabel("4");
|
|
auto _5 = createLabel("5");
|
|
auto _6 = createLabel("6");
|
|
auto _7 = createLabel("7");
|
|
auto _8 = createLabel("8");
|
|
auto _9 = createLabel("9");
|
|
auto _10 = createLabel("10");
|
|
auto _11 = createLabel("11");
|
|
auto _12 = createLabel("12");
|
|
auto _13 = createLabel("13");
|
|
auto _14 = createLabel("14");
|
|
auto _15 = createLabel("15");
|
|
auto _16 = createLabel("16");
|
|
auto _17 = createLabel("17");
|
|
auto _18 = createLabel("18");
|
|
auto _19 = createLabel("19");
|
|
auto _20 = createLabel("20");
|
|
auto _21 = createLabel("21");
|
|
auto _22 = createLabel("22");
|
|
auto _23 = createLabel("23");
|
|
auto _24 = createLabel("24");
|
|
auto _25 = createLabel("25");
|
|
auto _26 = createLabel("26");
|
|
auto _27 = createLabel("27");
|
|
auto _28 = createLabel("28");
|
|
auto _29 = createLabel("29");
|
|
auto _30 = createLabel("30");
|
|
auto _31 = createLabel("31");
|
|
auto _32 = createLabel("32");
|
|
auto _33 = createLabel("33");
|
|
auto _34 = createLabel("34");
|
|
auto _35 = createLabel("35");
|
|
auto _36 = createLabel("36");
|
|
auto _37 = createLabel("37");
|
|
auto _38 = createLabel("38");
|
|
auto _39 = createLabel("39");
|
|
auto _40 = createLabel("40");
|
|
auto _41 = createLabel("41");
|
|
auto _42 = createLabel("42");
|
|
auto _43 = createLabel("43");
|
|
auto _44 = createLabel("44");
|
|
auto _45 = createLabel("45");
|
|
auto _46 = createLabel("46");
|
|
auto _47 = createLabel("47");
|
|
auto _48 = createLabel("48");
|
|
auto _49 = createLabel("49");
|
|
auto _50 = createLabel("50");
|
|
auto _51 = createLabel("51");
|
|
auto _52 = createLabel("52");
|
|
auto _53 = createLabel("53");
|
|
auto _54 = createLabel("54");
|
|
auto _55 = createLabel("55");
|
|
auto _56 = createLabel("56");
|
|
auto _57 = createLabel("57");
|
|
auto _58 = createLabel("58");
|
|
auto _59 = createLabel("59");
|
|
auto _60 = createLabel("60");
|
|
auto _61 = createLabel("61");
|
|
createBranch(_1, _2);
|
|
createConditionalBranch(_2, _4, _3);
|
|
createConditionalBranch(_3, _61, _60);
|
|
createConditionalBranch(_4, _6, _5);
|
|
createConditionalBranch(_5, _28, _27);
|
|
createConditionalBranch(_6, _8, _7);
|
|
createConditionalBranch(_7, _18, _17);
|
|
createConditionalBranch(_8, _10, _9);
|
|
createBranch(_9, _7);
|
|
createConditionalBranch(_10, _12, _11);
|
|
createBranch(_11, _7);
|
|
createConditionalBranch(_12, _14, _13);
|
|
createBranch(_13, _7);
|
|
createConditionalBranch(_14, _16, _15);
|
|
createBranch(_15, _7);
|
|
createBranch(_16, _15);
|
|
createConditionalBranch(_17, _20, _19);
|
|
createBranch(_18, _5);
|
|
createBranch(_19, _5);
|
|
createConditionalBranch(_20, _22, _21);
|
|
createBranch(_21, _5);
|
|
createConditionalBranch(_22, _24, _23);
|
|
createBranch(_23, _21);
|
|
createConditionalBranch(_24, _26, _25);
|
|
createBranch(_25, _21);
|
|
createBranch(_26, _25);
|
|
createConditionalBranch(_27, _30, _29);
|
|
createBranch(_28, _27);
|
|
createConditionalBranch(_29, _50, _49);
|
|
createConditionalBranch(_30, _32, _31);
|
|
createConditionalBranch(_31, _29, _3);
|
|
createConditionalBranch(_32, _34, _33);
|
|
createConditionalBranch(_33, _48, _3);
|
|
createConditionalBranch(_34, _36, _35);
|
|
createConditionalBranch(_35, _47, _3);
|
|
createConditionalBranch(_36, _38, _37);
|
|
createConditionalBranch(_37, _46, _3);
|
|
createConditionalBranch(_38, _40, _39);
|
|
createConditionalBranch(_39, _45, _3);
|
|
createConditionalBranch(_40, _42, _41);
|
|
createConditionalBranch(_41, _44, _3);
|
|
createConditionalBranch(_42, _43, _3);
|
|
createBranch(_43, _29);
|
|
createBranch(_44, _29);
|
|
createBranch(_45, _29);
|
|
createBranch(_46, _29);
|
|
createBranch(_47, _29);
|
|
createBranch(_48, _29);
|
|
createConditionalBranch(_49, _58, _57);
|
|
createConditionalBranch(_50, _52, _51);
|
|
createConditionalBranch(_51, _49, _3);
|
|
createConditionalBranch(_52, _54, _53);
|
|
createConditionalBranch(_53, _56, _3);
|
|
createConditionalBranch(_54, _55, _3);
|
|
createBranch(_55, _49);
|
|
createBranch(_56, _49);
|
|
createBranch(_57, _58);
|
|
createBranch(_58, _59);
|
|
createReturn(_59);
|
|
createBranch(_60, _61);
|
|
createBranch(_61, _59);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test conditional CFG structurization
|
|
*/
|
|
TEST_F(GcnShaderTest, ConditionalCfgStructurization) {
|
|
auto _1 = createLabel("1");
|
|
auto _2 = createLabel("2");
|
|
auto _3 = createLabel("3");
|
|
auto _4 = createLabel("4");
|
|
|
|
// Build conditional CFG: 1 -> if(2,3), 2 -> 4, 3 -> 4
|
|
createConditionalBranch(_1, _2, _3);
|
|
createBranch(_2, _4);
|
|
createBranch(_3, _4);
|
|
createReturn(_4);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test loop CFG structurization
|
|
*/
|
|
TEST_F(GcnShaderTest, LoopCfgStructurization) {
|
|
// Create simple loop: 1 -> 2 -> conditional back to 1 or forward to 3
|
|
auto _1 = createLabel("1");
|
|
auto _2 = createLabel("2");
|
|
auto _3 = createLabel("3");
|
|
|
|
createBranch(_1, _2);
|
|
createConditionalBranch(_2, _1, _3);
|
|
createReturn(_3);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test that new implementation doesn't crash with complex patterns
|
|
*/
|
|
TEST_F(GcnShaderTest, ComplexPatternResilience) {
|
|
// Create a more complex CFG and ensure it doesn't crash
|
|
auto _1 = createLabel("1");
|
|
auto _2 = createLabel("2");
|
|
auto _3 = createLabel("3");
|
|
|
|
createBranch(_1, _2);
|
|
createBranch(_2, _3);
|
|
createReturn(_3);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test academic algorithm forward goto transformation
|
|
*/
|
|
TEST_F(GcnShaderTest, ForwardGotoTransformation) {
|
|
// Test the academic algorithm's handling of forward gotos
|
|
auto _1 = createLabel("1");
|
|
auto _2 = createLabel("2");
|
|
|
|
createBranch(_1, _2);
|
|
createReturn(_2);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test nested loops CFG
|
|
*/
|
|
TEST_F(GcnShaderTest, NestedLoopCfgStructurization) {
|
|
auto entry = createLabel("entry");
|
|
auto outer_loop = createLabel("outer_loop");
|
|
auto inner_loop = createLabel("inner_loop");
|
|
auto inner_body = createLabel("inner_body");
|
|
auto exit = createLabel("exit");
|
|
|
|
// Build nested loop CFG
|
|
createBranch(entry, outer_loop);
|
|
createConditionalBranch(outer_loop, inner_loop, exit);
|
|
createConditionalBranch(inner_loop, outer_loop, inner_body);
|
|
createConditionalBranch(inner_body, inner_loop, exit);
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
TEST_F(GcnShaderTest, LoopWithHeaderCfgStructurization) {
|
|
// Create nested loops: outer -> inner -> inner_body -> inner (back), inner
|
|
// -> outer (back), outer -> exit
|
|
|
|
auto outer_loop = createLabel("outer_loop");
|
|
auto inner_loop = createLabel("inner_loop");
|
|
auto inner_body = createLabel("inner_body");
|
|
auto exit = createLabel("exit");
|
|
|
|
// Build nested loop CFG
|
|
createBranch(outer_loop, inner_loop);
|
|
createConditionalBranch(inner_loop, outer_loop, inner_body);
|
|
createConditionalBranch(inner_body, inner_loop, exit);
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test irreducible CFG (multiple entry loop)
|
|
*/
|
|
TEST_F(GcnShaderTest, IrreducibleCfgStructurization) {
|
|
// Create irreducible: entry -> if(A, B), A -> C, B -> C, C -> if(A, exit)
|
|
|
|
auto entry = createLabel("entry");
|
|
auto A = createLabel("A");
|
|
auto B = createLabel("B");
|
|
auto C = createLabel("C");
|
|
auto exit = createLabel("exit");
|
|
|
|
createConditionalBranch(entry, A, B);
|
|
createBranch(A, C);
|
|
createBranch(B, C);
|
|
createConditionalBranch(C, A, exit); // Back edge to A
|
|
createReturn(exit);
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test switch-like CFG
|
|
*/
|
|
TEST_F(GcnShaderTest, SwitchLikeCfgStructurization) {
|
|
// Create switch-like: entry -> if(case1, case2), case1 -> merge, case2 ->
|
|
// if(case3, merge), case3 -> merge
|
|
auto entry = createLabel("entry");
|
|
auto case1 = createLabel("case1");
|
|
auto case2 = createLabel("case2");
|
|
auto case3 = createLabel("case3");
|
|
auto merge = createLabel("merge");
|
|
|
|
createConditionalBranch(entry, case1, case2);
|
|
createBranch(case1, merge);
|
|
createConditionalBranch(case2, case3, merge);
|
|
createBranch(case3, merge);
|
|
createReturn(merge);
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test academic algorithm data structures (verify no regression)
|
|
*/
|
|
TEST_F(GcnShaderTest, AcademicAlgorithmDataStructures) {
|
|
// Create a basic CFG and ensure structure is created
|
|
auto _1 = createLabel("1");
|
|
auto _2 = createLabel("2");
|
|
auto _3 = createLabel("3");
|
|
auto _4 = createLabel("4");
|
|
|
|
createBranch(_1, _2);
|
|
createReturn(_2);
|
|
|
|
createBranch(_3, _4);
|
|
createReturn(_4);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test deeply nested loops (3 levels)
|
|
*/
|
|
TEST_F(GcnShaderTest, DeeplyNestedLoops) {
|
|
auto outer = createLabel("outer");
|
|
auto middle = createLabel("middle");
|
|
auto inner = createLabel("inner");
|
|
auto body = createLabel("body");
|
|
auto exit = createLabel("exit");
|
|
|
|
createBranch(outer, middle);
|
|
createConditionalBranch(middle, inner, outer); // middle can exit to outer
|
|
createConditionalBranch(inner, body, middle); // inner can exit to middle
|
|
createConditionalBranch(body, inner, exit); // body loops back or exits
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test loop with multiple exits
|
|
*/
|
|
TEST_F(GcnShaderTest, LoopWithMultipleExits) {
|
|
auto header = createLabel("header");
|
|
auto body1 = createLabel("body1");
|
|
auto body2 = createLabel("body2");
|
|
auto exit1 = createLabel("exit1");
|
|
auto exit2 = createLabel("exit2");
|
|
auto final_exit = createLabel("final_exit");
|
|
|
|
createConditionalBranch(header, body1, body2);
|
|
createConditionalBranch(body1, header, exit1); // loop or exit
|
|
createConditionalBranch(body2, header, exit2); // loop or exit
|
|
createBranch(exit1, final_exit);
|
|
createBranch(exit2, final_exit);
|
|
createReturn(final_exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test loop with early return from inner block
|
|
*/
|
|
TEST_F(GcnShaderTest, LoopWithEarlyReturn) {
|
|
auto header = createLabel("header");
|
|
auto check = createLabel("check");
|
|
auto body = createLabel("body");
|
|
auto exit = createLabel("exit");
|
|
|
|
createBranch(header, check);
|
|
createConditionalBranch(check, body, exit);
|
|
createConditionalBranch(body, header, exit); // loop back or return
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test irreducible CFG with multiple entry points
|
|
*/
|
|
TEST_F(GcnShaderTest, IrreducibleMultipleEntry) {
|
|
auto entry = createLabel("entry");
|
|
auto A = createLabel("A");
|
|
auto B = createLabel("B");
|
|
auto C = createLabel("C");
|
|
auto D = createLabel("D");
|
|
auto exit = createLabel("exit");
|
|
|
|
createConditionalBranch(entry, A, B);
|
|
createBranch(A, C);
|
|
createBranch(B, D);
|
|
createConditionalBranch(C, D, A); // C can go to D or back to A
|
|
createConditionalBranch(D, C, exit); // D can go to C or exit
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test switch-like structure with fall-through
|
|
*/
|
|
TEST_F(GcnShaderTest, SwitchWithFallthrough) {
|
|
auto entry = createLabel("entry");
|
|
auto case1 = createLabel("case1");
|
|
auto case2 = createLabel("case2");
|
|
auto case3 = createLabel("case3");
|
|
auto default_case = createLabel("default");
|
|
auto merge = createLabel("merge");
|
|
|
|
createConditionalBranch(entry, case1, case2);
|
|
createConditionalBranch(case1, case3, merge); // case1 can fall through
|
|
createBranch(case2, case3); // case2 falls through
|
|
createConditionalBranch(case3, default_case, merge);
|
|
createBranch(default_case, merge);
|
|
createReturn(merge);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test loop with break and continue
|
|
*/
|
|
TEST_F(GcnShaderTest, LoopWithBreakContinue) {
|
|
auto header = createLabel("header");
|
|
auto condition = createLabel("condition");
|
|
auto body = createLabel("body");
|
|
auto continue_block = createLabel("continue_block");
|
|
auto exit = createLabel("exit");
|
|
|
|
createBranch(header, condition);
|
|
createConditionalBranch(condition, body, exit); // condition check
|
|
createConditionalBranch(body, continue_block, exit); // break or continue
|
|
createBranch(continue_block, header); // continue - back to header
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test diamond pattern inside loop
|
|
*/
|
|
TEST_F(GcnShaderTest, DiamondInLoop) {
|
|
auto header = createLabel("header");
|
|
auto diamond_entry = createLabel("diamond_entry");
|
|
auto left = createLabel("left");
|
|
auto right = createLabel("right");
|
|
auto diamond_merge = createLabel("diamond_merge");
|
|
auto exit = createLabel("exit");
|
|
|
|
createBranch(header, diamond_entry);
|
|
createConditionalBranch(diamond_entry, left, right);
|
|
createBranch(left, diamond_merge);
|
|
createBranch(right, diamond_merge);
|
|
createConditionalBranch(diamond_merge, header, exit); // loop or exit
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test loop inside diamond
|
|
*/
|
|
TEST_F(GcnShaderTest, LoopInDiamond) {
|
|
auto entry = createLabel("entry");
|
|
auto left_branch = createLabel("left_branch");
|
|
auto right_branch = createLabel("right_branch");
|
|
auto loop_header = createLabel("loop_header");
|
|
auto loop_body = createLabel("loop_body");
|
|
auto merge = createLabel("merge");
|
|
|
|
createConditionalBranch(entry, left_branch, right_branch);
|
|
createBranch(left_branch, loop_header);
|
|
createBranch(right_branch, merge);
|
|
createConditionalBranch(loop_header, loop_body, merge);
|
|
createBranch(loop_body, loop_header); // loop back
|
|
createReturn(merge);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test crossing edges pattern
|
|
*/
|
|
TEST_F(GcnShaderTest, CrossingEdges) {
|
|
auto entry = createLabel("entry");
|
|
auto A = createLabel("A");
|
|
auto B = createLabel("B");
|
|
auto C = createLabel("C");
|
|
auto D = createLabel("D");
|
|
auto exit = createLabel("exit");
|
|
|
|
createConditionalBranch(entry, A, B);
|
|
createConditionalBranch(A, C, D); // A goes to C or D
|
|
createConditionalBranch(B, D, C); // B goes to D or C (crossing)
|
|
createBranch(C, exit);
|
|
createBranch(D, exit);
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test multiple nested loops with shared exit
|
|
*/
|
|
TEST_F(GcnShaderTest, MultipleLoopsSharedExit) {
|
|
auto entry = createLabel("entry");
|
|
auto loop1_header = createLabel("loop1_header");
|
|
auto loop1_body = createLabel("loop1_body");
|
|
auto loop2_header = createLabel("loop2_header");
|
|
auto loop2_body = createLabel("loop2_body");
|
|
auto shared_exit = createLabel("shared_exit");
|
|
|
|
createConditionalBranch(entry, loop1_header, loop2_header);
|
|
createConditionalBranch(loop1_header, loop1_body, shared_exit);
|
|
createBranch(loop1_body, loop1_header);
|
|
createConditionalBranch(loop2_header, loop2_body, shared_exit);
|
|
createBranch(loop2_body, loop2_header);
|
|
createReturn(shared_exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test while-do-while combined pattern
|
|
*/
|
|
TEST_F(GcnShaderTest, WhileDoWhileCombined) {
|
|
auto entry = createLabel("entry");
|
|
auto while_check = createLabel("while_check");
|
|
auto while_body = createLabel("while_body");
|
|
auto do_body = createLabel("do_body");
|
|
auto do_check = createLabel("do_check");
|
|
auto exit = createLabel("exit");
|
|
|
|
createBranch(entry, while_check);
|
|
createConditionalBranch(while_check, while_body, exit);
|
|
createBranch(while_body, do_body);
|
|
createBranch(do_body, do_check);
|
|
createConditionalBranch(do_check, do_body,
|
|
while_check); // do-while + back to while
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test loop with exception-like exit
|
|
*/
|
|
TEST_F(GcnShaderTest, LoopWithExceptionExit) {
|
|
auto header = createLabel("header");
|
|
auto try_block = createLabel("try_block");
|
|
auto catch_block = createLabel("catch_block");
|
|
auto normal_exit = createLabel("normal_exit");
|
|
auto exception_exit = createLabel("exception_exit");
|
|
auto final_exit = createLabel("final_exit");
|
|
|
|
createBranch(header, try_block);
|
|
createConditionalBranch(try_block, header, catch_block); // loop or exception
|
|
createConditionalBranch(catch_block, normal_exit, exception_exit);
|
|
createBranch(normal_exit, final_exit);
|
|
createBranch(exception_exit, final_exit);
|
|
createReturn(final_exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test figure-8 pattern (two interconnected loops)
|
|
*/
|
|
TEST_F(GcnShaderTest, Figure8Pattern) {
|
|
auto entry = createLabel("entry");
|
|
auto loop1_header = createLabel("loop1_header");
|
|
auto loop1_body = createLabel("loop1_body");
|
|
auto loop2_header = createLabel("loop2_header");
|
|
auto loop2_body = createLabel("loop2_body");
|
|
auto exit = createLabel("exit");
|
|
|
|
createBranch(entry, loop1_header);
|
|
createConditionalBranch(loop1_header, loop1_body, exit);
|
|
createConditionalBranch(loop1_body, loop1_header,
|
|
loop2_header); // to loop1 or loop2
|
|
createConditionalBranch(loop2_header, loop2_body, exit);
|
|
createConditionalBranch(loop2_body, loop2_header,
|
|
loop1_header); // to loop2 or loop1
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test complex irreducible with 4-node cycle
|
|
*/
|
|
TEST_F(GcnShaderTest, ComplexIrreducible4Node) {
|
|
auto entry = createLabel("entry");
|
|
auto A = createLabel("A");
|
|
auto B = createLabel("B");
|
|
auto C = createLabel("C");
|
|
auto D = createLabel("D");
|
|
auto exit = createLabel("exit");
|
|
|
|
createBranch(entry, A);
|
|
createConditionalBranch(A, B, C);
|
|
createBranch(B, D);
|
|
createConditionalBranch(C, D, A); // back to A
|
|
createConditionalBranch(D, B, exit); // back to B or exit
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test nested loops with cross-loop edges
|
|
*/
|
|
TEST_F(GcnShaderTest, NestedLoopsWithCrossEdges) {
|
|
auto outer_header = createLabel("outer_header");
|
|
auto outer_body = createLabel("outer_body");
|
|
auto inner_header = createLabel("inner_header");
|
|
auto inner_body = createLabel("inner_body");
|
|
auto cross_block = createLabel("cross_block");
|
|
auto exit = createLabel("exit");
|
|
|
|
createBranch(outer_header, outer_body);
|
|
createConditionalBranch(outer_body, inner_header, cross_block);
|
|
createConditionalBranch(inner_header, inner_body,
|
|
outer_header); // inner to outer
|
|
createConditionalBranch(inner_body, inner_header,
|
|
cross_block); // inner loop or cross
|
|
createConditionalBranch(cross_block, outer_header,
|
|
exit); // back to outer or exit
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test parallel diamond patterns
|
|
*/
|
|
TEST_F(GcnShaderTest, ParallelDiamonds) {
|
|
auto entry = createLabel("entry");
|
|
auto diamond1_entry = createLabel("diamond1_entry");
|
|
auto diamond1_left = createLabel("diamond1_left");
|
|
auto diamond1_right = createLabel("diamond1_right");
|
|
auto diamond1_merge = createLabel("diamond1_merge");
|
|
auto diamond2_entry = createLabel("diamond2_entry");
|
|
auto diamond2_left = createLabel("diamond2_left");
|
|
auto diamond2_right = createLabel("diamond2_right");
|
|
auto diamond2_merge = createLabel("diamond2_merge");
|
|
auto final_merge = createLabel("final_merge");
|
|
|
|
createConditionalBranch(entry, diamond1_entry, diamond2_entry);
|
|
|
|
// Diamond 1
|
|
createConditionalBranch(diamond1_entry, diamond1_left, diamond1_right);
|
|
createBranch(diamond1_left, diamond1_merge);
|
|
createBranch(diamond1_right, diamond1_merge);
|
|
createBranch(diamond1_merge, final_merge);
|
|
|
|
// Diamond 2
|
|
createConditionalBranch(diamond2_entry, diamond2_left, diamond2_right);
|
|
createBranch(diamond2_left, diamond2_merge);
|
|
createBranch(diamond2_right, diamond2_merge);
|
|
createBranch(diamond2_merge, final_merge);
|
|
|
|
createReturn(final_merge);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test loop with internal switch
|
|
*/
|
|
TEST_F(GcnShaderTest, LoopWithInternalSwitch) {
|
|
auto header = createLabel("header");
|
|
auto switch_entry = createLabel("switch_entry");
|
|
auto case_a = createLabel("case_a");
|
|
auto case_b = createLabel("case_b");
|
|
auto case_c = createLabel("case_c");
|
|
auto switch_merge = createLabel("switch_merge");
|
|
auto exit = createLabel("exit");
|
|
|
|
createBranch(header, switch_entry);
|
|
createConditionalBranch(switch_entry, case_a, case_b);
|
|
createConditionalBranch(case_a, case_c, switch_merge);
|
|
createBranch(case_b, switch_merge);
|
|
createBranch(case_c, switch_merge);
|
|
createConditionalBranch(switch_merge, header, exit); // loop or exit
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test deeply nested conditionals
|
|
*/
|
|
TEST_F(GcnShaderTest, DeeplyNestedConditionals) {
|
|
auto entry = createLabel("entry");
|
|
auto cond1 = createLabel("cond1");
|
|
auto cond2 = createLabel("cond2");
|
|
auto cond3 = createLabel("cond3");
|
|
auto leaf1 = createLabel("leaf1");
|
|
auto leaf2 = createLabel("leaf2");
|
|
auto leaf3 = createLabel("leaf3");
|
|
auto leaf4 = createLabel("leaf4");
|
|
auto merge3 = createLabel("merge3");
|
|
auto merge2 = createLabel("merge2");
|
|
auto merge1 = createLabel("merge1");
|
|
|
|
createConditionalBranch(entry, cond1, merge1);
|
|
createConditionalBranch(cond1, cond2, merge2);
|
|
createConditionalBranch(cond2, cond3, merge3);
|
|
createConditionalBranch(cond3, leaf1, leaf2);
|
|
createBranch(leaf1, merge3);
|
|
createBranch(leaf2, merge3);
|
|
createBranch(merge3, leaf3);
|
|
createBranch(leaf3, merge2);
|
|
createBranch(merge2, leaf4);
|
|
createBranch(leaf4, merge1);
|
|
createReturn(merge1);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test loop with multiple latches
|
|
*/
|
|
TEST_F(GcnShaderTest, LoopWithMultipleLatches) {
|
|
auto header = createLabel("header");
|
|
auto branch_point = createLabel("branch_point");
|
|
auto latch1 = createLabel("latch1");
|
|
auto latch2 = createLabel("latch2");
|
|
auto body = createLabel("body");
|
|
auto exit = createLabel("exit");
|
|
|
|
createBranch(header, branch_point);
|
|
createConditionalBranch(branch_point, latch1, latch2);
|
|
createConditionalBranch(latch1, header, body); // latch1 can loop back
|
|
createConditionalBranch(latch2, header, body); // latch2 can loop back
|
|
createBranch(body, exit);
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test triangular loop pattern
|
|
*/
|
|
TEST_F(GcnShaderTest, TriangularLoop) {
|
|
auto A = createLabel("A");
|
|
auto B = createLabel("B");
|
|
auto C = createLabel("C");
|
|
auto exit = createLabel("exit");
|
|
|
|
createConditionalBranch(A, B, exit);
|
|
createConditionalBranch(B, C, A); // B to C or back to A
|
|
createConditionalBranch(C, A, exit); // C back to A or exit
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test loop with nested exception handling
|
|
*/
|
|
TEST_F(GcnShaderTest, LoopWithNestedExceptionHandling) {
|
|
auto header = createLabel("header");
|
|
auto try_outer = createLabel("try_outer");
|
|
auto try_inner = createLabel("try_inner");
|
|
auto catch_inner = createLabel("catch_inner");
|
|
auto catch_outer = createLabel("catch_outer");
|
|
auto finally_block = createLabel("finally_block");
|
|
auto exit = createLabel("exit");
|
|
|
|
createBranch(header, try_outer);
|
|
createConditionalBranch(try_outer, try_inner, catch_outer);
|
|
createConditionalBranch(try_inner, finally_block, catch_inner);
|
|
createBranch(catch_inner, finally_block);
|
|
createBranch(catch_outer, finally_block);
|
|
createConditionalBranch(finally_block, header, exit); // loop or exit
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test hourglass pattern (two diamonds connected)
|
|
*/
|
|
TEST_F(GcnShaderTest, HourglassPattern) {
|
|
auto entry = createLabel("entry");
|
|
auto top_left = createLabel("top_left");
|
|
auto top_right = createLabel("top_right");
|
|
auto middle = createLabel("middle");
|
|
auto bottom_left = createLabel("bottom_left");
|
|
auto bottom_right = createLabel("bottom_right");
|
|
auto exit = createLabel("exit");
|
|
|
|
createConditionalBranch(entry, top_left, top_right);
|
|
createBranch(top_left, middle);
|
|
createBranch(top_right, middle);
|
|
createConditionalBranch(middle, bottom_left, bottom_right);
|
|
createBranch(bottom_left, exit);
|
|
createBranch(bottom_right, exit);
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test loop with break to outer scope
|
|
*/
|
|
TEST_F(GcnShaderTest, LoopWithBreakToOuter) {
|
|
auto outer_header = createLabel("outer_header");
|
|
auto inner_header = createLabel("inner_header");
|
|
auto inner_body = createLabel("inner_body");
|
|
auto break_check = createLabel("break_check");
|
|
auto outer_continue = createLabel("outer_continue");
|
|
auto exit = createLabel("exit");
|
|
|
|
createBranch(outer_header, inner_header);
|
|
createConditionalBranch(inner_header, inner_body, outer_continue);
|
|
createBranch(inner_body, break_check);
|
|
createConditionalBranch(break_check, inner_header,
|
|
exit); // continue inner or break outer
|
|
createBranch(outer_continue, outer_header);
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test complex switch with nested loops
|
|
*/
|
|
TEST_F(GcnShaderTest, ComplexSwitchWithNestedLoops) {
|
|
auto entry = createLabel("entry");
|
|
auto switch_header = createLabel("switch_header");
|
|
auto case1_loop = createLabel("case1_loop");
|
|
auto case1_body = createLabel("case1_body");
|
|
auto case2_loop = createLabel("case2_loop");
|
|
auto case2_body = createLabel("case2_body");
|
|
auto switch_exit = createLabel("switch_exit");
|
|
|
|
createBranch(entry, switch_header);
|
|
createConditionalBranch(switch_header, case1_loop, case2_loop);
|
|
|
|
// Case 1 with loop
|
|
createConditionalBranch(case1_loop, case1_body, switch_exit);
|
|
createBranch(case1_body, case1_loop);
|
|
|
|
// Case 2 with loop
|
|
createConditionalBranch(case2_loop, case2_body, switch_exit);
|
|
createBranch(case2_body, case2_loop);
|
|
|
|
createReturn(switch_exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test ladder pattern (sequential conditions)
|
|
*/
|
|
TEST_F(GcnShaderTest, LadderPattern) {
|
|
auto entry = createLabel("entry");
|
|
auto check1 = createLabel("check1");
|
|
auto check2 = createLabel("check2");
|
|
auto check3 = createLabel("check3");
|
|
auto action1 = createLabel("action1");
|
|
auto action2 = createLabel("action2");
|
|
auto action3 = createLabel("action3");
|
|
auto final_action = createLabel("final_action");
|
|
auto exit = createLabel("exit");
|
|
|
|
createBranch(entry, check1);
|
|
createConditionalBranch(check1, action1, check2);
|
|
createBranch(action1, final_action);
|
|
createConditionalBranch(check2, action2, check3);
|
|
createBranch(action2, final_action);
|
|
createConditionalBranch(check3, action3, final_action);
|
|
createBranch(action3, final_action);
|
|
createBranch(final_action, exit);
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test spiral pattern (increasing complexity)
|
|
*/
|
|
TEST_F(GcnShaderTest, SpiralPattern) {
|
|
auto center = createLabel("center");
|
|
auto ring1 = createLabel("ring1");
|
|
auto ring2 = createLabel("ring2");
|
|
auto ring3 = createLabel("ring3");
|
|
auto connector1 = createLabel("connector1");
|
|
auto connector2 = createLabel("connector2");
|
|
auto exit = createLabel("exit");
|
|
|
|
createConditionalBranch(center, ring1, exit);
|
|
createConditionalBranch(ring1, ring2, connector1);
|
|
createConditionalBranch(ring2, ring3, connector2);
|
|
createConditionalBranch(ring3, center, exit); // spiral back to center
|
|
createBranch(connector1, center);
|
|
createBranch(connector2, ring1);
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test binary tree traversal pattern
|
|
*/
|
|
TEST_F(GcnShaderTest, BinaryTreeTraversal) {
|
|
auto root = createLabel("root");
|
|
auto left_subtree = createLabel("left_subtree");
|
|
auto right_subtree = createLabel("right_subtree");
|
|
auto left_leaf = createLabel("left_leaf");
|
|
auto right_leaf = createLabel("right_leaf");
|
|
auto process_left = createLabel("process_left");
|
|
auto process_right = createLabel("process_right");
|
|
auto merge = createLabel("merge");
|
|
|
|
createConditionalBranch(root, left_subtree, right_subtree);
|
|
createConditionalBranch(left_subtree, left_leaf, process_left);
|
|
createConditionalBranch(right_subtree, right_leaf, process_right);
|
|
createBranch(left_leaf, merge);
|
|
createBranch(right_leaf, merge);
|
|
createBranch(process_left, merge);
|
|
createBranch(process_right, merge);
|
|
createReturn(merge);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test state machine pattern
|
|
*/
|
|
TEST_F(GcnShaderTest, StateMachine) {
|
|
auto init_state = createLabel("init_state");
|
|
auto state_a = createLabel("state_a");
|
|
auto state_b = createLabel("state_b");
|
|
auto state_c = createLabel("state_c");
|
|
auto transition = createLabel("transition");
|
|
auto final_state = createLabel("final_state");
|
|
|
|
createBranch(init_state, state_a);
|
|
createConditionalBranch(state_a, state_b, transition);
|
|
createConditionalBranch(state_b, state_c, state_a); // B to C or back to A
|
|
createConditionalBranch(state_c, state_a, final_state); // C to A or final
|
|
createConditionalBranch(transition, state_b, final_state);
|
|
createReturn(final_state);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test mesh pattern (highly connected)
|
|
*/
|
|
TEST_F(GcnShaderTest, MeshPattern) {
|
|
auto node1 = createLabel("node1");
|
|
auto node2 = createLabel("node2");
|
|
auto node3 = createLabel("node3");
|
|
auto node4 = createLabel("node4");
|
|
auto hub = createLabel("hub");
|
|
auto exit = createLabel("exit");
|
|
|
|
createConditionalBranch(node1, node2, node3);
|
|
createConditionalBranch(node2, node4, hub);
|
|
createConditionalBranch(node3, node4, hub);
|
|
createConditionalBranch(node4, node1, hub); // back to node1
|
|
createConditionalBranch(hub, node1, exit); // back to node1 or exit
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test pipeline pattern (sequential stages)
|
|
*/
|
|
TEST_F(GcnShaderTest, PipelinePattern) {
|
|
auto input_stage = createLabel("input_stage");
|
|
auto process_stage1 = createLabel("process_stage1");
|
|
auto process_stage2 = createLabel("process_stage2");
|
|
auto process_stage3 = createLabel("process_stage3");
|
|
auto output_stage = createLabel("output_stage");
|
|
auto error_handler = createLabel("error_handler");
|
|
auto retry = createLabel("retry");
|
|
auto exit = createLabel("exit");
|
|
|
|
createBranch(input_stage, process_stage1);
|
|
createConditionalBranch(process_stage1, process_stage2, error_handler);
|
|
createConditionalBranch(process_stage2, process_stage3, error_handler);
|
|
createConditionalBranch(process_stage3, output_stage, error_handler);
|
|
createBranch(output_stage, exit);
|
|
createConditionalBranch(error_handler, retry, exit);
|
|
createBranch(retry, input_stage); // retry from beginning
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test fractal-like recursive pattern
|
|
*/
|
|
TEST_F(GcnShaderTest, FractalPattern) {
|
|
auto entry = createLabel("entry");
|
|
auto level1 = createLabel("level1");
|
|
auto level2 = createLabel("level2");
|
|
auto level3 = createLabel("level3");
|
|
auto recurse_check = createLabel("recurse_check");
|
|
auto base_case = createLabel("base_case");
|
|
auto combine = createLabel("combine");
|
|
auto exit = createLabel("exit");
|
|
|
|
createBranch(entry, level1);
|
|
createConditionalBranch(level1, level2, recurse_check);
|
|
createConditionalBranch(level2, level3, base_case);
|
|
createConditionalBranch(level3, base_case, combine);
|
|
createConditionalBranch(recurse_check, level1, combine); // recurse or combine
|
|
createBranch(base_case, combine);
|
|
createConditionalBranch(combine, level1, exit); // recurse again or exit
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test butterfly pattern (symmetric branches that cross)
|
|
*/
|
|
TEST_F(GcnShaderTest, ButterflyPattern) {
|
|
auto entry = createLabel("entry");
|
|
auto wing1_top = createLabel("wing1_top");
|
|
auto wing1_bottom = createLabel("wing1_bottom");
|
|
auto wing2_top = createLabel("wing2_top");
|
|
auto wing2_bottom = createLabel("wing2_bottom");
|
|
auto body_center = createLabel("body_center");
|
|
auto exit = createLabel("exit");
|
|
|
|
createConditionalBranch(entry, wing1_top, wing2_top);
|
|
createConditionalBranch(wing1_top, wing2_bottom,
|
|
body_center); // cross pattern
|
|
createConditionalBranch(wing2_top, wing1_bottom,
|
|
body_center); // cross pattern
|
|
createBranch(wing1_bottom, exit);
|
|
createBranch(wing2_bottom, exit);
|
|
createBranch(body_center, exit);
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test nested try-catch with multiple exception types
|
|
*/
|
|
TEST_F(GcnShaderTest, NestedTryCatchMultipleExceptions) {
|
|
auto entry = createLabel("entry");
|
|
auto try_outer = createLabel("try_outer");
|
|
auto try_inner = createLabel("try_inner");
|
|
auto risky_operation = createLabel("risky_operation");
|
|
auto catch_type1 = createLabel("catch_type1");
|
|
auto catch_type2 = createLabel("catch_type2");
|
|
auto catch_all = createLabel("catch_all");
|
|
auto finally_inner = createLabel("finally_inner");
|
|
auto finally_outer = createLabel("finally_outer");
|
|
auto success = createLabel("success");
|
|
auto exit = createLabel("exit");
|
|
|
|
createBranch(entry, try_outer);
|
|
createBranch(try_outer, try_inner);
|
|
createBranch(try_inner, risky_operation);
|
|
createConditionalBranch(risky_operation, success, catch_type1);
|
|
createConditionalBranch(catch_type1, finally_inner, catch_type2);
|
|
createConditionalBranch(catch_type2, finally_inner, catch_all);
|
|
createBranch(catch_all, finally_inner);
|
|
createBranch(success, finally_inner);
|
|
createBranch(finally_inner, finally_outer);
|
|
createBranch(finally_outer, exit);
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test producer-consumer pattern
|
|
*/
|
|
TEST_F(GcnShaderTest, ProducerConsumerPattern) {
|
|
auto entry = createLabel("entry");
|
|
auto producer = createLabel("producer");
|
|
auto buffer_check = createLabel("buffer_check");
|
|
auto consumer = createLabel("consumer");
|
|
auto process_item = createLabel("process_item");
|
|
auto sync_point = createLabel("sync_point");
|
|
auto exit = createLabel("exit");
|
|
|
|
createConditionalBranch(entry, producer, consumer);
|
|
createBranch(producer, buffer_check);
|
|
createConditionalBranch(buffer_check, consumer,
|
|
producer); // buffer full, switch
|
|
createBranch(consumer, process_item);
|
|
createConditionalBranch(process_item, sync_point,
|
|
consumer); // process or continue
|
|
createConditionalBranch(sync_point, producer, exit); // sync or exit
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test multi-level break pattern
|
|
*/
|
|
TEST_F(GcnShaderTest, MultiLevelBreakPattern) {
|
|
auto outer_loop = createLabel("outer_loop");
|
|
auto middle_loop = createLabel("middle_loop");
|
|
auto inner_loop = createLabel("inner_loop");
|
|
auto inner_body = createLabel("inner_body");
|
|
auto break_check = createLabel("break_check");
|
|
auto middle_continue = createLabel("middle_continue");
|
|
auto outer_continue = createLabel("outer_continue");
|
|
auto exit = createLabel("exit");
|
|
|
|
createBranch(outer_loop, middle_loop);
|
|
createBranch(middle_loop, inner_loop);
|
|
createBranch(inner_loop, inner_body);
|
|
createBranch(inner_body, break_check);
|
|
createConditionalBranch(break_check, inner_loop,
|
|
middle_continue); // continue inner or break
|
|
createConditionalBranch(middle_continue, middle_loop,
|
|
outer_continue); // continue middle or break
|
|
createConditionalBranch(outer_continue, outer_loop,
|
|
exit); // continue outer or exit
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test parallel processing pattern
|
|
*/
|
|
TEST_F(GcnShaderTest, ParallelProcessingPattern) {
|
|
auto entry = createLabel("entry");
|
|
auto fork_point = createLabel("fork_point");
|
|
auto thread1 = createLabel("thread1");
|
|
auto thread2 = createLabel("thread2");
|
|
auto thread3 = createLabel("thread3");
|
|
auto work1 = createLabel("work1");
|
|
auto work2 = createLabel("work2");
|
|
auto work3 = createLabel("work3");
|
|
auto barrier = createLabel("barrier");
|
|
auto join_point = createLabel("join_point");
|
|
auto exit = createLabel("exit");
|
|
|
|
createBranch(entry, fork_point);
|
|
createConditionalBranch(fork_point, thread1, thread2);
|
|
createConditionalBranch(thread1, work1, thread3);
|
|
createBranch(thread2, work2);
|
|
createBranch(thread3, work3);
|
|
createBranch(work1, barrier);
|
|
createBranch(work2, barrier);
|
|
createBranch(work3, barrier);
|
|
createBranch(barrier, join_point);
|
|
createBranch(join_point, exit);
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test recursive descent parser pattern
|
|
*/
|
|
TEST_F(GcnShaderTest, RecursiveDescentParser) {
|
|
auto parse_expression = createLabel("parse_expression");
|
|
auto parse_term = createLabel("parse_term");
|
|
auto parse_factor = createLabel("parse_factor");
|
|
auto parse_number = createLabel("parse_number");
|
|
auto parse_parentheses = createLabel("parse_parentheses");
|
|
auto operator_check = createLabel("operator_check");
|
|
auto success = createLabel("success");
|
|
auto error = createLabel("error");
|
|
auto exit = createLabel("exit");
|
|
|
|
createBranch(parse_expression, parse_term);
|
|
createConditionalBranch(parse_term, parse_factor, operator_check);
|
|
createConditionalBranch(parse_factor, parse_number, parse_parentheses);
|
|
createBranch(parse_number, success);
|
|
createConditionalBranch(parse_parentheses, parse_expression,
|
|
error); // recursive call
|
|
createConditionalBranch(operator_check, parse_term,
|
|
success); // continue parsing
|
|
createBranch(success, exit);
|
|
createBranch(error, exit);
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test pathological irreducible CFG (worst case)
|
|
*/
|
|
TEST_F(GcnShaderTest, PathologicalIrreducibleCFG) {
|
|
auto entry = createLabel("entry");
|
|
auto A = createLabel("A");
|
|
auto B = createLabel("B");
|
|
auto C = createLabel("C");
|
|
auto D = createLabel("D");
|
|
auto E = createLabel("E");
|
|
auto F = createLabel("F");
|
|
auto exit = createLabel("exit");
|
|
|
|
// Create a highly irreducible pattern where multiple nodes can reach multiple
|
|
// nodes
|
|
createConditionalBranch(entry, A, B);
|
|
createConditionalBranch(A, C, D);
|
|
createConditionalBranch(B, D, E);
|
|
createConditionalBranch(C, E, exit);
|
|
createConditionalBranch(D, F, A); // back to A
|
|
createConditionalBranch(E, A, B); // back to A or B
|
|
createConditionalBranch(F, B, C); // back to B or C
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test loop with computed goto simulation
|
|
*/
|
|
TEST_F(GcnShaderTest, ComputedGotoSimulation) {
|
|
auto entry = createLabel("entry");
|
|
auto dispatch_table = createLabel("dispatch_table");
|
|
auto target1 = createLabel("target1");
|
|
auto target2 = createLabel("target2");
|
|
auto target3 = createLabel("target3");
|
|
auto target4 = createLabel("target4");
|
|
auto merge_point = createLabel("merge_point");
|
|
auto loop_back = createLabel("loop_back");
|
|
auto exit = createLabel("exit");
|
|
|
|
createBranch(entry, dispatch_table);
|
|
createConditionalBranch(dispatch_table, target1, target2);
|
|
createConditionalBranch(target1, target3, merge_point);
|
|
createConditionalBranch(target2, target4, merge_point);
|
|
createBranch(target3, merge_point);
|
|
createBranch(target4, merge_point);
|
|
createConditionalBranch(merge_point, loop_back, exit);
|
|
createBranch(loop_back, dispatch_table); // computed goto loop
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test coroutine-like pattern with yield points
|
|
*/
|
|
TEST_F(GcnShaderTest, CoroutineLikePattern) {
|
|
auto entry = createLabel("entry");
|
|
auto state_machine = createLabel("state_machine");
|
|
auto yield_point1 = createLabel("yield_point1");
|
|
auto work1 = createLabel("work1");
|
|
auto yield_point2 = createLabel("yield_point2");
|
|
auto work2 = createLabel("work2");
|
|
auto yield_point3 = createLabel("yield_point3");
|
|
auto final_work = createLabel("final_work");
|
|
auto exit = createLabel("exit");
|
|
|
|
createBranch(entry, state_machine);
|
|
createConditionalBranch(state_machine, yield_point1, yield_point2);
|
|
createBranch(yield_point1, work1);
|
|
createConditionalBranch(work1, state_machine,
|
|
yield_point2); // yield or continue
|
|
createBranch(yield_point2, work2);
|
|
createConditionalBranch(work2, state_machine,
|
|
yield_point3); // yield or continue
|
|
createBranch(yield_point3, final_work);
|
|
createConditionalBranch(final_work, state_machine, exit); // yield or complete
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test extreme nesting depth (stress test)
|
|
*/
|
|
TEST_F(GcnShaderTest, ExtremeNestingDepth) {
|
|
auto level0 = createLabel("level0");
|
|
auto level1 = createLabel("level1");
|
|
auto level2 = createLabel("level2");
|
|
auto level3 = createLabel("level3");
|
|
auto level4 = createLabel("level4");
|
|
auto level5 = createLabel("level5");
|
|
auto level6 = createLabel("level6");
|
|
auto level7 = createLabel("level7");
|
|
auto level8 = createLabel("level8");
|
|
auto level9 = createLabel("level9");
|
|
auto deep_core = createLabel("deep_core");
|
|
auto unwind = createLabel("unwind");
|
|
auto exit = createLabel("exit");
|
|
|
|
// Create deeply nested structure
|
|
createConditionalBranch(level0, level1, unwind);
|
|
createConditionalBranch(level1, level2, unwind);
|
|
createConditionalBranch(level2, level3, unwind);
|
|
createConditionalBranch(level3, level4, unwind);
|
|
createConditionalBranch(level4, level5, unwind);
|
|
createConditionalBranch(level5, level6, unwind);
|
|
createConditionalBranch(level6, level7, unwind);
|
|
createConditionalBranch(level7, level8, unwind);
|
|
createConditionalBranch(level8, level9, unwind);
|
|
createConditionalBranch(level9, deep_core, unwind);
|
|
createConditionalBranch(deep_core, level0, exit); // back to top or exit
|
|
createBranch(unwind, exit);
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
// ========================= COMPREHENSIVE SWITCH PATTERN TESTS
|
|
// =========================
|
|
|
|
/**
|
|
* Test simple switch with 3 cases
|
|
*/
|
|
TEST_F(GcnShaderTest, SimpleSwitch3Cases) {
|
|
auto entry = createLabel("entry");
|
|
auto case0 = createLabel("case0");
|
|
auto case1 = createLabel("case1");
|
|
auto case2 = createLabel("case2");
|
|
auto exit = createLabel("exit");
|
|
|
|
createSwitchBranch(entry, exit, {{0, case0}, {1, case1}, {2, case2}});
|
|
createBranch(case0, exit);
|
|
createBranch(case1, exit);
|
|
createBranch(case2, exit);
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test switch with fallthrough patterns
|
|
*/
|
|
TEST_F(GcnShaderTest, SwitchWithFallthroughPattern) {
|
|
auto entry = createLabel("entry");
|
|
auto case0 = createLabel("case0");
|
|
auto case1 = createLabel("case1");
|
|
auto case2 = createLabel("case2");
|
|
auto case3 = createLabel("case3");
|
|
auto exit = createLabel("exit");
|
|
|
|
createSwitchBranch(entry, exit,
|
|
{{0, case0}, {1, case1}, {2, case2}, {3, case3}});
|
|
createBranch(case0, case1); // fallthrough
|
|
createBranch(case1, case2); // fallthrough
|
|
createBranch(case2, exit); // break
|
|
createBranch(case3, exit); // break
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test switch with mixed fallthrough and breaks
|
|
*/
|
|
TEST_F(GcnShaderTest, SwitchMixedFallthroughBreaks) {
|
|
auto entry = createLabel("entry");
|
|
auto case0 = createLabel("case0");
|
|
auto case1 = createLabel("case1");
|
|
auto case2 = createLabel("case2");
|
|
auto case3 = createLabel("case3");
|
|
auto case4 = createLabel("case4");
|
|
auto exit = createLabel("exit");
|
|
|
|
createSwitchBranch(
|
|
entry, exit,
|
|
{{0, case0}, {1, case1}, {2, case2}, {3, case3}, {4, case4}});
|
|
createBranch(case0, exit); // break
|
|
createBranch(case1, case2); // fallthrough
|
|
createBranch(case2, case3); // fallthrough
|
|
createBranch(case3, exit); // break
|
|
createBranch(case4, exit); // break
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test large switch with 10 cases
|
|
*/
|
|
TEST_F(GcnShaderTest, LargeSwitch10Cases) {
|
|
auto entry = createLabel("entry");
|
|
auto case0 = createLabel("case0");
|
|
auto case1 = createLabel("case1");
|
|
auto case2 = createLabel("case2");
|
|
auto case3 = createLabel("case3");
|
|
auto case4 = createLabel("case4");
|
|
auto case5 = createLabel("case5");
|
|
auto case6 = createLabel("case6");
|
|
auto case7 = createLabel("case7");
|
|
auto case8 = createLabel("case8");
|
|
auto case9 = createLabel("case9");
|
|
auto exit = createLabel("exit");
|
|
|
|
createSwitchBranch(entry, exit,
|
|
{{0, case0},
|
|
{1, case1},
|
|
{2, case2},
|
|
{3, case3},
|
|
{4, case4},
|
|
{5, case5},
|
|
{6, case6},
|
|
{7, case7},
|
|
{8, case8},
|
|
{9, case9}});
|
|
createBranch(case0, exit);
|
|
createBranch(case1, exit);
|
|
createBranch(case2, exit);
|
|
createBranch(case3, exit);
|
|
createBranch(case4, exit);
|
|
createBranch(case5, exit);
|
|
createBranch(case6, exit);
|
|
createBranch(case7, exit);
|
|
createBranch(case8, exit);
|
|
createBranch(case9, exit);
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test very large switch with 25 cases
|
|
*/
|
|
TEST_F(GcnShaderTest, VeryLargeSwitch25Cases) {
|
|
auto entry = createLabel("entry");
|
|
std::vector<ir::Value> cases;
|
|
for (int i = 0; i < 25; i++) {
|
|
cases.push_back(createLabel("case" + std::to_string(i)));
|
|
}
|
|
auto exit = createLabel("exit");
|
|
|
|
// Create switch with 25 cases
|
|
std::vector<std::pair<std::uint32_t, ir::Value>> switch_cases;
|
|
for (int i = 0; i < 25; i++) {
|
|
switch_cases.push_back({i, cases[i]});
|
|
}
|
|
createSwitchBranch(entry, exit, switch_cases);
|
|
|
|
// Each case branches to exit
|
|
for (auto case_label : cases) {
|
|
createBranch(case_label, exit);
|
|
}
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test huge switch with 50 cases
|
|
*/
|
|
TEST_F(GcnShaderTest, HugeSwitch50Cases) {
|
|
auto entry = createLabel("entry");
|
|
std::vector<ir::Value> cases;
|
|
for (int i = 0; i < 50; i++) {
|
|
cases.push_back(createLabel("case" + std::to_string(i)));
|
|
}
|
|
auto exit = createLabel("exit");
|
|
|
|
// Create switch with 50 cases
|
|
std::vector<std::pair<std::uint32_t, ir::Value>> switch_cases;
|
|
for (int i = 0; i < 50; i++) {
|
|
switch_cases.push_back({i, cases[i]});
|
|
}
|
|
createSwitchBranch(entry, exit, switch_cases);
|
|
|
|
// Each case branches to exit
|
|
for (auto case_label : cases) {
|
|
createBranch(case_label, exit);
|
|
}
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test massive switch with 100 cases
|
|
*/
|
|
TEST_F(GcnShaderTest, MassiveSwitch100Cases) {
|
|
auto entry = createLabel("entry");
|
|
std::vector<ir::Value> cases;
|
|
for (int i = 0; i < 100; i++) {
|
|
cases.push_back(createLabel("case" + std::to_string(i)));
|
|
}
|
|
auto exit = createLabel("exit");
|
|
|
|
// Create switch with 100 cases
|
|
std::vector<std::pair<std::uint32_t, ir::Value>> switch_cases;
|
|
for (int i = 0; i < 100; i++) {
|
|
switch_cases.push_back({i, cases[i]});
|
|
}
|
|
createSwitchBranch(entry, exit, switch_cases);
|
|
|
|
// Each case branches to exit
|
|
for (auto case_label : cases) {
|
|
createBranch(case_label, exit);
|
|
}
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test switch with nested conditionals in cases
|
|
*/
|
|
TEST_F(GcnShaderTest, SwitchWithNestedConditionals) {
|
|
auto entry = createLabel("entry");
|
|
auto case0 = createLabel("case0");
|
|
auto case1 = createLabel("case1");
|
|
auto case2 = createLabel("case2");
|
|
auto nested_true_0 = createLabel("nested_true_0");
|
|
auto nested_false_0 = createLabel("nested_false_0");
|
|
auto nested_true_1 = createLabel("nested_true_1");
|
|
auto nested_false_1 = createLabel("nested_false_1");
|
|
auto exit = createLabel("exit");
|
|
|
|
createSwitchBranch(entry, exit, {{0, case0}, {1, case1}, {2, case2}});
|
|
|
|
// Case 0 has nested conditional
|
|
createConditionalBranch(case0, nested_true_0, nested_false_0);
|
|
createBranch(nested_true_0, exit);
|
|
createBranch(nested_false_0, exit);
|
|
|
|
// Case 1 has nested conditional
|
|
createConditionalBranch(case1, nested_true_1, nested_false_1);
|
|
createBranch(nested_true_1, exit);
|
|
createBranch(nested_false_1, exit);
|
|
|
|
// Case 2 is simple
|
|
createBranch(case2, exit);
|
|
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test switch with nested loops in cases
|
|
*/
|
|
TEST_F(GcnShaderTest, SwitchWithNestedLoops) {
|
|
auto entry = createLabel("entry");
|
|
auto case0 = createLabel("case0");
|
|
auto case1 = createLabel("case1");
|
|
auto case2 = createLabel("case2");
|
|
auto loop_body_0 = createLabel("loop_body_0");
|
|
auto loop_body_1 = createLabel("loop_body_1");
|
|
auto exit = createLabel("exit");
|
|
|
|
createSwitchBranch(entry, exit, {{0, case0}, {1, case1}, {2, case2}});
|
|
|
|
// Case 0 has a loop
|
|
createConditionalBranch(case0, loop_body_0, exit);
|
|
createConditionalBranch(loop_body_0, case0, exit); // loop back
|
|
|
|
// Case 1 has a loop
|
|
createConditionalBranch(case1, loop_body_1, exit);
|
|
createConditionalBranch(loop_body_1, case1, exit); // loop back
|
|
|
|
// Case 2 is simple
|
|
createBranch(case2, exit);
|
|
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test switch with sparse case values
|
|
*/
|
|
TEST_F(GcnShaderTest, SwitchWithSparseCases) {
|
|
auto entry = createLabel("entry");
|
|
auto case1 = createLabel("case1");
|
|
auto case10 = createLabel("case10");
|
|
auto case50 = createLabel("case50");
|
|
auto case100 = createLabel("case100");
|
|
auto case999 = createLabel("case999");
|
|
auto exit = createLabel("exit");
|
|
|
|
createSwitchBranch(
|
|
entry, exit,
|
|
{{1, case1}, {10, case10}, {50, case50}, {100, case100}, {999, case999}});
|
|
createBranch(case1, exit);
|
|
createBranch(case10, exit);
|
|
createBranch(case50, exit);
|
|
createBranch(case100, exit);
|
|
createBranch(case999, exit);
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test switch with negative case values
|
|
*/
|
|
TEST_F(GcnShaderTest, SwitchWithNegativeCases) {
|
|
auto entry = createLabel("entry");
|
|
auto case_neg10 = createLabel("case_neg10");
|
|
auto case_neg5 = createLabel("case_neg5");
|
|
auto case0 = createLabel("case0");
|
|
auto case5 = createLabel("case5");
|
|
auto case10 = createLabel("case10");
|
|
auto exit = createLabel("exit");
|
|
|
|
createSwitchBranch(entry, exit,
|
|
{{-10, case_neg10},
|
|
{-5, case_neg5},
|
|
{0, case0},
|
|
{5, case5},
|
|
{10, case10}});
|
|
createBranch(case_neg10, exit);
|
|
createBranch(case_neg5, exit);
|
|
createBranch(case0, exit);
|
|
createBranch(case5, exit);
|
|
createBranch(case10, exit);
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test switch inside loop
|
|
*/
|
|
TEST_F(GcnShaderTest, SwitchInsideLoop) {
|
|
auto entry = createLabel("entry");
|
|
auto loop_header = createLabel("loop_header");
|
|
auto switch_block = createLabel("switch_block");
|
|
auto case0 = createLabel("case0");
|
|
auto case1 = createLabel("case1");
|
|
auto case2 = createLabel("case2");
|
|
auto loop_continue = createLabel("loop_continue");
|
|
auto exit = createLabel("exit");
|
|
|
|
createBranch(entry, loop_header);
|
|
createConditionalBranch(loop_header, switch_block, exit);
|
|
|
|
createSwitchBranch(switch_block, loop_continue,
|
|
{{0, case0}, {1, case1}, {2, case2}});
|
|
createBranch(case0, loop_continue);
|
|
createBranch(case1, exit); // break from loop
|
|
createBranch(case2, loop_continue);
|
|
|
|
createBranch(loop_continue, loop_header);
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test loop inside switch case
|
|
*/
|
|
TEST_F(GcnShaderTest, LoopInsideSwitchCase) {
|
|
auto entry = createLabel("entry");
|
|
auto case0 = createLabel("case0");
|
|
auto case1 = createLabel("case1");
|
|
auto case2 = createLabel("case2");
|
|
auto loop_header = createLabel("loop_header");
|
|
auto loop_body = createLabel("loop_body");
|
|
auto exit = createLabel("exit");
|
|
|
|
createSwitchBranch(entry, exit, {{0, case0}, {1, case1}, {2, case2}});
|
|
|
|
// Case 0 is simple
|
|
createBranch(case0, exit);
|
|
|
|
// Case 1 contains a loop
|
|
createBranch(case1, loop_header);
|
|
createConditionalBranch(loop_header, loop_body, exit);
|
|
createConditionalBranch(loop_body, loop_header, exit);
|
|
|
|
// Case 2 is simple
|
|
createBranch(case2, exit);
|
|
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test nested switches
|
|
*/
|
|
TEST_F(GcnShaderTest, NestedSwitches) {
|
|
auto entry = createLabel("entry");
|
|
auto case0 = createLabel("case0");
|
|
auto case1 = createLabel("case1");
|
|
auto case2 = createLabel("case2");
|
|
auto inner_case0 = createLabel("inner_case0");
|
|
auto inner_case1 = createLabel("inner_case1");
|
|
auto inner_case2 = createLabel("inner_case2");
|
|
auto exit = createLabel("exit");
|
|
|
|
createSwitchBranch(entry, exit, {{0, case0}, {1, case1}, {2, case2}});
|
|
|
|
// Case 0 is simple
|
|
createBranch(case0, exit);
|
|
|
|
// Case 1 contains nested switch
|
|
createSwitchBranch(case1, exit,
|
|
{{0, inner_case0}, {1, inner_case1}, {2, inner_case2}});
|
|
createBranch(inner_case0, exit);
|
|
createBranch(inner_case1, exit);
|
|
createBranch(inner_case2, exit);
|
|
|
|
// Case 2 is simple
|
|
createBranch(case2, exit);
|
|
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test switch with complex fallthrough chain
|
|
*/
|
|
TEST_F(GcnShaderTest, SwitchComplexFallthroughChain) {
|
|
auto entry = createLabel("entry");
|
|
auto case0 = createLabel("case0");
|
|
auto case1 = createLabel("case1");
|
|
auto case2 = createLabel("case2");
|
|
auto case3 = createLabel("case3");
|
|
auto case4 = createLabel("case4");
|
|
auto case5 = createLabel("case5");
|
|
auto case6 = createLabel("case6");
|
|
auto case7 = createLabel("case7");
|
|
auto exit = createLabel("exit");
|
|
|
|
createSwitchBranch(entry, exit,
|
|
{{0, case0},
|
|
{1, case1},
|
|
{2, case2},
|
|
{3, case3},
|
|
{4, case4},
|
|
{5, case5},
|
|
{6, case6},
|
|
{7, case7}});
|
|
|
|
// Complex fallthrough pattern: 0->1->2, 3->break, 4->5->6->7, 7->break
|
|
createBranch(case0, case1); // fallthrough
|
|
createBranch(case1, case2); // fallthrough
|
|
createBranch(case2, exit); // break
|
|
createBranch(case3, exit); // break
|
|
createBranch(case4, case5); // fallthrough
|
|
createBranch(case5, case6); // fallthrough
|
|
createBranch(case6, case7); // fallthrough
|
|
createBranch(case7, exit); // break
|
|
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test switch with cross-case jumps (goto simulation)
|
|
*/
|
|
TEST_F(GcnShaderTest, SwitchWithCrossCaseJumps) {
|
|
auto entry = createLabel("entry");
|
|
auto case0 = createLabel("case0");
|
|
auto case1 = createLabel("case1");
|
|
auto case2 = createLabel("case2");
|
|
auto case3 = createLabel("case3");
|
|
auto shared_code = createLabel("shared_code");
|
|
auto exit = createLabel("exit");
|
|
|
|
createSwitchBranch(entry, exit,
|
|
{{0, case0}, {1, case1}, {3, case3}, {2, case2}});
|
|
|
|
// Cases can jump to shared code or each other
|
|
createBranch(case0, shared_code);
|
|
createBranch(case1, case3); // jump to case 3
|
|
createBranch(case2, shared_code);
|
|
createBranch(case3, exit);
|
|
createBranch(shared_code, exit);
|
|
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test switch with multiple shared exit points
|
|
*/
|
|
TEST_F(GcnShaderTest, SwitchMultipleSharedExits) {
|
|
auto entry = createLabel("entry");
|
|
auto case0 = createLabel("case0");
|
|
auto case1 = createLabel("case1");
|
|
auto case2 = createLabel("case2");
|
|
auto case3 = createLabel("case3");
|
|
auto exit1 = createLabel("exit1");
|
|
auto exit2 = createLabel("exit2");
|
|
auto final_exit = createLabel("final_exit");
|
|
|
|
createSwitchBranch(entry, final_exit,
|
|
{{0, case0}, {1, case1}, {2, case2}, {3, case3}});
|
|
|
|
// Cases branch to different exit points
|
|
createBranch(case0, exit1);
|
|
createBranch(case1, exit1);
|
|
createBranch(case2, exit2);
|
|
createBranch(case3, exit2);
|
|
|
|
createBranch(exit1, final_exit);
|
|
createBranch(exit2, final_exit);
|
|
createReturn(final_exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test switch with return statements in cases
|
|
*/
|
|
TEST_F(GcnShaderTest, SwitchWithReturns) {
|
|
auto entry = createLabel("entry");
|
|
auto case0 = createLabel("case0");
|
|
auto case1 = createLabel("case1");
|
|
auto case2 = createLabel("case2");
|
|
auto case3 = createLabel("case3");
|
|
auto exit = createLabel("exit");
|
|
|
|
createSwitchBranch(entry, exit,
|
|
{{0, case0}, {1, case1}, {2, case2}, {3, case3}});
|
|
|
|
// Some cases return directly, others continue
|
|
createReturn(case0); // direct return
|
|
createBranch(case1, exit);
|
|
createReturn(case2); // direct return
|
|
createBranch(case3, exit);
|
|
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test switch with exception-like jumps
|
|
*/
|
|
TEST_F(GcnShaderTest, SwitchWithExceptionJumps) {
|
|
auto entry = createLabel("entry");
|
|
auto case0 = createLabel("case0");
|
|
auto case1 = createLabel("case1");
|
|
auto case2 = createLabel("case2");
|
|
auto normal_flow = createLabel("normal_flow");
|
|
auto exception_handler = createLabel("exception_handler");
|
|
auto exit = createLabel("exit");
|
|
|
|
createSwitchBranch(entry, exit, {{0, case0}, {1, case1}, {2, case2}});
|
|
|
|
// Cases can either continue normally or jump to exception handler
|
|
createConditionalBranch(case0, normal_flow, exception_handler);
|
|
createConditionalBranch(case1, normal_flow, exception_handler);
|
|
createBranch(case2, normal_flow);
|
|
|
|
createBranch(normal_flow, exit);
|
|
createBranch(exception_handler, exit);
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test switch-based state machine
|
|
*/
|
|
TEST_F(GcnShaderTest, SwitchBasedStateMachine) {
|
|
auto entry = createLabel("entry");
|
|
auto header = createLabel("header");
|
|
auto state_machine = createLabel("state_machine");
|
|
auto state0 = createLabel("state0");
|
|
auto state1 = createLabel("state1");
|
|
auto state2 = createLabel("state2");
|
|
auto state3 = createLabel("state3");
|
|
auto update_state = createLabel("update_state");
|
|
auto exit = createLabel("exit");
|
|
|
|
createBranch(entry, header);
|
|
|
|
// State machine loop
|
|
createConditionalBranch(header, exit, state_machine); // continue condition
|
|
createSwitchBranch(state_machine, exit,
|
|
{{0, state0}, {1, state1}, {2, state2}, {3, state3}});
|
|
|
|
// Each state processes and transitions
|
|
createBranch(state0, update_state);
|
|
createBranch(state1, update_state);
|
|
createBranch(state2, update_state);
|
|
createBranch(state3, exit); // final state
|
|
|
|
createBranch(update_state, state_machine); // loop back
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test switch with computed jump table simulation
|
|
*/
|
|
TEST_F(GcnShaderTest, SwitchComputedJumpTable) {
|
|
auto entry = createLabel("entry");
|
|
auto compute_index = createLabel("compute_index");
|
|
auto jump_table = createLabel("jump_table");
|
|
auto target0 = createLabel("target0");
|
|
auto target1 = createLabel("target1");
|
|
auto target2 = createLabel("target2");
|
|
auto target3 = createLabel("target3");
|
|
auto target4 = createLabel("target4");
|
|
auto merge = createLabel("merge");
|
|
auto exit = createLabel("exit");
|
|
|
|
createBranch(entry, compute_index);
|
|
createBranch(compute_index, jump_table);
|
|
|
|
// Jump table with 5 targets
|
|
createSwitchBranch(
|
|
jump_table, merge,
|
|
{{0, target0}, {1, target1}, {2, target2}, {3, target3}, {4, target4}});
|
|
|
|
// Each target does some work then merges
|
|
createBranch(target0, merge);
|
|
createBranch(target1, merge);
|
|
createBranch(target2, merge);
|
|
createBranch(target3, merge);
|
|
createBranch(target4, merge);
|
|
|
|
createBranch(merge, exit);
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test switch with interleaved loops and conditionals
|
|
*/
|
|
TEST_F(GcnShaderTest, SwitchInterleavedLoopsConditionals) {
|
|
auto entry = createLabel("entry");
|
|
auto case0 = createLabel("case0");
|
|
auto case1 = createLabel("case1");
|
|
auto case2 = createLabel("case2");
|
|
auto loop0 = createLabel("loop0");
|
|
auto cond0 = createLabel("cond0");
|
|
auto loop1 = createLabel("loop1");
|
|
auto exit = createLabel("exit");
|
|
|
|
createSwitchBranch(entry, exit, {{0, case0}, {1, case1}, {2, case2}});
|
|
|
|
// Case 0: loop then conditional
|
|
createBranch(case0, loop0);
|
|
createConditionalBranch(loop0, loop0, cond0); // loop
|
|
createConditionalBranch(cond0, exit, exit);
|
|
|
|
// Case 1: conditional then loop
|
|
createConditionalBranch(case1, loop1, exit);
|
|
createConditionalBranch(loop1, loop1, exit); // loop
|
|
|
|
// Case 2: simple exit
|
|
createBranch(case2, exit);
|
|
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test ultra-large switch with 200 cases (stress test)
|
|
*/
|
|
TEST_F(GcnShaderTest, UltraLargeSwitch200Cases) {
|
|
auto entry = createLabel("entry");
|
|
std::vector<ir::Value> cases;
|
|
for (int i = 0; i < 200; i++) {
|
|
cases.push_back(createLabel("case" + std::to_string(i)));
|
|
}
|
|
auto exit = createLabel("exit");
|
|
|
|
// Create switch with 200 cases
|
|
std::vector<std::pair<std::uint32_t, ir::Value>> switch_cases;
|
|
for (int i = 0; i < 200; i++) {
|
|
switch_cases.push_back({i, cases[i]});
|
|
}
|
|
createSwitchBranch(entry, exit, switch_cases);
|
|
|
|
// Each case branches to exit (simple to focus on switch complexity)
|
|
for (auto case_label : cases) {
|
|
createBranch(case_label, exit);
|
|
}
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|
|
|
|
/**
|
|
* Test mega switch with 500 cases (ultimate stress test)
|
|
*/
|
|
TEST_F(GcnShaderTest, MegaSwitch500Cases) {
|
|
auto entry = createLabel("entry");
|
|
std::vector<ir::Value> cases;
|
|
for (int i = 0; i < 500; i++) {
|
|
cases.push_back(createLabel("case" + std::to_string(i)));
|
|
}
|
|
auto exit = createLabel("exit");
|
|
|
|
// Create switch with 500 cases
|
|
std::vector<std::pair<std::uint32_t, ir::Value>> switch_cases;
|
|
for (int i = 0; i < 500; i++) {
|
|
switch_cases.push_back({i, cases[i]});
|
|
}
|
|
createSwitchBranch(entry, exit, switch_cases);
|
|
|
|
// Each case branches to exit
|
|
for (auto case_label : cases) {
|
|
createBranch(case_label, exit);
|
|
}
|
|
createReturn(exit);
|
|
|
|
EXPECT_TRUE(testStructurization());
|
|
}
|