Partial support for branch with CC, and fix a edge case of branch out of loop on shaders

This commit is contained in:
gdk 2019-11-27 00:38:56 -03:00 committed by Thog
parent 99f236fcf0
commit 442485adb3
5 changed files with 60 additions and 14 deletions

View file

@ -4,12 +4,16 @@ namespace Ryujinx.Graphics.Shader.Decoders
{ {
class OpCodeBranch : OpCode class OpCodeBranch : OpCode
{ {
public Condition Condition { get; }
public int Offset { get; } public int Offset { get; }
public bool PushTarget { get; protected set; } public bool PushTarget { get; protected set; }
public OpCodeBranch(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) public OpCodeBranch(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
{ {
Condition = (Condition)(opCode & 0x1f);
Offset = ((int)(opCode >> 20) << 8) >> 8; Offset = ((int)(opCode >> 20) << 8) >> 8;
PushTarget = false; PushTarget = false;

View file

@ -288,7 +288,9 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.Copy(dest, res); context.Copy(dest, res);
} }
// TODO: CC, X SetZnFlags(context, res, op.SetCondCode, op.Extended);
// TODO: X
} }
public static void Isetp(EmitterContext context) public static void Isetp(EmitterContext context)

View file

@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader.Translation;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
namespace Ryujinx.Graphics.Shader.Instructions namespace Ryujinx.Graphics.Shader.Instructions
@ -129,22 +130,29 @@ namespace Ryujinx.Graphics.Shader.Instructions
private static void EmitBranch(EmitterContext context, ulong address) private static void EmitBranch(EmitterContext context, ulong address)
{ {
OpCode op = context.CurrOp;
// If we're branching to the next instruction, then the branch // If we're branching to the next instruction, then the branch
// is useless and we can ignore it. // is useless and we can ignore it.
if (address == context.CurrOp.Address + 8) if (address == op.Address + 8)
{ {
return; return;
} }
Operand label = context.GetLabel(address); Operand label = context.GetLabel(address);
Operand pred = Register(context.CurrOp.Predicate); Operand pred = Register(op.Predicate);
if (context.CurrOp.Predicate.IsPT) if (op is OpCodeBranch opBranch && opBranch.Condition != Condition.Always)
{
pred = context.BitwiseAnd(pred, GetCondition(context, opBranch.Condition));
}
if (op.Predicate.IsPT)
{ {
context.Branch(label); context.Branch(label);
} }
else if (context.CurrOp.InvertPredicate) else if (op.InvertPredicate)
{ {
context.BranchIfFalse(label, pred); context.BranchIfFalse(label, pred);
} }
@ -153,5 +161,21 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.BranchIfTrue(label, pred); context.BranchIfTrue(label, pred);
} }
} }
private static Operand GetCondition(EmitterContext context, Condition cond)
{
// TODO: More condition codes, figure out how they work.
switch (cond)
{
case Condition.Equal:
case Condition.EqualUnordered:
return GetZF(context);
case Condition.NotEqual:
case Condition.NotEqualUnordered:
return context.BitwiseNot(GetZF(context));
}
return Const(IrConsts.True);
}
} }
} }

View file

