unit srPrivate;

{$mode ObjFPC}{$H+}

interface

uses
 sysutils,
 spirv,
 ginodes,
 srNode,
 srType,
 srTypes,
 srLayout,
 srOp,
 srOpUtils,
 srReg,
 srVariable,
 srConst,
 srBitcast,
 srCFGParser;

type
 TsrPrivate=class;

 TStoreNode=class(TsrNode)
  pPrev,pNext:TStoreNode;
  //
  src :TsrRegNode;
  //line:TspirvOp;
  //
 end;
 TStoreNodeList=specialize TNodeListClass<TStoreNode>;

 TsrVolatile=class(TsrNode)
  var
   pPrev,pNext:TsrVolatile;
   FPrivate :TsrPrivate;
   FSlot    :PsrRegSlot;
   FList    :TStoreNodeList;
   FBase    :TsrRegNode;
   FZeroRead:Boolean;
   ForceBool:Boolean;
  //
  Procedure _zero_read;                    override;
  Procedure _zero_unread;                  override;
  Procedure _PrepType(node:PPrepTypeNode); override;
  //
  function  ListCount:Integer;
  Procedure AddStore(src:TsrRegNode);
  Procedure PushStore(node:TStoreNode);
  Function  PopStore:TStoreNode;
  Procedure RemoveAllStore;
  procedure PrepType(new:TsrDataType);
 end;

 ntVolatile=TsrVolatile;

 TsrPrivate=class(TsrDescriptor)
  public
   pPrev,pNext :TsrPrivate;
   pLeft,pRight:TsrPrivate;
   class function c(n1,n2:PPsrRegSlot):Integer; static;
  private
   //
   FPrivId:Integer;
   key:PsrRegSlot;
   //
   FLineList:TDependenceNodeList;
   //
   Procedure SetRegType(rtype:TsrDataType);
   function  GetRegType:TsrDataType;
  public
   //
   Procedure _PrepType(node:PPrepTypeNode); override;
   function  _GetStorageName:RawByteString; override;
   //
   property  dtype:TsrDataType read GetRegType write SetRegType;
   property  Source:PsrRegSlot read key;
   Procedure Init; inline;
   function  GetStorageName:RawByteString;
   function  adjust_type(new:TsrDataType):TsrDataType;
   Procedure UpdateRegType;
   Procedure PrepType(new:TsrDataType);
   Procedure SortLines;
   Procedure Optimize;
   procedure AddLine(pLine:TspirvOp);
   procedure AddLine(pLine:TspirvOp;node:TDependenceNode);
   procedure FetchLoad (pLine:TspirvOp;dst:TsrRegNode);
   Procedure FetchStore(pLine:TspirvOp;src:TsrRegNode);
 end;

 ntPrivate=TsrPrivate;

 PsrPrivateList=^TsrPrivateList;
 TsrPrivateList=object
  type
   TPrivList=specialize TNodeListClass<TsrPrivate>;
   TVoltList=specialize TNodeListClass<TsrVolatile>;
  var
   FEmit:TCustomEmit;
   FPrivList:TPrivList;
   FVoltList:TVoltList;
   FPrivId:Integer;
  procedure Init(Emit:TCustomEmit); inline;
  function  NewVolatile(Slot:PsrRegSlot):TsrVolatile;
  function  Fetch(pSource:PsrRegSlot):TsrPrivate;
  function  FetchCustom(dtype:TsrDataType):TsrPrivate;
  function  FetchArray(dtype:TsrDataType;array_count:DWORD):TsrPrivate;
  function  First:TsrPrivate; inline;
  //
  procedure build_slot_reset(pSlot:PsrRegSlot;var orig:TsrRegNode);
  procedure build_slot_ctrue(var ctx:TsrVolatileContext;pSlot:PsrRegSlot;var orig,prev,_next:TsrRegNode);
  procedure build_slot_endif(var ctx:TsrVolatileContext;pSlot:PsrRegSlot;var orig,prev,_next:TsrRegNode);
  procedure build_slot_break(var ctx:TsrVolatileContext;pSlot:PsrRegSlot;var orig,prev,_next:TsrRegNode);
  procedure build_slot_conti(var ctx:TsrVolatileContext;pSlot:PsrRegSlot;var orig,prev,_next:TsrRegNode);
  procedure build_volatile_reset(orig:PsrRegsSnapshot);
  procedure build_volatile_ctrue(var ctx:TsrVolatileContext;orig,prev,_next:PsrRegsSnapshot);
  procedure build_volatile_endif(var ctx:TsrVolatileContext;orig,prev,_next:PsrRegsSnapshot);
  procedure build_volatile_break(var ctx:TsrVolatileContext;orig,prev,_next:PsrRegsSnapshot);
  procedure build_volatile_conti(var ctx:TsrVolatileContext;orig,prev,_next:PsrRegsSnapshot);
  procedure make_copy_slot(pSlot:PsrRegSlot);
  procedure make_copy_all;
  function  PrepVolatile(dst:TspirvOp;src:TsrRegNode):TsrRegNode;
  Procedure RemoveAllStore;
  Procedure Post;
 end;

