fpPS4/spirv/srOpUtils.pas
2023-10-27 23:03:22 +03:00

537 lines
12 KiB
ObjectPascal

unit srOpUtils;
{$mode objfpc}{$H+}
interface
uses
spirv,
srNode,
srOp,
srReg,
srLayout,
srVariable,
srCFGLabel;
const
OpIAddExt=DWORD(-1);
OpISubExt=DWORD(-2);
OpAbsDiff=DWORD(-3);
OpWQM32 =DWORD(-4);
OpBFE_32 =DWORD(-5);
OpBFIB32 =DWORD(-6);
OpPackAnc=DWORD(-7);
OpPackOfs=DWORD(-8);
OpMakeExp=DWORD(-9);
OpMakeVec=DWORD(-10);
OpMakeCub=DWORD(-11);
OpCUBEID =DWORD(-12);
OpCUBESC =DWORD(-13);
OpCUBETC =DWORD(-14);
OpCUBEMA =DWORD(-15);
function InsSpirvOp(pLine,pNew:PspirvOp):PspirvOp;
Function get_inverse_left_cmp_op(OpId:DWORD):DWORD;
Function get_inverse_not_cmp_op(OpId:DWORD):DWORD;
Function is_term_op(OpId:DWORD):Boolean;
Function is_merge_op(OpId:DWORD):Boolean;
Function is_term_op(pLine:PspirvOp):Boolean;
procedure up_merge_line(var pLine:PspirvOp);
function FindUpSameOp(pLine,node:PspirvOp):PspirvOp;
function IsDominUp(pNodeUp,pLine:PspirvOp):Boolean;
function GetGlobalIndex(pLine:PspirvOp):DWORD;
function GetGlobalIndexA(pLine:PspirvOp):DWORD;
function MaxLine(p1,p2:PspirvOp):PspirvOp;
function MinLine(p1,p2:PspirvOp):PspirvOp;
Function IsParentOf(pLine,pCurr:PsrOpBlock):Boolean;
Function IsParentOfLine(pLine,pCurr:PSpirvOp):Boolean;
function GetMaxPlace(pLine:PSpirvOp;count:Byte;src:PPsrRegNode):PSpirvOp;
function GetChainRegNode(node:PsrRegNode):PsrChain;
function GetSourceRegNode(node:PsrRegNode):PsrNode;
function flow_down_next_up(pLine:PspirvOp):PspirvOp;
function flow_down_prev_up(pLine:PspirvOp):PspirvOp;
function flow_prev_up(pLine:PspirvOp):PspirvOp;
implementation
//--
function flow_down_next_up(pLine:PspirvOp):PspirvOp;
begin
Result:=pLine^.First; //down
if (Result=nil) then
begin
repeat //up
Result:=pLine^.Next;
pLine:=pLine^.Parent;
until (pLine=nil) or (Result<>nil);
end;
end;
function flow_down_prev_up(pLine:PspirvOp):PspirvOp;
begin
Result:=pLine^.Last; //down
if (Result=nil) then
begin
repeat //up
Result:=pLine^.Prev;
pLine:=pLine^.Parent;
until (pLine=nil) or (Result<>nil);
end;
end;
function flow_prev_up(pLine:PspirvOp):PspirvOp;
function _last(p:PspirvOp):PspirvOp;
begin
Result:=nil;
if (p<>nil) then
if p^.IsType(ntOpBlock) then
if not (PsrOpBlock(p)^.Block.bType in [btCond,btLoop]) then
begin
Result:=p^.Last;
end;
end;
begin
Result:=_last(pLine); //down
if (Result=nil) then
begin
repeat //up
Result:=pLine^.Prev;
pLine:=pLine^.Parent;
until (pLine=nil) or (Result<>nil);
end;
end;
function InsSpirvOp(pLine,pNew:PspirvOp):PspirvOp;
var
tmp:PspirvOp;
begin
Result:=nil;
Assert(pLine<>nil);
if (pLine=nil) or (pNew=nil) then Exit;
if (pLine^.Next<>nil) then
if pNew^.IsType(ntOp) then
begin
tmp:=pLine;
while (not tmp^.IsType(ntOp)) do
begin
tmp:=flow_down_prev_up(tmp);
Assert(tmp<>nil);
end;
pNew^.Adr:=tmp^.Adr;
end;
pLine^.InsertAfter(pNew);
Result:=pNew;
end;
function GetCurrentIndex(pLine:PspirvOp):DWORD;
var
pParent:PsrOpBlock;
node:PspirvOp;
begin
Result:=0;
if (pLine=nil) then Exit;
pParent:=pLine^.Parent;
if (pParent=nil) then Exit;
node:=pParent^.First;
While (node<>nil) and (node<>pLine) do
begin
Result:=Result+node^.GetIndexCount;
node:=node^.Next;
end;
end;
function GetGlobalIndex(pLine:PspirvOp):DWORD;
var
pParent:PsrOpBlock;
node:PspirvOp;
begin
Result:=0;
if (pLine=nil) then Exit;
node:=pLine;
pParent:=node^.Parent;
While (pParent<>nil) do
begin
Result:=Result+GetCurrentIndex(node);
node:=PspirvOp(pParent);
pParent:=node^.Parent;
end;
end;
function GetGlobalIndexA(pLine:PspirvOp):DWORD;
begin
Result:=pLine^.GetIndexCount+GetGlobalIndex(pLine);
end;
function isGTLine(p1,p2:PspirvOp):Boolean; //(p1>p2)
begin
Result:=False;
p2:=p2^.Next;
While (p2<>nil) do
begin
if (p1=p2) then Exit(True);
p2:=p2^.Next;
end;
end;
Function IsGTLevel(p1,p2:PspirvOp):Boolean; //(p1>p2)
var
pParent1:PsrOpBlock;
pParent2:PsrOpBlock;
begin
Result:=False;
pParent1:=p1^.Parent;
pParent2:=p2^.Parent;
Result:=(pParent1^.Level>pParent2^.Level);
end;
function MaxLine(p1,p2:PspirvOp):PspirvOp;
var
pParent:PsrOpBlock;
node:PspirvOp;
i,w:DWORD;
begin
Result:=nil;
if (p1=nil) then Exit(p2);
if (p2=nil) or (p1=p2) then Exit(p1);
if IsGTLevel(p2,p1) then //(p2>p1)
begin
//swap
node:=p1;
p1:=p2;
p2:=node;
end;
i:=0;
node:=p1;
pParent:=node^.Parent;
While (pParent<>nil) do
begin
if (pParent=p2^.Parent) then
begin
if isGTLine(node,p2) then //(node>p2)
begin
//Assert(not isGTLine(p2,node));
Exit(p1);
end else
begin
//Assert(isGTLine(p2,node));
Exit(p2);
end;
end;
i:=i+GetCurrentIndex(node);
node:=PspirvOp(pParent);
pParent:=node^.Parent;
end;
w:=GetGlobalIndex(p2);
if (i>w) then
begin
Result:=p1;
end else
begin
Result:=p2;
end;
end;
function MinLine(p1,p2:PspirvOp):PspirvOp;
begin
if (MaxLine(p1,p2)=p1) then
begin
Result:=p2;
end else
begin
Result:=p1;
end;
end;
Function IsParentOf(pLine,pCurr:PsrOpBlock):Boolean;
begin
Result:=False;
if not pLine^.IsType(ntOpBlock) then Exit;
if not pCurr^.IsType(ntOpBlock) then Exit;
While (pLine<>nil) do
begin
if (pLine=pCurr) then Exit(True);
pLine:=pLine^.Parent;
end;
end;
Function IsParentOfLine(pLine,pCurr:PSpirvOp):Boolean;
begin
Result:=IsParentOf(pLine^.Parent,pCurr^.Parent);
end;
function GetMaxPlace(pLine:PSpirvOp;count:Byte;src:PPsrRegNode):PSpirvOp;
var
m:PSpirvOp;
i:Byte;
begin
Result:=pLine;
if (count=0) or (src=nil) then Exit;
m:=nil;
For i:=0 to count-1 do
begin
if (not src[i]^.is_const) then
if IsParentOfLine(pLine,src[i]^.pLine) then
begin
m:=MaxLine(m,src[i]^.pLine);
end;
end;
if (m<>nil) then
begin
Result:=m;
end;
end;
Function get_inverse_left_cmp_op(OpId:DWORD):DWORD;
begin
Result:=0;
Case OpId of
Op.OpFOrdLessThan :Result:=Op.OpFOrdGreaterThan ;
Op.OpFOrdEqual :Result:=Op.OpFOrdEqual ;
Op.OpFOrdLessThanEqual :Result:=Op.OpFOrdGreaterThanEqual ;
Op.OpFOrdGreaterThan :Result:=Op.OpFOrdLessThan ;
Op.OpFOrdNotEqual :Result:=Op.OpFOrdNotEqual ;
Op.OpFOrdGreaterThanEqual :Result:=Op.OpFOrdLessThanEqual ;
Op.OpOrdered :Result:=Op.OpOrdered ;
Op.OpUnordered :Result:=Op.OpUnordered ;
Op.OpFUnordLessThan :Result:=Op.OpFUnordGreaterThan ;
Op.OpFUnordEqual :Result:=Op.OpFUnordEqual ;
Op.OpFUnordLessThanEqual :Result:=Op.OpFUnordGreaterThanEqual;
Op.OpFUnordGreaterThan :Result:=Op.OpFUnordLessThan ;
Op.OpFUnordNotEqual :Result:=Op.OpFUnordNotEqual ;
Op.OpFUnordGreaterThanEqual:Result:=Op.OpFUnordLessThanEqual ;
Op.OpIEqual :Result:=Op.OpIEqual ;
Op.OpINotEqual :Result:=Op.OpINotEqual ;
Op.OpSLessThan :Result:=Op.OpSGreaterThan ;
Op.OpSLessThanEqual :Result:=Op.OpSGreaterThanEqual ;
Op.OpSGreaterThan :Result:=Op.OpSLessThan ;
Op.OpSGreaterThanEqual :Result:=Op.OpSLessThanEqual ;
Op.OpULessThan :Result:=Op.OpUGreaterThan ;
Op.OpULessThanEqual :Result:=Op.OpUGreaterThanEqual ;
Op.OpUGreaterThan :Result:=Op.OpULessThan ;
Op.OpUGreaterThanEqual :Result:=Op.OpULessThanEqual ;
else;
end;
end;
Function get_inverse_not_cmp_op(OpId:DWORD):DWORD;
begin
Result:=0;
Case OpId of
Op.OpFOrdLessThan :Result:=Op.OpFUnordGreaterThanEqual;
Op.OpFOrdEqual :Result:=Op.OpFUnordNotEqual;
Op.OpFOrdLessThanEqual :Result:=Op.OpFUnordGreaterThan;
Op.OpFOrdGreaterThan :Result:=Op.OpFUnordLessThanEqual;
Op.OpFOrdNotEqual :Result:=Op.OpFUnordEqual;
Op.OpFOrdGreaterThanEqual :Result:=Op.OpFUnordLessThan;
Op.OpOrdered :Result:=Op.OpUnordered;
Op.OpUnordered :Result:=Op.OpOrdered;
Op.OpFUnordLessThan :Result:=Op.OpFOrdGreaterThanEqual;
Op.OpFUnordEqual :Result:=Op.OpFOrdNotEqual;
Op.OpFUnordLessThanEqual :Result:=Op.OpFOrdGreaterThan;
Op.OpFUnordGreaterThan :Result:=Op.OpFOrdLessThanEqual;
Op.OpFUnordNotEqual :Result:=Op.OpFOrdEqual;
Op.OpFUnordGreaterThanEqual:Result:=Op.OpFOrdLessThan;
Op.OpIEqual :Result:=Op.OpINotEqual;
Op.OpINotEqual :Result:=Op.OpIEqual;
Op.OpSLessThan :Result:=Op.OpSGreaterThanEqual;
Op.OpSLessThanEqual :Result:=Op.OpSGreaterThan;
Op.OpSGreaterThan :Result:=Op.OpSLessThanEqual;
Op.OpSGreaterThanEqual :Result:=Op.OpSLessThan;
Op.OpULessThan :Result:=Op.OpUGreaterThanEqual;
Op.OpULessThanEqual :Result:=Op.OpUGreaterThan;
Op.OpUGreaterThan :Result:=Op.OpULessThanEqual;
Op.OpUGreaterThanEqual :Result:=Op.OpULessThan;
else;
end;
end;
Function is_term_op(OpId:DWORD):Boolean;
begin
Case OpId of
Op.OpBranch,
Op.OpBranchConditional,
Op.OpSwitch,
Op.OpReturn,
Op.OpReturnValue,
Op.OpKill,
Op.OpTerminateInvocation,
Op.OpDemoteToHelperInvocation,
Op.OpUnreachable:Result:=True;
else
Result:=False;
end;
end;
Function is_merge_op(OpId:DWORD):Boolean;
begin
Case OpId of
Op.OpSelectionMerge,
Op.OpLoopMerge:Result:=True;
else
Result:=False;
end;
end;
Function is_term_op(pLine:PspirvOp):Boolean;
begin
Result:=False;
if (pLine=nil) then Exit;
repeat //up
if pLine^.IsType(ntOpBlock) then
begin
//
end else
if pLine^.IsType(ntOp) then
begin
if not pLine^.is_cleared then
begin
Case pLine^.OpId of
Op.OpNop:; //
else
Break;
end;
end;
end else
begin
Exit;
end;
pLine:=flow_down_prev_up(pLine);
until false;
Result:=is_term_op(pLine^.OpId);
end;
procedure up_merge_line(var pLine:PspirvOp);
var
node:PspirvOp;
begin
repeat
if pLine^.IsType(ntOp) then
begin
if pLine^.is_cleared or is_merge_op(pLine^.OpId) then
begin
node:=pLine^.Prev;
if (node<>nil) then
begin
pLine:=node;
Continue;
end;
end;
end;
Break;
until false;
end;
function CompareParam(p1,p2:POpParamNode):Boolean;
begin
Result:=(p1^.Value=p2^.Value);
end;
function CompareOp(p1,p2:PspirvOp):Boolean;
var
n1,n2:POpParamNode;
begin
Result:=False;
if not p1^.IsType(ntOp) then Exit;
if not p2^.IsType(ntOp) then Exit;
if (p1^.OpId<>p2^.OpId) then Exit;
if (p1^.pDst<>p2^.pDst) then Exit;
n1:=p1^.ParamNode(0);
n2:=p2^.ParamNode(0);
While (n1<>nil) do
begin
if (n2=nil) then Exit;
if not CompareParam(n1,n2) then Exit;
n1:=n1^.Next;
n2:=n2^.Next;
end;
Result:=(n2=nil);
end;
function FindUpSameOp(pLine,node:PspirvOp):PspirvOp;
begin
Result:=nil;
if (pLine=nil) or (node=nil) then Exit;
While (pLine<>nil) do
begin
if not pLine^.is_cleared then
begin
if CompareOp(pLine,node) then Exit(pLine);
end;
pLine:=flow_prev_up(pLine);
end;
end;
function IsDominUp(pNodeUp,pLine:PspirvOp):Boolean;
begin
Result:=False;
if (pNodeUp=nil) or (pLine=nil) then Exit;
While (pLine<>nil) do
begin
if (pNodeUp=pLine) then Exit(True);
pLine:=flow_prev_up(pLine);
end;
end;
function GetChainRegNode(node:PsrRegNode):PsrChain;
var
pOp:PSpirvOp;
begin
Result:=nil;
node:=RegDown(node);
pOp:=node^.pWriter^.AsType(ntOp);
if (pOp=nil) then Exit;
if (pOp^.OpId<>Op.OpLoad) then Exit;
Result:=pOp^.ParamNode(0)^.Value^.AsType(ntChain);
end;
function GetSourceRegNode(node:PsrRegNode):PsrNode;
var
pOp:PSpirvOp;
pVar:PsrVariable;
begin
Result:=nil;
node:=RegDown(node);
pOp:=node^.pWriter^.AsType(ntOp);
if (pOp=nil) then Exit;
if (pOp^.OpId<>Op.OpLoad) then Exit;
pVar:=pOp^.ParamNode(0)^.Value^.AsType(ntVariable);
if (pVar=nil) then Exit;
Result:=pVar^.pSource;
end;
end.