unit emit_SOP1;

{$mode objfpc}{$H+}

interface

uses
  sysutils,
  ps4_pssl,
  spirv,
  srCFGParser,
  srFlow,
  srType,
  srConst,
  srReg,
  srLayout,
  srOpUtils,
  emit_fetch;

type
 TEmit_SOP1=class(TEmitFetch)
  procedure emit_SOP1;
  Function  GetFuncPtr(src:PPsrRegNode):Pointer;
  procedure emit_S_MOV_B32;
  procedure emit_S_MOV_B64;
  procedure OpISccNotZero(src:TsrRegNode);
  procedure OpISccNotZero2(src0,src1:TsrRegNode);
  procedure emit_S_NOT_B32;
  procedure emit_S_NOT_B64;
  procedure emit_S_GETPC_B64;
  procedure emit_S_SETPC_B64;
  procedure emit_S_SWAPPC_B64;
  procedure emit_S_AND_SAVEEXEC_B64;
  procedure _OpWQM32(dst:PsrRegSlot;src:TsrRegNode);
  procedure emit_S_WQM_B32;
  procedure emit_S_WQM_B64;
  procedure emit_S_BREV_B32;
  procedure emit_S_BREV_B64;
 end;

implementation

Function TEmit_SOP1.GetFuncPtr(src:PPsrRegNode):Pointer;
var
 chain:TsrChains;
 pConst:array[0..1] of TsrConst;
 pLayout:TsrDataLayout;
begin
 Result:=nil;

 src[0]:=RegDown(src[0]);
 src[1]:=RegDown(src[1]);

 Assert(src[0]<>nil);
 Assert(src[1]<>nil);

 if (src[0].is_const) and (src[1].is_const) then
 begin
  pConst[0]:=src[0].AsConst;
  pConst[1]:=src[1].AsConst;

  QWORD(Result):=QWORD(pConst[0].AsUint32) or (QWORD(pConst[1].AsUint32) shl 32);
 end else
 begin
  chain:=Default(TsrChains);
  chain[0]:=GetChainRegNode(src[0]);
  chain[1]:=GetChainRegNode(src[1]);

  pLayout:=DataLayoutList.Grouping(chain,rtFunPtr2);
  Result:=pLayout.GetData;
 end;

 Assert(Result<>nil);
end;

procedure TEmit_SOP1.emit_S_MOV_B32;
Var
 dst:PsrRegSlot;
 src:TsrRegNode;
begin
 dst:=get_sdst7(FSPI.SOP1.SDST);
 src:=fetch_ssrc9(FSPI.SOP1.SSRC,dtUnknow);

 MakeCopy(dst,src);
end;

procedure TEmit_SOP1.emit_S_MOV_B64;
Var
 dst:array[0..1] of PsrRegSlot;
 src:array[0..1] of TsrRegNode;
begin
 if not get_sdst7_pair(FSPI.SOP1.SDST,@dst) then Assert(false);

 if not fetch_ssrc9_pair(FSPI.SOP1.SSRC,@src,dtUnknow) then Assert(false);

 MakeCopy(dst[0],src[0]);
 MakeCopy(dst[1],src[1]);
end;

procedure TEmit_SOP1.OpISccNotZero(src:TsrRegNode); //SCC = (sdst.u != 0)
begin
 if src.is_const then
 begin
  //early optimization
  SetConst_b(get_scc,src.AsConst.AsBool);
 end else
 begin
  MakeCopy(get_scc,src);
  get_scc^.current.dtype:=dtBool; //implict cast (int != 0)
 end;
end;

procedure TEmit_SOP1.OpISccNotZero2(src0,src1:TsrRegNode);
begin
 if src0.is_const and
    src1.is_const then
 begin
  //early optimization
  SetConst_b(get_scc,src0.AsConst.AsBool or src1.AsConst.AsBool);
 end else
 begin
  //force type
  src0:=BitcastList.FetchRead(dtBool,src0);
  src1:=BitcastList.FetchRead(dtBool,src1);

  OpLogicalOr(get_scc,src0,src1); //implict cast (int != 0)
 end;
end;

procedure TEmit_SOP1.emit_S_NOT_B32; //sdst = ~ssrc; SCC = (sdst != 0)
Var
 dst:PsrRegSlot;
 src:TsrRegNode;