@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{ {
private HashSet<BasicBlock> _loopTails; private HashSet<BasicBlock> _loopTails;
private Stack<(AstBlock Block, int EndIndex)> _blockStack; private Stack<(AstBlock Block, int CurrEndIndex, int LoopEndIndex)> _blockStack;
private Dictionary<Operand, AstOperand> _localsMap; private Dictionary<Operand, AstOperand> _localsMap;
@ -22,6 +22,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
private AstBlock _currBlock; private AstBlock _currBlock;
private int _currEndIndex; private int _currEndIndex;
private int _loopEndIndex;
public StructuredProgramInfo Info { get; } public StructuredProgramInfo Info { get; }
@ -31,7 +32,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{ {
_loopTails = new HashSet<BasicBlock>(); _loopTails = new HashSet<BasicBlock>();
_blockStack = new Stack<(AstBlock, int)>(); _blockStack = new Stack<(AstBlock, int, int)>();
_localsMap = new Dictionary<Operand, AstOperand>(); _localsMap = new Dictionary<Operand, AstOperand>();
@ -42,6 +43,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
_currBlock = new AstBlock(AstBlockType.Main); _currBlock = new AstBlock(AstBlockType.Main);
_currEndIndex = blocksCount; _currEndIndex = blocksCount;
_loopEndIndex = blocksCount;
Info = new StructuredProgramInfo(_currBlock); Info = new StructuredProgramInfo(_currBlock);
@ -52,7 +54,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{ {
while (_currEndIndex == block.Index) while (_currEndIndex == block.Index)
{ {
(_currBlock, _currEndIndex) = _blockStack.Pop(); (_currBlock, _currEndIndex, _loopEndIndex) = _blockStack.Pop();
} }
if (_gotoTempAsgs.TryGetValue(block.Index, out AstAssignment gotoTempAsg)) if (_gotoTempAsgs.TryGetValue(block.Index, out AstAssignment gotoTempAsg))
@ -107,9 +109,19 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
return; return;
} }
// We can only enclose the "if" when the branch lands before
// the end of the current block. If the current enclosing block
// is not a loop, then we can also do so if the branch lands
// right at the end of the current block. When it is a loop,
// this is not valid as the loop condition would be evaluated,
// and it could erroneously jump back to the start of the loop.
bool inRange =
block.Branch.Index < _currEndIndex ||
(block.Branch.Index == _currEndIndex && block.Branch.Index < _loopEndIndex);
bool isLoop = block.Branch.Index <= block.Index; bool isLoop = block.Branch.Index <= block.Index;
if (block.Branch.Index <= _currEndIndex && !isLoop) if (inRange && !isLoop)
{ {
NewBlock(AstBlockType.If, branchOp, block.Branch.Index); NewBlock(AstBlockType.If, branchOp, block.Branch.Index);
} }
@ -171,10 +183,15 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
AddNode(childBlock); AddNode(childBlock);
_blockStack.Push((_currBlock, _currEndIndex)); _blockStack.Push((_currBlock, _currEndIndex, _loopEndIndex));
_currBlock = childBlock; _currBlock = childBlock;
_currEndIndex = endIndex; _currEndIndex = endIndex;
if (type == AstBlockType.DoWhile)
{
_loopEndIndex = endIndex;
}
} }
private IAstNode GetBranchCond(AstBlockType type, Operation branchOp) private IAstNode GetBranchCond(AstBlockType type, Operation branchOp)

View file

@ -1,6 +1,5 @@
using Ryujinx.Graphics.Shader.CodeGen.Glsl; using Ryujinx.Graphics.Shader.CodeGen.Glsl;
using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.Decoders;
using Ryujinx.Graphics.Shader.Instructions;
using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation.Optimizations; using Ryujinx.Graphics.Shader.Translation.Optimizations;
@ -219,15 +218,15 @@ namespace Ryujinx.Graphics.Shader.Translation
Operand predSkipLbl = null; Operand predSkipLbl = null;
bool skipPredicateCheck = op.Emitter == InstEmit.Bra; bool skipPredicateCheck = op is OpCodeBranch opBranch && !opBranch.PushTarget;
if (op is OpCodeBranchPop opBranchPop) if (op is OpCodeBranchPop opBranchPop)
{ {
// If the instruction is a SYNC instruction with only one // If the instruction is a SYNC or BRK instruction with only one
// possible target address, then the instruction is basically // possible target address, then the instruction is basically
// just a simple branch, we can generate code similar to branch // just a simple branch, we can generate code similar to branch
// instructions, with the condition check on the branch itself. // instructions, with the condition check on the branch itself.
skipPredicateCheck |= opBranchPop.Targets.Count < 2; skipPredicateCheck = opBranchPop.Targets.Count < 2;
} }
if (!(op.Predicate.IsPT || skipPredicateCheck)) if (!(op.Predicate.IsPT || skipPredicateCheck))