implementation

Procedure TsrVolatile._zero_read;
var
 node:TStoreNode;
begin
 if FZeroRead then Exit;
 //
 node:=FList.pHead;
 While (node<>nil) do
 begin
  node.src.mark_read(Self);
  node:=node.pNext;
 end;
 //
 FZeroRead:=True;
end;

Procedure TsrVolatile._zero_unread;
var
 node:TStoreNode;
begin
 if not FZeroRead then Exit;
 //
 node:=FList.pHead;
 While (node<>nil) do
 begin
  node.src.mark_unread(Self);
  node:=node.pNext;
 end;
 //
 FZeroRead:=False;
end;

Procedure TsrVolatile._PrepType(node:PPrepTypeNode);
begin
 PrepType(TsrDataType(node^.rtype));
 node^.dnode:=nil;
end;

//

procedure TsrVolatile.PrepType(new:TsrDataType);
var
 node:TStoreNode;
begin
 new:=FPrivate.adjust_type(new);
 if (new=dtUnknow) then exit;

 node:=FList.pHead;
 While (node<>nil) do
 begin

  if node.src.pWriter.IsType(TsrVolatile) then Break;

  node.src.PrepType(ord(new));
  node:=node.pNext;
 end;
end;

function TsrVolatile.ListCount:Integer;
var
 node:TStoreNode;
begin
 Result:=0;
 //
 node:=FList.pHead;
 While (node<>nil) do
 begin
  Inc(Result);
  //
  node:=node.pNext;
 end;
end;

Procedure TsrVolatile.AddStore(src:TsrRegNode);
var
 node:TStoreNode;
 pLine:TspirvOp;
begin
 if (src=nil) then Exit;

 if (src.IsType(TsrVectorArray)) then
 begin
  Assert(false,'AddStore:TsrVectorArray');
 end;

 Assert(src.pLine<>nil);
 pLine:=src.pLine;
 Assert(pLine<>nil);
 node:=Emit.specialize New<TStoreNode>; //cache in free list?
 node.src :=src;
 //node.line:=pLine;
 if FZeroRead then
 begin
  src.mark_read(Self);
 end;
 FList.Push_head(node);
end;

Procedure TsrVolatile.PushStore(node:TStoreNode);
begin
 if (node=nil) then Exit;
 //Assert(node.src.pLine<>nil);
 //Assert(node.line<>nil);
 if FZeroRead then
 begin
  node.src.mark_read(Self);
 end;
 FList.Push_head(node);
end;

Function TsrVolatile.PopStore:TStoreNode;
begin
 Result:=FList.Pop_head;

 if (Result<>nil) then
 if FZeroRead then
 begin
  Result.src.mark_unread(Self);
 end;
end;

Procedure TsrVolatile.RemoveAllStore;
var
 node:TStoreNode;
begin
 repeat
  node:=PopStore;
 until (node=nil);
end;

//

Procedure TsrPrivate._PrepType(node:PPrepTypeNode);
begin
 PrepType(TsrDataType(node^.rtype));
 node^.dnode:=nil;
end;

function TsrPrivate._GetStorageName:RawByteString;
begin
 Result:=GetStorageName;
end;

//

class function TsrPrivate.c(n1,n2:PPsrRegSlot):Integer;
begin
 Result:=ord(n1^>n2^)-ord(n1^<n2^);
end;

Procedure TsrPrivate.Init; inline;
begin
 FStorage:=StorageClass.Private_;
 FBinding:=-1;
end;

function TsrPrivate.GetStorageName:RawByteString;
begin
 if (Source=nil) then
 begin
  Result:='h'+IntToStr(FPrivId);
 end else
 begin
  Result:='v'+Source^.Name+'_'+IntToStr(FPrivId);
 end;
end;