begin
 dst:=get_sdst7(FSPI.SOP1.SDST);
 src:=fetch_ssrc9(FSPI.SOP1.SSRC,dtUnknow);

 OpNot(dst,src);

 src:=MakeRead(dst,dtUnknow);

 OpISccNotZero(src);
end;

procedure TEmit_SOP1.emit_S_NOT_B64; //sdst[2] = ~ssrc0[2]; SCC = (sdst[2] != 0)
Var
 dst:array[0..1] of PsrRegSlot;
 src:array[0..1] of TsrRegNode;
begin
 if not get_sdst7_pair(FSPI.SOP1.SDST,@dst) then Assert(false);

 if not fetch_ssrc9_pair(FSPI.SOP1.SSRC,@src,dtUnknow) then Assert(false);

 OpNot(dst[0],src[0]);
 OpNot(dst[1],src[1]);

 src[0]:=dst[0]^.current;
 src[1]:=dst[1]^.current;

 OpISccNotZero2(src[0],src[1]); //implict cast (int != 0)
end;

procedure TEmit_SOP1.emit_S_GETPC_B64;
Var
 dst:array[0..1] of PsrRegSlot;

 oldptr:Pointer;
begin
 if not get_sdst7_pair(FSPI.SOP1.SDST,@dst) then Assert(false);

 oldptr:=get_code_ptr;

 SetConst_q(dst[0],dtUint32,QWORD(oldptr));
 SetConst_q(dst[1],dtUint32,QWORD(oldptr) shr 32);
end;

procedure TEmit_SOP1.emit_S_SETPC_B64;
Var
 src:array[0..1] of TsrRegNode;

 newptr:Pointer;
begin
 //ret
 if not fetch_ssrc9_pair(FSPI.SOP1.SSRC,@src,dtUnknow) then Assert(false);

 newptr:=GetFuncPtr(@src);

 set_code_ptr(newptr,btMain);
end;

procedure TEmit_SOP1.emit_S_SWAPPC_B64;
Var
 dst:array[0..1] of PsrRegSlot;
 src:array[0..1] of TsrRegNode;

 oldptr,newptr:Pointer;
begin
 //call
 if not get_sdst7_pair(FSPI.SOP1.SDST,@dst) then Assert(false);

 if not fetch_ssrc9_pair(FSPI.SOP1.SSRC,@src,dtUnknow) then Assert(false);

 newptr:=GetFuncPtr(@src);

 oldptr:=get_code_ptr;

 SetConst_q(dst[0],dtUint32,QWORD(oldptr));
 SetConst_q(dst[1],dtUint32,QWORD(oldptr) shr 32);

 set_code_ptr(newptr,btSetpc);
end;

procedure TEmit_SOP1.emit_S_AND_SAVEEXEC_B64; //sdst.du = EXEC;| NEW_EXEC = (ssrc.du & EXEC);| SCC = (NEW_EXEC != 0)
Var
 dst:array[0..1] of PsrRegSlot;
 src:array[0..1] of TsrRegNode;
 exc:array[0..1] of TsrRegNode;
begin
 if not get_sdst7_pair(FSPI.SOP1.SDST,@dst) then Assert(False);

 if not fetch_ssrc9_pair(FSPI.SOP1.SSRC,@src,dtUnknow) then Assert(False); //ssrc8

 exc[0]:=MakeRead(get_exec0,dtUnknow);
 exc[1]:=MakeRead(get_exec1,dtUnknow);

 MakeCopy(dst[0],exc[0]);
 MakeCopy(dst[1],exc[1]);

 OpBitwiseAnd(get_exec0,src[0],exc[0]);
 OpBitwiseAnd(get_exec1,src[1],exc[1]);

 //set if the new value of EXEC is non-zero
 exc[0]:=MakeRead(get_exec0,dtUnknow);
 exc[1]:=MakeRead(get_exec1,dtUnknow);

 //SCC = ((exc[0] != 0) or ((exc[1] != 0))

 OpISccNotZero2(exc[0],exc[1]); //implict cast (int != 0)

 //SCC = (NEW_EXEC != 0)    SCC = ((exc[0] != 0) or ((exc[1] != 0))
end;

function F_WQM_32(D:DWORD):DWORD;
var
 i:Byte;
begin
 Result:=0;
 if (D=0) then Exit;
 For i:=0 to 7 do
 begin
  if (((D shr (i*4)) and 15)<>0) then
  begin
   Result:=Result or ($F shl (i*4));
  end;
 end;
