private static int BinarySearch(List <InstOp> opCodes, ulong address) { int left = 0; int middle = 0; int right = opCodes.Count - 1; while (left <= right) { int size = right - left; middle = left + (size >> 1); InstOp opCode = opCodes[middle]; if (address == opCode.Address) { break; } if (address < opCode.Address) { right = middle - 1; } else { left = middle + 1; } } return(middle); }
private static bool FindBrxTargets(ShaderConfig config, IEnumerable <Block> blocks, Func <ulong, Block> getBlock) { bool hasNewTarget = false; foreach (Block block in blocks) { InstOp lastOp = block.GetLastOp(); bool hasNext = block.HasNext(); if (lastOp.Name == InstName.Brx && block.Successors.Count == (hasNext ? 1 : 0)) { InstBrx opBrx = new InstBrx(lastOp.RawOpCode); ulong baseOffset = lastOp.GetAbsoluteAddress(); // An indirect branch could go anywhere, // try to get the possible target offsets from the constant buffer. (int cbBaseOffset, int cbOffsetsCount) = FindBrxTargetRange(block, opBrx.SrcA); if (cbOffsetsCount != 0) { hasNewTarget = true; } for (int i = 0; i < cbOffsetsCount; i++) { uint targetOffset = config.GpuAccessor.ConstantBuffer1Read(cbBaseOffset + i * 4); Block target = getBlock(baseOffset + targetOffset); target.Predecessors.Add(block); block.Successors.Add(target); } } } return(hasNewTarget); }
private static void FillBlock(ShaderConfig config, Block block, ulong limitAddress, ulong startAddress) { IGpuAccessor gpuAccessor = config.GpuAccessor; ulong address = block.Address; int bufferOffset = 0; ReadOnlySpan <ulong> buffer = ReadOnlySpan <ulong> .Empty; InstOp op = default; do { if (address + 7 >= limitAddress) { break; } // Ignore scheduling instructions, which are written every 32 bytes. if ((address & 0x1f) == 0) { address += 8; bufferOffset++; continue; } if (bufferOffset >= buffer.Length) { buffer = gpuAccessor.GetCode(startAddress + address, 8); bufferOffset = 0; } ulong opCode = buffer[bufferOffset++]; op = InstTable.GetOp(address, opCode); if (op.Props.HasFlag(InstProps.TexB)) { config.SetUsedFeature(FeatureFlags.Bindless); } if (op.Name == InstName.Ald || op.Name == InstName.Ast || op.Name == InstName.Ipa) { SetUserAttributeUses(config, op.Name, opCode); } else if (op.Name == InstName.Ssy || op.Name == InstName.Pbk) { block.AddPushOp(op); } block.OpCodes.Add(op); address += 8; }while (!op.Props.HasFlag(InstProps.Bra)); block.EndAddress = address; }
private static bool IsUnconditional(ref InstOp op) { InstConditional condOp = new InstConditional(op.RawOpCode); if (op.Name == InstName.Exit && condOp.Ccc != Ccc.T) { return(false); } return(condOp.Pred == RegisterConsts.PredicateTrueIndex && !condOp.PredInv); }
private static bool WritesToRegister(InstOp op, int regIndex) { // Predicate instruction only ever writes to predicate, so we shouldn't check those. if ((op.Props & (InstProps.Rd | InstProps.Rd2)) == 0) { return(false); } if (op.Props.HasFlag(InstProps.Rd2) && (byte)(op.RawOpCode >> 28) == regIndex) { return(true); } return((byte)op.RawOpCode == regIndex); }
public bool IsImmInst(InstName name) { InstOp op = Block.OpCodes[Index]; return(op.Name == name && op.Props.HasFlag(InstProps.Ib)); }
public static bool IsUnconditionalBranch(ref InstOp op) { return(IsUnconditional(ref op) && op.Props.HasFlag(InstProps.Bra)); }
public static DecodedProgram Decode(ShaderConfig config, ulong startAddress) { Queue <DecodedFunction> functionsQueue = new Queue <DecodedFunction>(); Dictionary <ulong, DecodedFunction> functionsVisited = new Dictionary <ulong, DecodedFunction>(); DecodedFunction EnqueueFunction(ulong address) { if (!functionsVisited.TryGetValue(address, out DecodedFunction function)) { functionsVisited.Add(address, function = new DecodedFunction(address)); functionsQueue.Enqueue(function); } return(function); } DecodedFunction mainFunction = EnqueueFunction(0); while (functionsQueue.TryDequeue(out DecodedFunction currentFunction)) { List <Block> blocks = new List <Block>(); Queue <Block> workQueue = new Queue <Block>(); Dictionary <ulong, Block> visited = new Dictionary <ulong, Block>(); Block GetBlock(ulong blkAddress) { if (!visited.TryGetValue(blkAddress, out Block block)) { block = new Block(blkAddress); workQueue.Enqueue(block); visited.Add(blkAddress, block); } return(block); } GetBlock(currentFunction.Address); bool hasNewTarget; do { while (workQueue.TryDequeue(out Block currBlock)) { // Check if the current block is inside another block. if (BinarySearch(blocks, currBlock.Address, out int nBlkIndex)) { Block nBlock = blocks[nBlkIndex]; if (nBlock.Address == currBlock.Address) { throw new InvalidOperationException("Found duplicate block address on the list."); } nBlock.Split(currBlock); blocks.Insert(nBlkIndex + 1, currBlock); continue; } // If we have a block after the current one, set the limit address. ulong limitAddress = ulong.MaxValue; if (nBlkIndex != blocks.Count) { Block nBlock = blocks[nBlkIndex]; int nextIndex = nBlkIndex + 1; if (nBlock.Address < currBlock.Address && nextIndex < blocks.Count) { limitAddress = blocks[nextIndex].Address; } else if (nBlock.Address > currBlock.Address) { limitAddress = blocks[nBlkIndex].Address; } } FillBlock(config, currBlock, limitAddress, startAddress); if (currBlock.OpCodes.Count != 0) { // We should have blocks for all possible branch targets, // including those from SSY/PBK instructions. foreach (PushOpInfo pushOp in currBlock.PushOpCodes) { GetBlock(pushOp.Op.GetAbsoluteAddress()); } // Set child blocks. "Branch" is the block the branch instruction // points to (when taken), "Next" is the block at the next address, // executed when the branch is not taken. For Unconditional Branches // or end of program, Next is null. InstOp lastOp = currBlock.GetLastOp(); if (lastOp.Name == InstName.Cal) { EnqueueFunction(lastOp.GetAbsoluteAddress()).AddCaller(currentFunction); } else if (lastOp.Name == InstName.Bra) { Block succBlock = GetBlock(lastOp.GetAbsoluteAddress()); currBlock.Successors.Add(succBlock); succBlock.Predecessors.Add(currBlock); } if (!IsUnconditionalBranch(ref lastOp)) { Block succBlock = GetBlock(currBlock.EndAddress); currBlock.Successors.Insert(0, succBlock); succBlock.Predecessors.Add(currBlock); } } // Insert the new block on the list (sorted by address). if (blocks.Count != 0) { Block nBlock = blocks[nBlkIndex]; blocks.Insert(nBlkIndex + (nBlock.Address < currBlock.Address ? 1 : 0), currBlock); } else { blocks.Add(currBlock); } } // Propagate SSY/PBK addresses into their uses (SYNC/BRK). foreach (Block block in blocks.Where(x => x.PushOpCodes.Count != 0)) { for (int pushOpIndex = 0; pushOpIndex < block.PushOpCodes.Count; pushOpIndex++) { PropagatePushOp(visited, block, pushOpIndex); } } // Try to find targets for BRX (indirect branch) instructions. hasNewTarget = FindBrxTargets(config, blocks, GetBlock); // If we discovered new branch targets from the BRX instruction, // we need another round of decoding to decode the new blocks. // Additionally, we may have more SSY/PBK targets to propagate, // and new BRX instructions. }while (hasNewTarget); currentFunction.SetBlocks(blocks.ToArray()); } return(new DecodedProgram(mainFunction, functionsVisited)); }
public PushOpInfo(InstOp op) { Op = op; Consumers = new Dictionary <Block, Operand>(); }