function TsrPrivate.adjust_type(new:TsrDataType):TsrDataType;
begin
 if (Self=nil) then Exit(new);

 if (Source<>nil) then
 begin
  if (Source^.isBoolOnly) then
  begin
   Exit(dtBool);
  end;
 end;

 if (FType<>nil) then
 begin
  Exit(FType.dtype);
 end;

 Result:=new;
end;

Procedure TsrPrivate.SetRegType(rtype:TsrDataType);
var
 pTypeList:PsrTypeList;
begin
 pTypeList:=Emit.GetTypeList;
 Ftype:=pTypeList^.Fetch(rtype);

 UpdateRegType;
end;

function TsrPrivate.GetRegType:TsrDataType;
begin
 Result:=FType.dtype;
end;

Procedure TsrPrivate.UpdateRegType;
var
 pBitcastList:PsrBitcastList;
 node:TDependenceNode;
 pLine:TspirvOp;
 Value:TsrNode;
 dst:TsrRegNode;
 old,rtype:TsrDataType;
 ConstList:PsrConstList;
 cst:TsrConst;
begin
 rtype:=FType.dtype;

 pBitcastList:=Emit.GetBitcastList;

 node:=FLineList.pHead;
 While (node<>nil) do
 begin
  pLine:=node.pNode;

  Case pLine.OpId of

   Op.OpLoad:
    begin
     Value:=pLine.pDst;
     Value.PrepType(ord(rtype));

     pLine.pType:=Ftype;

     dst:=Value.specialize AsType<ntReg>;
     if (dst<>nil) then
     begin
      old:=dst.dtype;
      if (old<>dtUnknow) and (rtype<>old) then
      begin
       //OpLoad -> new -> dst
       dst:=pBitcastList^.FetchDstr(rtype,dst);
       pLine.pDst:=dst;
      end;
     end;
    end;

   Op.OpStore:
    begin
     Value:=pLine.ParamNode(1).Value;
     Value.PrepType(ord(rtype));

     dst:=Value.specialize AsType<ntReg>;
     if (dst<>nil) then
     begin
      //
      if dst.is_const then
      begin
       cst:=dst.pWriter;
       if (dst.dtype<>cst.dtype) then
       begin
        ConstList:=Emit.GetConstList;
        //
        cst:=ConstList^.Bitcast(dst.dtype,cst);
        dst.pWriter:=cst;
       end;
      end;
      //
      old:=dst.dtype;
      if (old<>dtUnknow) and (rtype<>old) then
      begin
       //OpStore <- new <- dst
       dst:=pBitcastList^.FetchRead(rtype,dst);
       pLine.ParamNode(1).Value:=dst;
      end;
     end;


    end;

   else;
  end;

  node:=node.pNext;
 end;
end;

Procedure TsrPrivate.PrepType(new:TsrDataType);
var
 old:TsrDataType;
begin
 new:=adjust_type(new);
 if (new=dtUnknow) then exit;

 old:=GetRegType;
 if is_unprep_type(old,new,True) then
 begin
  SetRegType(new);
 end;
end;


{
procedure _update_store_line(pLine:TspirvOp);
var
 pReg:TsrRegNode;
 pCur:TspirvOp;
begin
 if (pLine.OpId<>Op.OpStore) then Exit;
 pReg:=RegDown(pLine.ParamNode(1).Value.specialize AsType<ntReg>);
 if (pReg=nil) then Exit;
 pCur:=pReg.pLine;

 if (pLine<>pCur) and (MaxLine(pLine,pCur)=pCur) then //pCur>pLine
 begin
  pLine.Remove;
  pCur.InsertAfter(pLine);
 end;
end;
}

Procedure TsrPrivate.SortLines;
var
 dnode,dnext:TDependenceNode;
 //pLine:array[0..1] of TspirvOp;
 nswp:Boolean;
begin
 //indexing
 dnode:=FLineList.pHead;
 While (dnode<>nil) do
 begin
  dnode.fread_count:=GetGlobalIndex(dnode.pNode);
  dnode:=dnode.pNext;
 end;
 //bubble sort
 repeat
  nswp:=True;
  dnode:=FLineList.pHead;
  While (dnode<>nil) do
  begin
   dnext:=dnode.pNext;
   if (dnext=nil) then Break;

   //pLine[0]:=dnode.pNode;
   //pLine[1]:=dnext.pNode;

   //_update_store_line(pLine[0]);
   //_update_store_line(pLine[1]);

   //if (MaxLine(pLine[0],pLine[1])=pLine[0]) then //dnode>dnext
   if (dnode.fread_count>dnext.fread_count) then
   begin
    //swap
    nswp:=False;
    FLineList.Remove(dnode);
    FLineList.InsertAfter(dnext,dnode);
   end else
   begin
    dnode:=dnext;
   end;

  end;
 until nswp;
