FPPS4/spirv/srCFGParser.pas

2468 lines
48 KiB
Plaintext

unit srCFGParser;
{$mode ObjFPC}{$H+}
interface
uses
sysutils,
ps4_pssl,
ginodes,
srNode,
srCFGLabel;
type
TsrCFGBlock=class;
TsrCFGBlockList=specialize TNodeListClass<TsrCFGBlock>;
TsrStatementList=specialize TNodeListClass<TsrStatement>;
TsrCFGBlock=class
pParent,pPrev,pNext:TsrCFGBlock;
FList:TsrCFGBlockList;
pIf :TsrCFGBlock;
pElse:TsrCFGBlock;
pBLabel:TsrLabel;
pELabel:TsrLabel;
pCond:TsrStatement;
FBefore:TsrStatementList;
FAfter :TsrStatementList;
FStart :TsrStatementList;
FEnded :TsrStatementList;
EndFixed:Boolean;
bType:TsrBlockType;
order:Integer;
function beg_adr:TSrcAdr;
function end_adr:TSrcAdr;
function First:TsrCFGBlock;
function Last :TsrCFGBlock;
function pReal:TsrCFGBlock;
function IsEndOf (Adr:TSrcAdr):Boolean;
function IsBigOf (Adr:TSrcAdr):Boolean;
function IsContain(Adr:TSrcAdr):Boolean;
function IsInside (Adr:TSrcAdr):Boolean;
function FindBlock(Adr:TSrcAdr):TsrCFGBlock;
function DownBlock(Adr:TSrcAdr):TsrCFGBlock;
function WorkBlock(Adr:TSrcAdr):TsrCFGBlock;
function FindNext (Adr:TSrcAdr):TsrCFGBlock;
function FindPrev (Adr:TSrcAdr):TsrCFGBlock;
function UpBlock (Adr:TSrcAdr):TsrCFGBlock;
function FindUpLoop:TsrCFGBlock;
Procedure Push_front (new:TsrCFGBlock;_first:TsrCFGBlock=nil;_last:TsrCFGBlock=nil);
Procedure Push_back (new:TsrCFGBlock;_first:TsrCFGBlock=nil;_last:TsrCFGBlock=nil);
Procedure Insert_after(node,new:TsrCFGBlock;_first:TsrCFGBlock=nil;_last:TsrCFGBlock=nil);
Procedure MoveTo(_parent,_first,_last:TsrCFGBlock);
Procedure InsertAfterTo(_parent,_after,_first,_last:TsrCFGBlock);
Procedure MoveFromByRange(_parent:TsrCFGBlock;b_adr,e_adr:TSrcAdr);
function ExistByRange(b_adr,e_adr:TSrcAdr):Boolean;
function get_level:DWORD;
end;
TsrCodeBlock=class(TsrLabelBlock)
pNext:TsrCodeBlock;
//----
FTop:TsrCFGBlock;
end;
TsrUnknowGoto=class
pPrev,pNext:TsrUnknowGoto;
goto_stmt :TsrStatement;
label_stmt:TsrLabel;
end;
TsrUnknowGotoList=specialize TNodeListClass<TsrUnknowGoto>;
TsrCFGParser=object
FCursor:TsrLCursor;
pCode :TsrCodeBlock;
pBlock :TsrCFGBlock;
FUnknowGotoList:TsrUnknowGotoList;
FSPI:TSPI;
order:Integer;
procedure AddUnknowGoto(goto_stmt,next_stmt,label_stmt:TsrLabel;cond:TsrCondition);
procedure _print_label(Adr:TSrcAdr);
function get_str_prefix(node:TsrCFGBlock;adr:TSrcAdr):RawByteString;
procedure Print();
function Parse():Integer;
function FindUpCond(cond:TsrCondition;Adr:TSrcAdr):TsrCFGBlock;
procedure OpenExec;
procedure CloseExec(Before:Boolean);
function NextParse:Integer;
Procedure Finalize;
function CheckBlockEnd:Boolean;
function emit_SOP1:Boolean;
function emit_SOPP:Boolean;
Function NewBlock:TsrCFGBlock;
Procedure PushBlock(New:TsrCFGBlock);
function PopBlock:Boolean;
function FindUpperLoop(branch_adr:TSrcAdr;var r_child,r_parent:TsrCFGBlock):Integer;
function FindLowerLoop(branch_adr:TSrcAdr;var r_child,r_parent:TsrCFGBlock):Integer;
procedure emit_S_BRANCH(cond:TsrCondition);
procedure ExecPass;
procedure ExecConvert(node:TsrCFGBlock);
procedure ExecDivide(adr:TSrcAdr);
procedure GotoPass;
function BlockOf(stmt:TsrLabel):TsrCFGBlock;
function LevelOf(stmt:TsrLabel):DWORD;
function IsDirectlyRelated(goto_stmt,label_stmt:TsrLabel):Boolean;
function IsIndirectlyRelated(goto_stmt,label_stmt:TsrLabel):Boolean;
function AreSiblings(stmt:TsrCFGBlock;label_stmt:TsrLabel):Boolean;
function AreSiblings(stmt,label_stmt:TsrLabel):Boolean;
function SiblingFromNephew(uncle,nephew:TsrLabel):TsrCFGBlock;
function AreOrdered(left_sibling:TsrCFGBlock;right_sibling:TsrLabel):Boolean;
function AreOrdered(left_sibling,right_sibling:TsrLabel):Boolean;
function NeedsLift(goto_stmt,label_stmt:TsrLabel):Boolean;
procedure RemoveGoto(goto_stmt:TsrStatement;label_stmt:TsrLabel);
procedure EliminateAsConditional(goto_stmt:TsrStatement;label_stmt:TsrLabel);
procedure EliminateAsLoop(goto_stmt:TsrStatement;label_stmt:TsrLabel);
function MoveOutward(goto_stmt:TsrStatement;label_stmt:TsrLabel):TsrStatement;
function MoveOutwardIf(goto_stmt:TsrStatement;label_stmt:TsrLabel):TsrStatement;
function MoveOutwardLoop(goto_stmt:TsrStatement;label_stmt:TsrLabel):TsrStatement;
function MoveInward(goto_stmt:TsrStatement;label_stmt:TsrLabel):TsrStatement;
function Lift(goto_stmt:TsrStatement;label_stmt:TsrLabel):TsrStatement;
end;
function last_block:TsrCFGBlock;
function is_valid(v:TsrCFGBlock):Boolean;
function parse_code_cfg(bType:TsrBlockType;pCode:TsrCodeBlock):Integer;
implementation
uses
srConfig;
function parse_code_cfg(bType:TsrBlockType;pCode:TsrCodeBlock):Integer;
var
LParser:TsrCFGParser;
begin
pCode.FTop.bType:=bType;
LParser:=Default(TsrCFGParser);
LParser.pCode:=pCode;
Result:=LParser.Parse();
if PsrConfig(pCode.FEmit.GetConfig)^.PrintCfg then
begin
LParser.Print();
end;
end;
function TsrCFGBlock.beg_adr:TSrcAdr;
var
node:TsrCFGBlock;
begin
node:=Self;
Result:=Default(TSrcAdr);
//
while (node<>nil) do
begin
if (node.pBLabel=nil) then
begin
if (node.pPrev=nil) then
begin
node:=node.pParent;
end else
begin
node:=node.pPrev;
end;
end else
begin
Exit(node.pBLabel.Adr);
end;
end;
end;
function TsrCFGBlock.end_adr:TSrcAdr;
var
node:TsrCFGBlock;
begin
node:=Self;
Result:=Default(TSrcAdr);
Result.Offdw:=High(PtrUInt) div 4;
//
while (node<>nil) do
begin
if (node.pELabel=nil) then
begin
if (node.pNext=nil) then
begin
node:=node.pParent;
end else
begin
node:=node.pNext;
end;
end else
begin
Exit(node.pELabel.Adr);
end;
end;
end;
function TsrCFGBlock.First:TsrCFGBlock;
begin
Result:=FList.pHead;
end;
function TsrCFGBlock.Last:TsrCFGBlock;
begin
Result:=FList.pTail;
end;
function TsrCFGBlock.pReal:TsrCFGBlock;
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 TsrCFGBlock.IsEndOf(Adr:TSrcAdr):Boolean;
begin
Result:=False;
if (pELabel<>nil) then
begin
Result:=(pELabel.Adr.get_code_ptr<=Adr.get_code_ptr);
end;
end;
function TsrCFGBlock.IsBigOf(Adr:TSrcAdr):Boolean;
begin
Result:=False;
if (pELabel<>nil) then
begin
Result:=(pELabel.Adr.get_code_ptr<Adr.get_code_ptr);
end;
end;
function TsrCFGBlock.IsContain(Adr:TSrcAdr):Boolean;
var
b,e:Boolean;
begin
b:=true;
if (pBLabel<>nil) then
begin
b:=(pBLabel.Adr.get_code_ptr<=Adr.get_code_ptr);
end;
e:=true;
if (pELabel<>nil) then
begin
e:=(pELabel.Adr.get_code_ptr>Adr.get_code_ptr);
end;
Result:=b and e;
end;
function TsrCFGBlock.IsInside(Adr:TSrcAdr):Boolean;
var
b,e:Boolean;
begin
b:=true;
if (pBLabel<>nil) then
begin
b:=(pBLabel.Adr.get_code_ptr<Adr.get_code_ptr);
end;
e:=true;
if (pELabel<>nil) then
begin
e:=(pELabel.Adr.get_code_ptr>Adr.get_code_ptr);
end;
Result:=b and e;
end;
function TsrCFGBlock.FindBlock(Adr:TSrcAdr):TsrCFGBlock;
var
node:TsrCFGBlock;
begin
Result:=nil;
node:=First;
While (node<>nil) do
begin
if node.IsContain(Adr) then Exit(node);
node:=node.pNext;
end;
end;
function TsrCFGBlock.DownBlock(Adr:TSrcAdr):TsrCFGBlock;
var
next:TsrCFGBlock;
begin
Result:=Self;
repeat
next:=Result.FindBlock(Adr);
if (next=nil) then Exit;
Result:=next;
until false;
end;
function TsrCFGBlock.WorkBlock(Adr:TSrcAdr):TsrCFGBlock;
var
next:TsrCFGBlock;
begin
Result:=Self;
repeat
next:=Result.FindBlock(Adr);
if (next=nil) then Exit;
if (next.beg_adr.get_code_ptr=Adr.get_code_ptr) then Exit;
Result:=next;
until false;
end;
function last_block:TsrCFGBlock;
begin
Result:=TsrCFGBlock(Pointer(High(ptruint)));
end;
function is_valid(v:TsrCFGBlock):Boolean;
begin
Result:=(v<>nil) and (v<>last_block);
end;
function TsrCFGBlock.FindNext(Adr:TSrcAdr):TsrCFGBlock;
var
node:TsrCFGBlock;
begin
Result:=nil;
node:=First;
While (node<>nil) do
begin
if (node.beg_adr.get_code_ptr>Adr.get_code_ptr) then
begin
Exit(node);
end;
node:=node.pNext;
end;
Result:=last_block;
end;
function TsrCFGBlock.FindPrev(Adr:TSrcAdr):TsrCFGBlock;
var
node:TsrCFGBlock;
begin
Result:=last_block;
node:=Last;
While (node<>nil) do
begin
if (node.end_adr.get_code_ptr<=Adr.get_code_ptr) then
begin
Exit(node);
end;
node:=node.pPrev;
end;
Result:=nil;
end;
function TsrCFGBlock.UpBlock(Adr:TSrcAdr):TsrCFGBlock;
var
next:TsrCFGBlock;
begin
Result:=Self;
While (Result.IsEndOf(Adr)) do
begin
next:=Result.pParent;
if (next=nil) then Exit;
Result:=next;
end;
end;
function TsrCFGBlock.FindUpLoop:TsrCFGBlock;
var
node:TsrCFGBlock;
begin
Result:=nil;
node:=Self;
While (node<>nil) do
begin
if (node.bType=btLoop) then Exit(node);
node:=node.pParent;
end;
end;
Procedure TsrCFGBlock.Push_front(new:TsrCFGBlock;_first:TsrCFGBlock=nil;_last:TsrCFGBlock=nil);
begin
if (New=nil) then Exit;
new.pParent:=Self;
MoveTo(new,_first,_last);
FList.Push_head(new);
end;
Procedure TsrCFGBlock.Push_back(new:TsrCFGBlock;_first:TsrCFGBlock=nil;_last:TsrCFGBlock=nil);
begin
if (New=nil) then Exit;
new.pParent:=Self;
MoveTo(new,_first,_last);
FList.Push_tail(new);
end;
Procedure TsrCFGBlock.Insert_after(node,new:TsrCFGBlock;_first:TsrCFGBlock=nil;_last:TsrCFGBlock=nil);
begin
if (New=nil) then Exit;
new.pParent:=Self;
MoveTo(new,_first,_last);
//
if (node=nil) then
begin
FList.Push_head(new);
end else
if (node=last_block) then
begin
FList.Push_tail(new);
end else
begin
FList.InsertAfter(node,new);
end;
end;
Procedure TsrCFGBlock.MoveTo(_parent,_first,_last:TsrCFGBlock);
var
next:TsrCFGBlock;
begin
While (_first<>nil) do
begin
Assert(_first.pParent=Self);
next:=_first.pNext;
FList.Remove(_first);
_parent.FList.Push_tail(_first);
_first.pParent:=_parent;
if (_first=_last) then Break;
_first:=next;
end;
end;
Procedure TsrCFGBlock.InsertAfterTo(_parent,_after,_first,_last:TsrCFGBlock);
var
next:TsrCFGBlock;
begin
While (_first<>nil) do
begin
Assert(_first.pParent=Self);
next:=_first.pNext;
FList.Remove(_first);
_parent.FList.InsertAfter(_after,_first);
_after:=_first;
_first.pParent:=_parent;
if (_first=_last) then Break;
_first:=next;
end;
end;
Procedure TsrCFGBlock.MoveFromByRange(_parent:TsrCFGBlock;b_adr,e_adr:TSrcAdr);
var
node:TsrCFGBlock;
next:TsrCFGBlock;
begin
node:=_parent.First;
While (node<>nil) do
begin
next:=node.pNext;
if (node.beg_adr.get_code_ptr>=b_adr.get_code_ptr) then
if (node.end_adr.get_code_ptr<=e_adr.get_code_ptr) then
begin
_parent.FList.Remove(node);
Self.FList.Push_tail(node);
node.pParent:=Self;
end;
node:=next;
end;
end;
function TsrCFGBlock.ExistByRange(b_adr,e_adr:TSrcAdr):Boolean;
var
node:TsrCFGBlock;
begin
Result:=False;
node:=First;
While (node<>nil) do
begin
if (node.beg_adr.get_code_ptr>=b_adr.get_code_ptr) then
if (node.end_adr.get_code_ptr<=e_adr.get_code_ptr) then
begin
Exit(True);
end;
node:=node.pNext;
end;
end;
function TsrCFGBlock.get_level:DWORD;
var
node:TsrCFGBlock;
begin
node:=Self;
Result:=0;
While (node<>nil) do
begin
if isReal(node.bType) then
begin
Inc(Result);
end;
node:=node.pParent;
end;
end;
procedure TsrCFGParser.AddUnknowGoto(goto_stmt,next_stmt,label_stmt:TsrLabel;cond:TsrCondition);
var
pCond:TsrStatement;
pGoto:TsrStatement;
G:TsrUnknowGoto;
begin
goto_stmt.AddType(ltGoto);
pCond:=pCode.NewCond(cond);
pGoto:=pCode.NewGoto(goto_stmt,next_stmt,pCond);
G:=pCode.FEmit.specialize New<TsrUnknowGoto>;
G.goto_stmt :=pGoto;
G.label_stmt:=label_stmt;
FUnknowGotoList.Push_tail(G);
end;
procedure TsrCFGParser._print_label(Adr:TSrcAdr);
var
pLabel:TsrLabel;
begin
pLabel:=pCode.FindLabel(Adr);
if (pLabel<>nil) then
begin
Write('label_',HexStr(Adr.Offdw*4,4),': ;');
if (ltUnknow in pLabel.lType) then Write('ltUnknow ');
if (ltGoto in pLabel.lType) then Write('ltGoto ');
if (ltContinue in pLabel.lType) then Write('ltContinue ');
if (ltBreak in pLabel.lType) then Write('ltBreak ');
writeln;
end;
end;
function Base64(b:Byte):Char;
begin
case (b and 63) of
0..25:Result:=Char(b+Byte('A')-0);
26..51:Result:=Char(b+Byte('a')-26);
52..61:Result:=Char(b+Byte('0')-52);
62:Result:='+';
63:Result:='-';
end;
end;
Function RevOrder(pBlock:TsrCFGBlock):RawByteString;
begin
Result:='';
while (pBlock<>nil) do
begin
if isReal(pBlock.bType) then
begin
Result:=Base64(pBlock.order)+Result;
end;
pBlock:=pBlock.pParent;
end;
Result:=' '+Result+' ';
end;
const
CondStr:array[TsrCondition] of RawByteString=(
'None',
'False',
'True',
'Scc0',
'Scc1',
'Vccz',
'Vccnz',
'Execz',
'Execnz'
);
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;
//sGoto :;
sVar :begin Result:=Result+'V'+IntToStr(node.u.id); Break; end;
//sStore:;
sLoad :begin Result:=Result+'L'+IntToStr(node.u.id); Break; end;
//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 GetStatment(node:TsrStatement):RawByteString;
begin
Result:='';
case node.sType of
//sCond :'
//sGoto :;
//sVar :'
sStore:Result:=GetCondStr(node.pDst)+' = '+GetCondStr(node.pSrc);
sLoad :Result:=GetCondStr(node) +' = '+GetCondStr(node.pSrc);
sBreak:Result:='break';
//sNot:;
//sOr:;
//sAnd:;
else;
end;
end;
procedure PrintStatmentList(const prefix:RawByteString;List:TsrStatementList);
var
node:TsrStatement;
begin
node:=List.pHead;
while (node<>nil) do
begin
case node.sType of
sCond:; //skip
sNot :; //skip
sOr :; //skip
sAnd :; //skip
else
Writeln(prefix,GetStatment(node)+';');
end;
//
node:=node.pNext;
end;
end;
procedure PrintDown(const prefix:RawByteString;node:TsrCFGBlock);
begin
case node.bType of
btCond:Writeln(prefix,'if (',GetCondStr(node.pCond),') {');
btLoop:Writeln(prefix,'do {');
else;
end;
end;
procedure PrintUp(const prefix:RawByteString;node:TsrCFGBlock);
begin
case node.bType of
btCond:begin
if (node.pElse=nil) then
begin
Writeln(prefix,'};');
end else
begin
Writeln(prefix,'} else {');
end;
end;
btElse:Writeln(prefix,'};');
btLoop:begin
if (node.pCond=nil) then
begin
Writeln(prefix,' break;');
Writeln(prefix,'} while (true);');
end else
begin
Writeln(prefix,'} while (',GetCondStr(node.pCond),');');
end;
end;
else;
end;
end;
function TsrCFGParser.get_str_prefix(node:TsrCFGBlock;adr:TSrcAdr):RawByteString;
const
print_block_id=False;
begin
if print_block_id then
begin
Result:=' '+HexStr(adr.Offdw*4,3)+RevOrder(node);
end else
begin
Result:=' '+HexStr(adr.Offdw*4,3)+Space(node.get_level);
end;
end;
procedure TsrCFGParser.Print();
var
prefix:RawByteString;
node,next:TsrCFGBlock;
Adr:TSrcAdr;
i:Integer;
begin
FCursor.Init(TsrLabelBlock(pCode));
pBlock:=pCode.FTop;
prefix:=get_str_prefix(pBlock,FCursor.Adr);
PrintStatmentList(prefix,pBlock.FBefore);
PrintStatmentList(prefix,pBlock.FStart);
repeat
Adr:=FCursor.Adr;
//pBlock:=pBlock.DownBlock(Adr);
node:=pBlock;
repeat
next:=node.FindBlock(Adr);
if (next=nil) then Break;
//
prefix:=get_str_prefix(node,Adr);
PrintStatmentList(prefix,next.FBefore);
PrintDown(prefix,next);
//
prefix:=get_str_prefix(next,Adr);
PrintStatmentList(prefix,next.FStart);
//
node:=next;
until false;
pBlock:=node;
_print_label(Adr);
prefix:=get_str_prefix(pBlock,Adr);
Write(prefix);
FSPI:=Default(TSPI);
i:=FCursor.Next(FSPI);
Case i of
0,1:begin
Case FSPI.CMD.EN of
W_SOP1:if emit_SOP1 then i:=1;
end;
print_spi(FSPI);
Adr:=FCursor.Adr;
//pBlock:=pBlock.UpBlock(Adr);
node:=pBlock;
While (node.IsEndOf(Adr)) do
begin
next:=node.pParent;
if (next=nil) then Break;
//
prefix:=get_str_prefix(node,Adr);
PrintStatmentList(prefix,node.FEnded);
//
prefix:=get_str_prefix(next,Adr);
PrintUp(prefix,node);
PrintStatmentList(prefix,node.FAfter);
//
node:=next;
end;
pBlock:=node;
end;
end;
until (i<>0);
_print_label(Adr);
end;
function TsrCFGParser.Parse():Integer;
begin
if (pCode=nil) then Exit(4);
FCursor.Init(TsrLabelBlock(pCode));
pBlock:=pCode.FTop;
pCode.FTop.pBLabel:=pCode.FetchLabel(FCursor.Adr);
repeat
Result:=NextParse;
Case Result of
0:;
1:Break;
else
Break;
end;
until false;
//
ExecPass;
//
GotoPass;
end;
function GetExecFlow(var FSPI:TSPI):TsrCondition;
begin
Case FSPI.CMD.EN of
W_VOP1 :
Case FSPI.VOP1.OP of
V_NOP :Result:=cNone;
V_READFIRSTLANE_B32:Result:=cFalse;
else
Result:=cTrue;
end;
//W_VOPC -> used separately
W_VOP3 :
Case FSPI.VOP3a.OP of
//VOP3c -> used separately
0..255 :Result:=cFalse;
384+V_NOP :Result:=cNone;
256+V_READLANE_B32 :Result:=cFalse;
256+V_WRITELANE_B32 :Result:=cFalse;
384+V_READFIRSTLANE_B32:Result:=cFalse;
else
Result:=cTrue;
end;
W_DS :
Case FSPI.DS.OP of
DS_NOP:Result:=cNone;
else
Result:=cTrue;
end;
W_MUBUF :Result:=cTrue;
W_MTBUF :Result:=cTrue;
//W_EXP -> used separately
W_MIMG :Result:=cTrue;
W_VOP2 :
Case FSPI.VOP2.OP of
V_READLANE_B32 :Result:=cFalse;
V_WRITELANE_B32:Result:=cFalse;
else
Result:=cTrue;
end;
W_SOPP :
Case FSPI.SOPP.OP of
S_NOP :Result:=cNone;
S_WAITCNT:Result:=cNone;
else
Result:=cFalse;
end;
else
Result:=cFalse;
end;
end;
function TsrCFGParser.FindUpCond(cond:TsrCondition;Adr:TSrcAdr):TsrCFGBlock;
var
node:TsrCFGBlock;
begin
Result:=nil;
node:=pBlock;
While (node<>nil) do
begin
if (node.beg_adr.get_code_ptr<>Adr.get_code_ptr) then
begin
//early exit
Exit(nil);
end;
//
if (node.bType=btCond) then
if (node.pCond<>nil) then
if (node.pCond.sType=sCond) then
if (node.pCond.u.cond=cond) then
begin
Exit(node);
end;
//
node:=node.pParent;
end;
end;
procedure TsrCFGParser.OpenExec;
var
node:TsrCFGBlock;
begin
if (pBlock.bType=btExec) then
begin
//is opened
Exit;
end;
node:=NewBlock;
node.pBLabel:=pCode.FetchLabel(FCursor.prev_adr);
node.bType:=btExec;
PushBlock(node);
if (FindUpCond(cExecnz,FCursor.prev_adr)<>nil) then
begin
//nested cond
node.pCond:=nil;
end else
begin
node.pCond:=pCode.NewCond(cExecnz);
end;
end;
procedure TsrCFGParser.CloseExec(Before:Boolean);
var
node:TsrCFGBlock;
begin
if (pBlock.bType<>btExec) then
begin
//is not opened
Exit;
end;
node:=pBlock;
if Before then
begin
node.pELabel:=pCode.FetchLabel(FCursor.prev_adr);
end else
begin
node.pELabel:=pCode.FetchLabel(FCursor.Adr);
end;
PopBlock;
end;
procedure TsrCFGParser.ExecConvert(node:TsrCFGBlock);
var
parent :TsrCFGBlock;
if_stmt:TsrCFGBlock;
if_merg:TsrCFGBlock;
begin
parent:=node.pParent;
if_stmt:=node;
if (if_stmt.pCond=nil) then
begin
//nested cond
if_stmt.InsertAfterTo(parent,if_stmt,if_stmt.First,nil);
parent.FList.Remove(if_stmt);
Exit;
end;
if_stmt.bType:=btCond;
if_merg:=NewBlock;
if_merg.pBLabel:=if_stmt.pBLabel;
if_merg.pELabel:=if_stmt.pELabel;
if_merg.bType:=btMerg;
parent.Insert_after(if_stmt,if_merg);
parent.MoveTo(if_merg,if_stmt,if_stmt);
end;
procedure TsrCFGParser.ExecDivide(Adr:TSrcAdr);
var
parent:TsrCFGBlock;
node :TsrCFGBlock;
new :TsrCFGBlock;
pLabel:TsrLabel;
begin
node:=pCode.FTop.DownBlock(adr);
if (node<>nil) then
if (node.bType=btExec) then
if (node.beg_adr.get_code_ptr<>Adr.get_code_ptr) and
(node.end_adr.get_code_ptr<>Adr.get_code_ptr) then
begin
parent:=node.pParent;
pLabel:=pCode.FetchLabel(Adr);
//[B..C]
node.pELabel:=pLabel;
//[C..E]
new:=NewBlock;
new.pBLabel:=pLabel;
new.pELabel:=node.pELabel;
new.bType:=btExec;
if (node.pCond<>nil) then
begin
new.pCond:=pCode.NewCond(cExecnz);
end;
parent.Insert_after(node,new);
if (pBlock=node) then
begin
//change current
pBlock:=new;
end;
end;
end;
function flow_down_next_up(node:TsrCFGBlock):TsrCFGBlock;
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 TsrCFGParser.ExecPass;
var
node,prev:TsrCFGBlock;
begin
if (pCode.FTop=nil) then Exit;
node:=pCode.FTop.First;
While (node<>nil) do
begin
//test
Assert(node.beg_adr.get_code_ptr<=node.end_adr.get_code_ptr,'WTF');
Assert(node.pBLabel<>nil,'WTF');
Assert(node.pELabel<>nil,'WTF');
if (node.bType=btExec) then
begin
ExecConvert(node);
end;
prev:=node;
node:=flow_down_next_up(node);
//test
if (node<>nil) then
begin
if (node.pParent<>prev) then
begin
Assert(prev.end_adr.get_code_ptr<=node.beg_adr.get_code_ptr,'WTF');
end;
end;
end;
end;
function TsrCFGParser.NextParse:Integer;
begin
FSPI:=Default(TSPI);
//
Result:=FCursor.Next(FSPI);
//
Case Result of
0,1:;
else Exit;
end;
//
case GetExecFlow(FSPI) of
cFalse:CloseExec(True);
cTrue :OpenExec;
else;
end;
//
Case FSPI.CMD.EN of
W_SOP1:if emit_SOP1 then Result:=1;
W_SOPP:if emit_SOPP then Result:=1;
end;
//
While (CheckBlockEnd) do;
//
if (Result=1) then
begin
Finalize;
end;
end;
Procedure TsrCFGParser.Finalize;
begin
pCode.FTop.pELabel:=pCode.FetchLabel(FCursor.Adr);
pCode.Size:=FCursor.OFFSET_DW*4;
end;
function TsrCFGParser.CheckBlockEnd:Boolean;
var
node:TsrCFGBlock;
begin
Result:=False;
if (pBlock=nil) then Exit;
if (pBlock.pParent=nil) then Exit;
node:=pBlock;
if (node.bType=btExec) then
begin
node:=node.pParent;
end;
Result:=node.IsEndOf(FCursor.Adr);
if Result then
begin
CloseExec(False);
Assert(node.bType<>btExec);
PopBlock;
end;
end;
function TsrCFGParser.emit_SOP1:Boolean;
begin
Result:=False;
Case FSPI.SOP1.OP of
S_SETPC_B64:
if (pBlock.bType=btSetpc) then
begin
Result:=True;
end;
end;
end;
function TsrCFGParser.emit_SOPP:Boolean;
begin
Result:=False;
Case FSPI.SOPP.OP of
S_ENDPGM:;
S_CBRANCH_SCC0 :emit_S_BRANCH(cScc0);
S_CBRANCH_SCC1 :emit_S_BRANCH(cScc1);
S_CBRANCH_VCCZ :emit_S_BRANCH(cVccz);
S_CBRANCH_VCCNZ :emit_S_BRANCH(cVccnz);
S_CBRANCH_EXECZ :emit_S_BRANCH(cExecz);
S_CBRANCH_EXECNZ:emit_S_BRANCH(cExecnz);
S_BRANCH :emit_S_BRANCH(cTrue);
else;
end;
end;
Function TsrCFGParser.NewBlock:TsrCFGBlock;
begin
Inc(order);
Result:=pCode.FEmit.specialize New<TsrCFGBlock>;
Result.order:=order;
end;
Procedure TsrCFGParser.PushBlock(New:TsrCFGBlock);
begin
if (New=nil) then Exit;
Assert(pBlock.bType<>btExec);
pBlock.FList.Push_tail(New);
New.pParent:=pBlock;
pBlock:=New;
end;
function TsrCFGParser.PopBlock:Boolean;
begin
Result:=False;
if (pBlock=nil) then Exit;
if (pBlock.pParent=nil) then Exit;
pBlock:=pBlock.pParent;
Result:=True;
end;
function TsrCFGParser.FindUpperLoop(branch_adr:TSrcAdr;var r_child,r_parent:TsrCFGBlock):Integer;
var
child,parent,next:TsrCFGBlock;
begin
Result:=0;
r_child :=nil;
r_parent:=nil;
child :=pBlock.FList.pTail;
parent:=pBlock;
repeat
if parent.IsInside(branch_adr) then
begin
next:=child;
while (child<>nil) do
begin
if (parent.bType=btLoop) then
if (not child.EndFixed) then
if (child.pCond=nil) then //no post conditions
begin
//is exist loop
if (child.beg_adr.get_code_ptr=branch_adr.get_code_ptr) then
begin
//case 3
r_child :=child;
r_parent:=parent;
Exit(3);
end;
end;
if (child.end_adr.get_code_ptr<=branch_adr.get_code_ptr) then
begin
//restore
child:=next;
Break;
end;
//
next:=child;
child:=child.pPrev;
end;
//restore
child:=next;
if (child<>nil) then
begin
if child.IsInside(branch_adr) then
begin
Exit(0);
end;
if (child.end_adr.get_code_ptr<=branch_adr.get_code_ptr) then
begin
//no child
child:=nil;
end;
end;
//case 2
r_child :=child;
r_parent:=parent;
Exit(2);
end else //IsInside
if (parent.bType=btLoop) then
begin
//is exist loop
if (parent.beg_adr.get_code_ptr=branch_adr.get_code_ptr) then
begin
//case 1
r_parent:=parent;
Exit(1);
end else
begin
Exit(0);
end;
end;
if (parent.beg_adr.get_code_ptr<=branch_adr.get_code_ptr) then Break;
if (parent.pParent=nil) then Break;
child :=parent;
parent:=parent.pParent;
until false;
end;
function TsrCFGParser.FindLowerLoop(branch_adr:TSrcAdr;var r_child,r_parent:TsrCFGBlock):Integer;
var
child,parent:TsrCFGBlock;
begin
Result:=0;
r_child :=nil;
r_parent:=nil;
child :=nil;
parent:=pBlock;
repeat
if parent.IsInside(branch_adr) or
(parent.end_adr.get_code_ptr=branch_adr.get_code_ptr) then
begin
//case 2
r_child :=child;
r_parent:=parent;
Exit(2);
end else
if (parent.bType=btLoop) then
begin
//is exist loop
if (parent.end_adr.get_code_ptr=branch_adr.get_code_ptr) then
begin
//case 1
r_parent:=parent;
Exit(1);
end else
begin
Exit(0);
end;
end;
if (parent.end_adr.get_code_ptr>branch_adr.get_code_ptr) then Break;
if (parent.pParent=nil) then Break;
child :=parent;
parent:=parent.pParent;
until false;
end;
procedure UpdateUp(node:TsrCFGBlock);
var
child:TsrCFGBlock;
updateB,updateE:Boolean;
begin
while (node<>nil) do
begin
case node.bType of
btCond:;
btElse:;
btLoop:;
btMerg:;
else
Break;
end;
updateB:=False;
updateE:=False;
//update begin label
child:=node.First;
if (child<>nil) then
begin
if (node.pBLabel=nil) then
begin
updateB:=True;
end else
if (node.pBLabel.Adr.get_code_ptr>child.pBLabel.Adr.get_code_ptr) then
begin
updateB:=True;
end;
//
if updateB then
begin
node.pBLabel:=child.pBLabel;
end;
end;
//update end label
child:=node.FList.pTail;
if (child<>nil) then
begin
if (node.pELabel=nil) then
begin
updateE:=True;
end else
if (node.pELabel.Adr.get_code_ptr<child.pELabel.Adr.get_code_ptr) then
begin
updateE:=True;
end;
//
if updateE then
begin
node.pELabel:=child.pELabel;
end;
end;
if (updateB=False) and
(updateE=False) then
begin
Break;
end;
//
node:=node.pParent;
end;
end;
procedure TsrCFGParser.emit_S_BRANCH(cond:TsrCondition);
var
pPrev,pNext,pBranch:TsrLabel;
c_adr,b_adr:TSrcAdr;
node,child,parent:TsrCFGBlock;
prevLabel:TsrLabelType;
begin
if (FSPI.SOPP.SIMM=0) then
begin
Exit; //skip
end;
c_adr:=FCursor.Adr;
b_adr:=c_adr;
b_adr.Offdw:=get_branch_offset(FSPI);
pNext :=pCode.FetchLabel(c_adr);
pBranch:=pCode.FetchLabel(b_adr);
//pPrev:=pCode.FetchLabel(FCursor.prev_adr);
ExecDivide(b_adr);
{
//test
begin
pPrev:=pCode.FetchLabel(FCursor.prev_adr);
AddUnknowGoto(pPrev,pNext,pBranch,cond);
Exit;
end;
}
prevLabel:=ltUnknow;
if (SmallInt(FSPI.SOPP.SIMM)<0) then //up
begin
{
[case:1]
while () do<-\
{ |
if (eval) |
{ |
continue---/
}
}
[case:2]
<------------\
|
if (eval) |
{ |
//do |
} |
|
if (eval) |
{ |
continue----/
}
[case:3]
while () do<-\
{ |
//do |
} |
|
if (eval) |
{ |
//do |
} |
|
if (eval) |
{ |
continue----/
}
}
parent:=nil;
child :=nil;
case FindUpperLoop(b_adr,child,parent) of
1:
begin
prevLabel:=ltContinue;
end;
2:
begin
//new loop block
prevLabel:=ltContinue;
pNext.RemType(ltUnknow);
node:=NewBlock;
node.pBLabel:=pBranch;
node.pELabel:=pNext;
node.bType:=btLoop;
parent.Push_back(node,child);
//update end label
UpdateUp(node);
//update block
pBlock:=pCode.FTop.DownBlock(FCursor.prev_adr);
end;
3:
begin
//move to loop block
prevLabel:=ltContinue;
node:=child; //loop
child:=node.pNext;
parent.MoveTo(node,child,nil);
//update end label
UpdateUp(node);
//update block
pBlock:=pCode.FTop.DownBlock(FCursor.prev_adr);
end;
else
begin
//Assert(False,'AddUnknowGoto');
pPrev:=pCode.FetchLabel(FCursor.prev_adr);
AddUnknowGoto(pPrev,pNext,pBranch,cond);
Exit;
end;
end;
end else //down
begin
{
[case:0]
if (eval)
{
//do
}
[case:1]
while () do
{
if (eval)
{
break------\
} |
}<-----------/
[case:2]
if (eval)
{
break-------\
} |
|
<------------/
}
if (pBlock.end_adr.get_code_ptr<b_adr.get_code_ptr) then
begin
//loop end
parent:=nil;
child :=nil;
case FindLowerLoop(b_adr,child,parent) of
1:
begin
prevLabel:=ltBreak;
parent.EndFixed:=True;
end;
2:
begin
{
if (eval)
{
......
branch------\
} else { |
...... |
}<-----------/
}
if (cond=cTrue) and //only unconditional branch
(child.bType=btMerg) and //is merge block
(child=pBlock.pParent) and //is current merge block
(pBlock.end_adr.get_code_ptr=c_adr.get_code_ptr) then //is ended this
begin
//new else block
//save block
child :=pBlock;
parent:=pBlock.pParent;
//{} else {}
//add else block to merge block
node:=NewBlock;
node.pBLabel:=pNext;
node.pELabel:=pBranch;
node.bType:=btElse;
parent.Push_back(node);
//update end label
UpdateUp(parent);
//set current block to else
pBlock:=node;
//set links
child.pElse:=node; //if->else
node .pIf :=child; //else->if
end else
begin
//new loop block
prevLabel:=ltBreak;
node:=NewBlock;
node.pBLabel:=pNext;
node.pELabel:=pBranch;
node.bType:=btLoop;
node.EndFixed:=True;
parent.Push_back(node,child);
//update begin label
UpdateUp(node);
//update block
pBlock:=pCode.FTop.DownBlock(FCursor.prev_adr);
end;
end;
else
begin
//Assert(False,'AddUnknowGoto');
pPrev:=pCode.FetchLabel(FCursor.prev_adr);
AddUnknowGoto(pPrev,pNext,pBranch,cond);
Exit;
end;
end;
end else
begin
//add merge block
node:=NewBlock;
node.pBLabel:=pNext;
node.pELabel:=pBranch;
node.bType:=btMerg;
PushBlock(node);
//if (eval) {}
node:=NewBlock;
node.pBLabel:=pNext;
node.pELabel:=pBranch;
node.bType:=btCond;
PushBlock(node);
//set cond to block
if (cond<>cTrue) then
begin
//The instruction specifies which block to skip, so need to invert the condition!
node.pCond:=pCode.NewCond(InvertCond[cond]);
//mark used
pPrev:=pCode.FetchLabel(FCursor.prev_adr);
pPrev.AddType(ltGoto);
end;
end;
end; //if (SmallInt(FSPI.SOPP.SIMM)<0) then
if (prevLabel<>ltUnknow) then
begin
pPrev:=pCode.FetchLabel(FCursor.prev_adr);
pPrev.AddType(prevLabel);
//add extra block
if (cond<>cTrue) then
begin
//add merge block
node:=NewBlock;
node.pBLabel:=pPrev; //prev_adr
node.pELabel:=pNext; //c_adr
node.bType:=btMerg;
PushBlock(node);
//if (eval) {}
node:=NewBlock;
node.pBLabel:=pPrev;
node.pELabel:=pNext;
node.bType:=btCond;
PushBlock(node);
end;
end;
end;
procedure TsrCFGParser.GotoPass;
var
node:TsrUnknowGoto;
begin
node:=FUnknowGotoList.pHead;
while (node<>nil) do
begin
//Print();
//
RemoveGoto(node.goto_stmt,node.label_stmt);
//
node:=node.pNext;
end;
end;
function TsrCFGParser.BlockOf(stmt:TsrLabel):TsrCFGBlock;
begin
Result:=pCode.FTop.DownBlock(stmt.Adr);
end;
function TsrCFGParser.LevelOf(stmt:TsrLabel):DWORD;
begin
Result:=BlockOf(stmt).get_level;
end;
function TsrCFGParser.IsDirectlyRelated(goto_stmt,label_stmt:TsrLabel):Boolean;
var
min_level :DWORD;
max_level :DWORD;
l :DWORD;
min_up:TsrCFGBlock;
max_up:TsrCFGBlock;
m :TsrCFGBlock;
begin
min_up:=BlockOf(goto_stmt);
max_up:=BlockOf(label_stmt);
min_level:=min_up.get_level;
max_level:=max_up.get_level;
if (max_level < min_level) then
begin
m :=min_up;
min_up:=max_up;
max_up:=m;
//
l :=min_level;
min_level:=max_level;
max_level:=l;
end;
while (max_up<>nil) do
begin
if (min_up=max_up) then
begin
Exit(True);
end;
max_up:=max_up.pParent.pReal;
end;
Result:=False;
end;
function TsrCFGParser.IsIndirectlyRelated(goto_stmt,label_stmt:TsrLabel):Boolean;
begin
Result:=(BlockOf(goto_stmt) <> BlockOf(label_stmt)) and
(not IsDirectlyRelated(goto_stmt,label_stmt));
end;
function TsrCFGParser.AreSiblings(stmt:TsrCFGBlock;label_stmt:TsrLabel):Boolean;
var
pParent1:TsrCFGBlock;
pParent2:TsrCFGBlock;
begin
pParent1:=stmt.pParent.pReal;
pParent2:=BlockOf(label_stmt);
//
Result:=(pParent1=pParent2);
end;
function TsrCFGParser.AreSiblings(stmt,label_stmt:TsrLabel):Boolean;
begin
Result:=BlockOf(stmt)=BlockOf(label_stmt);
end;
function TsrCFGParser.SiblingFromNephew(uncle,nephew:TsrLabel):TsrCFGBlock;
var
parent:TsrCFGBlock;
it_up :TsrCFGBlock;
it :TsrCFGBlock;
begin
parent:=BlockOf(uncle);
it_up :=BlockOf(nephew);
it :=it_up;
while (it_up<>parent) do
begin
it :=it_up;
it_up:=it.pParent.pReal;
end;
Result:=it;
end;
function TsrCFGParser.AreOrdered(left_sibling:TsrCFGBlock;right_sibling:TsrLabel):Boolean;
begin
if AreSiblings(left_sibling,right_sibling) then
begin
Result:=right_sibling.Adr.get_code_ptr>left_sibling.beg_adr.get_code_ptr;
end else
begin
Result:=False;
end;
end;
function TsrCFGParser.AreOrdered(left_sibling,right_sibling:TsrLabel):Boolean;
begin
if AreSiblings(left_sibling,right_sibling) then
begin
Result:=right_sibling.Adr.get_code_ptr>left_sibling.Adr.get_code_ptr;
end else
begin
Result:=False;
end;
end;
function TsrCFGParser.NeedsLift(goto_stmt,label_stmt:TsrLabel):Boolean;
var
sibling:TsrCFGBlock;
begin
sibling:=SiblingFromNephew(goto_stmt,label_stmt);
Result:=AreOrdered(sibling,goto_stmt);
end;
procedure TsrCFGParser.RemoveGoto(goto_stmt:TsrStatement;label_stmt:TsrLabel);
label
_rep;
var
label_level:DWORD;
goto_level :DWORD;
begin
{
Writeln('---');
_print_label(goto_stmt.sLabel.Adr);
_print_label(label_stmt.Adr);
Writeln('---');
}
_rep:
// Force goto_stmt and label_stmt to be directly related
if IsIndirectlyRelated(goto_stmt.sLabel,label_stmt) then
begin
// Move goto_stmt out using outward-movement transformation until it becomes
// directly related to label_stmt
while (not IsDirectlyRelated(goto_stmt.sLabel, label_stmt)) do
begin
goto_stmt:=MoveOutward(goto_stmt,label_stmt);
end;
end;
// Force goto_stmt and label_stmt to be siblings
if IsDirectlyRelated(goto_stmt.sLabel, label_stmt) then
begin
label_level:=LevelOf(label_stmt);
goto_level :=LevelOf(goto_stmt.sLabel);
if (goto_level > label_level) then
begin
// Move goto_stmt out of its level using outward-movement transformations
while (goto_level > label_level) do
begin
goto_stmt:=MoveOutward(goto_stmt,label_stmt);
//Dec(goto_level);
goto_level:=LevelOf(goto_stmt.sLabel);
end;
end else // Level(goto_stmt) < Level(label_stmt)
begin
if (NeedsLift(goto_stmt.sLabel, label_stmt)) then
begin
// Lift goto_stmt to above stmt containing label_stmt using goto-lifting
// transformations
goto_stmt:=Lift(goto_stmt,label_stmt);
//update
label_level:=LevelOf(label_stmt);
goto_level :=LevelOf(goto_stmt.sLabel);
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,label_stmt);
//Inc(goto_level);
goto_level:=LevelOf(goto_stmt.sLabel);
end;
end;
end;
if (not AreSiblings(goto_stmt.sLabel, label_stmt)) then
begin
//Goto is not a sibling with the label
goto _rep;
end;
// goto_stmt and label_stmt are guaranteed to be siblings, eliminate
if (goto_stmt.sNext.Adr.get_code_ptr=label_stmt.Adr.get_code_ptr) then
begin
// Simply eliminate the goto if the label is next to it
end else
if AreOrdered(goto_stmt.sLabel, label_stmt) then
begin
// Eliminate goto_stmt with a conditional
EliminateAsConditional(goto_stmt, label_stmt);
end else
begin
// Eliminate goto_stmt with a loop
EliminateAsLoop(goto_stmt, label_stmt);
end;
end;
procedure TsrCFGParser.EliminateAsConditional(goto_stmt:TsrStatement;label_stmt:TsrLabel);
var
parent :TsrCFGBlock;
cond :TsrStatement;
neg_cond:TsrStatement;
if_stmt :TsrCFGBlock;
if_merg :TsrCFGBlock;
prev :TsrCFGBlock;
begin
parent:=BlockOf(goto_stmt.sLabel);
cond :=goto_stmt.pSrc;
neg_cond:=pCode.NewNot(cond);
if_stmt:=NewBlock;
if_stmt.pBLabel:=goto_stmt.sNext;
if_stmt.pELabel:=label_stmt;
if_stmt.pCond:=neg_cond;
if_stmt.bType:=btCond;
if_stmt.MoveFromByRange(parent,if_stmt.beg_adr,if_stmt.end_adr);
if_merg:=NewBlock;
if_merg.pBLabel:=if_stmt.pBLabel;
if_merg.pELabel:=if_stmt.pELabel;
if_merg.bType:=btMerg;
if_merg.Push_back(if_stmt);
if_merg.FBefore.Push_tail(neg_cond);
prev:=parent.FindPrev(if_merg.beg_adr);
parent.Insert_after(prev,if_merg);
//update labels
UpdateUp(if_stmt);
end;
procedure TsrCFGParser.EliminateAsLoop(goto_stmt:TsrStatement;label_stmt:TsrLabel);
var
parent :TsrCFGBlock;
cond :TsrStatement;
loop_stmt:TsrCFGBlock;
prev :TsrCFGBlock;
begin
parent:=BlockOf(goto_stmt.sLabel);
cond:=goto_stmt.pSrc;
loop_stmt:=NewBlock;
loop_stmt.pBLabel:=label_stmt;
loop_stmt.pELabel:=goto_stmt.sLabel;
loop_stmt.pCond:=cond;
loop_stmt.bType:=btLoop;
loop_stmt.MoveFromByRange(parent,loop_stmt.beg_adr,loop_stmt.end_adr);
prev:=parent.FindPrev(loop_stmt.beg_adr);
parent.Insert_after(prev,loop_stmt);
//update labels
UpdateUp(loop_stmt);
end;
function TsrCFGParser.MoveOutward(goto_stmt:TsrStatement;label_stmt:TsrLabel):TsrStatement;
var
parent:TsrCFGBlock;
begin
if (goto_stmt.sNext.Adr.get_code_ptr=label_stmt.Adr.get_code_ptr) then
begin
Exit(goto_stmt);
end;
parent:=BlockOf(goto_stmt.sLabel);
case parent.bType of
btCond:Result:=MoveOutwardIf (goto_stmt,label_stmt);
btElse:Result:=MoveOutwardIf (goto_stmt,label_stmt);
btLoop:Result:=MoveOutwardLoop(goto_stmt,label_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;
}
//-->
{
....
goto_L1 = false;
if (expr)
{
stmt_1;
....
goto_L1 = cond;
if (!cond)
{
stmt_2;
....
stmt_i;
}
}
if (goto_L1) goto L1;
....
L1:
stmt_n;
}
function TsrCFGParser.MoveOutwardIf(goto_stmt:TsrStatement;label_stmt:TsrLabel):TsrStatement;
var
parent :TsrCFGBlock;
pmerge :TsrCFGBlock;
prev :TsrCFGBlock;
new_var :TsrStatement;
set_var :TsrStatement;
cond :TsrStatement;
neg_cond:TsrStatement;
new_cond:TsrStatement;
new_goto:TsrStatement;
if_stmt :TsrCFGBlock;
if_merg :TsrCFGBlock;
begin
parent:=BlockOf(goto_stmt.sLabel);
pmerge:=parent.pParent;
Assert(pmerge<>nil);
Assert(pmerge.bType=btMerg);
cond:=goto_stmt.pSrc;
new_var:=pCode.NewVar;
//goto_L1 = false;
set_var:=pCode.NewStore(new_var,pCode.NewCond(cFalse));
pmerge.FBefore.Push_tail(set_var);
//goto_L1 = cond;
set_var:=pCode.NewStore(new_var,cond);
if (goto_stmt.sNext.Adr.get_code_ptr=parent.pELabel.Adr.get_code_ptr) then
begin
//empty body
//goto_L1 = cond;
parent.FEnded.Push_tail(set_var);
end else
begin
neg_cond:=pCode.NewNot(cond);
if_stmt:=NewBlock;
if_stmt.pBLabel:=goto_stmt.sNext;
if_stmt.pELabel:=parent.pELabel;
if_stmt.pCond:=neg_cond;
if_stmt.bType:=btCond;
if_stmt.MoveFromByRange(parent,if_stmt.beg_adr,if_stmt.end_adr);
if_merg:=NewBlock;
if_merg.pBLabel:=if_stmt.pBLabel;
if_merg.pELabel:=if_stmt.pELabel;
if_merg.bType:=btMerg;
if_merg.Push_back(if_stmt);
if_merg.FBefore.Push_tail(neg_cond);
//goto_L1 = cond;
if_merg.FBefore.Push_tail(set_var);
prev:=parent.FindPrev(if_merg.beg_adr);
parent.Insert_after(prev,if_merg);
//update labels
UpdateUp(if_stmt);
end;
new_cond:=pCode.NewLoad(new_var);
pmerge.FAfter.Push_tail(new_cond);
new_goto:=pCode.NewGoto(parent.pELabel,parent.pELabel,new_cond);
Result:=new_goto;
end;
{
....
do {
stmt_1;
....
if (cond) goto L1;
....
stmt_i;
} while (expr)
....
L1:
stmt_n;
}
//-->
{
....
goto_L1 = false;
do {
stmt_1;
....
goto_L1 = cond;
if (!cond)
{
stmt_2;
....
stmt_i;
....
break;
}
} while (expr)
if (goto_L1) goto L1;
....
L1:
stmt_n;
}
function TsrCFGParser.MoveOutwardLoop(goto_stmt:TsrStatement;label_stmt:TsrLabel):TsrStatement;
var
parent :TsrCFGBlock;
prev :TsrCFGBlock;
new_var :TsrStatement;
set_var :TsrStatement;
cond :TsrStatement;
neg_cond :TsrStatement;
new_cond :TsrStatement;
new_break:TsrStatement;
new_goto :TsrStatement;
if_stmt :TsrCFGBlock;
if_merg :TsrCFGBlock;
begin
parent:=BlockOf(goto_stmt.sLabel);
cond:=goto_stmt.pSrc;
new_var:=pCode.NewVar;
//goto_L1 = false;
set_var:=pCode.NewStore(new_var,pCode.NewCond(cFalse));
parent.FBefore.Push_tail(set_var);
//goto_L1 = cond;
set_var:=pCode.NewStore(new_var,cond);
if (goto_stmt.sNext.Adr.get_code_ptr=parent.pELabel.Adr.get_code_ptr) then
begin
//empty body
//goto_L1 = cond;
parent.FEnded.Push_tail(set_var);
end else
begin
neg_cond:=pCode.NewNot(cond);
if_stmt:=NewBlock;
if_stmt.pBLabel:=goto_stmt.sNext;
if_stmt.pELabel:=parent.pELabel;
if_stmt.pCond:=neg_cond;
if_stmt.bType:=btCond;
if_stmt.MoveFromByRange(parent,if_stmt.beg_adr,if_stmt.end_adr);
if_merg:=NewBlock;
if_merg.pBLabel:=if_stmt.pBLabel;
if_merg.pELabel:=if_stmt.pELabel;
if_merg.bType:=btMerg;
if_merg.Push_back(if_stmt);
if_merg.FBefore.Push_tail(neg_cond);
//goto_L1 = cond;
if_merg.FBefore.Push_tail(set_var);
prev:=parent.FindPrev(if_merg.beg_adr);
parent.Insert_after(prev,if_merg);
//update labels
UpdateUp(if_stmt);
end;
//break;
new_break:=pCode.NewBreak(parent.pELabel);
parent.FEnded.Push_tail(new_break);
new_cond:=pCode.NewLoad(new_var);
parent.FAfter.Push_tail(new_cond);
new_goto:=pCode.NewGoto(parent.pELabel,parent.pELabel,new_cond);
Result:=new_goto;
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 (!cond && 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 {
goto_L2 = goto_L1;
goto_L1 = false;
if (goto_L2) goto L1;
stmt_j;
...
L1:
...
stmt_n;
} while (expr)
}
function TsrCFGParser.MoveInward(goto_stmt:TsrStatement;label_stmt:TsrLabel):TsrStatement;
var
parent :TsrCFGBlock;
prev :TsrCFGBlock;
nested_stmt:TsrCFGBlock;
nested_real:TsrCFGBlock;
new_var :TsrStatement;
set_var :TsrStatement;
cond :TsrStatement;
neg_cond :TsrStatement;
new_op :TsrStatement;
new_goto :TsrStatement;
if_stmt :TsrCFGBlock;
if_merg :TsrCFGBlock;
begin
parent:=BlockOf(goto_stmt.sLabel);
nested_stmt:=SiblingFromNephew(goto_stmt.sLabel,label_stmt);
nested_real:=nested_stmt.pReal;
new_var:=pCode.NewVar;
cond :=goto_stmt.pSrc;
//goto_L1 = cond;
set_var:=pCode.NewStore(new_var,cond);
if (goto_stmt.sNext.Adr.get_code_ptr=parent.pELabel.Adr.get_code_ptr) then
begin
//empty body
nested_real.FBefore.Push_tail(set_var);
end else
begin
neg_cond:=pCode.NewNot(cond);
if_stmt:=NewBlock;
if_stmt.pBLabel:=goto_stmt.sNext;
if_stmt.pELabel:=nested_stmt.pBLabel;
if_stmt.pCond:=neg_cond;
if_stmt.bType:=btCond;
if_stmt.MoveFromByRange(parent,if_stmt.beg_adr,if_stmt.end_adr);
if_merg:=NewBlock;
if_merg.pBLabel:=if_stmt.pBLabel;
if_merg.pELabel:=if_stmt.pELabel;
if_merg.bType:=btMerg;
if_merg.Push_back(if_stmt);
if_merg.FBefore.Push_tail(set_var);
if_merg.FBefore.Push_tail(neg_cond);
prev:=parent.FindPrev(if_merg.beg_adr);
parent.Insert_after(prev,if_merg);
//update labels
UpdateUp(if_stmt);
end;
case nested_stmt.bType of
btCond:
begin
//load before
cond:=pCode.NewLoad(new_var);
//(goto_L1 || expr)
Assert(nested_stmt.pCond<>nil);
new_op:=pCode.NewOr(nested_stmt.pCond,cond);
//
nested_real.FBefore.Push_tail(cond);
nested_real.FBefore.Push_tail(new_op);
// Update nested if condition
nested_stmt.pCond:=new_op;
end;
btElse:
begin
if_stmt:=nested_stmt.pIf;
Assert(if_stmt<>nil);
//load before
cond:=pCode.NewLoad(new_var);
//!cond
neg_cond:=pCode.NewNot(cond);
//(!cond && expr)
Assert(nested_stmt.pCond<>nil);
new_op:=pCode.NewAnd(nested_stmt.pCond,cond);
//
nested_real.FBefore.Push_tail(cond);
nested_real.FBefore.Push_tail(neg_cond);
nested_real.FBefore.Push_tail(new_op);
// Update nested if condition
nested_stmt.pCond:=new_op;
end;
btLoop:
begin
//goto_L2 = goto_L1;
cond:=pCode.NewLoad(new_var);
nested_stmt.FStart.Push_tail(cond);
//goto_L1 = false;
set_var:=pCode.NewStore(new_var,pCode.NewCond(cFalse));
nested_stmt.FStart.Push_tail(set_var);
end;
else
begin
Writeln(stderr,'Invalid inward movement:',nested_stmt.bType);
Assert(false,'Invalid inward movement');
end;
end;
new_goto:=pCode.NewGoto(nested_stmt.pBLabel,nested_stmt.pBLabel,cond);
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 TsrCFGParser.Lift(goto_stmt:TsrStatement;label_stmt:TsrLabel):TsrStatement;
var
parent :TsrCFGBlock;
prev :TsrCFGBlock;
nested_stmt:TsrCFGBlock;
new_var :TsrStatement;
cond :TsrStatement;
new_goto :TsrStatement;
set_var :TsrStatement;
loop_stmt :TsrCFGBlock;
begin
parent:=BlockOf(goto_stmt.sLabel);
nested_stmt:=SiblingFromNephew(goto_stmt.sLabel,label_stmt);
new_var:=pCode.NewVar;
cond :=goto_stmt.pSrc;
loop_stmt:=NewBlock;
loop_stmt.pBLabel:=nested_stmt.pBLabel;
loop_stmt.pELabel:=goto_stmt.sLabel;
loop_stmt.pCond:=cond;
loop_stmt.bType:=btLoop;
loop_stmt.MoveFromByRange(parent,loop_stmt.beg_adr,loop_stmt.end_adr);
//TODO: SanitizeNoBreaks(loop_stmt);
prev:=parent.FindPrev(loop_stmt.beg_adr);
parent.Insert_after(prev,loop_stmt);
//update labels
UpdateUp(loop_stmt);
cond :=pCode.NewLoad(new_var);
loop_stmt.FStart.Push_tail(cond);
new_goto:=pCode.NewGoto(label_stmt,label_stmt,cond);
// goto_L1 = cond;
cond :=goto_stmt.pSrc;
set_var:=pCode.NewStore(new_var,cond);
loop_stmt.FEnded.Push_tail(set_var);
//goto_L1 = false;
set_var:=pCode.NewStore(new_var,pCode.NewCond(cFalse));
loop_stmt.FBefore.Push_tail(set_var);
Result:=new_goto;
end;
end.