mirror of https://github.com/red-prig/fpPS4.git
2874 lines
52 KiB
Plaintext
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.
|
|
|