end;

Function FindByHalfSpace(node,prev:TspirvOp):Boolean;
begin
 Result:=False;
 while (node<>nil) do
 begin
  if (node=prev) then Exit(True);
  //
  if node.IsType(ntOpBlock) then
  if IsReal(TsrOpBlock(node).bType) then
  begin
   Exit(False);
  end;
  //
  node:=flow_down_prev_up(node);
 end;
end;

Procedure TsrPrivate.Optimize;
var
 dnode,dprev:TDependenceNode;
 pLine:array[0..1] of TspirvOp;
 pRegs:array[0..1] of TsrRegNode;
begin
 //exit;

 dnode:=FLineList.pTail;
 While (dnode<>nil) do
 begin
  dprev:=dnode.pPrev;
  if (dprev=nil) then Break;

  pLine[0]:=dnode.pNode;
  pLine[1]:=dprev.pNode;

  if (pLine[0].Parent=pLine[1].Parent) then
  if FindByHalfSpace(pLine[0],pLine[1]) then
  begin
   //OpStore %v ; pLine[1] ; (dprev)
   //OpStore %v ; pLine[0] ; (dnode)
   if (pLine[0].OpId=Op.OpStore) and (pLine[1].OpId=Op.OpStore) then
   begin
    //Remove dprev
    FLineList.Remove(dprev);
    pLine[1].mark([soNotUsed]);
    Continue;
   end else
   if (pLine[0].OpId=Op.OpStore) and (pLine[1].OpId=Op.OpLoad) then
   begin
    //%r = OpLoad %type %v ; pLine[1] ; (dprev)
    //OpStore %v %r        ; pLine[0] ; (dnode)

    pRegs[0]:=RegDown(pLine[0].ParamNode(1).Value.specialize AsType<ntReg>);
    pRegs[1]:=RegDown(pLine[1].pDst.specialize AsType<ntReg>);

    if (pRegs[0]<>nil) and (pRegs[1]<>nil) and CompareReg(pRegs[0],pRegs[1]) then
    begin
     //Remove dnode
     FLineList.Remove(dnode);
     pLine[0].mark([soNotUsed]);

     dnode:=dprev;
     if (dnode.pNext<>nil) then
     begin
      dnode:=dnode.pNext;
     end;

     Continue;
    end;
   end else
   if (pLine[0].OpId=Op.OpLoad) and (pLine[1].OpId=Op.OpLoad) then
   begin
    //%r1 = OpLoad %type %v ; pLine[1] ; (dprev)
    //%r2 = OpLoad %type %v ; pLine[0] ; (dnode)

    pRegs[0]:=pLine[0].pDst.specialize AsType<ntReg>;
    pRegs[1]:=pLine[1].pDst.specialize AsType<ntReg>;

    if (pRegs[0]<>nil) and (pRegs[1]<>nil) then
    begin
     pRegs[0].pWriter:=pRegs[1];

     //Remove dnode
     FLineList.Remove(dnode);

     dnode:=dprev;
     if (dnode.pNext<>nil) then
     begin
      dnode:=dnode.pNext;
     end;

     Continue;
    end;

   end;

  end;

  dnode:=dprev;
 end;
end;

procedure TsrPrivate.AddLine(pLine:TspirvOp);
var
 node:TDependenceNode;
begin
 node:=NewDependence;
 node.pNode:=pLine;
 FLineList.Push_tail(node);
end;

procedure TsrPrivate.AddLine(pLine:TspirvOp;node:TDependenceNode);
begin
 //hack reuse
 node.pNode:=pLine;
 FLineList.Push_tail(node);
end;

procedure TsrPrivate.FetchLoad(pLine:TspirvOp;dst:TsrRegNode);
begin
 Assert(dst<>nil);

 pLine:=Emit.OpLoad(pLine,FType,dst,FVar);

 AddLine(pLine);
end;

Procedure TsrPrivate.FetchStore(pLine:TspirvOp;src:TsrRegNode);
begin
 if (src=nil) then Exit;

 pLine:=Emit.OpStore(pLine,FVar,src);

 AddLine(pLine);
end;

//

procedure TsrPrivateList.Init(Emit:TCustomEmit); inline;
begin
 FEmit:=Emit;
end;

function TsrPrivateList.NewVolatile(Slot:PsrRegSlot):TsrVolatile;
begin
 Result:=FEmit.specialize New<TsrVolatile>;
 Result.FSlot:=Slot;
 FVoltList.Push_tail(Result);
