FPPS4/spirv/srCFGParser.pas

2874 lines
52 KiB
Plaintext

unit srCFGParser;
{$mode ObjFPC}{$H+}
interface
uses
sysutils,
ps4_pssl,
ginodes,
srNode,
srConfig;
type
TsrCodeRegion=class;
PSrcAdr=^TSrcAdr;
TSrcAdr=object
pCode :TsrCodeRegion;
Offset:PtrUInt;
function get_code_ptr:PDWORD;
function get_dmem_ptr:PDWORD;
end;
TsrSourceNode=class;
TsrSourceNodeType=class of TsrSourceNode;
TsrSourceBlock=class;
TsrSourceNode=class
pParent:TsrSourceBlock;
pPrev,pNext:TsrSourceNode;
//
order:PtrUint;
//
function ntype:TsrSourceNodeType; inline;
function First:TsrSourceNode; virtual;
function Last :TsrSourceNode; virtual;
function get_level:DWORD;
end;
TsrSourceNodeList=specialize TNodeListClass<TsrSourceNode>;
//
TsrSourceAdr=class(TsrSourceNode)
key :TSrcAdr; //b_adr
e_adr:TSrcAdr;
property b_adr:TSrcAdr read key write key;
class function c(n1,n2:PSrcAdr):Integer; static;
end;
TsrSourceLabel=class(TsrSourceAdr)
pLeft,pRight:TsrSourceLabel;
links_count:PtrUint;
end;
TsrSourceLabelTree=specialize TNodeTreeClass<TsrSourceLabel>;
TsrSourceInstruction=class(TsrSourceAdr)
pLeft,pRight:TsrSourceInstruction;
//
FSPI:TSPI;
//
used:Boolean;
end;
TsrSourceInstructionTree=specialize TNodeTreeClass<TsrSourceInstruction>;
//
TsrCondition=(
cNone,
cFalse,
cTrue,
cScc0,
cScc1,
cVccz,
cVccnz,
cExecz,
cExecnz,
cTidz,
cTidnz
);
TsrStatementType=(
sCond,
sCopy,
sVar,
sStore,
sBreak,
sNot,
sOr,
sAnd
);
//sCond TsrCondition
//sCopy [self] [false]:sCond/sCopy/sNot/sOr/sAnd
//sVar id
//sStore [var]:sVar [false]:sCond/sCopy/sNot/sOr/sAnd
//sBreak
//sNot [cond]:sCond/sCopy/sNot/sOr
//sOr [cond]:sCond/sCopy/sNot/sOr/sAnd [cond]:sCond/sLoad/sNot/sOr/sAnd
//sAnd [cond]:sCond/sCopy/sNot/sOr/sAnd [cond]:sCond/sLoad/sNot/sOr/sAnd
TsrStatement=class(TsrSourceNode)
sType:TsrStatementType;
pSrc :TsrStatement;
pDst :TsrStatement;
//
pCache:TObject;
u:record
Case Byte of
0:(id :PtrUint);
1:(cond:TsrCondition);
end;
end;
TsrSourceGoto=class(TsrSourceNode)
FGoto:record
pParent:Pointer;
pPrev,pNext:TsrSourceGoto;
end;
//
pCond :TsrStatement;
pLabel:TsrSourceLabel;
end;
TsrGotoList=object
pHead,pTail:TsrSourceGoto;
//pMiddle:TsrSourceGoto;
procedure Push_head(Node:TsrSourceGoto);
procedure Push_tail(Node:TsrSourceGoto);
procedure InsertAfter (node,new:TsrSourceGoto);
procedure InsertBefore(node,new:TsrSourceGoto);
function Pop_tail:TsrSourceGoto;
procedure Remove(node:TsrSourceGoto);
end;
//
TsrBlockType=(btMain,btSetpc,btCond,btElse,btLoop,btMerg,btInline,btOther);
TsrSourceBlock=class(TsrSourceNode)
FList:TsrSourceNodeList;
FInit:TsrSourceNodeList;
pCond:TsrStatement;
bType:TsrBlockType;
//
pIf :TsrSourceBlock;
pElse:TsrSourceBlock;
//
function pReal:TsrSourceBlock;
function First:TsrSourceNode; override;
function Last :TsrSourceNode; override;
procedure Push_head(Node:TsrSourceNode);
procedure Push_tail(Node:TsrSourceNode);
function Pop_head:TsrSourceNode;
function Pop_tail:TsrSourceNode;
procedure InsertAfter(node,new:TsrSourceNode);
procedure InsertBefore(node,new:TsrSourceNode);
procedure Remove(node:TsrSourceNode);
Procedure splice(node_first,node_last:TsrSourceNode);
end;
//
TsrCodeRegion=class
pNext :TsrCodeRegion;
//
FEmit :TCustomEmit;
Body :Pointer;
DMem :Pointer;
Size :PtrUint;
//
FTop :TsrSourceBlock;
//
function FindByPtr(base:Pointer):TsrSourceNode;
end;
function get_branch_offset(var FSPI:TSPI):ptrint;
function IsReal(b:TsrBlockType):Boolean;
function parse_code_cfg2(var pCode:TsrCodeRegion;bType:TsrBlockType;Body,Dmem:Pointer;FEmit:TCustomEmit):Integer;
implementation
type
TsrCFGParser2=object
FEmit:TCustomEmit;
pCode:TsrCodeRegion;
pCurr:TsrSourceNode;
order:PtrUint;
VarId:Ptruint;
mode :Integer;
hits :Integer;
//
FLabelTree:TsrSourceLabelTree;
FInstructionTree:TsrSourceInstructionTree;
FGotoList:TsrGotoList;
FGotoFree:TsrGotoList;
//
Function NewInstruction(b_adr,e_adr:TSrcAdr;var FSPI:TSPI):TsrSourceInstruction;
Function NewLabel (Adr:TSrcAdr):TsrSourceLabel;
Function NewGoto (Cond:TsrCondition;Adr:TSrcAdr):TsrSourceGoto;
Function NewGoto (pCond:TsrStatement;pLabel:TsrSourceLabel):TsrSourceGoto;
procedure FreeGoto (node:TsrSourceGoto);
procedure RestoreGoto (node:TsrSourceGoto);
Function NewBlock (bType:TsrBlockType):TsrSourceBlock;
//
Function NewCond (cond:TsrCondition):TsrStatement;
Function NewCopy (pCond:TsrStatement):TsrStatement;
Function NewVar :TsrStatement;
Function NewStore(pVar,pCond:TsrStatement):TsrStatement;
Function NewBreak:TsrStatement;
Function NewNot (pCond:TsrStatement):TsrStatement;
Function NewOr (pCond1,pCond2:TsrStatement):TsrStatement;
Function NewAnd (pCond1,pCond2:TsrStatement):TsrStatement;
//
procedure EmitGoto(instr:TsrSourceInstruction;Adr:TSrcAdr);
function Parse:Integer;
procedure EmitLabels;
procedure GotoPass;
procedure InitMovePass;
procedure RemoveGoto(goto_stmt:TsrSourceGoto);
procedure EliminateAsConditional(goto_stmt:TsrSourceGoto);
procedure EliminateAsLoop (goto_stmt:TsrSourceGoto);
function MoveOutward (goto_stmt:TsrSourceGoto):TsrSourceGoto;
function MoveOutwardIf (goto_stmt:TsrSourceGoto):TsrSourceGoto;
function MoveOutwardElse (goto_stmt:TsrSourceGoto):TsrSourceGoto;
function MoveOutwardElseElse (goto_stmt:TsrSourceGoto):TsrSourceGoto;
function MoveOutwardLoop (goto_stmt:TsrSourceGoto):TsrSourceGoto;
function MoveOutwardSwitch (goto_stmt:TsrSourceGoto):TsrSourceGoto;
function MoveInward (goto_stmt:TsrSourceGoto):TsrSourceGoto;
function Lift (goto_stmt:TsrSourceGoto):TsrSourceGoto;
procedure ExecPass;
procedure Print;
end;
function get_branch_offset(var FSPI:TSPI):ptrint;
begin
Result:=FSPI.OFFSET_DW+Smallint(FSPI.SOPP.SIMM)+1;
end;
function IsReal(b:TsrBlockType):Boolean;
begin
case b of
btMain,
btSetpc,
btCond,
btElse,
btLoop:Result:=True;
else
Result:=False;
end;
end;
function TsrSourceNode.ntype:TsrSourceNodeType; inline;
begin
Result:=TsrSourceNodeType(ClassType);
end;
function TsrSourceNode.First:TsrSourceNode;
begin
Result:=nil;
end;
function TsrSourceNode.Last:TsrSourceNode;
begin
Result:=nil;
end;
function TsrSourceNode.get_level:DWORD;
var
node:TsrSourceBlock;
begin
node:=pParent;
Result:=0;
While (node<>nil) do
begin
if isReal(node.bType) then
begin
Inc(Result);
end;
node:=node.pParent;
end;
end;
///
class function TsrSourceAdr.c(n1,n2:PSrcAdr):Integer;
var
ptr1,ptr2:Pointer;
begin
ptr1:=n1^.get_code_ptr;
ptr2:=n2^.get_code_ptr;
Result:=ord(ptr1>ptr2)-ord(ptr1<ptr2);
end;
///
procedure TsrGotoList.Push_head(Node:TsrSourceGoto);
begin
Assert(Node.FGoto.pParent=nil);
//
if (pHead=nil) then
begin
pTail:=node;
node.FGoto.pNext:=nil;
end else
begin
pHead.FGoto.pPrev:=node;
node.FGoto.pNext:=pHead;
end;
node.FGoto.pPrev:=nil;
pHead:=node;
//
Node.FGoto.pParent:=@Self;
end;
procedure TsrGotoList.Push_tail(Node:TsrSourceGoto);
begin
Assert(Node.FGoto.pParent=nil);
//
if (pTail=nil) then
begin
pHead:=node;
node.FGoto.pPrev:=nil;
end else
begin
pTail.FGoto.pNext:=node;
node.FGoto.pPrev:=pTail;
end;
node.FGoto.pNext:=nil;
pTail:=node;
//
Node.FGoto.pParent:=@Self;
end;
procedure TsrGotoList.InsertAfter(node,new:TsrSourceGoto);
begin
Assert(new.FGoto.pParent=nil);
//
new.FGoto.pPrev:=node;
if (node.FGoto.pNext=nil) then
begin
new.FGoto.pNext:=nil;
pTail:=new;
end else
begin
Assert(node.FGoto.pNext.FGoto.pPrev=node);
new.FGoto.pNext:=node.FGoto.pNext;
node.FGoto.pNext.FGoto.pPrev:=new;
end;
node.FGoto.pNext:=new;
//
new.FGoto.pParent:=@Self;
end;
procedure TsrGotoList.InsertBefore(node,new:TsrSourceGoto);
begin
Assert(new.FGoto.pParent=nil);
//
new.FGoto.pNext:=node;
if (node.FGoto.pPrev=nil) then
begin
new.FGoto.pPrev:=nil;
pHead:=new;
end else
begin
Assert(node.FGoto.pPrev.FGoto.pNext=node);
new.FGoto.pPrev:=node.FGoto.pPrev;
node.FGoto.pPrev.FGoto.pNext:=new;
end;
node.FGoto.pPrev:=new;
//
new.FGoto.pParent:=@Self;
end;
function TsrGotoList.Pop_tail:TsrSourceGoto;
begin
if (pTail=nil) then
begin
Result:=nil;
end else
begin
Result:=pTail;
pTail:=pTail.FGoto.pPrev;
if (pTail=nil) then
begin
pHead:=nil;
end else
begin
pTail.FGoto.pNext:=nil;
end;
Result.FGoto.pPrev:=nil;
Result.FGoto.pNext:=nil;
//
Result.FGoto.pParent:=nil;
end;
end;
procedure TsrGotoList.Remove(node:TsrSourceGoto);
begin
Assert(node.FGoto.pParent=@Self);
//
if (node.FGoto.pPrev=nil) then
begin
if (pHead=node) then
begin
pHead:=node.FGoto.pNext;
end;
end else
begin
Assert(node.FGoto.pPrev.FGoto.pNext=node);
node.FGoto.pPrev.FGoto.pNext:=node.FGoto.pNext;
end;
if (node.FGoto.pNext=nil) then
begin
if (pTail=node) then
begin
pTail:=node.FGoto.pPrev;
end;
end else
begin
Assert(node.FGoto.pNext.FGoto.pPrev=node);
node.FGoto.pNext.FGoto.pPrev:=node.FGoto.pPrev;
end;
node.FGoto.pPrev:=nil;
node.FGoto.pNext:=nil;
//
node.FGoto.pParent:=nil;
end;
///
function TsrSourceBlock.pReal:TsrSourceBlock;
begin
if (Self=nil) then Exit(nil);
Result:=Self;
while (Result<>nil) do
begin
if IsReal(Result.bType) then Break;
Result:=Result.pParent;
end;
end;
function TsrSourceBlock.First:TsrSourceNode;
begin
Result:=FList.pHead;
end;
function TsrSourceBlock.Last:TsrSourceNode;
begin
Result:=FList.pTail;
end;
procedure TsrSourceBlock.Push_head(Node:TsrSourceNode);
begin
Assert(Node.pParent=nil);
FList.Push_head(Node);
Node.pParent:=Self;
end;
procedure TsrSourceBlock.Push_tail(Node:TsrSourceNode);
begin
Assert(Node.pParent=nil);
FList.Push_tail(Node);
Node.pParent:=Self;
end;
function TsrSourceBlock.Pop_head:TsrSourceNode;
begin
Result:=FList.Pop_head;
if (Result<>nil) then
begin
Result.pParent:=nil;
end;
end;
function TsrSourceBlock.Pop_tail:TsrSourceNode;
begin
Result:=FList.Pop_tail;
if (Result<>nil) then
begin
Result.pParent:=nil;
end;
end;
procedure TsrSourceBlock.InsertAfter(node,new:TsrSourceNode);
begin
Assert(node<>nil);
Assert(node.pParent=Self);
Assert(new.pParent=nil);
//
FList.InsertAfter(Node,new);
new.pParent:=Self;
end;
procedure TsrSourceBlock.InsertBefore(node,new:TsrSourceNode);
begin
Assert(node<>nil);
Assert(node.pParent=Self);
Assert(new.pParent=nil);
//
FList.InsertBefore(Node,new);
new.pParent:=Self;
end;
procedure TsrSourceBlock.Remove(node:TsrSourceNode);
begin
Assert(node<>nil);
Assert(node.pParent=Self);
//
FList.Remove(node);
node.pParent:=nil;
node.pPrev :=nil;
node.pNext :=nil;
end;
Procedure TsrSourceBlock.splice(node_first,node_last:TsrSourceNode); //[node_first..node_last)
var
node:TsrSourceNode;
next:TsrSourceNode;
parent:TsrSourceBlock;
begin
Assert(node_first<>nil);
if (node_last<>nil) then
begin
Assert(node_first.pParent=node_last.pParent);
end;
parent:=node_first.pParent;
node:=node_first;
While (node<>nil) and (node<>node_last) do
begin
Assert(parent=node.pParent);
next:=node.pNext;
parent.Remove(node);
Push_tail(node);
node:=next;
end;
end;
///
Function TsrCFGParser2.NewInstruction(b_adr,e_adr:TSrcAdr;var FSPI:TSPI):TsrSourceInstruction;
begin
Result:=FEmit.specialize New<TsrSourceInstruction>;
Inc(order);
Result.order:=order;
Result.b_adr:=b_adr;
Result.e_adr:=e_adr;
Result.FSPI :=FSPI;
Result.used :=True;
end;
Function TsrCFGParser2.NewLabel(Adr:TSrcAdr):TsrSourceLabel;
begin
Result:=FLabelTree.Find(@Adr);
if (Result=nil) then
begin
Result:=FEmit.specialize New<TsrSourceLabel>;
Inc(order);
Result.order:=order;
Result.b_adr:=Adr;
Result.e_adr:=Adr;
//
FLabelTree.Insert(Result);
end;
Inc(Result.links_count);
end;
Function TsrCFGParser2.NewGoto(Cond:TsrCondition;Adr:TSrcAdr):TsrSourceGoto;
begin
Result:=FGotoFree.Pop_tail;
if (Result=nil) then
begin
Result:=FEmit.specialize New<TsrSourceGoto>;
end;
Inc(order);
Result.order :=order;
Result.pCond :=NewCond(Cond);
Result.pLabel:=NewLabel(Adr);
end;
Function TsrCFGParser2.NewGoto(pCond:TsrStatement;pLabel:TsrSourceLabel):TsrSourceGoto;
begin
Result:=FGotoFree.Pop_tail;
if (Result=nil) then
begin
Result:=FEmit.specialize New<TsrSourceGoto>;
end;
Inc(order);
Result.order :=order;
Result.pCond :=pCond;
Result.pLabel:=pLabel;
end;
procedure TsrCFGParser2.FreeGoto(node:TsrSourceGoto);
begin
if (node.FGoto.pParent<>nil) then
begin
FGotoList.Remove(node);
end;
if (node.pParent<>nil) then
begin
node.pParent.Remove(node);
end;
FGotoFree.Push_tail(node);
end;
procedure TsrCFGParser2.RestoreGoto(node:TsrSourceGoto);
begin
if (node.FGoto.pParent=nil) then
begin
FGotoList.Push_tail(node);
end;
end;
Function TsrCFGParser2.NewBlock(bType:TsrBlockType):TsrSourceBlock;
begin
Result:=FEmit.specialize New<TsrSourceBlock>;
Inc(order);
Result.order:=order;
Result.bType:=bType;
end;
Function TsrCFGParser2.NewCond(cond:TsrCondition):TsrStatement;
begin
Result:=FEmit.specialize New<TsrStatement>;
Inc(order);
Result.order :=order;
Result.sType :=sCond;
Result.u.cond:=cond;
end;
Function TsrCFGParser2.NewCopy(pCond:TsrStatement):TsrStatement;
begin
Result:=FEmit.specialize New<TsrStatement>;
Inc(order);
Result.order:=order;
Result.sType:=sCopy;
Result.pDst :=Result;
Result.pSrc :=pCond;
Result.u.id :=VarId;
Inc(VarId);
end;
Function TsrCFGParser2.NewVar:TsrStatement;
begin
Result:=FEmit.specialize New<TsrStatement>;
Inc(order);
Result.order:=order;
Result.sType:=sVar;
Result.u.id :=VarId;
Inc(VarId);
end;
Function TsrCFGParser2.NewStore(pVar,pCond:TsrStatement):TsrStatement;
begin
Result:=FEmit.specialize New<TsrStatement>;
Inc(order);
Result.order:=order;
Result.sType:=sStore;
Result.pDst :=pVar;
Result.pSrc :=pCond;
end;
Function TsrCFGParser2.NewBreak:TsrStatement;
begin
Result:=FEmit.specialize New<TsrStatement>;
Inc(order);
Result.order:=order;
Result.sType:=sBreak;
end;
const
InvertCond:array[TsrCondition] of TsrCondition=(
cNone, //cNone,
cTrue, //cFalse,
cFalse, //cTrue,
cScc1, //cScc0,
cScc0, //cScc1,
cVccnz, //cVccz,
cVccz, //cVccnz,
cExecnz, //cExecz,
cExecz, //cExecnz,
cTidnz, //cTidz,
cTidz //cTidnz
);
Function TsrCFGParser2.NewNot(pCond:TsrStatement):TsrStatement;
begin
case pCond.sType of
sCond:Result:=NewCond(InvertCond[pCond.u.cond]);
else
begin
Result:=FEmit.specialize New<TsrStatement>;
Inc(order);
Result.order:=order;
Result.sType:=sNot;
Result.pSrc :=pCond;
end;
end;
end;
Function TsrCFGParser2.NewOr(pCond1,pCond2:TsrStatement):TsrStatement;
begin
Result:=FEmit.specialize New<TsrStatement>;
Inc(order);
Result.order:=order;
Result.sType:=sOr;
Result.pSrc :=pCond1;
Result.pDst :=pCond2;
end;
Function TsrCFGParser2.NewAnd(pCond1,pCond2:TsrStatement):TsrStatement;
begin
Result:=FEmit.specialize New<TsrStatement>;
Inc(order);
Result.order:=order;
Result.sType:=sAnd;
Result.pSrc :=pCond1;
Result.pDst :=pCond2;
end;
///
function TSrcAdr.get_code_ptr:PDWORD;
begin
if (pCode=nil) then
begin
Result:=nil;
end else
begin
Result:=pCode.Body;
end;
//
Result:=Pointer(Result)+Offset;
end;
function TSrcAdr.get_dmem_ptr:PDWORD;
begin
if (pCode=nil) then
begin
Result:=nil
end else
begin
Result:=pCode.DMem;
end;
//
Result:=Pointer(Result)+Offset;
end;
///
function ToAdr(pCode:TsrCodeRegion;Offset:PtrUInt):TSrcAdr; inline;
begin
Result.pCode :=pCode ;
Result.Offset:=Offset;
end;
procedure TsrCFGParser2.EmitGoto(instr:TsrSourceInstruction;Adr:TSrcAdr);
var
node:TsrSourceGoto;
begin
instr.used:=False;
//
Adr.Offset:=Adr.Offset+(Smallint(instr.FSPI.SOPP.SIMM)*SizeOf(DWORD));
//
Case instr.FSPI.SOPP.OP of
S_CBRANCH_SCC0 :node:=NewGoto(cScc0 ,Adr);
S_CBRANCH_SCC1 :node:=NewGoto(cScc1 ,Adr);
S_CBRANCH_VCCZ :node:=NewGoto(cVccz ,Adr);
S_CBRANCH_VCCNZ :node:=NewGoto(cVccnz ,Adr);
S_CBRANCH_EXECZ :node:=NewGoto(cExecz ,Adr);
S_CBRANCH_EXECNZ:node:=NewGoto(cExecnz,Adr);
S_BRANCH :node:=NewGoto(cTrue ,Adr);
else
Assert(false);
end;
//
pCurr.pParent.Push_tail(node);
pCurr:=node;
//
if (Smallint(instr.FSPI.SOPP.SIMM)<0) then
begin
//prior to cycles in revers
FGotoList.Push_head(node);
//
{
if (FGotoList.pMiddle=nil) then
begin
FGotoList.pMiddle:=node;
end;
}
end else
if (node.pCond.u.cond=cTrue) then
begin
FGotoList.Push_tail(node);
{
//middle unconditional in revers
if (FGotoList.pMiddle=nil) then
begin
FGotoList.pMiddle:=node;
//
FGotoList.Push_head(node);
end else
begin
//FGotoList.InsertBefore(FGotoList.pMiddle,node);
FGotoList.InsertAfter(FGotoList.pMiddle,node);
end;
}
end else
begin
FGotoList.Push_tail(node);
end;
end;
function TsrCFGParser2.Parse:Integer;
var
FCursor:TShaderParser;
b_adr,e_adr:TSrcAdr;
FSPI:TSPI;
node:TsrSourceInstruction;
begin
if (pCode=nil) then Exit(4);
//init first label
pCurr:=NewLabel(ToAdr(pCode,0));
pCode.FTop.Push_tail(pCurr);
FCursor:=Default(TShaderParser);
FCursor.Body:=pCode.Dmem;
FSPI:=Default(TSPI);
repeat
b_adr:=ToAdr(pCode,FCursor.OFFSET_DW*SizeOf(DWORD));
//
Result:=FCursor.Next(FSPI);
if (Result=-1) then Break;
//
e_adr:=ToAdr(pCode,FCursor.OFFSET_DW*SizeOf(DWORD));
//
node:=NewInstruction(b_adr,e_adr,FSPI);
//
pCurr.pParent.Push_tail(node);
pCurr:=node;
//
FInstructionTree.Insert(node);
//
Case FSPI.CMD.EN of
W_SOP1:
Case FSPI.SOP1.OP of
S_SETPC_B64:
if (pCurr.pParent.bType=btSetpc) then
begin
Result:=1;
Break;
end;
end;
W_SOPP:
Case FSPI.SOPP.OP of
S_CBRANCH_SCC0 :EmitGoto(node,e_adr);
S_CBRANCH_SCC1 :EmitGoto(node,e_adr);
S_CBRANCH_VCCZ :EmitGoto(node,e_adr);
S_CBRANCH_VCCNZ :EmitGoto(node,e_adr);
S_CBRANCH_EXECZ :EmitGoto(node,e_adr);
S_CBRANCH_EXECNZ:EmitGoto(node,e_adr);
S_BRANCH :EmitGoto(node,e_adr);
else;
end;
end;
//
if (Result<>0) then Break;
//
until false;
//
pCode.Size:=FCursor.OFFSET_DW*SizeOf(DWORD);
end;
function flow_down_next_up(node:TsrSourceNode):TsrSourceNode;
begin
Result:=node.First; //down
if (Result=nil) then
begin
repeat //up
Result:=node.pNext;
node:=node.pParent;
until (node=nil) or (Result<>nil);
end;
end;
procedure TsrCFGParser2.EmitLabels;
var
node:TsrSourceGoto;
inst:TsrSourceInstruction;
begin
node:=FGotoList.pHead;
//
while (node<>nil) do
begin
inst:=FInstructionTree.Find(@node.pLabel.key);
Assert(inst<>nil);
//
if (node.pLabel.pParent=nil) then //if not inserted
begin
inst.pParent.InsertBefore(inst,node.pLabel);
end;
//
node:=node.FGoto.pNext;
end;
end;
function Level(stmt:TsrSourceNode):DWORD; inline;
begin
Result:=stmt.get_level;
end;
function IsDirectlyRelated(goto_stmt,label_stmt:TsrSourceNode):Boolean;
var
goto_level,label_level:DWORD;
min_level,max_level:DWORD;
min,max:TsrSourceNode;
begin
goto_level :=Level(goto_stmt);
label_level:=Level(label_stmt);
if (label_level < goto_level) then
begin
min_level:=label_level;
max_level:=goto_level;
min:=label_stmt;
max:=goto_stmt;
end else
begin // goto_level < label_level
min_level:=goto_level;
max_level:=label_level;
min:=goto_stmt;
max:=label_stmt;
end;
while (max_level > min_level) do
begin
Dec(max_level);
max:=max.pParent.pReal;
end;
Result:=(min.pParent.pReal=max.pParent.pReal);
end;
function IsIndirectlyRelated(goto_stmt,label_stmt:TsrSourceNode):Boolean;
begin
Result:=(goto_stmt.pParent.pReal <> label_stmt.pParent.pReal) and
(not IsDirectlyRelated(goto_stmt, label_stmt));
end;
function AreSiblings(goto_stmt,label_stmt:TsrSourceNode):Boolean; inline;
begin
Result:=(goto_stmt.pParent.pReal=label_stmt.pParent.pReal);
end;
function SiblingFromNephew(uncle,nephew:TsrSourceNode;up:Boolean):TsrSourceNode;
var
parent,it,it_up:TsrSourceNode;
begin
parent:=uncle.pParent.pReal;
it :=nephew;
it_up :=it.pParent.pReal;
while (it_up <> nil) and (it_up <> parent) do
begin
it :=it_up;
it_up:=it.pParent.pReal;
end;
if up then
if (it.pParent<>nil) then
if (it.pParent.bType=btMerg) then
begin
it:=it.pParent;
end;
Result:=it;
end;
function AreOrdered(left_sibling,right_sibling:TsrSourceNode):Boolean;
var
it:TsrSourceNode;
begin
if AreSiblings(left_sibling,right_sibling) then
begin
it:=right_sibling;
while (it<>nil) do
begin
if (it=left_sibling) then Exit(False);
//
it:=it.pNext;
end;
Result:=True;
end else
begin
Result:=False;
end;
end;
function NeedsLift(goto_stmt,label_stmt:TsrSourceNode):Boolean;
var
sibling:TsrSourceNode;
begin
sibling:=SiblingFromNephew(goto_stmt,label_stmt,True);
Result:=AreOrdered(sibling,goto_stmt);
end;
Function IsUnconditional(cond:TsrStatement):Boolean; inline;
begin
Result:=(cond.sType=sCond) and (cond.u.cond=cTrue)
end;
Function IsUnreachable(cond:TsrStatement):Boolean; inline;
begin
Result:=(cond.sType=sCond) and (cond.u.cond=cFalse)
end;
procedure TsrCFGParser2.GotoPass;
var
node,next:TsrSourceGoto;
begin
mode:=0;
hits:=0;
//
node:=FGotoList.pHead;
//
while (node<>nil) do
begin
next:=node.FGoto.pNext;
//
RemoveGoto(node);
//
node:=next;
end;
//
repeat
mode:=1;
hits:=0;
//
node:=FGotoList.pHead;
//
while (node<>nil) do
begin
next:=node.FGoto.pNext;
//
RemoveGoto(node);
//
node:=next;
end;
//
until (hits=0);
//
mode:=2;
//
node:=FGotoList.pHead;
//
while (node<>nil) do
begin
next:=node.FGoto.pNext;
//
RemoveGoto(node);
//
node:=next;
end;
end;
procedure MoveListBefore(var List:TsrSourceNodeList;before:TsrSourceNode);
var
node:TsrSourceNode;
begin
while (List.pHead<>nil) do
begin
node:=List.Pop_head;
before.pParent.InsertBefore(before,node);
end;
end;
procedure TsrCFGParser2.InitMovePass;
var
node,next:TsrSourceNode;
begin
node:=pCode.FTop.First;
while (node<>nil) do
begin
next:=flow_down_next_up(node);
if (node.ntype=TsrSourceBlock) then
begin
MoveListBefore(TsrSourceBlock(node).FInit,node);
end;
node:=next;
end;
end;
Function NextUsed(node:TsrSourceNode):TsrSourceNode;
begin
Result:=node.pNext;
while (Result<>nil) do
begin
if (Result.ntype=TsrSourceInstruction) then
begin
if not TsrSourceInstruction(Result).used then
begin
Result:=Result.pNext;
Continue;
end;
end;
Break;
end;
end;
Function NextUsedFlowUp(node:TsrSourceNode):TsrSourceNode;
function flow_next_up(node:TsrSourceNode):TsrSourceNode;
begin
repeat //up
Result:=node.pNext;
node:=node.pParent;
//exclude "else"
if (Result<>nil) then
if (node<>nil) then
if (node.ntype=TsrSourceBlock) then
if (TsrSourceBlock(node).bType=btMerg) then
begin
Result:=nil;
end;
//
until (node=nil) or (Result<>nil);
end;
begin
Result:=flow_next_up(node);
while (Result<>nil) do
begin
if (Result.ntype=TsrSourceInstruction) then
begin
if not TsrSourceInstruction(Result).used then
begin
Result:=flow_next_up(Result);
Continue;
end;
end;
Break;
end;
end;
Function NextUsedFlowDown(node:TsrSourceNode):TsrSourceNode;
function flow_down_next(node:TsrSourceNode):TsrSourceNode;
begin
Result:=node.First; //down
if (Result=nil) then
begin
//next
Result:=node.pNext;
end;
end;
begin
Result:=flow_down_next(node);
while (Result<>nil) do
begin
if (Result.ntype=TsrSourceInstruction) then
begin
if not TsrSourceInstruction(Result).used then
begin
Result:=flow_down_next(Result);
Continue;
end;
end;
Break;
end;
end;
procedure TsrCFGParser2.RemoveGoto(goto_stmt:TsrSourceGoto);
var
label_stmt:TsrSourceLabel;
label_level,goto_level:DWORD;
begin
// Force goto_stmt and label_stmt to be directly related
label_stmt:=goto_stmt.pLabel;
if (IsIndirectlyRelated(goto_stmt, label_stmt)) then
begin
if (mode=0) then Exit;
// Move goto_stmt out using outward-movement transformation until it becomes
// directly related to label_stmt
while (not IsDirectlyRelated(goto_stmt, label_stmt)) do
begin
goto_stmt:=MoveOutward(goto_stmt);
//deffered
if (goto_stmt=nil) then Exit;
end
end;
// Force goto_stmt and label_stmt to be siblings
if (IsDirectlyRelated(goto_stmt, label_stmt)) then
begin
label_level:=Level(label_stmt);
goto_level :=Level(goto_stmt);
if (goto_level > label_level) then
begin
if (mode=0) then Exit;
// Move goto_stmt out of its level using outward-movement transformations
while (goto_level > label_level) do
begin
goto_stmt:=MoveOutward(goto_stmt);
//deffered
if (goto_stmt=nil) then Exit;
//Dec(goto_level);
goto_level:=Level(goto_stmt);
end;
end else
if (goto_level < label_level) then
begin
if (mode<2) then Exit;
//
if (NeedsLift(goto_stmt, label_stmt)) then
begin
// Lift goto_stmt to above stmt containing label_stmt using goto-lifting
// transformations
goto_stmt:=Lift(goto_stmt);
end;
// Move goto_stmt into label_stmt's level using inward-movement transformation
while (goto_level < label_level) do
begin
goto_stmt:=MoveInward(goto_stmt);
Inc(goto_level);
end;
end;
end;
//
if (not AreSiblings(goto_stmt, label_stmt)) then
begin
Assert(false,'Goto is not a sibling with the label');
end;
// goto_stmt and label_stmt are guaranteed to be siblings, eliminate
if (NextUsed(goto_stmt) = label_stmt) then
begin
// Simply eliminate the goto if the label is next to it
FreeGoto(goto_stmt);
end else
if (AreOrdered(goto_stmt, label_stmt)) then
begin
// Eliminate goto_stmt with a conditional
EliminateAsConditional(goto_stmt);
end else
begin
// Eliminate goto_stmt with a loop
EliminateAsLoop(goto_stmt);
end;
end;
procedure InsertAfter(node,new:TsrSourceNode);
var
parent:TsrSourceBlock;
begin
parent:=node.pParent;
if (parent.bType=btMerg) then
begin
node:=parent;
parent:=node.pParent;
end;
parent.InsertAfter(node,new);
end;
procedure InsertBefore(node,new:TsrSourceNode);
var
parent:TsrSourceBlock;
begin
parent:=node.pParent;
if (parent.bType=btMerg) then
begin
node:=parent;
parent:=node.pParent;
end;
parent.InsertBefore(node,new);
end;
procedure TsrCFGParser2.EliminateAsConditional(goto_stmt:TsrSourceGoto);
var
label_stmt:TsrSourceLabel;
cond :TsrStatement;
neg_cond :TsrStatement;
if_stmt :TsrSourceBlock;
if_merg :TsrSourceBlock;
begin
label_stmt:=goto_stmt.pLabel;
if_merg:=NewBlock(btMerg);
if_stmt:=NewBlock(btCond);
if_merg.Push_tail(if_stmt);
if_stmt.splice(goto_stmt.pNext,label_stmt);
InsertAfter(goto_stmt,if_merg);
cond:=goto_stmt.pCond;
neg_cond:=NewNot(cond);
if_merg.FInit.Push_tail(neg_cond);
if_stmt.pCond:=neg_cond;
Inc(hits);
//
FreeGoto(goto_stmt);
end;
function SanitizeNoBreaks(node_first,node_last:TsrSourceNode):Boolean;
var
node,next:TsrSourceNode;
begin
Result:=True;
node:=node_first;
while (node<>nil) and (node<>node_last) do
begin
if (node.ntype=TsrStatement) then
begin
if (TsrStatement(node).sType=sBreak) then
begin
Exit(False);
end;
end;
next:=node.First; //down
if (next<>nil) then
if (TsrSourceBlock(node).bType=btLoop) then
begin
next:=nil;
end;
if (next=nil) then
begin
repeat //up
next:=node.pNext;
node:=node.pParent;
until (node=nil) or (next<>nil) or (node=node_last);
end;
node:=next;
end;
end;
procedure TsrCFGParser2.EliminateAsLoop(goto_stmt:TsrSourceGoto);
var
label_stmt:TsrSourceLabel;
cond :TsrStatement;
loop_stmt :TsrSourceBlock;
begin
label_stmt:=goto_stmt.pLabel;
if not SanitizeNoBreaks(label_stmt, goto_stmt) then
begin
Assert(false,'SanitizeNoBreaks:EliminateAsLoop');
end;
loop_stmt:=NewBlock(btLoop);
loop_stmt.splice(label_stmt,goto_stmt);
InsertBefore(goto_stmt,loop_stmt);
cond:=goto_stmt.pCond;
loop_stmt.pCond:=cond;
Inc(hits);
//
FreeGoto(goto_stmt);
end;
function FindUpLoop(node:TsrSourceBlock):TsrSourceBlock;
begin
Result:=nil;
While (node<>nil) do
begin
if (node.bType=btLoop) then Exit(node);
node:=node.pParent;
end;
end;
function IsBreakLoop(goto_stmt:TsrSourceGoto):Boolean;
var
loop:TsrSourceBlock;
node:TsrSourceNode;
begin
Result:=False;
loop:=FindUpLoop(goto_stmt.pParent);
if (loop<>nil) then
begin
node:=goto_stmt.pLabel;
while (node<>nil) do
begin
if (node=loop) then
begin
//label inside loop
Exit(False);
end;
//
node:=node.pParent;
end;
//
Result:=True;
end;
end;
{
if (eval)
{
break-------\
} |
... |
<------------/
}
function IsBreakElse(goto_stmt:TsrSourceGoto):Boolean;
var
parent:TsrSourceBlock;
begin
Result:=False;
parent:=goto_stmt.pParent;
if (parent=nil) then Exit;
if (parent.bType=btCond) then
if IsUnconditional(goto_stmt.pCond) then
if (NextUsed(goto_stmt)=nil) then
begin
parent:=parent.pParent;
if (parent<>nil) then
if (parent.bType=btMerg) then
begin
Result:=True;
end;
end;
end;
function IsBreakSwitch(goto_stmt:TsrSourceGoto):Boolean;
var
label_stmt:TsrSourceLabel;
parent:TsrSourceBlock;
begin
Result:=False;
label_stmt:=goto_stmt.pLabel;
//if IsUnconditional(goto_stmt.pCond) then
//if (NextUsed(goto_stmt)=nil) then
if (goto_stmt.pParent<>nil) then
if (goto_stmt.pParent.bType=btCond) then
if (label_stmt.links_count>1) then
begin
parent:=goto_stmt.pParent;
parent:=parent.pParent;
if (NextUsedFlowUp(parent)<>label_stmt) then
while (parent<>nil) do
begin
if (parent.bType=btLoop) then
begin
Exit(False);
end else
if AreSiblings(parent,label_stmt) then
begin
if (parent.pParent<>nil) then
if (parent.pParent.bType=btMerg) then
begin
parent:=parent.pParent;
end;
if (NextUsedFlowUp(parent)=label_stmt) then
begin
Exit(False);
end else
if SanitizeNoBreaks(parent,label_stmt) then
begin
Exit(True);
end else
begin
Exit(False);
end;
end;
parent:=parent.pParent;
end;
end;
end;
function TsrCFGParser2.MoveOutward(goto_stmt:TsrSourceGoto):TsrSourceGoto;
var
parent:TsrSourceBlock;
begin
if (NextUsed(goto_stmt)=goto_stmt.pLabel) then
begin
Exit(goto_stmt);
end;
if IsBreakElse(goto_stmt) then
begin
Exit(MoveOutwardElse(goto_stmt));
end;
if (mode=2) then
if IsBreakSwitch(goto_stmt) then
begin
Exit(MoveOutwardSwitch(goto_stmt));
end;
if IsBreakLoop(goto_stmt) then
begin
Exit(MoveOutwardLoop(goto_stmt));
end;
parent:=goto_stmt.pParent.pReal;
case parent.bType of
btCond:Result:=MoveOutwardIf (goto_stmt);
btElse:Result:=MoveOutwardIf (goto_stmt);
btLoop:Result:=MoveOutwardLoop(goto_stmt);
else
begin
Writeln(stderr,'Invalid outward movement:',parent.bType);
Assert(false,'Invalid outward movement');
end;
end;
end;
{
....
if (expr)
{
stmt_1;
....
if (cond) goto L1;
....
stmt_i;
}
....
L1:
stmt_n;
}
//--> normal
{
....
goto_L1 = false;
if (expr)
{
stmt_1;
....
goto_L1 = cond;
if (!cond)
{
stmt_2;
....
stmt_i;
}
}
if (goto_L1) goto L1;
....
L1:
stmt_n;
}
//--> no body
{
....
goto_L1 = false;
if (expr)
{
stmt_1;
....
goto_L1 = cond;
}
if (goto_L1) goto L1;
....
L1:
stmt_n;
}
//--> no body, no cond, "if" body -> "else"
{
....
if (expr)
{
stmt_1;
....
----
}
if (expr) goto L1;
....
L1:
stmt_n;
}
function TsrCFGParser2.MoveOutwardIf(goto_stmt:TsrSourceGoto):TsrSourceGoto;
var
label_stmt:TsrSourceLabel;
parent :TsrSourceBlock;
pmerge :TsrSourceBlock;
new_var :TsrStatement;
set_var :TsrStatement;
cond :TsrStatement;
neg_cond :TsrStatement;
new_goto :TsrSourceGoto;
if_stmt :TsrSourceBlock;
if_merg :TsrSourceBlock;
begin
label_stmt:=goto_stmt.pLabel;
parent:=goto_stmt.pParent;
pmerge:=parent.pParent;
Assert(pmerge<>nil);
Assert(pmerge.bType=btMerg);
cond:=goto_stmt.pCond;
if (NextUsedFlowUp(pmerge)<>label_stmt) then
begin
if (mode<>2) then
begin
RestoreGoto(goto_stmt);
Exit(nil);
end;
//backup action
if (parent.bType=btCond) and
(IsUnconditional(goto_stmt.pCond)) and
(NextUsed(goto_stmt)=nil) then
begin
//else is detect
Exit(MoveOutwardElse(goto_stmt));
end;
//else goto up
if (parent.bType=btElse) and
(IsUnconditional(goto_stmt.pCond)) and
(NextUsed(goto_stmt)=nil) then
begin
//else in else
Exit(MoveOutwardElseElse(goto_stmt));
end;
new_var:=NewVar;
end else
begin
Inc(hits);
//simplification
new_var:=nil;
end;
if (new_var<>nil) then
begin
//goto_L1 = false;
set_var:=NewStore(new_var,NewCond(cFalse));
pmerge.FInit.Push_tail(set_var);
end;
if (NextUsed(goto_stmt)=nil) then
begin
//empty body
if (new_var<>nil) then
begin
//goto_L1 = cond;
set_var:=NewStore(new_var,cond);
parent.Push_tail(set_var);
end;
end else
begin
if_merg:=NewBlock(btMerg);
if_stmt:=NewBlock(btCond);
if_merg.Push_tail(if_stmt);
if_stmt.splice(goto_stmt.pNext,nil);
InsertAfter(goto_stmt,if_merg);
if (new_var<>nil) then
begin
//goto_L1 = cond;
set_var:=NewStore(new_var,cond);
if_merg.FInit.Push_tail(set_var);
end;
neg_cond:=NewNot(cond);
if_merg.FInit.Push_tail(neg_cond);
if_stmt.pCond:=neg_cond;
//
end;
//
FreeGoto(goto_stmt);
//
if (new_var=nil) then
begin
if IsUnconditional(Cond) then
begin
new_var:=Cond;
end else
begin
new_var:=NewCond(cTrue);
end;
end;
new_goto:=NewGoto(new_var,label_stmt);
InsertAfter(pmerge,new_goto);
Result:=new_goto;
end;
{
....
if (expr)
{
stmt_1;
....
goto L1;
}
stmt_2;
{
....
L1:
....
}
stmt_n;
}
//-->
{
....
if (expr)
{
stmt_1;
....
----
} else {
stmt_2;
}
if (expr) goto L1;
{
....
L1:
....
}
stmt_n;
}
function TsrCFGParser2.MoveOutwardElse(goto_stmt:TsrSourceGoto):TsrSourceGoto;
var
label_stmt:TsrSourceLabel;
pmerge :TsrSourceBlock;
cond :TsrStatement;
new_goto :TsrSourceGoto;
if_stmt :TsrSourceBlock;
if_else :TsrSourceBlock;
begin
label_stmt:=goto_stmt.pLabel;
if_stmt:=goto_stmt.pParent;
Assert(if_stmt<>nil);
pmerge:=if_stmt.pParent;
Assert(pmerge<>nil);
Assert(pmerge.bType=btMerg);
cond:=goto_stmt.pCond;
Assert(IsUnconditional(cond));
if (mode<>2) then
begin
if not AreOrdered(pmerge,label_stmt) then
begin
if (NextUsedFlowUp(pmerge.pParent.Last)<>label_stmt) then
begin
RestoreGoto(goto_stmt);
Exit(nil);
end;
end;
Inc(hits);
end;
if (NextUsed(pmerge)=nil) or
(NextUsedFlowUp(pmerge)=label_stmt) then
begin
//no else body
end else
begin
if_else:=NewBlock(btElse);
if_else.pIf :=if_stmt;
if_stmt.pElse:=if_else;
pmerge.InsertAfter(if_stmt,if_else);
if AreOrdered(pmerge,label_stmt) then
begin
if_else.splice(pmerge.pNext,label_stmt);
end else
begin
if_else.splice(pmerge.pNext,nil);
end;
end;
//
FreeGoto(goto_stmt);
//
//if (expr)
cond:=if_stmt.pCond;
if (NextUsedFlowUp(pmerge)<>label_stmt) then
begin
cond:=NewCopy(cond);
pmerge.FInit.Push_tail(cond);
end else
begin
//simplification
Exit(nil);
end;
new_goto:=NewGoto(cond,label_stmt);
InsertAfter(pmerge,new_goto);
Result:=new_goto;
end;
//--> no body, no cond, "else" body
{
....
goto_L1 = false;
if (expr)
{
stmt_1;
....
} else {
....
----
}
if (!expr) goto L1;
....
L1:
stmt_n;
}
function TsrCFGParser2.MoveOutwardElseElse(goto_stmt:TsrSourceGoto):TsrSourceGoto;
var
label_stmt:TsrSourceLabel;
pmerge :TsrSourceBlock;
cond :TsrStatement;
new_goto :TsrSourceGoto;
if_else :TsrSourceBlock;
if_stmt :TsrSourceBlock;
begin
label_stmt:=goto_stmt.pLabel;
if_else:=goto_stmt.pParent;
Assert(if_else<>nil);
pmerge:=if_else.pParent;
Assert(pmerge<>nil);
Assert(pmerge.bType=btMerg);
if_stmt:=if_else.pIf;
Assert(if_stmt<>nil);
cond:=goto_stmt.pCond;
Assert(IsUnconditional(cond));
if (mode<>2) then
begin
if (NextUsedFlowUp(pmerge)<>label_stmt) then
begin
RestoreGoto(goto_stmt);
Exit(nil);
end;
Inc(hits);
end;
//
FreeGoto(goto_stmt);
//
//if (expr) else {}
cond:=if_stmt.pCond;
Assert(cond<>nil);
if (NextUsedFlowUp(pmerge)<>label_stmt) then
begin
//(!expr)
cond:=NewNot(cond);
pmerge.FInit.Push_tail(cond);
cond:=NewCopy(cond);
pmerge.FInit.Push_tail(cond);
end else
begin
//simplification
Exit(nil);
end;
new_goto:=NewGoto(cond,label_stmt);
InsertAfter(pmerge,new_goto);
Result:=new_goto;
end;
{
....
do {
stmt_1;
....
if (expr) {
if (cond) goto L1;
}
....
stmt_i;
} while (expr)
....
L1:
stmt_n;
}
//-->
{
....
goto_L1 = false;
do {
stmt_1;
....
if (expr) {
if (cond) {
goto_L1 = true;
break;
}
}
....
stmt_i;
} while (expr)
if (goto_L1) goto L1;
....
L1:
stmt_n;
}
function TsrCFGParser2.MoveOutwardLoop(goto_stmt:TsrSourceGoto):TsrSourceGoto;
var
label_stmt :TsrSourceLabel;
loop_parent:TsrSourceBlock;
cond :TsrStatement;
new_var :TsrStatement;
set_var :TsrStatement;
new_goto :TsrSourceGoto;
if_stmt :TsrSourceBlock;
if_merg :TsrSourceBlock;
begin
label_stmt:=goto_stmt.pLabel;
loop_parent:=FindUpLoop(goto_stmt.pParent);
cond:=goto_stmt.pCond;
if (NextUsedFlowUp(loop_parent)<>label_stmt) then
begin
if (mode<>2) then
begin
RestoreGoto(goto_stmt);
Exit(nil);
end;
new_var:=NewVar;
//goto_L1 = false;
set_var:=NewStore(new_var,NewCond(cFalse));
loop_parent.FInit.Push_tail(set_var);
end else
begin
Inc(hits);
//simplification
new_var:=nil;
end;
//Unconditional
if IsUnconditional(cond) then
begin
if (new_var<>nil) then
begin
//goto_L1 = true;
set_var:=NewStore(new_var,NewCond(cTrue));
InsertBefore(goto_stmt,set_var);
end;
//break;
InsertBefore(goto_stmt,NewBreak);
end else
begin
//if (cond) {
if_merg:=NewBlock(btMerg);
if_stmt:=NewBlock(btCond);
if_merg.Push_tail(if_stmt);
InsertBefore(goto_stmt,if_merg);
if_stmt.pCond:=cond;
if (new_var<>nil) then
begin
//goto_L1 = true;
set_var:=NewStore(new_var,NewCond(cTrue));
if_stmt.Push_tail(set_var);
end;
//break;
if_stmt.Push_tail(NewBreak);
end;
//
FreeGoto(goto_stmt);
//
if (new_var=nil) then
begin
if IsUnconditional(Cond) then
begin
new_var:=Cond;
end else
begin
new_var:=NewCond(cTrue);
end;
end;
new_goto:=NewGoto(new_var,label_stmt);
InsertAfter(loop_parent,new_goto);
Result:=new_goto;
end;
{
if (expr) {
if (cond) goto L1;
}
...
L1:
...
}
function TsrCFGParser2.MoveOutwardSwitch(goto_stmt:TsrSourceGoto):TsrSourceGoto;
var
label_stmt :TsrSourceLabel;
nested_stmt:TsrSourceNode;
loop_stmt :TsrSourceBlock;
begin
label_stmt :=goto_stmt.pLabel;
nested_stmt:=SiblingFromNephew(label_stmt,goto_stmt,True);
if not SanitizeNoBreaks(nested_stmt, label_stmt) then
begin
Assert(false,'SanitizeNoBreaks:MoveOutwardSwitch');
end;
loop_stmt:=NewBlock(btLoop);
loop_stmt.pCond:=NewCond(cFalse);
loop_stmt.splice(nested_stmt, label_stmt);
InsertBefore(label_stmt,loop_stmt);
Result:=MoveOutwardLoop(goto_stmt);
end;
{ ...
if (cond) goto L1;
stmt_1;
...
stmt_i;
if (expr) {
stmt_k;
L1:
...
stmt_n;
}
}
//--> [Cond]
{
...
goto_L1 = cond;
if (!cond) {
stmt_1;
...
stmt_i;
}
if (goto_L1 || expr) {
if (goto_L1) goto L1;
stmt_k;
L1:
...
stmt_n;
}
}
//--> [Else]
{ ...
goto_L1 = cond;
if (!cond) {
stmt_1;
...
stmt_i;
}
if (!goto_L1 && expr) {
...
stmt_j;
...
}
else {
if (goto_L1) goto L1;
stmt_k;
L1:
...
stmt_n;
}
}
//--> [Loop]
{
...
goto_L1 = cond;
if (!cond) {
stmt_1;
...
stmt_i;
}
do {
if (goto_L1) goto L1;
stmt_j;
...
L1:
...
stmt_n;
} while (expr)
}
function TsrCFGParser2.MoveInward(goto_stmt:TsrSourceGoto):TsrSourceGoto;
var
label_stmt :TsrSourceLabel;
nested_stmt:TsrSourceBlock;
nested_real:TsrSourceBlock;
new_copy :TsrStatement;
cond :TsrStatement;
neg_cond :TsrStatement;
new_op :TsrStatement;
new_goto :TsrSourceGoto;
if_stmt :TsrSourceBlock;
if_merg :TsrSourceBlock;
begin
label_stmt:=goto_stmt.pLabel;
nested_stmt:=TsrSourceBlock(SiblingFromNephew(goto_stmt,label_stmt,False));
Assert(nested_stmt.ntype=TsrSourceBlock);
nested_real:=nested_stmt;
if (nested_stmt.pParent<>nil) then
if (nested_stmt.pParent.bType=btMerg) then
begin
nested_real:=nested_stmt.pParent;
end;
cond:=goto_stmt.pCond;
if (NextUsed(goto_stmt)=nested_real) then
begin
//empty body
//goto_L1 = cond;
if (cond.sType<>sCond) then
begin
new_copy:=cond;
end else
begin
new_copy:=NewCopy(cond);
nested_real.FInit.Push_tail(new_copy);
end;
end else
begin
if_merg:=NewBlock(btMerg);
if_stmt:=NewBlock(btCond);
if_merg.Push_tail(if_stmt);
if_stmt.splice(goto_stmt.pNext,nested_real);
InsertAfter(goto_stmt,if_merg);
//goto_L1 = cond;
if (cond.sType<>sCond) then
begin
new_copy:=cond;
end else
begin
new_copy:=NewCopy(cond);
if_merg.FInit.Push_tail(new_copy);
end;
neg_cond:=NewNot(cond);
if_merg.FInit.Push_tail(neg_cond);
if_stmt.pCond:=neg_cond;
//
end;
case TsrSourceBlock(nested_stmt).bType of
btCond:
begin
Assert(nested_stmt.pCond<>nil);
//load before
if IsUnreachable(nested_stmt.pCond) then
begin
new_op:=new_copy;
end else
begin
//(goto_L1 || expr)
new_op:=NewOr(new_copy,nested_stmt.pCond);
//
nested_real.FInit.Push_tail(new_op);
end;
// Update nested if condition
nested_stmt.pCond:=new_op;
end;
btElse:
begin
if_stmt:=nested_stmt.pIf;
Assert(if_stmt<>nil);
Assert(nested_stmt.pCond<>nil);
//load before
if IsUnreachable(nested_stmt.pCond) then
begin
new_op:=nested_stmt.pCond;
end else
begin
//!goto_L1
neg_cond:=NewNot(new_copy);
nested_real.FInit.Push_tail(neg_cond);
//(!goto_L1 && expr)
new_op:=NewAnd(neg_cond,nested_stmt.pCond);
//
nested_real.FInit.Push_tail(new_op);
end;
// Update nested if condition
nested_stmt.pCond:=new_op;
end;
btLoop:
begin
//
end;
else
begin
Writeln(stderr,'Invalid inward movement:',nested_stmt.bType);
Assert(false,'Invalid inward movement');
end;
end;
//
FreeGoto(goto_stmt);
//
new_goto:=NewGoto(new_copy,label_stmt);
nested_stmt.Push_head(new_goto);
Result:=new_goto;
end;
{
....
stmt_1;
stmt_2;
....
stmt_lab; /*contains L1*/
....
if (cond) goto L1;
....
stmt_n;
}
//-->
{
....
stmt_1;
stmt_2;
....
goto_L1 = false;
do {
if (goto_L1) goto L1;
stmt_lab; /*contains L1*/
....
goto_L1 = cond;
} while (cond);
....
stmt_n;
}
function TsrCFGParser2.Lift(goto_stmt:TsrSourceGoto):TsrSourceGoto;
var
label_stmt :TsrSourceLabel;
nested_stmt:TsrSourceNode;
loop_stmt :TsrSourceBlock;
cond :TsrStatement;
new_var :TsrStatement;
set_var :TsrStatement;
new_goto :TsrSourceGoto;
begin
label_stmt:=goto_stmt.pLabel;
nested_stmt:=SiblingFromNephew(goto_stmt,label_stmt,True);
if not SanitizeNoBreaks(nested_stmt, goto_stmt) then
begin
Assert(false,'SanitizeNoBreaks:Lift');
end;
loop_stmt:=NewBlock(btLoop);
loop_stmt.splice(nested_stmt, goto_stmt);
InsertBefore(goto_stmt,loop_stmt);
new_var:=NewVar;
cond :=goto_stmt.pCond;
loop_stmt.pCond:=cond;
// goto_L1 = cond;
set_var:=NewStore(new_var,cond);
loop_stmt.Push_tail(set_var);
//goto_L1 = false;
set_var:=NewStore(new_var,NewCond(cFalse));
loop_stmt.FInit.Push_tail(set_var);
//
FreeGoto(goto_stmt);
//
new_goto:=NewGoto(new_var,label_stmt);
loop_stmt.Push_head(new_goto);
Result:=new_goto;
end;
///
type
TsrExecFlow=(
efNone,
efFalse,
efTrue
);
function is_cmp_x(i:Integer):Boolean; inline;
begin
case i of
V_CMPX_F_F32 ..V_CMPX_T_F32,
V_CMPX_F_F64 ..V_CMPX_T_F64,
V_CMPSX_F_F32..V_CMPSX_T_F32,
V_CMPSX_F_F64..V_CMPSX_T_F64,
V_CMPX_F_I32 ..V_CMPX_T_I32,
V_CMPX_F_I64 ..V_CMPX_T_I64,
V_CMPX_F_U32 ..V_CMPX_T_U32,
V_CMPX_F_U64 ..V_CMPX_T_U64,
V_CMPX_CLASS_F32,
V_CMPX_CLASS_F64:
Result:=True;
else
Result:=False;
end;
end;
function GetExecFlow(var FSPI:TSPI):TsrExecFlow;
begin
Case FSPI.CMD.EN of
W_VOP1 :
Case FSPI.VOP1.OP of
V_NOP :Result:=efNone;
V_READFIRSTLANE_B32:Result:=efFalse;
else
Result:=efTrue;
end;
//-> The result is given in a scalar register, so it needs to be processed separately
W_VOPC :Result:=efFalse;
W_VOP3 :
Case FSPI.VOP3a.OP of
//-> The result is given in a scalar register, so it needs to be processed separately
0..255 :Result:=efFalse; //VOP3c
384+V_NOP :Result:=efNone;
256+V_READLANE_B32 :Result:=efFalse;
256+V_WRITELANE_B32 :Result:=efFalse;
384+V_READFIRSTLANE_B32:Result:=efFalse;
else
Result:=efTrue;
end;
W_DS :
Case FSPI.DS.OP of
DS_NOP:Result:=efNone;
else
Result:=efTrue;
end;
W_MUBUF :Result:=efTrue;
W_MTBUF :Result:=efTrue;
W_EXP :Result:=efFalse; //-> processed separately
W_MIMG :Result:=efTrue;
W_VOP2 :
Case FSPI.VOP2.OP of
V_READLANE_B32 :Result:=efFalse;
V_WRITELANE_B32:Result:=efFalse;
else
Result:=efTrue;
end;
W_SOPP :
Case FSPI.SOPP.OP of
S_NOP :Result:=efNone;
S_WAITCNT:Result:=efNone;
else
Result:=efFalse;
end;
else
Result:=efFalse;
end;
end;
function GetExecFlow(node:TsrSourceNode):TsrExecFlow;
begin
if (node.ntype=TsrSourceInstruction) then
begin
Result:=GetExecFlow(TsrSourceInstruction(node).FSPI);
end else
if (node.ntype=TsrSourceNode) then
begin
Result:=efNone;
end else
if (node.ntype=TsrSourceLabel) then
begin
Result:=efNone;
end else
begin
Result:=efFalse;
end;
end;
procedure TsrCFGParser2.ExecPass;
var
node :TsrSourceNode;
next :TsrSourceNode;
parent :TsrSourceNode;
exec :TsrSourceBlock;
if_stmt:TsrSourceBlock;
if_merg:TsrSourceBlock;
begin
node:=pCode.FTop.First;
exec:=nil;
while (node<>nil) do
begin
parent:=node.pParent;
next:=flow_down_next_up(node);
case GetExecFlow(node) of
efNone:
begin
if (exec<>nil) then
begin
exec.splice(node,node.pNext);
end;
end;
efFalse:
begin
exec:=nil;
end;
efTrue:
begin
if (exec=nil) then
begin
if_merg:=NewBlock(btMerg);
if_stmt:=NewBlock(btCond);
if_stmt.pCond:=NewCond(cTidnz);
if_merg.Push_tail(if_stmt);
InsertBefore(node,if_merg);
exec:=if_stmt;
end;
exec.splice(node,node.pNext);
end;
end;
if (next<>nil) then
if (parent<>next.pParent) then
begin
exec:=nil;
end;
node:=next;
end;
end;
///
function Vertical(node:TsrSourceNode):RawByteString;
var
i:Integer;
tmp:TsrSourceBlock;
begin
i:=node.get_level;
//
Result:='';
SetLength(Result,i);
tmp:=node.pParent;
While (tmp<>nil) do
begin
if isReal(tmp.bType) then
begin
if (tmp.bType=btLoop) then
begin
Result[i]:='!';
end else
begin
Result[i]:='|';
end;
Dec(i);
end;
tmp:=tmp.pParent;
end;
//
if (i<>0) and (node.ntype=TsrSourceBlock) then
begin
Result[i]:='+';
end;
end;
function get_str_prefix(count:Byte;node:TsrSourceNode):RawByteString;
begin
if (node.ntype=TsrSourceInstruction) then
begin
Result:=' '+HexStr(TsrSourceInstruction(node).b_adr.Offset,count)+Vertical(node);
end else
if (node.ntype=TsrSourceLabel) then
begin
Result:=' '+Space(count)+Space(node.get_level);
end else
begin
Result:=' '+Space(count)+Vertical(node);
end;
end;
function get_str_label(count:Byte;node:TsrSourceLabel):RawByteString;
begin
Result:='_label_'+HexStr(node.b_adr.Offset,count);
end;
const
CondStr:array[TsrCondition] of RawByteString=(
'None',
'False',
'True',
'Scc0',
'Scc1',
'Vccz',
'Vccnz',
'Execz',
'Execnz',
'Tidz',
'Tidnz'
);
function GetCondStr(node:TsrStatement):RawByteString;
begin
Result:='';
while (node<>nil) do
begin
case node.sType of
sCond :begin Result:=Result+CondStr[node.u.cond]; Break; end;
sCopy :begin Result:=Result+'C'+IntToStr(node.u.id); Break; end;
sVar :begin Result:=Result+'U'+IntToStr(node.u.id); Break; end;
//sStore:;
//sBreak:;
sNot :begin Result:='!'+Result; node:=node.pSrc; end;
sOr :begin Result:=Result+'('+GetCondStr(node.pSrc)+' || '+GetCondStr(node.pDst)+')'; Break; end;
sAnd :begin Result:=Result+'('+GetCondStr(node.pSrc)+' && '+GetCondStr(node.pDst)+')'; Break; end;
else Break;
end;
end;
end;
function GetStatmentStr(node:TsrStatement):RawByteString;
begin
Result:='';
case node.sType of
//sCond :'
sCopy :Result:=GetCondStr(node.pDst)+' = '+GetCondStr(node.pSrc)+'; //#'+IntToStr(node.order);
//sVar :'
sStore:Result:=GetCondStr(node.pDst)+' = '+GetCondStr(node.pSrc)+'; //#'+IntToStr(node.order);
sBreak:Result:='break; //#'+IntToStr(node.order);
//sNot :;
//sOr :;
//sAnd :;
else;
end;
end;
procedure PrintStatmentList(const prefix:RawByteString;List:TsrSourceNodeList);
var
node:TsrStatement;
s:RawByteString;
begin
node:=TsrStatement(List.pHead);
while (node<>nil) do
begin
case node.sType of
sCond:; //skip
sNot :; //skip
sOr :; //skip
sAnd :; //skip
else
begin
s:=GetStatmentStr(node);
if (s<>'') then
begin
Writeln(prefix+s);
end;
end;
end;
//
node:=TsrStatement(node.pNext);
end;
end;
function get_str_goto(count:Byte;node:TsrSourceGoto):RawByteString;
begin
Result:='if ('+GetCondStr(node.pCond)+') goto '+get_str_label(count,node.pLabel)+'; //#'+IntToStr(node.order);
end;
function get_down_str(node:TsrSourceBlock):RawByteString;
begin
Result:='';
case node.bType of
btCond:Result:='if ('+GetCondStr(node.pCond)+') { //#'+IntToStr(node.order);
btLoop:Result:='do { //#'+IntToStr(node.order);
//btMerg:Result:='merge { //#'+IntToStr(node.order);
else;
end;
end;
function get_up_str(node:TsrSourceBlock):RawByteString;
begin
Result:='';
case node.bType of
btCond:begin
if (node.pElse=nil) then
begin
Result:='}; //if #'+IntToStr(node.order);;
end else
begin
Result:='} else { //#'+IntToStr(node.order)+'-#'+IntToStr(node.pElse.order);
end;
end;
btElse:Result:='}; //else #'+IntToStr(node.order);
btLoop:Result:='} while ('+GetCondStr(node.pCond)+'); //#'+IntToStr(node.order);
//btMerg:Result:='} //merge #'+IntToStr(node.order);
else;
end;
end;
procedure TsrCFGParser2.Print;
var
count:Byte;
prefix,s:RawByteString;
node,next:TsrSourceNode;
begin
count:=BsrQWord(pCode.Size);
if (count=$FF) then count:=0;
count:=(count+4) div 4;
node:=pCode.FTop.First;
while (node<>nil) do
begin
prefix:=get_str_prefix(count,node);
if (node.ntype=TsrSourceInstruction) then
begin
Writeln(prefix+get_str_spi(TsrSourceInstruction(node).FSPI));
end else
if (node.ntype=TsrSourceLabel) then
begin
Writeln(prefix+get_str_label(count,TsrSourceLabel(node))+': //#'+IntToStr(node.order));
end else
if (node.ntype=TsrSourceGoto) then
begin
Writeln(prefix+get_str_goto(count,TsrSourceGoto(node)));
end else
if (node.ntype=TsrSourceBlock) then
begin
//
PrintStatmentList(prefix,TsrSourceBlock(node).FInit);
//down
s:=get_down_str(TsrSourceBlock(node));
if (s<>'') then
begin
Writeln(prefix+s);
end;
//up
if (node.First=nil) then
begin
s:=get_up_str(TsrSourceBlock(node));
if (s<>'') then
begin
Writeln(prefix+s);
end;
end;
//
end else
if (node.ntype=TsrStatement) then
begin
s:=GetStatmentStr(TsrStatement(node));
if (s<>'') then
begin
Writeln(prefix+s);
end;
end else
begin
//Writeln(prefix+node.ntype.ClassName);
end;
next:=node.First; //down
if (next=nil) then
begin
repeat //up
next:=node.pNext;
node:=node.pParent;
//
if (node<>nil) and (next=nil) then
begin
s:=get_up_str(TsrSourceBlock(node));
if (s<>'') then
begin
prefix:=get_str_prefix(count,node);
Writeln(prefix+s);
end;
end else
begin
Break;
end;
//
until false;
end;
node:=next;
end;
end;
///
function parse_code_cfg2(var pCode:TsrCodeRegion;bType:TsrBlockType;Body,Dmem:Pointer;FEmit:TCustomEmit):Integer;
var
parser:TsrCFGParser2;
begin
pCode:=FEmit.specialize New<TsrCodeRegion>;
//
parser:=Default(TsrCFGParser2);
parser.FEmit:=FEmit;
parser.pCode:=pCode;
//
pCode.FEmit:=FEmit;
pCode.Body :=Body;
pCode.Dmem :=Dmem;
pCode.FTop :=parser.NewBlock(bType);
Result:=parser.Parse;
if (Result>1) then Exit;
parser.EmitLabels;
parser.GotoPass;
parser.InitMovePass;
parser.ExecPass;
if PsrConfig(pCode.FEmit.GetConfig)^.PrintCfg then
begin
parser.Print;
end;
Result:=0;
end;
//
function TsrCodeRegion.FindByPtr(base:Pointer):TsrSourceNode;
var
node:TsrSourceNode;
begin
Result:=nil;
node:=FTop.First;
while (node<>nil) do
begin
if node.InheritsFrom(TsrSourceAdr) then
with TsrSourceAdr(node) do
begin
if (b_adr.get_code_ptr=base) then
begin
Exit(node);
end else
if (e_adr.get_code_ptr=base) then
begin
Exit(node);
end;
end;
node:=flow_down_next_up(node);
end;
end;
///
end.