end;

procedure TEmit_SOP1._OpWQM32(dst:PsrRegSlot;src:TsrRegNode);
begin
 if src.is_const then
 begin
  //early optimization
  SetConst_q(dst,src.dtype,F_WQM_32(src.AsConst.GetData));
 end else
 begin
  OpWQM32(dst,src);
 end;
end;

procedure TEmit_SOP1.emit_S_WQM_B32;
Var
 dst:PsrRegSlot;
 src:TsrRegNode;
begin
 dst:=get_sdst7(FSPI.SOP1.SDST);
 src:=fetch_ssrc9(FSPI.SOP1.SSRC,dtUnknow);

 _OpWQM32(dst,src);

 src:=MakeRead(dst,dtUnknow);
 OpISccNotZero(src);
end;

//TODO: VK_KHR_shader_quad_control:RequireFullQuadsKHR
procedure TEmit_SOP1.emit_S_WQM_B64;
Var
 dst:array[0..1] of PsrRegSlot;
 src:array[0..1] of TsrRegNode;

begin
 //S_WQM_B64 EXEC_LO, EXEC_LO   //sdst.du = wholeQuadMode(ssrc.du); SCC = (sdst.du != 0)
 //dst[q*4+3:q*4].du = (ssrc[4*q+3:4*q].du != 0) ? 0xF : 0
 //dst[3:0].du = (ssrc[3:0].du != 0) ? 0xF : 0
 //dst[63:60].du = (ssrc[63:60].du != 0) ? 0xF : 0
 //if (ssrc[3:0].du != 0) then dst[63:60].du=0xF else dst[63:60].du=0

 if not get_sdst7_pair(FSPI.SOP1.SDST,@dst) then Assert(False);

 if not fetch_ssrc9_pair(FSPI.SOP1.SSRC,@src,dtUnknow) then Assert(False); //ssrc8

 _OpWQM32(dst[0],src[0]);
 _OpWQM32(dst[1],src[1]);

 src[0]:=dst[0]^.current;
 src[1]:=dst[1]^.current;

 OpISccNotZero2(src[0],src[1]); //implict cast (int != 0)
end;

procedure TEmit_SOP1.emit_S_BREV_B32; //sdst[31:0] = ssrc[0:31]
Var
 dst:PsrRegSlot;
 src:TsrRegNode;
begin
 dst:=get_sdst7(FSPI.SOP1.SDST);
 src:=fetch_ssrc9(FSPI.SOP1.SSRC,dtUInt32);

 Op1(Op.OpBitReverse,dtUInt32,dst,src);
end;

procedure TEmit_SOP1.emit_S_BREV_B64; //sdst[63:0] = ssrc[0:63]
Var
 dst:array[0..1] of PsrRegSlot;
 src:array[0..1] of TsrRegNode;
begin
 if not get_sdst7_pair(FSPI.SOP1.SDST,@dst) then Assert(false);

 if not fetch_ssrc9_pair(FSPI.SOP1.SSRC,@src,dtUnknow) then Assert(false);

 Op1(Op.OpBitReverse,dtUInt32,dst[0],src[1]); //0 -> 1
 Op1(Op.OpBitReverse,dtUInt32,dst[1],src[0]); //1 -> 0
end;

procedure TEmit_SOP1.emit_SOP1;
begin

 Case FSPI.SOP1.OP of
  S_MOV_B32         : emit_S_MOV_B32;
  S_MOV_B64         : emit_S_MOV_B64;

  S_NOT_B32         : emit_S_NOT_B32;
  S_NOT_B64         : emit_S_NOT_B64;

  S_WQM_B32         : emit_S_WQM_B32;
  S_WQM_B64         : emit_S_WQM_B64;

  S_BREV_B32        : emit_S_BREV_B32;
  S_BREV_B64        : emit_S_BREV_B64;

  S_GETPC_B64       : emit_S_GETPC_B64;
  S_SETPC_B64       : emit_S_SETPC_B64;
  S_SWAPPC_B64      : emit_S_SWAPPC_B64;
  S_AND_SAVEEXEC_B64: emit_S_AND_SAVEEXEC_B64;
  else
   Assert(false,'SOP1?'+IntToStr(FSPI.SOP1.OP)+' '+get_str_spi(FSPI));
 end;

end;


end.