end;

function TsrPrivateList.Fetch(pSource:PsrRegSlot):TsrPrivate;
begin
 Assert(pSource<>nil);
 //
 Result:=FEmit.specialize New<TsrPrivate>;
 Result.Init;
 Result.FPrivId:=FPrivId; Inc(FPrivId);
 Result.key:=pSource;
 //
 FPrivList.Push_tail(Result);
end;

function TsrPrivateList.FetchCustom(dtype:TsrDataType):TsrPrivate;
var
 pTypeList:PsrTypeList;
begin
 Result:=FEmit.specialize New<TsrPrivate>;
 Result.Init;
 Result.FPrivId:=FPrivId; Inc(FPrivId);
 //
 if (dtype<>dtUnknow) then
 begin
  pTypeList:=FEmit.GetTypeList;
  Result.Ftype:=pTypeList^.Fetch(dtype);
 end;
 //
 FPrivList.Push_tail(Result);
end;

function TsrPrivateList.FetchArray(dtype:TsrDataType;array_count:DWORD):TsrPrivate;
var
 pTypeList:PsrTypeList;
begin
 Result:=FEmit.specialize New<TsrPrivate>;
 Result.Init;
 Result.InitVar; ////
 Result.FPrivId:=FPrivId; Inc(FPrivId);
 //
 if (dtype<>dtUnknow) then
 begin
  pTypeList:=FEmit.GetTypeList;
  Result.Ftype:=pTypeList^.Fetch(dtype);
  Result.Ftype:=pTypeList^.FetchArray(Result.Ftype,array_count);
 end;
 //
 FPrivList.Push_tail(Result);
end;

Function TsrPrivateList.First:TsrPrivate;
begin
 Result:=FPrivList.pHead;
end;

procedure TsrPrivateList.build_slot_reset(pSlot:PsrRegSlot;var orig:TsrRegNode);
begin
 pSlot^.current:=orig;
end;

procedure TsrPrivateList.build_slot_ctrue(var ctx:TsrVolatileContext;pSlot:PsrRegSlot;var orig,prev,_next:TsrRegNode);
begin
 //restore
 if (_next<>nil) then
 if (RegDownSlot(_next)<>RegDownSlot(prev)) then
 if _next.pWriter.IsType(ntVolatile) then
 begin
  _next.pWriter:=prev;
 end else
 begin
  Assert(false,'build_slot_ctrue');
 end;
end;

procedure TsrPrivateList.build_slot_endif(var ctx:TsrVolatileContext;pSlot:PsrRegSlot;var orig,prev,_next:TsrRegNode);
var
 pLine    :TSpirvOp;
 org,prv  :TsrRegNode;
 cur      :TsrRegNode;
 pVolatile:TsrVolatile;
 rtype    :TsrDataType;
 cur_c_org:Boolean;
 cur_c_prv:Boolean;
begin
 org:=RegDownSlot(orig);
 prv:=RegDownSlot(prev);
 cur:=RegDownSlot(pSlot^.current);

 cur_c_org:=CompareReg(cur,org);
 cur_c_prv:=CompareReg(cur,prv);

 if cur_c_org and cur_c_prv then
 begin
  Exit;
 end;

 org:=orig;
 prv:=prev;
 cur:=pSlot^.current;

 pVolatile:=NewVolatile(pSlot);

 {
 [orig]
  if (eval)
  (

  [prev]
  ) else (

  ) fork orig->prev/curr -> next
 [next]
 }

 rtype:=dtUnknow;

 if (org<>nil) then
 if (cur_c_org<>cur_c_prv)  then //Do not add orig if both values are assigned
 begin
  pVolatile.AddStore(org);
  rtype:=LazyType2(rtype,org.dtype);
 end;
 //
 if (prv<>nil) then
 if (not cur_c_prv) then //Do not add prev If the value has not changed
 begin
  pVolatile.AddStore(prv);
  rtype:=LazyType2(rtype,prv.dtype);
 end;
 //
 if (cur<>nil) then
 begin
  pVolatile.AddStore(cur);
  pVolatile.FBase:=cur;
  rtype:=LazyType2(rtype,cur.dtype);
 end;

 pLine:=ctx.after;

 //replace next
 _next:=pSlot^._New(rtype,pLine); //<-The slot value will not be updated
 _next.pWriter:=pVolatile;

 ctx.AddVolatile(pVolatile,_next);

 pLine.AddParam(_next); //post processing

 //fixed line
 _next.CustomLine:=pLine;
