unit emit_op;

{$mode objfpc}{$H+}

interface

uses
 spirv,
 srNode,
 srType,
 srTypes,
 srConst,
 srReg,
 srLayout,
 srVariable,
 srOp,
 srCFGLabel,
 srOpUtils,
 srCacheOp,
 srInterface;

type
 TEmitOp=class(TEmitInterface)
  //
  function  _Op1(pLine:PspirvOp;OpId:DWORD;dst,src:PsrRegNode):PSpirvOp;
  function  _Op2(pLine:PspirvOp;OpId:DWORD;dst,src0,src1:PsrRegNode):PSpirvOp;
  function  _Op3(pLine:PspirvOp;OpId:DWORD;dst,src0,src1,src2:PsrRegNode):PSpirvOp;
  function  _Op4(pLine:PspirvOp;OpId:DWORD;dst,src0,src1,src2,src3:PsrRegNode):PSpirvOp;
  //
  function  _OpGlsl1(pLine:PspirvOp;OpId:DWORD;dst,src:PsrRegNode):PSpirvOp;
  function  _OpGlsl2(pLine:PspirvOp;OpId:DWORD;dst,src0,src1:PsrRegNode):PSpirvOp;
  function  _OpGlsl3(pLine:PspirvOp;OpId:DWORD;dst,src0,src1,src2:PsrRegNode):PSpirvOp;
  //
  function  Op1(OpId:DWORD;rtype:TsrDataType;dst:PsrRegSlot;src:PsrRegNode):PSpirvOp;
  function  Op2(OpId:DWORD;rtype:TsrDataType;dst:PsrRegSlot;src0,src1:PsrRegNode):PSpirvOp;
  function  Op3(OpId:DWORD;rtype:TsrDataType;dst:PsrRegSlot;src0,src1,src2:PsrRegNode):PSpirvOp;
  function  Op4(OpId:DWORD;rtype:TsrDataType;dst:PsrRegSlot;src0,src1,src2,src3:PsrRegNode):PSpirvOp;
  //
  function  OpGlsl1(OpId:DWORD;rtype:TsrDataType;dst:PsrRegSlot;src:PsrRegNode):PSpirvOp;
  function  OpGlsl2(OpId:DWORD;rtype:TsrDataType;dst:PsrRegSlot;src0,src1:PsrRegNode):PSpirvOp;
  function  OpGlsl3(OpId:DWORD;rtype:TsrDataType;dst:PsrRegSlot;src0,src1,src2:PsrRegNode):PSpirvOp;
  //
  function  OpBitcast(pLine:PspirvOp;dst,src:PsrRegNode):PSpirvOp;
  function  OpBoolToInt(pLine:PspirvOp;dst,src:PsrRegNode):PSpirvOp;
  function  OpIntToBool(pLine:PspirvOp;dst,src:PsrRegNode):PSpirvOp;
  //
  function  OpCast (nLine,      dst,src:PsrNode):PsrNode; override;
  function  OpLoad (nLine,dtype,dst,src:PsrNode):PsrNode; override;
  function  OpStore(nLine,      dst,src:PsrNode):PsrNode; override;
  //
  function  OpLoad(pLine:PspirvOp;dst:PsrRegNode;src:PsrNode):PSpirvOp;
  //
  function  OpExtract(pLine:PspirvOp;dst,src:PsrRegNode;id:DWORD):PSpirvOp;
  function  OpConstruct(pLine:PspirvOp;dst:PsrRegNode):PSpirvOp;
  function  OpAccessChain(pLine:PspirvOp;vType:PsrType;dst:PsrChain;src:PsrVariable):PSpirvOp;
  function  OpCondMerge(pLine,pLabel:PspirvOp):PSpirvOp;
  function  OpLoopMerge(pLine,pLabel0,pLabel1:PspirvOp):PSpirvOp;
  function  OpBranch(pLine,pLabel:PspirvOp):PSpirvOp;
  function  OpBranchCond(pLine,pLabel0,pLabel1:PspirvOp;src:PsrRegNode):PSpirvOp;
  function  OpReturnValue(pLine:PspirvOp;src:PsrRegNode):PSpirvOp;
  //
  procedure OpFmaF32(dst:PsrRegSlot;src0,src1,src2:PsrRegNode);
  procedure OpFmaI32(dst:PsrRegSlot;src0,src1,src2:PsrRegNode);
  procedure OpFmaU32(dst:PsrRegSlot;src0,src1,src2:PsrRegNode);
  //
  procedure OpSelect(dst:PsrRegSlot;src0,src1,cond:PsrRegNode);
  //
  procedure OpIAddCar(pLine:PspirvOp;dst,car,src0,src1:PsrRegNode);
  procedure OpIAddExt(dst,car:PsrRegSlot;src0,src1:PsrRegNode);
  //
  procedure OpISubBor(pLine:PspirvOp;dst,bor,src0,src1:PsrRegNode);
  procedure OpISubExt(dst,bor:PsrRegSlot;src0,src1:PsrRegNode);
  //
  function  OpAbsDiff(pLine:PspirvOp;dst,src0,src1:PsrRegNode):PSpirvOp;
  procedure OpWQM32(dst:PsrRegSlot;src:PsrRegNode);
  //
  procedure OpBFEU32(dst:PsrRegSlot;base,src0,src1:PsrRegNode);
  procedure OpBFIB32(dst:PsrRegSlot;bitmsk,src0,src1:PsrRegNode);
  //
  function  OpBFITo(src0,src1,src2,src3:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
  //
  procedure OpPackAnc(dst:PsrRegSlot;prim,smid,rtid:PsrRegNode);
  //
  function  OpUMinTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpUMaxTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpFMinTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpFMaxTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpNMinTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpNMaxTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
  //
  procedure OpMED3F(dst:PsrRegSlot;src0,src1,src2:PsrRegNode);
  //
  function  OpPackOfs(pLine:PspirvOp;rtype:TsrDataType;count:Byte;src:PsrRegNode):PsrRegNode;
  function  OpMakeCon(pLine:PspirvOp;dst:PsrRegNode;src:PPsrRegNode):PSpirvOp;
  function  OpMakeVec(pLine:PspirvOp;rtype:TsrDataType;src:PPsrRegNode):PsrRegNode;
  function  OpMakeCub(pLine:PspirvOp;rtype:TsrDataType;src:PPsrRegNode):PsrRegNode;
  function  OpSampledImage(pLine:PspirvOp;Tgrp,Sgrp:PsrNode;dtype:TsrDataType;info:TsrTypeImageInfo):PsrRegNode;
  //
  procedure OpIAdd(dst:PsrRegSlot;src0,src1:PsrRegNode);
  procedure OpISub(dst:PsrRegSlot;src0,src1:PsrRegNode);
  procedure OpIMul(dst:PsrRegSlot;src0,src1:PsrRegNode);
  //
  function  OpShlTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpShlTo(src0:PsrRegNode;src1:QWORD;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpShrTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpShrTo(src0:PsrRegNode;src1:QWORD;ppLine:PPspirvOp=nil):PsrRegNode;
  //
  function  OpIAddTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpIAddTo(src0:PsrRegNode;src1:QWORD;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpISubTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpISubTo(src0:PsrRegNode;src1:QWORD;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpIMulTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpIMulTo(src0:PsrRegNode;src1:QWORD;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpIDivTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpIDivTo(src0:PsrRegNode;src1:QWORD;ppLine:PPspirvOp=nil):PsrRegNode;
  //
  function  OpFAddTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpFSubTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpFMulTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpFDivTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
  //
  function  OpFAddToS(src0:PsrRegNode;src1:Single;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpFMulToS(src0:PsrRegNode;src1:Single;ppLine:PPspirvOp=nil):PsrRegNode;
  //
  function  OpUToF(src:PsrRegNode;rtype:TsrDataType;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpSToF(src:PsrRegNode;rtype:TsrDataType;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpUToU(src:PsrRegNode;rtype:TsrDataType;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpSToS(src:PsrRegNode;rtype:TsrDataType;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpFToF(src:PsrRegNode;rtype:TsrDataType;ppLine:PPspirvOp=nil):PsrRegNode;
  //
  function  OpFloorTo(src:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpPowTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
  //
  procedure OpNot(dst:PsrRegSlot;src:PsrRegNode);
  procedure OpLogicalNot(dst:PsrRegSlot;src:PsrRegNode);
  procedure OpBitwiseOr(dst:PsrRegSlot;src0,src1:PsrRegNode);
  procedure OpBitwiseXor(dst:PsrRegSlot;src0,src1:PsrRegNode);
  procedure OpLogicalOr(dst:PsrRegSlot;src0,src1:PsrRegNode);
  procedure OpBitwiseAnd(dst:PsrRegSlot;src0,src1:PsrRegNode);
  procedure OpLogicalAnd(dst:PsrRegSlot;src0,src1:PsrRegNode);
  //
  function  OpNotTo(src:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpOrTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpAndTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
  function  OpAndTo(src0:PsrRegNode;src1:QWORD;ppLine:PPspirvOp=nil):PsrRegNode;
  //
  function  OpBitCountTo(src:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
  //
  function  OpImageSampleImplicitLod(pLine:PspirvOp;img:PsrNode;dst,coord:PsrRegNode):PSpirvOp;
  function  OpImageSampleExplicitLod(pLine:PspirvOp;img:PsrNode;dst,coord:PsrRegNode):PSpirvOp;
  function  OpImageSampleDrefImplicitLod(pLine:PspirvOp;img:PsrNode;dst,coord,pcf:PsrRegNode):PSpirvOp;
  function  OpImageSampleDrefExplicitLod(pLine:PspirvOp;img:PsrNode;dst,coord,pcf:PsrRegNode):PSpirvOp;
  function  OpImageGather(pLine:PspirvOp;img:PsrNode;dst,coord:PsrRegNode;id:Byte):PSpirvOp;
  function  OpImageDrefGather(pLine:PspirvOp;img:PsrNode;dst,coord,pcf:PsrRegNode):PSpirvOp;
  function  OpImageFetch(pLine:PspirvOp;Tgrp:PsrNode;dst,coord:PsrRegNode):PSpirvOp;
  function  OpImageRead(pLine:PspirvOp;Tgrp:PsrNode;dst,idx:PsrRegNode):PspirvOp;
  function  OpImageWrite(pLine:PspirvOp;Tgrp:PsrNode;idx,src:PsrRegNode):PspirvOp;
 end;

function isPowerOfTwo(x:QWORD):Boolean; inline;
function fastIntLog2(i:QWORD):QWORD; inline;

implementation

function isPowerOfTwo(x:QWORD):Boolean; inline;
begin
 Result:=((x-1) and x)=0;
end;

function fastIntLog2(i:QWORD):QWORD; inline;
begin
 Result:=BsfQWORD(i);
end;

procedure _set_line(ppLine:PPspirvOp;pLine:PspirvOp);
begin
 if (ppLine=nil) then Exit;
 ppLine^:=pLine;
end;

//

function TEmitOp._Op1(pLine:PspirvOp;OpId:DWORD;dst,src:PsrRegNode):PSpirvOp;
Var
 node:PSpirvOp;
begin
 node:=AddSpirvOp(pLine,OpId); //need first
 node^.pType:=TypeList.Fetch(dst^.dtype);
 node^.pDst:=dst;
 node^.AddParam(src);
 Result:=node;
end;

function TEmitOp._Op2(pLine:PspirvOp;OpId:DWORD;dst,src0,src1:PsrRegNode):PSpirvOp;
Var
 node:PSpirvOp;
begin
 node:=AddSpirvOp(pLine,OpId); //need first
 node^.pType:=TypeList.Fetch(dst^.dtype);
 node^.pDst:=dst;
 node^.AddParam(src0);
 node^.AddParam(src1);
 Result:=node;
end;

function TEmitOp._Op3(pLine:PspirvOp;OpId:DWORD;dst,src0,src1,src2:PsrRegNode):PSpirvOp;
Var
 node:PSpirvOp;
begin
 node:=AddSpirvOp(pLine,OpId); //need first
 node^.pType:=TypeList.Fetch(dst^.dtype);
 node^.pDst:=dst;
 node^.AddParam(src0);
 node^.AddParam(src1);
 node^.AddParam(src2);
 Result:=node;
end;

function TEmitOp._Op4(pLine:PspirvOp;OpId:DWORD;dst,src0,src1,src2,src3:PsrRegNode):PSpirvOp;
Var
 node:PSpirvOp;
begin
 node:=AddSpirvOp(pLine,OpId); //need first
 node^.pType:=TypeList.Fetch(dst^.dtype);
 node^.pDst:=dst;
 node^.AddParam(src0);
 node^.AddParam(src1);
 node^.AddParam(src2);
 node^.AddParam(src3);
 Result:=node;
end;

//

function TEmitOp._OpGlsl1(pLine:PspirvOp;OpId:DWORD;dst,src:PsrRegNode):PSpirvOp;
Var
 node:PSpirvOp;
begin
 node:=AddSGlslOp(pLine,OpId); //need first
 node^.pType:=TypeList.Fetch(dst^.dtype);
 node^.pDst:=dst;
 node^.AddParam(src);
 Result:=node;
end;

function TEmitOp._OpGlsl2(pLine:PspirvOp;OpId:DWORD;dst,src0,src1:PsrRegNode):PSpirvOp;
Var
 node:PSpirvOp;
begin
 node:=AddSGlslOp(pLine,OpId); //need first
 node^.pType:=TypeList.Fetch(dst^.dtype);
 node^.pDst:=dst;
 node^.AddParam(src0);
 node^.AddParam(src1);
 Result:=node;
end;

function TEmitOp._OpGlsl3(pLine:PspirvOp;OpId:DWORD;dst,src0,src1,src2:PsrRegNode):PSpirvOp;
Var
 node:PSpirvOp;
begin
 node:=AddSGlslOp(pLine,OpId); //need first
 node^.pType:=TypeList.Fetch(dst^.dtype);
 node^.pDst:=dst;
 node^.AddParam(src0);
 node^.AddParam(src1);
 node^.AddParam(src2);
 Result:=node;
end;

//

function TEmitOp.Op1(OpId:DWORD;rtype:TsrDataType;dst:PsrRegSlot;src:PsrRegNode):PSpirvOp;
begin
 Result:=_Op1(line,OpId,dst^.New(line,rtype),src);
end;

function TEmitOp.Op2(OpId:DWORD;rtype:TsrDataType;dst:PsrRegSlot;src0,src1:PsrRegNode):PSpirvOp;
begin
 Result:=_Op2(line,OpId,dst^.New(line,rtype),src0,src1);
end;

function TEmitOp.Op3(OpId:DWORD;rtype:TsrDataType;dst:PsrRegSlot;src0,src1,src2:PsrRegNode):PSpirvOp;
begin
 Result:=_Op3(line,OpId,dst^.New(line,rtype),src0,src1,src2);
end;

function TEmitOp.Op4(OpId:DWORD;rtype:TsrDataType;dst:PsrRegSlot;src0,src1,src2,src3:PsrRegNode):PSpirvOp;
begin
 Result:=_Op4(line,OpId,dst^.New(line,rtype),src0,src1,src2,src3);
end;

function TEmitOp.OpGlsl1(OpId:DWORD;rtype:TsrDataType;dst:PsrRegSlot;src:PsrRegNode):PSpirvOp;
begin
 Result:=_OpGlsl1(line,OpId,dst^.New(line,rtype),src);
end;

function TEmitOp.OpGlsl2(OpId:DWORD;rtype:TsrDataType;dst:PsrRegSlot;src0,src1:PsrRegNode):PSpirvOp;
begin
 Result:=_OpGlsl2(line,OpId,dst^.New(line,rtype),src0,src1)
end;

function TEmitOp.OpGlsl3(OpId:DWORD;rtype:TsrDataType;dst:PsrRegSlot;src0,src1,src2:PsrRegNode):PSpirvOp;
begin
 Result:=_OpGlsl3(line,OpId,dst^.New(line,rtype),src0,src1,src2);
end;

function TEmitOp.OpBitcast(pLine:PspirvOp;dst,src:PsrRegNode):PSpirvOp;
begin
 Result:=_Op1(pLine,Op.OpBitcast,dst,src);
end;

function TEmitOp.OpBoolToInt(pLine:PspirvOp;dst,src:PsrRegNode):PSpirvOp;
Var
 src0,src1:PsrRegNode;
begin
 src0:=NewReg_q(dst^.dtype,0,@pLine);
 src1:=NewReg_q(dst^.dtype,1,@pLine);

 Result:=_Op3(pLine,Op.OpSelect,dst,src,src1,src0);
end;

function TEmitOp.OpIntToBool(pLine:PspirvOp;dst,src:PsrRegNode):PSpirvOp;
Var
 src0:PsrRegNode;
begin
 src0:=NewReg_q(src^.dtype,0,@pLine);

 Result:=_Op2(pLine,Op.OpINotEqual,dst,src,src0);
end;

function TEmitOp.OpCast(nLine,dst,src:PsrNode):PsrNode;
var
 rdst,rsrc,rtmp:PsrRegNode;
 pLine:PspirvOp;
begin
 pLine:=nLine^.AsType(ntOpCustom);
 rdst :=  dst^.AsType(ntReg);
 rsrc :=  src^.AsType(ntReg);

 Assert(pLine<>nil);
 Assert(dst<>nil);
 Assert(src<>nil);

 if (rsrc^.dtype=dtBool) then
 begin
  Case rdst^.dtype of
   dtInt32,
   dtUint32:Result:=OpBoolToInt(pLine,rdst,rsrc);
   else
    begin
     rtmp:=NewReg(dtUint32);
     OpBoolToInt(pLine,rtmp,rsrc);
     Result:=OpBitcast(pLine,rdst,rtmp);
    end;
  end;
 end else
 if (rdst^.dtype=dtBool) then
 begin
  Case rsrc^.dtype of
   dtInt32,
   dtUint32:Result:=OpIntToBool(pLine,rdst,rsrc);
   else
    begin
     rtmp:=NewReg(dtUint32);
     OpBitcast(pLine,rtmp,rsrc);
     Result:=OpIntToBool(pLine,rdst,rtmp);
    end;
  end;
 end else
 begin
  Result:=OpBitcast(pLine,rdst,rsrc);
 end;
end;

function TEmitOp.OpLoad(nLine,dtype,dst,src:PsrNode):PsrNode;
var
 pLine:PspirvOp;
 pType:PsrType;
 node:PSpirvOp;
begin
 pLine:=nLine^.AsType(ntOpCustom);
 pType:=dtype^.AsType(ntType);
 Assert(dst<>nil);
 Assert(src<>nil);
 Assert(pLine<>nil);
 //Assert(pType<>nil);
 node:=AddSpirvOp(pLine,Op.OpLoad);
 node^.pType:=pType;
 node^.pDst :=dst;
 node^.AddParam(src);
 Result:=node;
end;

function TEmitOp.OpStore(nLine,dst,src:PsrNode):PsrNode;
Var
 pLine:PspirvOp;
 node:PSpirvOp;
begin
 pLine:=nLine^.AsType(ntOpCustom);
 Assert(dst<>nil);
 Assert(src<>nil);
 Assert(pLine<>nil);
 node:=AddSpirvOp(pLine,Op.OpStore);
 node^.pDst:=dst; //write
 node^.AddParam(src);
 Result:=node;
end;

function TEmitOp.OpLoad(pLine:PspirvOp;dst:PsrRegNode;src:PsrNode):PSpirvOp;
Var
 dtype:PsrType;
begin
 dtype:=TypeList.Fetch(dst^.dtype);
 Result:=PSpirvOp(OpLoad(pLine,dtype,dst,src));
end;

function TEmitOp.OpExtract(pLine:PspirvOp;dst,src:PsrRegNode;id:DWORD):PSpirvOp;
Var
 node:PSpirvOp;
begin
 node:=AddSpirvOp(pLine,Op.OpCompositeExtract);
 node^.pType:=TypeList.Fetch(dst^.dtype);
 node^.pDst:=dst;
 node^.AddParam(src);
 node^.AddLiteral(id);
 Result:=node;
end;

function TEmitOp.OpConstruct(pLine:PspirvOp;dst:PsrRegNode):PSpirvOp;
var
 node:PSpirvOp;
begin
 node:=AddSpirvOp(pLine,Op.OpCompositeConstruct);
 node^.pType:=TypeList.Fetch(dst^.dtype);
 Assert(node^.pType<>nil);
 node^.pDst:=dst;
 Result:=node;
 //child add later
end;

function TEmitOp.OpAccessChain(pLine:PspirvOp;vType:PsrType;dst:PsrChain;src:PsrVariable):PSpirvOp;
Var
 node:PSpirvOp;
begin
 node:=AddSpirvOp(pLine,Op.OpAccessChain);
 Assert(vType<>nil);
 node^.pType:=TypeList.FetchPointer(vType,src^.GetStorageClass);
 node^.pDst:=dst;
 node^.AddParam(src); //base
 //dst^.pLine:=node;
 Result:=node;
 //index add later
end;

function TEmitOp.OpCondMerge(pLine,pLabel:PspirvOp):PSpirvOp;
Var
 node:PSpirvOp;
begin
 node:=AddSpirvOp(pLine,Op.OpSelectionMerge);
 node^.AddParam(pLabel^.pDst);
 node^.AddLiteral(SelectionControl.None,'None');
 Result:=node;
end;

function TEmitOp.OpLoopMerge(pLine,pLabel0,pLabel1:PspirvOp):PSpirvOp;
Var
 node:PSpirvOp;
begin
 node:=AddSpirvOp(pLine,Op.OpLoopMerge);
 node^.AddParam(pLabel0^.pDst);
 node^.AddParam(pLabel1^.pDst);
 node^.AddLiteral(LoopControl.None,'None');
 Result:=node;
end;

function TEmitOp.OpBranch(pLine,pLabel:PspirvOp):PSpirvOp;
Var
 node:PSpirvOp;
begin
 node:=AddSpirvOp(pLine,Op.OpBranch);
 node^.AddParam(pLabel^.pDst);
 Result:=node;
end;

function TEmitOp.OpBranchCond(pLine,pLabel0,pLabel1:PspirvOp;src:PsrRegNode):PSpirvOp;
Var
 node:PSpirvOp;
begin
 node:=AddSpirvOp(pLine,Op.OpBranchConditional);
 node^.AddParam(src);
 node^.AddParam(pLabel0^.pDst);
 node^.AddParam(pLabel1^.pDst);
 Result:=node;
end;

function TEmitOp.OpReturnValue(pLine:PspirvOp;src:PsrRegNode):PSpirvOp;
Var
 node:PSpirvOp;
begin
 node:=AddSpirvOp(pLine,Op.OpReturnValue);
 node^.AddParam(src);
 Result:=node;
end;

//

procedure TEmitOp.OpFmaF32(dst:PsrRegSlot;src0,src1,src2:PsrRegNode);
begin
 //vdst = vsrc0.f * vsrc1.f + vdst.f  -> fma
 //NoContraction  decoration
 OpGlsl3(GlslOp.Fma,dtFloat32,dst,src0,src1,src2);
end;

procedure TEmitOp.OpFmaI32(dst:PsrRegSlot;src0,src1,src2:PsrRegNode);
var
 mul,sum:PsrRegNode;
begin
 //vdst = vsrc0.i * vsrc1.i + vdst.i
 mul:=NewReg(dtInt32);
 sum:=dst^.New(line,dtInt32);

 _Op2(line,Op.OpIMul,mul,src0,src1);
 _Op2(line,Op.OpIAdd,sum,mul,src2);
end;

procedure TEmitOp.OpFmaU32(dst:PsrRegSlot;src0,src1,src2:PsrRegNode);
var
 mul,sum:PsrRegNode;
begin
 //vdst = vsrc0.u * vsrc1.u + vdst.u
 mul:=NewReg(dtUInt32);
 sum:=dst^.New(line,dtUInt32);

 _Op2(line,Op.OpIMul,mul,src0,src1);
 _Op2(line,Op.OpIAdd,sum,mul,src2);
end;

//

procedure TEmitOp.OpSelect(dst:PsrRegSlot;src0,src1,cond:PsrRegNode);
begin
 Op3(Op.OpSelect,LazyType2(src0^.dtype,src1^.dtype),dst,cond,src1,src0);
end;

procedure TEmitOp.OpIAddCar(pLine:PspirvOp;dst,car,src0,src1:PsrRegNode);
Var
 node:PSpirvOp;
 rsl:PsrRegNode;
begin
 node:=AddSpirvOp(pLine,Op.OpIAddCarry); //need first
 node^.AddParam(src0);
 node^.AddParam(src1);

 rsl:=NewReg(dtStruct2u);

 node^.pType:=TypeList.Fetch(dtStruct2u);
 node^.pDst:=rsl;

 dst:=BitcastList.FetchDstr(TsrDataType(dtStruct2u).Child,dst);
 car:=BitcastList.FetchDstr(TsrDataType(dtStruct2u).Child,car);

 pLine:=node;
 pLine:=OpExtract(pLine,dst,rsl,0);
 pLine:=OpExtract(pLine,car,rsl,1);
end;

procedure TEmitOp.OpIAddExt(dst,car:PsrRegSlot;src0,src1:PsrRegNode);
Var
 node:PSpirvOp;
 rsl:PsrRegPair;
begin
 node:=AddSpirvOp(srOpUtils.OpIAddExt); //need first
 node^.AddParam(src0);
 node^.AddParam(src1);

 rsl:=NewRegPair;
 rsl^.pWriter:=node;
 node^.pDst:=rsl;

 rsl^.pDst0:=dst^.New(line,dtUint32);
 rsl^.pDst1:=car^.New(line,dtUint32);
end;

procedure TEmitOp.OpISubBor(pLine:PspirvOp;dst,bor,src0,src1:PsrRegNode);
Var
 node:PSpirvOp;
 rsl:PsrRegNode;
begin
 node:=AddSpirvOp(pLine,Op.OpISubBorrow); //need first
 node^.AddParam(src0);
 node^.AddParam(src1);

 rsl:=NewReg(dtStruct2u);

 node^.pType:=TypeList.Fetch(dtStruct2u);
 node^.pDst:=rsl;

 dst:=BitcastList.FetchDstr(TsrDataType(dtStruct2u).Child,dst);
 bor:=BitcastList.FetchDstr(TsrDataType(dtStruct2u).Child,bor);

 pLine:=node;
 pLine:=OpExtract(pLine,dst,rsl,0);
 pLine:=OpExtract(pLine,bor,rsl,1);
end;

procedure TEmitOp.OpISubExt(dst,bor:PsrRegSlot;src0,src1:PsrRegNode);
Var
 node:PSpirvOp;
 rsl:PsrRegPair;
begin
 node:=AddSpirvOp(srOpUtils.OpISubExt); //need first
 node^.AddParam(src0);
 node^.AddParam(src1);

 rsl:=NewRegPair;
 rsl^.pWriter:=node;
 node^.pDst:=rsl;

 rsl^.pDst0:=dst^.New(line,dtUint32);
 rsl^.pDst1:=bor^.New(line,dtUint32);
end;

//

function TEmitOp.OpAbsDiff(pLine:PspirvOp;dst,src0,src1:PsrRegNode):PSpirvOp;
begin
 Result:=_Op2(pLine,srOpUtils.OpAbsDiff,dst,src0,src1);
end;

procedure TEmitOp.OpWQM32(dst:PsrRegSlot;src:PsrRegNode);
begin
 Op1(srOpUtils.OpWQM32,dtUnknow,dst,src);
end;

procedure TEmitOp.OpBFEU32(dst:PsrRegSlot;base,src0,src1:PsrRegNode);
begin
 Op3(srOpUtils.OpBFEU32,dtUint32,dst,base,src0,src1);
end;

procedure TEmitOp.OpBFIB32(dst:PsrRegSlot;bitmsk,src0,src1:PsrRegNode);
begin
 Op3(srOpUtils.OpBFIB32,dtUint32,dst,bitmsk,src0,src1);
end;

function TEmitOp.OpBFITo(src0,src1,src2,src3:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 Result:=NewReg(src0^.dtype);
 _set_line(ppLine,_Op4(_get_line(ppLine),Op.OpBitFieldInsert,Result,src0,src1,src2,src3));
end;

//

procedure TEmitOp.OpPackAnc(dst:PsrRegSlot;prim,smid,rtid:PsrRegNode);
begin
 Op3(srOpUtils.OpPackAnc,dtUint32,dst,prim,smid,rtid);
end;

//

function TEmitOp.OpUMinTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 Result:=NewReg(src0^.dtype);
 _set_line(ppLine,_OpGlsl2(_get_line(ppLine),GlslOp.UMin,Result,src0,src1));
end;

function TEmitOp.OpUMaxTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 Result:=NewReg(src0^.dtype);
 _set_line(ppLine,_OpGlsl2(_get_line(ppLine),GlslOp.UMax,Result,src0,src1));
end;

function TEmitOp.OpFMinTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 Result:=NewReg(src0^.dtype);
 _set_line(ppLine,_OpGlsl2(_get_line(ppLine),GlslOp.FMin,Result,src0,src1));
end;

function TEmitOp.OpFMaxTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 Result:=NewReg(src0^.dtype);
 _set_line(ppLine,_OpGlsl2(_get_line(ppLine),GlslOp.FMax,Result,src0,src1));
end;

function TEmitOp.OpNMinTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 Result:=NewReg(src0^.dtype);
 _set_line(ppLine,_OpGlsl2(_get_line(ppLine),GlslOp.NMin,Result,src0,src1));
end;

function TEmitOp.OpNMaxTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 Result:=NewReg(src0^.dtype);
 _set_line(ppLine,_OpGlsl2(_get_line(ppLine),GlslOp.NMax,Result,src0,src1));
end;

procedure TEmitOp.OpMED3F(dst:PsrRegSlot;src0,src1,src2:PsrRegNode);
var
 min:PsrRegNode;
 max:PsrRegNode;
 mmx:PsrRegNode;
begin
 min:=OpFMinTo(src0,src1); //min(s0,s1)
 max:=OpNMaxTo(src0,src1); //max(s0,s1)
 mmx:=OpFMinTo(max ,src2); //min(max(s0,s1),s2)

 OpGlsl2(GlslOp.NMax,src0^.dtype,dst,min,mmx); //max(min(s0,s1),min(max(s0,s1),s2))
end;

function TEmitOp.OpPackOfs(pLine:PspirvOp;rtype:TsrDataType;count:Byte;src:PsrRegNode):PsrRegNode;
Var
 p:PsrCacheOp;
 dst:PsrRegNode;
 node:PSpirvOp;
begin
 Assert(count<>0);
 Assert(src<>nil);

 pLine:=GetMaxPlace(pLine,1,@src);

 p:=CacheOpList.Fetch(pLine^.Parent,srOpUtils.OpPackOfs,rtype,1,@src);

 if (p^.pDst=nil) then
 begin
  node:=AddSpirvOp(pLine,srOpUtils.OpPackOfs); //need first

  dst:=NewReg(rtype);

  node^.pDst:=dst;
  node^.AddLiteral(count);
  node^.AddParam(src);

  p^.pDst:=dst; //save
  Result:=dst;
 end else
 begin
  Result:=PsrRegNode(p^.pDst);
 end;
end;

function TEmitOp.OpMakeCon(pLine:PspirvOp;dst:PsrRegNode;src:PPsrRegNode):PSpirvOp;
Var
 p:PsrCacheOp;
 node:PSpirvOp;
 rtype:TsrDataType;
 i:Byte;
begin
 Result:=pLine;
 Assert(src<>nil);

 rtype:=dst^.dtype;

 pLine:=GetMaxPlace(pLine,rtype.Count,src);

 p:=CacheOpList.Fetch(pLine^.Parent,Op.OpCompositeConstruct,rtype,rtype.Count,src);

 if (p^.pDst=nil) then
 begin
  node:=OpConstruct(pLine,dst);
  For i:=0 to rtype.Count-1 do
  begin
   node^.AddParam(src[i]);
  end;
  p^.pDst:=dst; //save
  Result:=node;
 end else
 begin
  dst^.pWriter:=p^.pDst;
 end;
end;

function TEmitOp.OpMakeVec(pLine:PspirvOp;rtype:TsrDataType;src:PPsrRegNode):PsrRegNode;
Var
 p:PsrCacheOp;
 dst:PsrRegNode;
 node:PSpirvOp;
 i:Byte;
begin
 Assert(src<>nil);

 pLine:=GetMaxPlace(pLine,rtype.Count,src);

 p:=CacheOpList.Fetch(pLine^.Parent,srOpUtils.OpMakeVec,rtype,rtype.Count,src);

 if (p^.pDst=nil) then
 begin
  node:=AddSpirvOp(pLine,srOpUtils.OpMakeVec); //need first

  dst:=NewReg(rtype);

  node^.pDst:=dst;

  For i:=0 to rtype.Count-1 do
  begin
   node^.AddParam(src[i]);
  end;

  p^.pDst:=dst; //save
  Result:=dst;
 end else
 begin
  Result:=PsrRegNode(p^.pDst);
 end;
end;

//x,y,face,slice
function TEmitOp.OpMakeCub(pLine:PspirvOp;rtype:TsrDataType;src:PPsrRegNode):PsrRegNode;
Var
 p:PsrCacheOp;
 dst:PsrRegNode;
 node:PSpirvOp;
 i:Byte;
begin
 Assert(src<>nil);

 pLine:=GetMaxPlace(pLine,rtype.Count,src);

 p:=CacheOpList.Fetch(pLine^.Parent,srOpUtils.OpMakeCub,rtype,rtype.Count,src);

 if (p^.pDst=nil) then
 begin
  node:=AddSpirvOp(pLine,srOpUtils.OpMakeCub); //need first

  dst:=NewReg(rtype);

  node^.pDst:=dst;

  For i:=0 to rtype.Count-1 do
  begin
   node^.AddParam(src[i]);
  end;

  p^.pDst:=dst; //save
  Result:=dst;
 end else
 begin
  Result:=PsrRegNode(p^.pDst);
 end;
end;

function TEmitOp.OpSampledImage(pLine:PspirvOp;Tgrp,Sgrp:PsrNode;dtype:TsrDataType;info:TsrTypeImageInfo):PsrRegNode;
Var
 src:array[0..1] of PsrNode;
 pType:PsrType;
 p:PsrCacheOp;
 dst:PsrRegNode;
 node:PSpirvOp;
 tmp:PSpirvOp;
label
 _prev;
begin
 src[0]:=Tgrp;
 src[1]:=Sgrp;

 Assert(pLine<>nil);

 p:=CacheOpList.Fetch(pLine^.Parent,Op.OpSampledImage,dtTypeSampledImage,2,@src);

 if (p^.pDst<>nil) then //check before break block
 begin
  node:=pLine;
  repeat

   if (node^.pDst=p^.pDst) then
   begin
    Break; //end
   end;

   if node^.IsType(ntOpBlock) then
   begin
    tmp:=nil;
    if (PsrOpBlock(node)^.Block.bType in [btCond,btLoop]) then
    begin
     p^.pDst:=nil; //reset
     Break;
    end else
    begin
     tmp:=node^.Last;
    end;
    if (tmp<>nil) then
    begin
     node:=tmp;
     Continue;
    end;
   end;

   _prev:

   tmp:=node^.Prev;

   if (tmp=nil) then
   begin
    if (node^.Parent=nil) or
       (node^.Parent=pLine^.Parent) then
    begin
     Break; //end
    end else
    begin
     node:=node^.Parent;
     Goto _prev;
    end;
   end else
   begin
    node:=tmp;
   end;

  until false;
 end;

 if (p^.pDst=nil) then
 begin
  node:=AddSpirvOp(pLine,Op.OpSampledImage); //need first

  dst:=NewReg(dtTypeSampledImage);

  pType:=TypeList.Fetch(dtype);
  pType:=TypeList.FetchImage(pType,info);
  pType:=TypeList.FetchSampledImage(pType);

  node^.pType:=pType;
  node^.pDst:=dst;

  node^.AddParam(Tgrp);
  node^.AddParam(Sgrp);

  p^.pDst:=dst; //save
  Result:=dst;
 end else
 begin
  Result:=PsrRegNode(p^.pDst);
 end;
end;

//

procedure TEmitOp.OpIAdd(dst:PsrRegSlot;src0,src1:PsrRegNode);
begin
 if (src0^.dtype=dtInt32) or (src1^.dtype=dtInt32) then
 begin
  Op2(Op.OpIAdd,dtInt32,dst,src0,src1);
 end else
 begin
  Op2(Op.OpIAdd,dtUInt32,dst,src0,src1);
 end;
end;

procedure TEmitOp.OpISub(dst:PsrRegSlot;src0,src1:PsrRegNode);
begin
 if (src0^.dtype=dtInt32) or (src1^.dtype=dtInt32) then
 begin
  Op2(Op.OpISub,dtInt32,dst,src0,src1);
 end else
 begin
  Op2(Op.OpISub,dtUInt32,dst,src0,src1);
 end;
end;

procedure TEmitOp.OpIMul(dst:PsrRegSlot;src0,src1:PsrRegNode);
begin
 if (src0^.dtype=dtInt32) or (src1^.dtype=dtInt32) then
 begin
  Op2(Op.OpIMul,dtInt32,dst,src0,src1);
 end else
 begin
  Op2(Op.OpIMul,dtUInt32,dst,src0,src1);
 end;
end;

//

function TEmitOp.OpShlTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src1=nil) then Exit(src0);

 Result:=NewReg(src0^.dtype);
 _set_line(ppLine,_Op2(_get_line(ppLine),Op.OpShiftLeftLogical,Result,src0,src1));
end;

function TEmitOp.OpShlTo(src0:PsrRegNode;src1:QWORD;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src0=nil) or (src1=0) then Exit(src0);
 Result:=OpShlTo(src0,NewReg_q(src0^.dtype,src1,ppLine),ppLine);
end;

function TEmitOp.OpShrTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src1=nil) then Exit(src0);

 Result:=NewReg(src0^.dtype);
 if (src0^.dtype.Sign<>0) then
 begin
  _set_line(ppLine,_Op2(_get_line(ppLine),Op.OpShiftRightArithmetic,Result,src0,src1));
 end else
 begin
  _set_line(ppLine,_Op2(_get_line(ppLine),Op.OpShiftRightLogical   ,Result,src0,src1));
 end;
end;

function TEmitOp.OpShrTo(src0:PsrRegNode;src1:QWORD;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src0=nil) or (src1=0) then Exit(src0);
 Result:=OpShrTo(src0,NewReg_q(src0^.dtype,src1,ppLine),ppLine);
end;

//

function TEmitOp.OpIAddTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src0=nil) then Exit(src1);
 if (src1=nil) then Exit(src0);

 if (src0^.dtype=dtInt32) or (src1^.dtype=dtInt32) then
 begin
  Result:=NewReg(dtInt32);
 end else
 begin
  Result:=NewReg(dtUInt32);
 end;

 _set_line(ppLine,_Op2(_get_line(ppLine),Op.OpIAdd,Result,src0,src1));
end;

function TEmitOp.OpIAddTo(src0:PsrRegNode;src1:QWORD;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src0=nil) or (src1=0) then Exit(src0);
 Result:=OpIAddTo(src0,NewReg_q(src0^.dtype,src1,ppLine),ppLine);
end;

function TEmitOp.OpISubTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src0=nil) then Exit(src1);
 if (src1=nil) then Exit(src0);

 if (src0^.dtype=dtInt32) or (src1^.dtype=dtInt32) then
 begin
  Result:=NewReg(dtInt32);
 end else
 begin
  Result:=NewReg(dtUInt32);
 end;

 _set_line(ppLine,_Op2(_get_line(ppLine),Op.OpISub,Result,src0,src1));
end;

function TEmitOp.OpISubTo(src0:PsrRegNode;src1:QWORD;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src0=nil) or (src1=0) then Exit(src0);
 Result:=OpISubTo(src0,NewReg_q(src0^.dtype,src1,ppLine),ppLine);
end;

function TEmitOp.OpIMulTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src0=nil) then Exit(src1);
 if (src1=nil) then Exit(src0);

 if (src0^.dtype=dtInt32) or (src1^.dtype=dtInt32) then
 begin
  Result:=NewReg(dtInt32);
 end else
 begin
  Result:=NewReg(dtUInt32);
 end;

 _set_line(ppLine,_Op2(_get_line(ppLine),Op.OpIMul,Result,src0,src1));
end;

function TEmitOp.OpIMulTo(src0:PsrRegNode;src1:QWORD;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src0=nil) or (src1<=1) then Exit(src0);

 if isPowerOfTwo(src1) then
 begin
  src1:=fastIntLog2(src1);
  Result:=OpShlTo (src0,src1,ppLine);
 end else
 begin
  Result:=OpIMulTo(src0,src1,ppLine);
 end;
end;

function TEmitOp.OpIDivTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src1=nil) then Exit(src0);

 Result:=NewReg(src0^.dtype);
 if (src0^.dtype.Sign<>0) then
 begin
  _set_line(ppLine,_Op2(_get_line(ppLine),Op.OpSDiv,Result,src0,src1));
 end else
 begin
  _set_line(ppLine,_Op2(_get_line(ppLine),Op.OpUDiv,Result,src0,src1));
 end;
end;

function TEmitOp.OpIDivTo(src0:PsrRegNode;src1:QWORD;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src0=nil) or (src1<=1) then Exit(src0);

 if isPowerOfTwo(src1) then
 begin
  src1:=fastIntLog2(src1);
  Result:=OpShrTo (src0,src1,ppLine);
 end else
 begin
  Result:=OpIDivTo(src0,src1,ppLine);
 end;
end;

//

function TEmitOp.OpFAddTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src0=nil) then Exit(src1);
 if (src1=nil) then Exit(src0);

 Result:=NewReg(src0^.dtype);
 _set_line(ppLine,_Op2(_get_line(ppLine),Op.OpFAdd,Result,src0,src1));
end;

function TEmitOp.OpFSubTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src0=nil) then Exit(src1);
 if (src1=nil) then Exit(src0);

 Result:=NewReg(src0^.dtype);
 _set_line(ppLine,_Op2(_get_line(ppLine),Op.OpFSub,Result,src0,src1));
end;

function TEmitOp.OpFMulTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src0=nil) then Exit(src1);
 if (src1=nil) then Exit(src0);

 Result:=NewReg(src0^.dtype);
 _set_line(ppLine,_Op2(_get_line(ppLine),Op.OpFMul,Result,src0,src1));
end;

function TEmitOp.OpFDivTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src1=nil) then Exit(src0);

 Result:=NewReg(src0^.dtype);
 _set_line(ppLine,_Op2(_get_line(ppLine),Op.OpFDiv,Result,src0,src1));
end;

//

function TEmitOp.OpFAddToS(src0:PsrRegNode;src1:Single;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src0=nil) or (src1=0) then Exit(src0);
 Result:=OpFAddTo(src0,NewReg_s(src0^.dtype,src1,ppLine),ppLine);
end;

function TEmitOp.OpFMulToS(src0:PsrRegNode;src1:Single;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src0=nil) or (src1=0) or (src1=1) then Exit(src0);
 Result:=OpFMulTo(src0,NewReg_s(src0^.dtype,src1,ppLine),ppLine);
end;

//

function TEmitOp.OpUToF(src:PsrRegNode;rtype:TsrDataType;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src=nil) then Exit(src);

 Result:=NewReg(rtype);
 _Op1(_get_line(ppLine),Op.OpConvertUToF,Result,src);
end;

function TEmitOp.OpSToF(src:PsrRegNode;rtype:TsrDataType;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src=nil) then Exit(src);

 Result:=NewReg(rtype);
 _Op1(_get_line(ppLine),Op.OpConvertSToF,Result,src);
end;

function TEmitOp.OpUToU(src:PsrRegNode;rtype:TsrDataType;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src=nil) then Exit(src);

 Result:=NewReg(rtype);
 _Op1(_get_line(ppLine),Op.OpUConvert,Result,src);
end;

function TEmitOp.OpSToS(src:PsrRegNode;rtype:TsrDataType;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src=nil) then Exit(src);

 Result:=NewReg(rtype);
 _Op1(_get_line(ppLine),Op.OpSConvert,Result,src);
end;

function TEmitOp.OpFToF(src:PsrRegNode;rtype:TsrDataType;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src=nil) then Exit(src);

 Result:=NewReg(rtype);
 _Op1(_get_line(ppLine),Op.OpFConvert,Result,src);
end;

//

function TEmitOp.OpFloorTo(src:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src=nil) then Exit(src);

 Result:=NewReg(src^.dtype);
 _OpGlsl1(_get_line(ppLine),GlslOp.Floor,Result,src)
end;

function TEmitOp.OpPowTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src1=nil) then Exit(nil);

 Result:=NewReg(src0^.dtype);
 _set_line(ppLine,_OpGlsl2(_get_line(ppLine),GlslOp.Pow,Result,src0,src1));
end;

//

procedure TEmitOp.OpNot(dst:PsrRegSlot;src:PsrRegNode);
begin
 Op1(Op.OpNot,dtUnknow,dst,src); //post type
end;

procedure TEmitOp.OpLogicalNot(dst:PsrRegSlot;src:PsrRegNode);
begin
 Op1(Op.OpLogicalNot,dtBool,dst,src);
end;

procedure TEmitOp.OpBitwiseOr(dst:PsrRegSlot;src0,src1:PsrRegNode);
begin
 Op2(Op.OpBitwiseOr,dtUnknow,dst,src0,src1); //post type
end;

procedure TEmitOp.OpBitwiseXor(dst:PsrRegSlot;src0,src1:PsrRegNode);
begin
 Op2(Op.OpBitwiseXor,dtUint32,dst,src0,src1); //post need?
end;

procedure TEmitOp.OpLogicalOr(dst:PsrRegSlot;src0,src1:PsrRegNode);
begin
 Op2(Op.OpLogicalOr,dtBool,dst,src0,src1);
end;

procedure TEmitOp.OpBitwiseAnd(dst:PsrRegSlot;src0,src1:PsrRegNode);
begin
 Op2(Op.OpBitwiseAnd,dtUnknow,dst,src0,src1); //post type
end;

procedure TEmitOp.OpLogicalAnd(dst:PsrRegSlot;src0,src1:PsrRegNode);
begin
 Op2(Op.OpLogicalAnd,dtBool,dst,src0,src1);
end;

//

function TEmitOp.OpNotTo(src:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 Result:=NewReg(dtUnknow);
 _set_line(ppLine,_Op1(_get_line(ppLine),Op.OpNot,Result,src)); //post type
end;

function TEmitOp.OpOrTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 Result:=NewReg(dtUnknow);
 _set_line(ppLine,_Op2(_get_line(ppLine),Op.OpBitwiseOr,Result,src0,src1)); //post type
end;

function TEmitOp.OpAndTo(src0,src1:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 Result:=NewReg(dtUnknow);
 _set_line(ppLine,_Op2(_get_line(ppLine),Op.OpBitwiseAnd,Result,src0,src1)); //post type
end;

function TEmitOp.OpAndTo(src0:PsrRegNode;src1:QWORD;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 if (src0=nil) then Exit(src0);
 Result:=OpAndTo(src0,NewReg_q(src0^.dtype,src1,ppLine),ppLine);
end;

//

function TEmitOp.OpBitCountTo(src:PsrRegNode;ppLine:PPspirvOp=nil):PsrRegNode;
begin
 Result:=NewReg(src^.dtype);
 _set_line(ppLine,_Op1(_get_line(ppLine),Op.OpBitCount,Result,src));
end;

//

function TEmitOp.OpImageSampleImplicitLod(pLine:PspirvOp;img:PsrNode;dst,coord:PsrRegNode):PSpirvOp;
Var
 node:PSpirvOp;
begin
 node:=AddSpirvOp(pLine,Op.OpImageSampleImplicitLod); //need first

 node^.pType:=TypeList.Fetch(dst^.dtype);
 node^.pDst:=dst;

 node^.AddParam(img);   //Sampled Image
 node^.AddParam(coord); //Coordinate
                        //Image Operands

 Result:=node;
end;

function TEmitOp.OpImageSampleExplicitLod(pLine:PspirvOp;img:PsrNode;dst,coord:PsrRegNode):PSpirvOp;
Var
 node:PSpirvOp;
begin
 node:=AddSpirvOp(pLine,Op.OpImageSampleExplicitLod); //need first

 node^.pType:=TypeList.Fetch(dst^.dtype);
 node^.pDst:=dst;

 node^.AddParam(img);   //Sampled Image
 node^.AddParam(coord); //Coordinate
                        //Image Operands
 Result:=node;
end;

function TEmitOp.OpImageSampleDrefImplicitLod(pLine:PspirvOp;img:PsrNode;dst,coord,pcf:PsrRegNode):PSpirvOp;
Var
 node:PSpirvOp;
begin
 node:=AddSpirvOp(pLine,Op.OpImageSampleDrefImplicitLod); //need first

 node^.pType:=TypeList.Fetch(dst^.dtype);
 node^.pDst:=dst;

 node^.AddParam(img);   //Sampled Image
 node^.AddParam(coord); //Coordinate
 node^.AddParam(pcf);   //Dref
                        //Image Operands

 Result:=node;
end;

function TEmitOp.OpImageSampleDrefExplicitLod(pLine:PspirvOp;img:PsrNode;dst,coord,pcf:PsrRegNode):PSpirvOp;
Var
 node:PSpirvOp;
begin
 node:=AddSpirvOp(pLine,Op.OpImageSampleDrefExplicitLod); //need first

 node^.pType:=TypeList.Fetch(dst^.dtype);
 node^.pDst:=dst;

 node^.AddParam(img);   //Sampled Image
 node^.AddParam(coord); //Coordinate
 node^.AddParam(pcf);   //Dref
                        //Image Operands

 Result:=node;
end;

function TEmitOp.OpImageGather(pLine:PspirvOp;img:PsrNode;dst,coord:PsrRegNode;id:Byte):PSpirvOp;
Var
 node:PSpirvOp;
 comp:PsrRegNode;
begin
 Case id of
  0..3:;
  else
   Assert(False);
 end;

 comp:=NewReg_i(dtUint32,id);

 node:=AddSpirvOp(pLine,Op.OpImageGather); //need first

 node^.pType:=TypeList.Fetch(dst^.dtype);
 node^.pDst:=dst;

 node^.AddParam(img);   //Sampled Image
 node^.AddParam(coord); //Coordinate
 node^.AddParam(comp);  //Component
                        //Image Operands

 Result:=node;
end;

function TEmitOp.OpImageDrefGather(pLine:PspirvOp;img:PsrNode;dst,coord,pcf:PsrRegNode):PSpirvOp;
Var
 node:PSpirvOp;
begin
 node:=AddSpirvOp(pLine,Op.OpImageDrefGather); //need first

 node^.pType:=TypeList.Fetch(dst^.dtype);
 node^.pDst:=dst;

 node^.AddParam(img);   //Sampled Image
 node^.AddParam(coord); //Coordinate
 node^.AddParam(pcf);   //Dref
                        //Image Operands

 Result:=node;
end;

function TEmitOp.OpImageFetch(pLine:PspirvOp;Tgrp:PsrNode;dst,coord:PsrRegNode):PSpirvOp;
Var
 node:PSpirvOp;
begin
 node:=AddSpirvOp(pLine,Op.OpImageFetch); //need first

 node^.pType:=TypeList.Fetch(dst^.dtype);
 node^.pDst:=dst;

 node^.AddParam(Tgrp);
 node^.AddParam(coord);

 Result:=node;
end;

function TEmitOp.OpImageRead(pLine:PspirvOp;Tgrp:PsrNode;dst,idx:PsrRegNode):PspirvOp;
Var
 node:PSpirvOp;
begin
 node:=AddSpirvOp(pLine,Op.OpImageRead); //need first

 node^.pType:=TypeList.Fetch(dst^.dtype);
 node^.pDst:=dst;

 node^.AddParam(Tgrp);

 if (idx<>nil) then
 begin
  node^.AddParam(idx);
 end else
 begin
  node^.AddParam(ConstList.Fetch_i(dtUint32,0));
 end;

 Result:=node;
end;

function TEmitOp.OpImageWrite(pLine:PspirvOp;Tgrp:PsrNode;idx,src:PsrRegNode):PspirvOp;
Var
 node:PSpirvOp;
begin
 node:=AddSpirvOp(pLine,Op.OpImageWrite); //need first
 node^.pDst:=Tgrp; //write

 if (idx<>nil) then
 begin
  node^.AddParam(idx);
 end else
 begin
  node^.AddParam(ConstList.Fetch_i(dtUint32,0));
 end;

 node^.AddParam(src);

 Result:=node;
end;


end.

