Partial support for branch with CC, and fix a edge case of branch out of loop on shaders
This commit is contained in:
parent
99f236fcf0
commit
442485adb3
5 changed files with 60 additions and 14 deletions
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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)
|
||||||
|
|
|
@ -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))
|
||||||
|
|
Loading…
Reference in a new issue