end;

procedure TsrPrivateList.build_slot_break(var ctx:TsrVolatileContext;pSlot:PsrRegSlot;var orig,prev,_next:TsrRegNode);
var
 pLine    :TSpirvOp;
 cur,nxt  :TsrRegNode;
 pVolatile:TsrVolatile;
 new_vol  :Boolean;
 rtype    :TsrDataType;
begin
 cur:=RegDownSlot(pSlot^.current);
 nxt:=RegDownSlot(_next);

 if CompareReg(cur,nxt) then
 begin
  Exit;
 end;

 cur:=pSlot^.current;
 nxt:=_next;

 {
 [orig]
 while () do
 [prev]
 (

  if (eval)
  (
   break; --\
  )         |
            |
 )<---------/
 [next]
 }

 pVolatile:=nil;

 //check Volatile
 if (pVolatile=nil) then
 begin
  pVolatile:=NewVolatile(pSlot);
  new_vol:=True;
 end else
 begin
  new_vol:=False;
 end;

 rtype:=dtUnknow;

 if (nxt<>nil) then
 begin
  if new_vol then
  begin
   //save if new created
   pVolatile.AddStore(nxt);
  end;
  rtype:=LazyType2(rtype,nxt.dtype);
 end;
 //
 if (cur<>nil) then
 begin
  pVolatile.AddStore(cur);
  pVolatile.FBase:=cur;
  rtype:=LazyType2(rtype,cur.dtype);
 end;

 //save Volatile
 if new_vol then
 begin
  pLine:=ctx.after;

  //replace next
  _next:=pSlot^._New(rtype,pLine); //<-The slot value will not be updated
  _next.pWriter:=pVolatile;

  ctx.AddVolatile(pVolatile,_next);

  pLine.AddParam(_next); //post processing

  //fixed line
  _next.CustomLine:=pLine;
 end;

 if (_next<>nil) then
 begin
  //update pref type
  if (rtype<>dtUnknow) then
  if (_next.dtype=dtUnknow) then
  begin
   _next.PrepType(ord(rtype));
  end;
 end;

end;

procedure TsrPrivateList.build_slot_conti(var ctx:TsrVolatileContext;pSlot:PsrRegSlot;var orig,prev,_next:TsrRegNode);
var
 pLine    :TSpirvOp;
 cur,prv  :TsrRegNode;
 pVolatile:TsrVolatile;
 new_vol  :Boolean;
 rtype    :TsrDataType;
begin
 cur:=RegDownSlot(pSlot^.current);
 prv:=RegDownSlot(prev);

 //Assert(CompareReg(RegDownSlot(orig),RegDownSlot(prev)));

 if CompareReg(cur,prv) then
 begin
  Exit;
 end;

 cur:=pSlot^.current;
 prv:=prev;

 {
 [orig]
 while () do
 [prev]
 (<------------\
               |
  if (eval)    |
  (            |
   continue; --/
  )

 )
 [next]
 }

 pVolatile:=nil;

 //get Volatile
 if (prv<>nil) then
 begin
  if prv.pWriter.IsType(ntVolatile) then
  begin //prev is volatile
   pVolatile:=prv.pWriter.specialize AsType<ntVolatile>;

   if (pVolatile.FSlot<>pSlot) then
   begin
    //Reset if it is another register
    pVolatile:=nil;
   end;

  end;
 end;

 //check Volatile
 if (pVolatile=nil) then
 begin
  pVolatile:=NewVolatile(pSlot);
  new_vol:=True;
 end else
 begin
  new_vol:=False;
 end;

 rtype:=dtUnknow;


 if (prv<>nil) then
 begin
  if new_vol then
  begin

   Assert(prv.pWriter=orig,'123');

   //need to use orig to create a loopback dependency
   prv:=orig;

   //save if new created
   pVolatile.AddStore(prv);
  end;
  rtype:=LazyType2(rtype,prv.dtype);
 end;

 {
 if new_vol then
 begin
  //use orig
  if (orig<>nil) then
  begin
   //save if new created
   pVolatile.AddStore(orig);
   rtype:=LazyType2(rtype,orig.dtype);
  end;
 end else
 begin
  //use prev
  if (prv<>nil) then
  begin
   rtype:=LazyType2(rtype,prv.dtype);
  end;
 end;
 }
 //
 if (cur<>nil) then
 begin
  pVolatile.AddStore(cur);
  pVolatile.FBase:=cur;
  rtype:=LazyType2(rtype,cur.dtype);
 end;

 //save Volatile
 if new_vol then
 begin
  pLine:=ctx.befor;

  if (prev=nil) then
  begin
   //The input register is not defined, should it be initialized to make_copy_slot?
   prev:=pSlot^._New(rtype,pLine); //<-The slot value will not be updated
  end;

  //set backedge dependence
  prev.pWriter:=pVolatile;

  ctx.AddVolatile(pVolatile,prev);

  pLine.AddParam(prev); //post processing

  //fixed line
  prev.CustomLine:=pLine;
 end;

 if (prev<>nil) then
 begin
  //update pref type
  if (rtype<>dtUnknow) then
  if (prev.dtype=dtUnknow) then
  begin
   prev.PrepType(ord(rtype));
  end;
 end;

