| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 |
- using Ryujinx.Graphics.Shader.IntermediateRepresentation;
- using System;
- using System.Collections.Generic;
- namespace Ryujinx.Graphics.Shader.StructuredIr
- {
- static class StructuredProgram
- {
- public static StructuredProgramInfo MakeStructuredProgram(BasicBlock[] blocks, ShaderConfig config)
- {
- PhiFunctions.Remove(blocks);
- StructuredProgramContext context = new StructuredProgramContext(blocks.Length, config);
- for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
- {
- BasicBlock block = blocks[blkIndex];
- context.EnterBlock(block);
- foreach (INode node in block.Operations)
- {
- Operation operation = (Operation)node;
- if (IsBranchInst(operation.Inst))
- {
- context.LeaveBlock(block, operation);
- }
- else
- {
- AddOperation(context, operation);
- }
- }
- }
- GotoElimination.Eliminate(context.GetGotos());
- AstOptimizer.Optimize(context);
- return context.Info;
- }
- private static void AddOperation(StructuredProgramContext context, Operation operation)
- {
- Instruction inst = operation.Inst;
- IAstNode[] sources = new IAstNode[operation.SourcesCount];
- for (int index = 0; index < sources.Length; index++)
- {
- sources[index] = context.GetOperandUse(operation.GetSource(index));
- }
- int componentMask = 1 << operation.ComponentIndex;
- AstTextureOperation GetAstTextureOperation(TextureOperation texOp)
- {
- return new AstTextureOperation(
- inst,
- texOp.Type,
- texOp.Flags,
- texOp.Handle,
- 4, // TODO: Non-hardcoded array size.
- componentMask,
- sources);
- }
- if (operation.Dest != null)
- {
- AstOperand dest = context.GetOperandDef(operation.Dest);
- if (inst == Instruction.LoadConstant)
- {
- Operand slot = operation.GetSource(0);
- if (slot.Type != OperandType.Constant)
- {
- throw new InvalidOperationException("Found load with non-constant constant buffer slot.");
- }
- context.Info.CBuffers.Add(slot.Value);
- }
- else if (inst == Instruction.LoadStorage)
- {
- Operand slot = operation.GetSource(0);
- if (slot.Type != OperandType.Constant)
- {
- throw new InvalidOperationException("Found load or store with non-constant storage buffer slot.");
- }
- context.Info.SBuffers.Add(slot.Value);
- }
- AstAssignment assignment;
- // If all the sources are bool, it's better to use short-circuiting
- // logical operations, rather than forcing a cast to int and doing
- // a bitwise operation with the value, as it is likely to be used as
- // a bool in the end.
- if (IsBitwiseInst(inst) && AreAllSourceTypesEqual(sources, VariableType.Bool))
- {
- inst = GetLogicalFromBitwiseInst(inst);
- }
- bool isCondSel = inst == Instruction.ConditionalSelect;
- bool isCopy = inst == Instruction.Copy;
- if (isCondSel || isCopy)
- {
- VariableType type = GetVarTypeFromUses(operation.Dest);
- if (isCondSel && type == VariableType.F32)
- {
- inst |= Instruction.FP;
- }
- dest.VarType = type;
- }
- else
- {
- dest.VarType = InstructionInfo.GetDestVarType(inst);
- }
- IAstNode source;
- if (operation is TextureOperation texOp)
- {
- AstTextureOperation astTexOp = GetAstTextureOperation(texOp);
- if (texOp.Inst == Instruction.ImageLoad)
- {
- context.Info.Images.Add(astTexOp);
- }
- else
- {
- context.Info.Samplers.Add(astTexOp);
- }
- source = astTexOp;
- }
- else if (!isCopy)
- {
- source = new AstOperation(inst, componentMask, sources);
- }
- else
- {
- source = sources[0];
- }
- assignment = new AstAssignment(dest, source);
- context.AddNode(assignment);
- }
- else if (operation.Inst == Instruction.Comment)
- {
- context.AddNode(new AstComment(((CommentNode)operation).Comment));
- }
- else if (operation is TextureOperation texOp)
- {
- AstTextureOperation astTexOp = GetAstTextureOperation(texOp);
- context.Info.Images.Add(astTexOp);
- context.AddNode(astTexOp);
- }
- else
- {
- if (inst == Instruction.StoreStorage)
- {
- Operand slot = operation.GetSource(0);
- if (slot.Type != OperandType.Constant)
- {
- throw new InvalidOperationException("Found load or store with non-constant storage buffer slot.");
- }
- context.Info.SBuffers.Add(slot.Value);
- }
- context.AddNode(new AstOperation(inst, sources));
- }
- // Those instructions needs to be emulated by using helper functions,
- // because they are NVIDIA specific. Those flags helps the backend to
- // decide which helper functions are needed on the final generated code.
- switch (operation.Inst)
- {
- case Instruction.Shuffle:
- context.Info.HelperFunctionsMask |= HelperFunctionsMask.Shuffle;
- break;
- case Instruction.ShuffleDown:
- context.Info.HelperFunctionsMask |= HelperFunctionsMask.ShuffleDown;
- break;
- case Instruction.ShuffleUp:
- context.Info.HelperFunctionsMask |= HelperFunctionsMask.ShuffleUp;
- break;
- case Instruction.ShuffleXor:
- context.Info.HelperFunctionsMask |= HelperFunctionsMask.ShuffleXor;
- break;
- case Instruction.SwizzleAdd:
- context.Info.HelperFunctionsMask |= HelperFunctionsMask.SwizzleAdd;
- break;
- }
- }
- private static VariableType GetVarTypeFromUses(Operand dest)
- {
- HashSet<Operand> visited = new HashSet<Operand>();
- Queue<Operand> pending = new Queue<Operand>();
- bool Enqueue(Operand operand)
- {
- if (visited.Add(operand))
- {
- pending.Enqueue(operand);
- return true;
- }
- return false;
- }
- Enqueue(dest);
- while (pending.TryDequeue(out Operand operand))
- {
- foreach (INode useNode in operand.UseOps)
- {
- if (!(useNode is Operation operation))
- {
- continue;
- }
- if (operation.Inst == Instruction.Copy)
- {
- if (operation.Dest.Type == OperandType.LocalVariable)
- {
- if (Enqueue(operation.Dest))
- {
- break;
- }
- }
- else
- {
- return OperandInfo.GetVarType(operation.Dest.Type);
- }
- }
- else
- {
- for (int index = 0; index < operation.SourcesCount; index++)
- {
- if (operation.GetSource(index) == operand)
- {
- return InstructionInfo.GetSrcVarType(operation.Inst, index);
- }
- }
- }
- }
- }
- return VariableType.S32;
- }
- private static bool AreAllSourceTypesEqual(IAstNode[] sources, VariableType type)
- {
- foreach (IAstNode node in sources)
- {
- if (!(node is AstOperand operand))
- {
- return false;
- }
- if (operand.VarType != type)
- {
- return false;
- }
- }
- return true;
- }
- private static bool IsBranchInst(Instruction inst)
- {
- switch (inst)
- {
- case Instruction.Branch:
- case Instruction.BranchIfFalse:
- case Instruction.BranchIfTrue:
- return true;
- }
- return false;
- }
- private static bool IsBitwiseInst(Instruction inst)
- {
- switch (inst)
- {
- case Instruction.BitwiseAnd:
- case Instruction.BitwiseExclusiveOr:
- case Instruction.BitwiseNot:
- case Instruction.BitwiseOr:
- return true;
- }
- return false;
- }
- private static Instruction GetLogicalFromBitwiseInst(Instruction inst)
- {
- switch (inst)
- {
- case Instruction.BitwiseAnd: return Instruction.LogicalAnd;
- case Instruction.BitwiseExclusiveOr: return Instruction.LogicalExclusiveOr;
- case Instruction.BitwiseNot: return Instruction.LogicalNot;
- case Instruction.BitwiseOr: return Instruction.LogicalOr;
- }
- throw new ArgumentException($"Unexpected instruction \"{inst}\".");
- }
- }
- }
|