#include "Converter.hpp" #include "CfBuilder.hpp" #include "ConverterContext.hpp" #include "Fragment.hpp" #include "Instruction.hpp" #include "RegisterState.hpp" #include "UniformBindings.hpp" #include "amdgpu/RemoteMemory.hpp" #include "cf.hpp" #include "scf.hpp" #include "util/unreachable.hpp" #include #include #include #include static void printInstructions(const scf::PrintOptions &options, unsigned depth, std::uint32_t *instBegin, std::size_t size) { auto instHex = instBegin; auto instEnd = instBegin + size / sizeof(std::uint32_t); while (instHex < instEnd) { auto instruction = amdgpu::shader::Instruction(instHex); std::printf("%s", options.makeIdent(depth).c_str()); instruction.dump(); std::printf("\n"); instHex += instruction.size(); } } namespace amdgpu::shader { class Converter { scf::Context *scfContext; cf::Context cfContext; RemoteMemory memory; Function *function = nullptr; std::forward_list states; std::vector freeStates; public: void convertFunction(RemoteMemory mem, scf::Context *scfCtxt, scf::Block *block, Function *fn) { scfContext = scfCtxt; function = fn; memory = mem; auto lastFragment = convertBlock(block, &function->entryFragment, nullptr); if (lastFragment != nullptr) { lastFragment->builder.createBranch(fn->exitFragment.entryBlockId); lastFragment->appendBranch(fn->exitFragment); } initState(&fn->exitFragment); } private: RegisterState *allocateState() { if (freeStates.empty()) { return &states.emplace_front(); } auto result = freeStates.back(); freeStates.pop_back(); *result = {}; return result; } void releaseState(RegisterState *state) { assert(state != nullptr); freeStates.push_back(state); } void initState(Fragment *fragment, std::uint64_t address = 0) { if (fragment->registers == nullptr) { fragment->registers = allocateState(); } if (address != 0) { fragment->registers->pc = address; } fragment->injectValuesFromPreds(); fragment->predecessors.clear(); } void releaseStateOf(Fragment *frag) { releaseState(frag->registers); frag->registers = nullptr; frag->values = {}; frag->outputs = {}; } bool needInjectExecTest(Fragment *fragment) { auto inst = memory.getPointer(fragment->registers->pc); auto instClass = getInstructionClass(*inst); return instClass == InstructionClass::Vop2 || instClass == InstructionClass::Vop3 || instClass == InstructionClass::Mubuf || instClass == InstructionClass::Mtbuf || instClass == InstructionClass::Mimg || instClass == InstructionClass::Ds || instClass == InstructionClass::Vintrp || instClass == InstructionClass::Exp || instClass == InstructionClass::Vop1 || instClass == InstructionClass::Vopc /* || instClass == InstructionClass::Smrd*/ ; } spirv::BoolValue createExecTest(Fragment *fragment) { auto context = fragment->context; auto &builder = fragment->builder; auto boolT = context->getBoolType(); auto uint32_0 = context->getUInt32(0); auto loIsNotZero = builder.createINotEqual(boolT, fragment->getExecLo().value, uint32_0); auto hiIsNotZero = builder.createINotEqual(boolT, fragment->getExecHi().value, uint32_0); return builder.createLogicalOr(boolT, loIsNotZero, hiIsNotZero); } Fragment *convertBlock(scf::Block *block, Fragment *rootFragment, Fragment *loopMergeFragment) { Fragment *currentFragment = nullptr; for (scf::Node *node = block->getRootNode(); node != nullptr; node = node->getNext()) { if (auto bb = dynCast(node)) { if (currentFragment == nullptr) { currentFragment = rootFragment; } else { auto newFragment = function->createFragment(); currentFragment->appendBranch(*newFragment); currentFragment->builder.createBranch(newFragment->entryBlockId); currentFragment = newFragment; } initState(currentFragment, bb->getAddress()); for (auto pred : currentFragment->predecessors) { releaseStateOf(pred); } if (needInjectExecTest(currentFragment)) { auto bodyFragment = function->createFragment(); auto mergeFragment = function->createFragment(); auto cond = createExecTest(currentFragment); currentFragment->appendBranch(*bodyFragment); currentFragment->appendBranch(*mergeFragment); currentFragment->builder.createSelectionMerge( mergeFragment->entryBlockId, {}); currentFragment->builder.createBranchConditional( cond, bodyFragment->entryBlockId, mergeFragment->entryBlockId); initState(bodyFragment, bb->getAddress()); bodyFragment->convert(bb->getSize()); bodyFragment->appendBranch(*mergeFragment); bodyFragment->builder.createBranch(mergeFragment->entryBlockId); initState(mergeFragment); releaseState(currentFragment->registers); releaseState(bodyFragment->registers); currentFragment = mergeFragment; } else { currentFragment->convert(bb->getSize()); } continue; } if (auto ifElse = dynCast(node)) { auto isBreakBlock = [](scf::Block *block) { if (block->isEmpty()) { return false; } if (block->getLastNode() != block->getRootNode()) { return false; } return dynamic_cast(block->getRootNode()) != nullptr; }; if (loopMergeFragment != nullptr && ifElse->ifTrue->isEmpty() && isBreakBlock(ifElse->ifFalse)) { auto mergeFragment = function->createFragment(); currentFragment->appendBranch(*mergeFragment); currentFragment->appendBranch(*loopMergeFragment); currentFragment->builder.createBranchConditional( currentFragment->branchCondition, mergeFragment->entryBlockId, loopMergeFragment->entryBlockId); initState(mergeFragment); releaseStateOf(currentFragment); currentFragment = mergeFragment; continue; } auto ifTrueFragment = function->createFragment(); auto ifFalseFragment = function->createFragment(); auto mergeFragment = function->createFragment(); currentFragment->appendBranch(*ifTrueFragment); currentFragment->appendBranch(*ifFalseFragment); auto ifTrueLastBlock = convertBlock(ifElse->ifTrue, ifTrueFragment, loopMergeFragment); auto ifFalseLastBlock = convertBlock(ifElse->ifFalse, ifFalseFragment, loopMergeFragment); if (ifTrueLastBlock != nullptr) { if (!ifTrueLastBlock->hasTerminator) { ifTrueLastBlock->builder.createBranch(mergeFragment->entryBlockId); ifTrueLastBlock->appendBranch(*mergeFragment); } if (ifTrueLastBlock->registers == nullptr) { initState(ifTrueLastBlock); } } if (ifFalseLastBlock != nullptr) { if (!ifFalseLastBlock->hasTerminator) { ifFalseLastBlock->builder.createBranch(mergeFragment->entryBlockId); ifFalseLastBlock->appendBranch(*mergeFragment); } if (ifFalseLastBlock->registers == nullptr) { initState(ifFalseLastBlock); } } currentFragment->builder.createSelectionMerge( mergeFragment->entryBlockId, {}); currentFragment->builder.createBranchConditional( currentFragment->branchCondition, ifTrueFragment->entryBlockId, ifFalseFragment->entryBlockId); releaseStateOf(currentFragment); initState(mergeFragment); if (ifTrueLastBlock != nullptr) { releaseStateOf(ifTrueLastBlock); } if (ifFalseLastBlock != nullptr) { releaseStateOf(ifFalseLastBlock); } currentFragment = mergeFragment; continue; } if (auto loop = dynCast(node)) { auto headerFragment = function->createFragment(); auto bodyFragment = function->createFragment(); auto mergeFragment = function->createDetachedFragment(); auto continueFragment = function->createDetachedFragment(); currentFragment->builder.createBranch(headerFragment->entryBlockId); currentFragment->appendBranch(*headerFragment); initState(headerFragment); releaseStateOf(currentFragment); headerFragment->builder.createLoopMerge( mergeFragment->entryBlockId, continueFragment->entryBlockId, spv::LoopControlMask::MaskNone, {}); headerFragment->builder.createBranch(bodyFragment->entryBlockId); headerFragment->appendBranch(*bodyFragment); auto bodyLastBlock = convertBlock(loop->body, bodyFragment, mergeFragment); if (bodyLastBlock != nullptr) { if (bodyLastBlock->registers == nullptr) { initState(bodyLastBlock); } bodyLastBlock->builder.createBranch(continueFragment->entryBlockId); bodyLastBlock->appendBranch(*continueFragment); } continueFragment->builder.createBranch(headerFragment->entryBlockId); continueFragment->appendBranch(*headerFragment); initState(continueFragment); releaseStateOf(headerFragment); initState(mergeFragment); if (bodyLastBlock != nullptr) { releaseStateOf(bodyLastBlock); } function->appendFragment(continueFragment); function->appendFragment(mergeFragment); releaseStateOf(continueFragment); currentFragment = mergeFragment; continue; } if (dynCast(node)) { auto jumpAddress = currentFragment->jumpAddress; std::printf("jump to %lx\n", jumpAddress); std::fflush(stdout); if (jumpAddress == 0) { util::unreachable("no jump register on unknown block"); } auto block = buildCf(cfContext, memory, jumpAddress); auto basicBlockPrinter = [this](const scf::PrintOptions &opts, unsigned depth, scf::BasicBlock *bb) { printInstructions(opts, depth, memory.getPointer(bb->getAddress()), bb->getSize()); }; auto scfBlock = scf::structurize(*scfContext, block); scfBlock->print({.blockPrinter = basicBlockPrinter}, 0); std::fflush(stdout); auto targetFragment = function->createFragment(); currentFragment->builder.createBranch(targetFragment->entryBlockId); currentFragment->appendBranch(*targetFragment); auto result = convertBlock(scfBlock, targetFragment, nullptr); if (currentFragment->registers == nullptr) { initState(targetFragment); releaseStateOf(currentFragment); } return result; } if (dynCast(node)) { currentFragment->appendBranch(function->exitFragment); currentFragment->builder.createBranch( function->exitFragment.entryBlockId); currentFragment->hasTerminator = true; return nullptr; } node->dump(); util::unreachable(); } return currentFragment != nullptr ? currentFragment : rootFragment; } }; }; // namespace amdgpu::shader amdgpu::shader::Shader amdgpu::shader::convert(RemoteMemory memory, Stage stage, std::uint64_t entry, std::span userSpgrs, std::uint32_t dimX, std::uint32_t dimY, std::uint32_t dimZ, util::MemoryAreaTable<> &dependencies) { ConverterContext ctxt(memory, stage, &dependencies); auto &builder = ctxt.getBuilder(); builder.createCapability(spv::Capability::Shader); builder.createCapability(spv::Capability::ImageQuery); builder.createCapability(spv::Capability::ImageBuffer); builder.createCapability(spv::Capability::UniformAndStorageBuffer8BitAccess); builder.createCapability(spv::Capability::UniformAndStorageBuffer16BitAccess); builder.createCapability(spv::Capability::Int64); builder.setMemoryModel(spv::AddressingModel::Logical, spv::MemoryModel::GLSL450); scf::Context scfContext; scf::Block *entryBlock = nullptr; { cf::Context cfContext; auto entryBB = buildCf(cfContext, memory, entry); entryBlock = scf::structurize(scfContext, entryBB); } // std::printf("========== stage: %u, user sgprs: %zu\n", (unsigned)stage, // userSpgrs.size()); // std::printf("structurized CFG:\n"); // auto basicBlockPrinter = [memory](const scf::PrintOptions &opts, // unsigned depth, scf::BasicBlock *bb) { // printInstructions(opts, depth, // memory.getPointer(bb->getAddress()), // bb->getSize()); // }; // entryBlock->print({.blockPrinter = basicBlockPrinter}, 0); // std::printf("==========\n"); auto mainFunction = ctxt.createFunction(0); mainFunction->userSgprs = userSpgrs; mainFunction->stage = stage; Converter converter; converter.convertFunction(memory, &scfContext, entryBlock, mainFunction); Shader result; std::fflush(stdout); mainFunction->exitFragment.outputs.clear(); std::size_t samplerCount = 0; std::size_t imageCount = 0; std::size_t bufferCount = 0; for (auto &uniform : ctxt.getUniforms()) { auto &newUniform = result.uniforms.emplace_back(); for (int i = 0; i < 8; ++i) { newUniform.buffer[i] = uniform.buffer[i]; } std::uint32_t descriptorSet = 0; switch (uniform.typeId) { case TypeId::Sampler: newUniform.kind = Shader::UniformKind::Sampler; newUniform.binding = UniformBindings::getSamplerBinding(stage, samplerCount++); break; case TypeId::Image2D: newUniform.kind = Shader::UniformKind::Image; newUniform.binding = UniformBindings::getImageBinding(stage, imageCount++); break; default: newUniform.kind = Shader::UniformKind::Buffer; newUniform.binding = UniformBindings::getBufferBinding(stage, bufferCount++); break; } ctxt.getBuilder().createDecorate( uniform.variable, spv::Decoration::DescriptorSet, {{descriptorSet}}); ctxt.getBuilder().createDecorate(uniform.variable, spv::Decoration::Binding, {{newUniform.binding}}); newUniform.accessOp = uniform.accessOp; } mainFunction->insertReturn(); for (auto frag : mainFunction->fragments) { mainFunction->builder.insertBlock(frag->builder); } mainFunction->builder.insertBlock(mainFunction->exitFragment.builder); builder.insertFunction(mainFunction->builder, mainFunction->getResultType(), spv::FunctionControlMask::MaskNone, mainFunction->getFunctionType()); if (stage == Stage::Vertex) { builder.createEntryPoint(spv::ExecutionModel::Vertex, mainFunction->builder.id, "main", ctxt.getInterfaces()); } else if (stage == Stage::Fragment) { builder.createEntryPoint(spv::ExecutionModel::Fragment, mainFunction->builder.id, "main", ctxt.getInterfaces()); builder.createExecutionMode(mainFunction->builder.id, spv::ExecutionMode::OriginUpperLeft, {}); } else if (stage == Stage::Compute) { builder.createEntryPoint(spv::ExecutionModel::GLCompute, mainFunction->builder.id, "main", ctxt.getInterfaces()); builder.createExecutionMode(mainFunction->builder.id, spv::ExecutionMode::LocalSize, {{dimX, dimY, dimZ}}); } result.spirv = ctxt.getBuilder().build(SPV_VERSION, 0); return result; }