end;

procedure TsrPrivateList.build_volatile_reset(orig:PsrRegsSnapshot);
var
 pRegsStory:PsrRegsStory;
begin
 pRegsStory:=FEmit.GetRegsStory;
 pRegsStory^.ForEachSnap(@build_slot_reset,orig);
end;

procedure TsrPrivateList.build_volatile_ctrue(var ctx:TsrVolatileContext;orig,prev,_next:PsrRegsSnapshot);
var
 pRegsStory:PsrRegsStory;
begin
 pRegsStory:=FEmit.GetRegsStory;
 pRegsStory^.ForEachSnap(@build_slot_ctrue,ctx,orig,prev,_next);
end;

procedure TsrPrivateList.build_volatile_endif(var ctx:TsrVolatileContext;orig,prev,_next:PsrRegsSnapshot);
var
 pRegsStory:PsrRegsStory;
begin
 pRegsStory:=FEmit.GetRegsStory;
 //NextVolatileID; FVolatileID:SizeUint;
 pRegsStory^.ForEachSnap(@build_slot_endif,ctx,orig,prev,_next);
end;

procedure TsrPrivateList.build_volatile_break(var ctx:TsrVolatileContext;orig,prev,_next:PsrRegsSnapshot);
var
 pRegsStory:PsrRegsStory;
begin
 pRegsStory:=FEmit.GetRegsStory;
 //NextVolatileID; FVolatileID:SizeUint;
 pRegsStory^.ForEachSnap(@build_slot_break,ctx,orig,prev,_next);
end;

procedure TsrPrivateList.build_volatile_conti(var ctx:TsrVolatileContext;orig,prev,_next:PsrRegsSnapshot);
var
 pRegsStory:PsrRegsStory;
begin
 pRegsStory:=FEmit.GetRegsStory;
 //NextVolatileID; FVolatileID:SizeUint;
 pRegsStory^.ForEachSnap(@build_slot_conti,ctx,orig,prev,_next);
end;

procedure TsrPrivateList.make_copy_slot(pSlot:PsrRegSlot);
var
 cur,node:TsrRegNode;
 pLine:TspirvOp;
begin
 cur:=pSlot^.current;

 if (cur<>nil) then
 begin
  pLine:=FEmit.curr_line;

  node:=pSlot^.New(cur.dtype,pLine);
  node.pWriter:=cur;

  pLine:=FEmit.PostLink(pLine,node); //post processing

  //fixed line
  node.CustomLine:=pLine;
 end;
end;

procedure TsrPrivateList.make_copy_all;
var
 pRegsStory:PsrRegsStory;
begin
 pRegsStory:=FEmit.GetRegsStory;
 pRegsStory^.ForEachSlot(@make_copy_slot);
end;

function get_load_from(r:TsrRegNode):TsrVariable;
var
 pOp:TspirvOp;
begin
 Result:=nil;
 pOp:=r.pWriter.specialize AsType<ntOp>;
 if (pOp=nil) then Exit;
 if (pOp.OpId<>Op.OpLoad) then Exit;
 Result:=pOp.ParamFirst.Value.specialize AsType<ntVariable>;
end;

function get_load_from2(r:TsrRegNode):TsrVolatile;
begin
 Result:=r.pWriter.specialize AsType<ntVolatile>;
end;

procedure _Move(dst,src:TsrVolatile);
var
 node:TStoreNode;
begin
 node:=src.PopStore;
 While (node<>nil) do
 begin
  dst.PushStore(node);
  node:=src.PopStore;
 end;
end;

function calc_most_used_type(V:TsrVolatile):TsrDataType;
type
 t_table=array[TsrDataType] of Integer;
var
 node :TStoreNode;
 Table:t_table;
 Count:Integer;
 i,max:TsrDataType;
begin
 if V.ForceBool then
 begin
  Exit(dtBool);
 end;
 //
 Table:=Default(t_table);
 Count:=0;
 //
 node:=V.FList.pHead;
 While (node<>nil) do
 begin
  Inc(Table[RegDown(node.src).dtype]);
  Inc(Count);
  //
  node:=node.pNext;
 end;
 //
 if (Count=0) then
 begin
  Result:=dtUnknow;
 end else
 if (Count=Table[dtBool]) then
 begin
  //Result:=dtBool;
  Result:=dtUint32;
 end else
 begin
  max:=dtUnknow;
  For i:=Low(TsrDataType) to High(TsrDataType) do
   if (i<>dtBool) then
   begin
    if ((max=dtUnknow) and (Table[i]<>0)) or
       (Table[i]>Table[max]) then
    begin
     max:=i;
    end;
   end;
  Result:=max;
 end;
end;

function TsrPrivateList.PrepVolatile(dst:TspirvOp;src:TsrRegNode):TsrRegNode; //use forward only
var
 tmp:TsrRegNode;
 pPrivate :TsrPrivate;
 pVolatile:TsrVolatile;
 node:TStoreNode;
 pLine:TspirvOp;
 //pTmp:TspirvOp;
 //vtmp:TsrVolatile;
 dtype:TsrDataType;
begin
 Result:=src;
 if (src=nil) then Exit;
 if (not src.pWriter.IsType(ntVolatile)) then Exit;

 //move to prev
 Assert(dst<>nil);
 dst:=flow_prev_up(dst);
 Assert(dst<>nil);
 up_merge_line(dst);
 Assert(dst<>nil);

 pVolatile:=src.pWriter.specialize AsType<ntVolatile>;

 if pVolatile.FSlot^.iUnattach then
 begin
  pPrivate:=pVolatile.FPrivate;
 end else
 begin
  pPrivate:=TsrPrivate(pVolatile.FSlot^.FPrivate);
 end;

 if (pPrivate=nil) then
 begin
  pPrivate:=Fetch(pVolatile.FSlot);

  if pVolatile.FSlot^.iUnattach then
  begin
   pVolatile.FPrivate:=pPrivate;
  end else
  begin
   pVolatile.FSlot^.FPrivate:=pPrivate;
  end;

 end;

 dtype:=calc_most_used_type(pVolatile);
 dtype:=lazyType2(dtype,StoreType(src.dtype));
 dtype:=lazyType2(dtype,dtFloat32);

 if (dtype=dtBool) and (pVolatile.FSlot^.Name='S0') then
 begin
  calc_most_used_type(pVolatile);
 end;

 pPrivate.InitVar();

 pPrivate .PrepType(dtype);

 pVolatile.PrepType(dtype);

 if (pPrivate.GetRegType=dtUnknow) then
 begin
  Assert(false);
 end;

 node:=pVolatile.PopStore;
 While (node<>nil) do
 begin

  tmp:=RegDown(node.src);

  if (src<>tmp) {and (pPrivate^.pVar<>get_load_from(tmp))} then
  begin

   //pLine:=TsrRegNode(node.pNode).pLine;
   //pLine:=node.line;
   pLine:=node.src.pLine;
   Assert(pLine<>nil);

   up_merge_line(pLine);

   {///
   if (tmp^.pWriter^.IsType(ntVolatile)) then
   begin
    vtmp:=tmp^.pWriter^.AsType(ntVolatile);
    Assert(vtmp<>nil);

    _Move(pVolatile,vtmp);

    tmp^.pWriter:=src;

    Continue;
   end;
   }///

   //up_merge_line(pLine);

   pPrivate.FetchStore(pLine,node.src);
  end;

  node:=pVolatile.PopStore;
 end;

 //ntVolatile -> src -> next
 //Opload     -> new

 Result:=src.pSlot^._New(src.dtype,dst); //<-The slot value will not be updated

 pPrivate.FetchLoad(dst,Result); //before reg
end;

procedure TsrPrivateList.RemoveAllStore;
var
 node:TsrVolatile;
begin
 node:=FVoltList.pHead;
 While (node<>nil) do
 begin
  node.RemoveAllStore;
  node:=node.pNext;
 end;
end;

Procedure TsrPrivateList.Post;
var
 node:TsrPrivate;
begin
 node:=FPrivList.pHead;
 While (node<>nil) do
 begin
  if node.IsUsed then
  begin
   node.SortLines;
   node.Optimize;
   node.UpdateRegType;
  end;
  node:=node.pNext;
 end;
end;


end.


