unit emit_op;

{$mode objfpc}{$H+}

interface

uses
 spirv,
 srNode,
 srType,
 srTypes,
 srConst,
 srReg,
 srOp,
 srCFGParser,
 srOpInternal,
 srOpUtils,
 srCacheOp,
 srInterface;

type
 TEmitOp=class(TEmitInterface)
  //
  procedure MakeCopy64(dst0,dst1:PsrRegSlot;src:TsrRegNode);
  function  GetThreadBit(exe0,exe1:PsrRegSlot;rtype:TsrDataType;ppLine:PPspirvOp=nil):TsrRegNode;
  procedure SetThreadBit(exe0,exe1:PsrRegSlot;val:TsrRegNode);
  //
  function  fetch_vccnz (ppLine:PPspirvOp):TsrRegNode;
  function  fetch_execnz(ppLine:PPspirvOp):TsrRegNode;
  function  fetch_execnz_tid(ppLine:PPspirvOp):TsrRegNode;
  function  fetch_scc  :TsrRegNode;
  //
  function  _Op1(pLine:TspirvOp;OpId:DWORD;dst,src:TsrRegNode):TspirvOp;
  function  _Op2(pLine:TspirvOp;OpId:DWORD;dst,src0,src1:TsrRegNode):TspirvOp;
  function  _Op3(pLine:TspirvOp;OpId:DWORD;dst,src0,src1,src2:TsrRegNode):TspirvOp;
  function  _Op4(pLine:TspirvOp;OpId:DWORD;dst,src0,src1,src2,src3:TsrRegNode):TspirvOp;
  //
  function  _OpGlsl1(pLine:TspirvOp;OpId:DWORD;dst,src:TsrRegNode):TspirvOp;
  function  _OpGlsl2(pLine:TspirvOp;OpId:DWORD;dst,src0,src1:TsrRegNode):TspirvOp;
  function  _OpGlsl3(pLine:TspirvOp;OpId:DWORD;dst,src0,src1,src2:TsrRegNode):TspirvOp;
  //
  function  Op1(OpId:DWORD;rtype:TsrDataType;dst:PsrRegSlot;src:TsrRegNode):TspirvOp;
  function  Op2(OpId:DWORD;rtype:TsrDataType;dst:PsrRegSlot;src0,src1:TsrRegNode):TspirvOp;
  function  Op3(OpId:DWORD;rtype:TsrDataType;dst:PsrRegSlot;src0,src1,src2:TsrRegNode):TspirvOp;
  function  Op4(OpId:DWORD;rtype:TsrDataType;dst:PsrRegSlot;src0,src1,src2,src3:TsrRegNode):TspirvOp;
  //
  function  OpGlsl1(OpId:DWORD;rtype:TsrDataType;dst:PsrRegSlot;src:TsrRegNode):TspirvOp;
  function  OpGlsl2(OpId:DWORD;rtype:TsrDataType;dst:PsrRegSlot;src0,src1:TsrRegNode):TspirvOp;
  function  OpGlsl3(OpId:DWORD;rtype:TsrDataType;dst:PsrRegSlot;src0,src1,src2:TsrRegNode):TspirvOp;
  //
  function  OpBitcast(pLine:TspirvOp;dst,src:TsrRegNode):TspirvOp;
  function  OpBitcast(pLine:TspirvOp;pType:TsrType;dst,src:TsrNode):TspirvOp;
  function  OpBoolToInt(pLine:TspirvOp;dst,src:TsrRegNode):TspirvOp;
  function  OpIntToBool(pLine:TspirvOp;dst,src:TsrRegNode):TspirvOp;
  function  OpIntToBoolTo(src:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  //
  function  OpCast (nLine,      dst,src:TsrNode):TsrNode; override;
  function  OpLoad (nLine,dtype,dst,src:TsrNode):TsrNode; override;
  function  OpStore(nLine,      dst,src:TsrNode):TsrNode; override;
  //
  function  OpLoad(pLine:TspirvOp;dst:TsrRegNode;src:TsrNode):TspirvOp;
  function  OpLoadTo(pType:TsrType;src:TsrNode;ppLine:PPspirvOp=nil):TsrRegNode;
  //
  function  OpLine(nLine:TsrNode;iLine,iColumn:DWORD):TsrNode;
  //
  function  OpExtract(pLine:TspirvOp;dst,src:TsrRegNode;id:DWORD):TspirvOp;
  function  OpConstruct(pLine:TspirvOp;dst:TsrRegNode):TspirvOp;
  function  OpAccessChain(pLine:TspirvOp;vType:TsrType;dst,src:TsrNode):TspirvOp;
  function  OpAccessChainTo(vType:TsrType;src:TsrNode;idx0:TsrNode;ppLine:PPspirvOp=nil):TsrNode;
  function  OpCondMerge(pLine,pLabel:TspirvOp):TspirvOp;
  function  OpLoopMerge(pLine,pLabel0,pLabel1:TspirvOp):TspirvOp;
  function  OpBranch(pLine,pLabel:TspirvOp):TspirvOp;
  function  OpBranchCond(pLine,pLabel0,pLabel1:TspirvOp;src:TsrRegNode):TspirvOp;
  function  OpReturnValue(pLine:TspirvOp;src:TsrRegNode):TspirvOp;
  //
  function  OpReturn(pLine:TspirvOp):TspirvOp;
  function  OpFunctionEnd(pLine:TspirvOp):TspirvOp;
  function  OpEmitVertex(pLine:TspirvOp):TspirvOp;
  function  OpEndPrimitive(pLine:TspirvOp):TspirvOp;
  //
  procedure OpFmaF32(dst:PsrRegSlot;src0,src1,src2:TsrRegNode);
  procedure OpFmaI32(dst:PsrRegSlot;src0,src1,src2:TsrRegNode);
  procedure OpFmaU32(dst:PsrRegSlot;src0,src1,src2:TsrRegNode);
  //
  procedure OpSelect(dst:PsrRegSlot;cond,src_true,src_false:TsrRegNode);
  function  OpSelectTo(cond,src_true,src_false:TsrRegNode):TsrRegNode;
  //
  procedure OpIAddCar(pLine:TspirvOp;dst,car,src0,src1:TsrRegNode);
  procedure OpIAddExt(dst,car:PsrRegSlot;src0,src1:TsrRegNode;rtype:TsrDataType);
  procedure OpIAddExt(dst,car,src0,src1:TsrRegNode);
  //
  procedure OpISubBor(pLine:TspirvOp;dst,bor,src0,src1:TsrRegNode);
  procedure OpISubExt(dst,bor:PsrRegSlot;src0,src1:TsrRegNode;rtype:TsrDataType);
  //
  function  OpAbsDiff(pLine:TspirvOp;dst,src0,src1:TsrRegNode):TspirvOp;
  procedure OpWQM32(dst:PsrRegSlot;src:TsrRegNode);
  //
  procedure OpBFE_32(dst:PsrRegSlot;base,src0,src1:TsrRegNode);
  procedure OpBFIB32(dst:PsrRegSlot;bitmsk,src0,src1:TsrRegNode);
  //
  function  OpBFITo (src0,src1,src2,src3:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpBFUETo(src0,src1,src2:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpBFSETo(src0,src1,src2:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  //
  procedure OpPackAnc(dst:PsrRegSlot;prim,smid,rtid:TsrRegNode);
  //
  function  OpSMinTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpSMaxTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpUMinTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpUMaxTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpFMinTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpFMaxTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpNMinTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpNMaxTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  //
  procedure OpMED3I(dst:PsrRegSlot;src0,src1,src2:TsrRegNode);
  procedure OpMED3U(dst:PsrRegSlot;src0,src1,src2:TsrRegNode);
  procedure OpMED3F(dst:PsrRegSlot;src0,src1,src2:TsrRegNode);
  //
  function  OpPackOfs(pLine:TspirvOp;rtype:TsrDataType;count:Byte;src:TsrRegNode):TsrRegNode;
  function  OpMakeCon(pLine:TspirvOp;dst:TsrRegNode;src:PPsrRegNode):TspirvOp;
  function  OpMakeVec(pLine:TspirvOp;rtype:TsrDataType;src:PPsrRegNode):TsrRegNode;
  function  fetch64  (src:PPsrRegNode;rtype:TsrDataType;ppLine:PPspirvOp=nil):TsrRegNode;
  function  fetch64  (src0,src1:TsrRegNode;rtype:TsrDataType;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpSampledImage(pLine:TspirvOp;Tgrp,Sgrp:TsrNode;dtype:TsrDataType;info:TsrTypeImageInfo):TsrRegSampledImage;
  //
  procedure OpIAdd(dst:PsrRegSlot;src0,src1:TsrRegNode);
  procedure OpISub(dst:PsrRegSlot;src0,src1:TsrRegNode);
  procedure OpIMul(dst:PsrRegSlot;src0,src1:TsrRegNode);
  //
  function  OpShlTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpShlTo(src0:TsrRegNode;src1:QWORD;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpShrTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpShrTo(src0:TsrRegNode;src1:QWORD;ppLine:PPspirvOp=nil):TsrRegNode;
  //
  function  OpIAddTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpIAddTo(src0:TsrRegNode;src1:QWORD;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpISubTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpISubTo(src0:TsrRegNode;src1:QWORD;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpIMulTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpIMulTo(src0:TsrRegNode;src1:QWORD;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpIDivTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpIDivTo(src0:TsrRegNode;src1:QWORD;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpIModTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  //
  function  OpFAddTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpFSubTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpFMulTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpFDivTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  //
  function  OpFAddToS(src0:TsrRegNode;src1:Single;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpFMulToS(src0:TsrRegNode;src1:Single;ppLine:PPspirvOp=nil):TsrRegNode;
  //
  function  OpUToF(src:TsrRegNode;rtype:TsrDataType;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpFToU(src:TsrRegNode;rtype:TsrDataType;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpSToF(src:TsrRegNode;rtype:TsrDataType;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpUToU(src:TsrRegNode;rtype:TsrDataType;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpSToS(src:TsrRegNode;rtype:TsrDataType;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpFToF(src:TsrRegNode;rtype:TsrDataType;ppLine:PPspirvOp=nil):TsrRegNode;
  //
  function  OpFloorTo  (src:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpPowTo    (src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpFAbsTo   (src:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpFNegateTo(src:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  //
  procedure OpNot       (dst:PsrRegSlot;src:TsrRegNode);
  procedure OpLogicalNot(dst:PsrRegSlot;src:TsrRegNode);
  procedure OpBitwiseOr (dst:PsrRegSlot;src0,src1:TsrRegNode);
  procedure OpBitwiseXor(dst:PsrRegSlot;src0,src1:TsrRegNode);
  procedure OpLogicalOr (dst:PsrRegSlot;src0,src1:TsrRegNode);
  procedure OpBitwiseAnd(dst:PsrRegSlot;src0,src1:TsrRegNode);
  procedure OpLogicalAnd(dst:PsrRegSlot;src0,src1:TsrRegNode);
  //
  function  OpLogicalNotTo(src:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpNotTo(src:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpOrTo (src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpAndTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpAndTo(src0:TsrRegNode;src1:QWORD;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpLogicalOrTo (src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpLogicalAndTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  //
  function  OpIsNanTo(src:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpIsSSignTo(src:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpIEqualTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpINotEqualTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpEqualTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpNotEqualTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  //
  function  OpBitCountTo(src:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  function  OpCmpTo(OpId:DWORD;src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
  //
  function  OpImageSampleImplicitLod(pLine:TspirvOp;img:TsrNode;dst,coord:TsrRegNode):TspirvOp;
  function  OpImageSampleExplicitLod(pLine:TspirvOp;img:TsrNode;dst,coord:TsrRegNode):TspirvOp;
  function  OpImageSampleDrefImplicitLod(pLine:TspirvOp;img:TsrNode;dst,coord,pcf:TsrRegNode):TspirvOp;
  function  OpImageSampleDrefExplicitLod(pLine:TspirvOp;img:TsrNode;dst,coord,pcf:TsrRegNode):TspirvOp;
  function  OpImageGather(pLine:TspirvOp;img:TsrNode;dst,coord:TsrRegNode;id:Byte):TspirvOp;
  function  OpImageDrefGather(pLine:TspirvOp;img:TsrNode;dst,coord,pcf:TsrRegNode):TspirvOp;
  function  OpImageFetch(pLine:TspirvOp;Tgrp:TsrNode;dst,coord:TsrRegNode):TspirvOp;
  function  OpImageRead(pLine:TspirvOp;Tgrp:TsrNode;dst,idx:TsrRegNode):TspirvOp;
  function  OpImageWrite(pLine:TspirvOp;Tgrp:TsrNode;idx,src:TsrRegNode):TspirvOp;
  function  OpImageQuerySizeLod(pLine:TspirvOp;img:TsrNode;dst,lod:TsrRegNode):TspirvOp;
  function  OpImageQueryLod(pLine:TspirvOp;img:TsrNode;dst,coord:TsrRegNode):TspirvOp;
 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:TspirvOp);
begin
 if (ppLine=nil) then Exit;
 ppLine^:=pLine;
end;

//

procedure TEmitOp.MakeCopy64(dst0,dst1:PsrRegSlot;src:TsrRegNode);
var
 dst:TsrRegNode;
 node:array[0..1] of TsrRegNode;
begin
 dst:=BitcastList.FetchRead(dtVec2u,src);

 node[0]:=dst0^.New(dtUint32,line);
 node[1]:=dst1^.New(dtUint32,line);

 OpExtract(line,node[0],dst,0);
 OpExtract(line,node[1],dst,1);
end;

function TEmitOp.GetThreadBit(exe0,exe1:PsrRegSlot;rtype:TsrDataType;ppLine:PPspirvOp=nil):TsrRegNode;
var
 mask:TsrRegNode;
begin
 if Config.UseExtendedEXECMask and (FExecutionModel=ExecutionModel.GLCompute) then
 begin
  mask:=fetch64(MakeRead(exe0,dtUint32),MakeRead(exe1,dtUint32),dtUint64,ppLine);

  Result:=OpShrTo(mask,FThread_id,ppLine);
  Result:=OpUToU (Result,dtUint32,ppLine);
  Result:=OpAndTo(Result,1,ppLine);
  Result.PrepType(ord(dtUint32));
  Result:=BitcastList.FetchRead(rtype,Result);

  //Result:=OpBFUETo(mask,FThread_id,NewImm_i(dtUint32,1));
  //Result:=OpUToU(Result,dtUint32);
  //Result:=BitcastList.FetchRead(rtype,Result);
 end else
 begin
  //It means that lane_id=0
  Result:=MakeRead(exe0,rtype);
 end;
end;

procedure TEmitOp.SetThreadBit(exe0,exe1:PsrRegSlot;val:TsrRegNode);
var
 mask,mask2:TsrRegNode;
begin
 if Config.UseExtendedEXECMask and (FExecutionModel=ExecutionModel.GLCompute) then
 begin

  val:=BitcastList.FetchRead(dtUint32,val);
  val:=OpUToU (val,dtUint64);
  val:=OpShlTo(val,FThread_id);

  mask:=fetch64(MakeRead(exe0,dtUint32),MakeRead(exe1,dtUint32),dtUint64);

  mask2:=OpShlTo(NewImm_i(dtUint64,1),FThread_id);
  mask2:=OpNotTo(mask2);
  mask2.PrepType(ord(dtUint64));

  mask:=OpAndTo(mask,mask2);
  mask.PrepType(ord(dtUint64));

  mask:=OpOrTo(mask,val);
  mask.PrepType(ord(dtUint64));

  MakeCopy64(exe0,exe1,mask);

  //val:=BitcastList.FetchRead(dtUint32,val);
  //val:=OpUToU(val,dtUint64);
  //mask:=fetch64(exe0^.current,exe1^.current,dtUint64);
  //mask:=OpBFITo(mask,val,FThread_id,NewImm_i(dtUint32,1));
  //MakeCopy64(exe0,exe1,mask);
  //
 end else
 begin
  //It means that lane_id=0
  MakeCopy  (exe0,val);
  SetConst_q(exe1,dtUnknow,0); //set zero
 end;
end;

//

function TEmitOp.fetch_vccnz(ppLine:PPspirvOp):TsrRegNode;
var
 src:array[0..1] of TsrRegNode;
begin
 //It means that (vcc0  != 0) || (vcc1  != 0)

 src[0]:=MakeRead(get_vcc0,dtBool); //implict cast (int != 0)
 src[1]:=MakeRead(get_vcc1,dtBool); //implict cast (int != 0)

 Result:=OpLogicalOrTo(src[0],src[1],ppLine);
end;

function TEmitOp.fetch_execnz(ppLine:PPspirvOp):TsrRegNode;
var
 src:array[0..1] of TsrRegNode;
begin
 //It means that (exec0 != 0) || (exec1 != 0)

 src[0]:=MakeRead(get_exec0,dtBool); //implict cast (int != 0)
 src[1]:=MakeRead(get_exec1,dtBool); //implict cast (int != 0)

 Result:=OpLogicalOrTo(src[0],src[1],ppLine);
end;

function TEmitOp.fetch_execnz_tid(ppLine:PPspirvOp):TsrRegNode;
begin
 //It means that (exec[thread_id:] == 0)

 Result:=GetThreadBit(get_exec0,get_exec1,dtBool,ppLine);
end;

function TEmitOp.fetch_scc:TsrRegNode;
begin
 Result:=MakeRead(get_scc,dtBool);
end;

//

function TEmitOp._Op1(pLine:TspirvOp;OpId:DWORD;dst,src:TsrRegNode):TspirvOp;
Var
 node:TspirvOp;
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:TspirvOp;OpId:DWORD;dst,src0,src1:TsrRegNode):TspirvOp;
Var
 node:TspirvOp;
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:TspirvOp;OpId:DWORD;dst,src0,src1,src2:TsrRegNode):TspirvOp;
Var
 node:TspirvOp;
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:TspirvOp;OpId:DWORD;dst,src0,src1,src2,src3:TsrRegNode):TspirvOp;
Var
 node:TspirvOp;
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:TspirvOp;OpId:DWORD;dst,src:TsrRegNode):TspirvOp;
Var
 node:TspirvOp;
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:TspirvOp;OpId:DWORD;dst,src0,src1:TsrRegNode):TspirvOp;
Var
 node:TspirvOp;
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:TspirvOp;OpId:DWORD;dst,src0,src1,src2:TsrRegNode):TspirvOp;
Var
 node:TspirvOp;
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:TsrRegNode):TspirvOp;
begin
 Result:=_Op1(line,OpId,dst^.New(rtype),src);
end;

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

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

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

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

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

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

function TEmitOp.OpBitcast(pLine:TspirvOp;dst,src:TsrRegNode):TspirvOp;
begin
 Result:=_Op1(pLine,Op.OpBitcast,dst,src);
end;

function TEmitOp.OpBitcast(pLine:TspirvOp;pType:TsrType;dst,src:TsrNode):TspirvOp;
Var
 node:TspirvOp;
begin
 node:=AddSpirvOp(pLine,Op.OpBitcast); //need first
 node.pType:=pType;
 node.pDst:=dst;
 node.AddParam(src);
 Result:=node;
end;

function TEmitOp.OpBoolToInt(pLine:TspirvOp;dst,src:TsrRegNode):TspirvOp;
Var
 src0,src1:TsrRegNode;
begin
 src0:=NewImm_q(dst.dtype,0,pLine);
 src1:=NewImm_q(dst.dtype,1,pLine);

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

function TEmitOp.OpIntToBool(pLine:TspirvOp;dst,src:TsrRegNode):TspirvOp;
Var
 src0:TsrRegNode;
begin
 src0:=NewImm_q(src.dtype,0,pLine);

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

function TEmitOp.OpIntToBoolTo(src:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
begin
 Result:=NewReg(dtBool);
 _set_line(ppLine,_Op1(_get_line(ppLine),Op.OpINotEqual,Result,src));
end;

function TEmitOp.OpCast(nLine,dst,src:TsrNode):TsrNode;
var
 rdst,rsrc,rtmp:TsrRegNode;
 pLine:TspirvOp;
begin
 pLine:=nLine.specialize AsType<ntOpCustom>;
 rdst :=dst.specialize AsType<ntReg>;
 rsrc :=src.specialize AsType<ntReg>;

 Assert(pLine<>nil);
 Assert(rdst<>nil);
 Assert(rsrc<>nil);

 if (rsrc.dtype=dtBool) then
 begin
  Case rdst.dtype of
   dtInt32,
   dtUint32:Result:=OpBoolToInt(pLine,rdst,rsrc);
   else
    begin
     rtmp:=NewReg(dtUint32);
     pLine :=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);
     pLine :=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:TsrNode):TsrNode;
var
 pLine:TspirvOp;
 pType:TsrType;
 node:TspirvOp;
begin
 pLine:=nLine.specialize AsType<ntOpCustom>;
 pType:=dtype.specialize 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:TsrNode):TsrNode;
Var
 pLine:TspirvOp;
 node:TspirvOp;
begin
 pLine:=nLine.specialize AsType<ntOpCustom>;
 Assert(dst<>nil);
 Assert(src<>nil);
 Assert(pLine<>nil);
 node:=AddSpirvOp(pLine,Op.OpStore);
 node.AddParam(dst); //write
 node.AddParam(src);
 Result:=node;
end;

function TEmitOp.OpLoad(pLine:TspirvOp;dst:TsrRegNode;src:TsrNode):TspirvOp;
Var
 dtype:TsrType;
begin
 dtype:=TypeList.Fetch(dst.dtype);
 Result:=OpLoad(pLine,dtype,dst,src);
end;

function TEmitOp.OpLoadTo(pType:TsrType;src:TsrNode;ppLine:PPspirvOp=nil):TsrRegNode;
begin
 Result:=NewReg(pType.dtype);
 //
 _set_line(ppLine,OpLoad(_get_line(ppLine),pType,Result,src));
end;

function TEmitOp.OpLine(nLine:TsrNode;iLine,iColumn:DWORD):TsrNode;
var
 pLine:TspirvOp;
 node:TspirvOp;
 nFile:TsrNode;
begin
 pLine:=nLine.specialize AsType<ntOpCustom>;
 Assert(pLine<>nil);
 nFile:=DebugInfoList.FileName;
 node:=AddSpirvOp(pLine,Op.OpLine);
 node.AddParam(nFile);
 node.AddLiteral(iLine,'0x');
 node.AddLiteral(iColumn);
 Result:=node;
end;

function TEmitOp.OpExtract(pLine:TspirvOp;dst,src:TsrRegNode;id:DWORD):TspirvOp;
Var
 node:TspirvOp;
begin
 Assert(src.dtype.isVector);
 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:TspirvOp;dst:TsrRegNode):TspirvOp;
var
 node:TspirvOp;
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:TspirvOp;vType:TsrType;dst,src:TsrNode):TspirvOp;
Var
 node:TspirvOp;
begin
 node:=AddSpirvOp(pLine,Op.OpAccessChain);
 Assert(vType<>nil);
 node.pType:=TypeList.FetchPointer(vType,src.GetStorageClass);
 node.pDst:=dst;
 node.AddParam(src); //base
 Result:=node;
 //index add later
end;

function TEmitOp.OpAccessChainTo(vType:TsrType;src:TsrNode;idx0:TsrNode;ppLine:PPspirvOp=nil):TsrNode;
Var
 node:TspirvOp;
begin
 Result:=NewRefNode;
 //
 node:=OpAccessChain(_get_line(ppLine),vType,Result,src);
 node.AddParam(idx0);
 _set_line(ppLine,node);
end;

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

function TEmitOp.OpLoopMerge(pLine,pLabel0,pLabel1:TspirvOp):TspirvOp;
Var
 node:TspirvOp;
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:TspirvOp):TspirvOp;
Var
 node:TspirvOp;
begin
 node:=AddSpirvOp(pLine,Op.OpBranch);
 node.AddParam(pLabel.pDst);
 Result:=node;
end;

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

function TEmitOp.OpReturnValue(pLine:TspirvOp;src:TsrRegNode):TspirvOp;
Var
 node:TspirvOp;
begin
 node:=AddSpirvOp(pLine,Op.OpReturnValue);
 node.AddParam(src);
 Result:=node;
end;

//

function TEmitOp.OpReturn(pLine:TspirvOp):TspirvOp;
begin
 Result:=AddSpirvOp(pLine,Op.OpReturn);
end;

function TEmitOp.OpFunctionEnd(pLine:TspirvOp):TspirvOp;
begin
 Result:=AddSpirvOp(pLine,Op.OpFunctionEnd);
end;

function TEmitOp.OpEmitVertex(pLine:TspirvOp):TspirvOp;
begin
 Result:=AddSpirvOp(pLine,Op.OpEmitVertex);
end;

function TEmitOp.OpEndPrimitive(pLine:TspirvOp):TspirvOp;
begin
 Result:=AddSpirvOp(pLine,Op.OpEndPrimitive);
end;

//

procedure TEmitOp.OpFmaF32(dst:PsrRegSlot;src0,src1,src2:TsrRegNode);
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:TsrRegNode);
var
 mul,sum:TsrRegNode;
begin
 //vdst = vsrc0.i * vsrc1.i + vdst.i
 mul:=NewReg(dtInt32);
 sum:=dst^.New(dtInt32);

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

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

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

//

procedure TEmitOp.OpSelect(dst:PsrRegSlot;cond,src_true,src_false:TsrRegNode);
begin
 Op3(Op.OpSelect,LazyType2(src_true.dtype,src_false.dtype),dst,cond,src_true,src_false);
end;

function TEmitOp.OpSelectTo(cond,src_true,src_false:TsrRegNode):TsrRegNode;
begin
 Result:=NewReg(LazyType2(src_true.dtype,src_false.dtype));
 //
 _Op3(line,Op.OpSelect,Result,cond,src_true,src_false);
end;

procedure TEmitOp.OpIAddCar(pLine:TspirvOp;dst,car,src0,src1:TsrRegNode);
Var
 node:TspirvOp;
 rsl:TsrRegNode;
 rtype:TsrDataType;
begin
 node:=AddSpirvOp(pLine,Op.OpIAddCarry); //need first
 node.AddParam(src0);
 node.AddParam(src1);

 rtype:=dst.dtype.AsStruct2;
 Assert(rtype<>dtUnknow);

 rsl:=NewReg(rtype);

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

 dst:=BitcastList.FetchDstr(rtype.Child,dst);
 car:=BitcastList.FetchDstr(rtype.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:TsrRegNode;rtype:TsrDataType);
Var
 node:TspirvOp;
 rsl:TsrRegPair;
begin
 Assert(rtype.Sign=0);

 node:=AddSpirvOp(srOpInternal.OpIAddExt); //need first
 node.AddParam(src0);
 node.AddParam(src1);

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

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

procedure TEmitOp.OpIAddExt(dst,car,src0,src1:TsrRegNode);
Var
 node:TspirvOp;
 rsl:TsrRegPair;
begin
 Assert(dst.dtype.Sign=0);

 node:=AddSpirvOp(srOpInternal.OpIAddExt); //need first
 node.AddParam(src0);
 node.AddParam(src1);

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

 rsl.pDst0:=dst; //dtUint32,dtUint64
 rsl.pDst1:=car; //dtUint32,dtUint64
end;

procedure TEmitOp.OpISubBor(pLine:TspirvOp;dst,bor,src0,src1:TsrRegNode);
Var
 node:TspirvOp;
 rsl:TsrRegNode;
 rtype:TsrDataType;
begin
 node:=AddSpirvOp(pLine,Op.OpISubBorrow); //need first
 node.AddParam(src0);
 node.AddParam(src1);

 rtype:=dst.dtype.AsStruct2;
 Assert(rtype<>dtUnknow);

 rsl:=NewReg(rtype);

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

 dst:=BitcastList.FetchDstr(rtype.Child,dst);
 bor:=BitcastList.FetchDstr(rtype.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:TsrRegNode;rtype:TsrDataType);
Var
 node:TspirvOp;
 rsl:TsrRegPair;
begin
 Assert(rtype.Sign=0);

 node:=AddSpirvOp(srOpInternal.OpISubExt); //need first
 node.AddParam(src0);
 node.AddParam(src1);

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

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

//

function TEmitOp.OpAbsDiff(pLine:TspirvOp;dst,src0,src1:TsrRegNode):TspirvOp;
begin
 Result:=_Op2(pLine,srOpInternal.OpAbsDiff,dst,src0,src1);
end;

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

procedure TEmitOp.OpBFE_32(dst:PsrRegSlot;base,src0,src1:TsrRegNode);
begin
 Op3(srOpInternal.OpBFE_32,base.dtype,dst,base,src0,src1);
end;

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

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

function TEmitOp.OpBFUETo(src0,src1,src2:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
begin
 Result:=NewReg(src0.dtype);
 _set_line(ppLine,_Op3(_get_line(ppLine),Op.OpBitFieldUExtract,Result,src0,src1,src2));
end;

function TEmitOp.OpBFSETo(src0,src1,src2:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
begin
 Result:=NewReg(src0.dtype);
 _set_line(ppLine,_Op3(_get_line(ppLine),Op.OpBitFieldSExtract,Result,src0,src1,src2));
end;

//

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

//

function TEmitOp.OpSMinTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
begin
 Result:=NewReg(src0.dtype);
 _set_line(ppLine,_OpGlsl2(_get_line(ppLine),GlslOp.SMin,Result,src0,src1));
end;

function TEmitOp.OpSMaxTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
begin
 Result:=NewReg(src0.dtype);
 _set_line(ppLine,_OpGlsl2(_get_line(ppLine),GlslOp.SMax,Result,src0,src1));
end;

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

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

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

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

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

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

procedure TEmitOp.OpMED3I(dst:PsrRegSlot;src0,src1,src2:TsrRegNode);
var
 min:TsrRegNode;
 max:TsrRegNode;
 mmx:TsrRegNode;
begin
 min:=OpSMinTo(src0,src1); //min(s0,s1)
 max:=OpSMaxTo(src0,src1); //max(s0,s1)
 mmx:=OpSMinTo(max ,src2); //min(max(s0,s1),s2)

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

procedure TEmitOp.OpMED3U(dst:PsrRegSlot;src0,src1,src2:TsrRegNode);
var
 min:TsrRegNode;
 max:TsrRegNode;
 mmx:TsrRegNode;
begin
 min:=OpUMinTo(src0,src1); //min(s0,s1)
 max:=OpUMaxTo(src0,src1); //max(s0,s1)
 mmx:=OpUMinTo(max ,src2); //min(max(s0,s1),s2)

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

procedure TEmitOp.OpMED3F(dst:PsrRegSlot;src0,src1,src2:TsrRegNode);
var
 min:TsrRegNode;
 max:TsrRegNode;
 mmx:TsrRegNode;
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:TspirvOp;rtype:TsrDataType;count:Byte;src:TsrRegNode):TsrRegNode;
Var
 p:TsrCacheOp;
 dst:TsrRegNode;
 node:TspirvOp;
begin
 Assert(count<>0);
 Assert(src<>nil);

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

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

 if (p.pDst=nil) then
 begin
  node:=AddSpirvOp(pLine,srOpInternal.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:=p.pDst;
 end;
end;

function TEmitOp.OpMakeCon(pLine:TspirvOp;dst:TsrRegNode;src:PPsrRegNode):TspirvOp;
Var
 p:TsrCacheOp;
 node:TspirvOp;
 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:TspirvOp;rtype:TsrDataType;src:PPsrRegNode):TsrRegNode;
Var
 p:TsrCacheOp;
 dst:TsrRegNode;
 node:TspirvOp;
 i:Byte;
begin
 Assert(src<>nil);

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

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

 if (p.pDst=nil) then
 begin
  node:=AddSpirvOp(pLine,srOpInternal.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:=p.pDst;
 end;
end;

type
 t_bridge_info=record
  val:TsrRegNode;
  pos:PtrUint;
 end;

function get_bridge(src:TsrRegNode):t_bridge_info;
var
 pLine:TSpirvOp;
begin
 Result:=Default(t_bridge_info);

 pLine:=src.pWriter.specialize AsType<ntOp>;
 if (pLine=nil) then Exit;
 if (pLine.OpId<>Op.OpCompositeExtract) then Exit;

 if not pLine.ParamNode(1).TryGetValue(Result.pos) then Exit;

 Result.val:=pLine.ParamNode(0).AsReg;
end;

function TEmitOp.fetch64(src:PPsrRegNode;rtype:TsrDataType;ppLine:PPspirvOp=nil):TsrRegNode;
var
 bri:array[0..1] of t_bridge_info;
 dst:TsrRegNode;
begin
 bri[0]:=get_bridge(src[0]);
 bri[1]:=get_bridge(src[1]);

 dst:=nil;

 if (bri[0].val<>nil) and
    (bri[0].val=bri[1].val) and
    (bri[0].pos=0) and
    (bri[1].pos=1) then
 if (bri[0].val.dtype.Count=2) then
 begin
  dst:=bri[0].val;
 end;

 if (dst=nil) then
 begin
  dst:=NewReg(dtVec2u);
  _set_line(ppLine,OpMakeCon(_get_line(ppLine),dst,src));
 end;

 Result:=BitcastList.FetchRead(rtype,dst);
end;

function TEmitOp.fetch64(src0,src1:TsrRegNode;rtype:TsrDataType;ppLine:PPspirvOp=nil):TsrRegNode;
var
 src:array[0..1] of TsrRegNode;
begin
 src[0]:=src0;
 src[1]:=src1;

 Result:=fetch64(@src,rtype,ppLine);
end;

Function FindByHalfSpace(node:TspirvOp;pDst:TsrNode):Boolean;
begin
 Result:=False;
 while (node<>nil) do
 begin
  if (node.pDst=pDst) 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;

function TEmitOp.OpSampledImage(pLine:TspirvOp;Tgrp,Sgrp:TsrNode;dtype:TsrDataType;info:TsrTypeImageInfo):TsrRegSampledImage;
Var
 src:array[0..1] of TsrNode;
 pType:TsrType;
 p:TsrCacheOp;
 dst:TsrRegSampledImage;
 node:TspirvOp;
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 that it is in the current half-space
 begin
  if FindByHalfSpace(pLine,p.pDst) then
  begin
   //use it
  end else
  begin
   //reset
   p.pDst:=nil;
  end;
 end;

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

  dst:=specialize New<TsrRegSampledImage>;
  dst.pSlot:=@RegsStory.FUnattach;
  dst.dtype:=dtTypeSampledImage;
  dst.Tgrp :=Tgrp;
  dst.Sgrp :=Sgrp;
  dst.etype:=dtype;
  dst.info :=info;

  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:=TsrRegSampledImage(p.pDst);
 end;
end;

//

procedure TEmitOp.OpIAdd(dst:PsrRegSlot;src0,src1:TsrRegNode);
begin
 Op2(Op.OpIAdd,LazyIntType(src0.dtype,src1.dtype),dst,src0,src1);
end;

procedure TEmitOp.OpISub(dst:PsrRegSlot;src0,src1:TsrRegNode);
begin
 Op2(Op.OpISub,LazyIntType(src0.dtype,src1.dtype),dst,src0,src1);
end;

procedure TEmitOp.OpIMul(dst:PsrRegSlot;src0,src1:TsrRegNode);
begin
 Op2(Op.OpIMul,LazyIntType(src0.dtype,src1.dtype),dst,src0,src1);
end;

//

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

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

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

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

 Result:=NewReg(LazyIntType(src0.dtype,src1.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:TsrRegNode;src1:QWORD;ppLine:PPspirvOp=nil):TsrRegNode;
begin
 if (src0=nil) or (src1=0) then Exit(src0);
 Result:=OpShrTo(src0,NewImm_q(src0.dtype,src1,_get_line(ppLine)),ppLine);
end;

//

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

 Result:=NewReg(LazyIntType(src0.dtype,src1.dtype));

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

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

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

 Result:=NewReg(LazyIntType(src0.dtype,src1.dtype));

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

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

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

 Result:=NewReg(LazyIntType(src0.dtype,src1.dtype));

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

function TEmitOp.OpIMulTo(src0:TsrRegNode;src1:QWORD;ppLine:PPspirvOp=nil):TsrRegNode;
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,NewImm_q(src0.dtype,src1,_get_line(ppLine)),ppLine);
 end;
end;

function TEmitOp.OpIDivTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
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:TsrRegNode;src1:QWORD;ppLine:PPspirvOp=nil):TsrRegNode;
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,NewImm_q(src0.dtype,src1,_get_line(ppLine)),ppLine);
 end;
end;

function TEmitOp.OpIModTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
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.OpSMod,Result,src0,src1));
 end else
 begin
  _set_line(ppLine,_Op2(_get_line(ppLine),Op.OpUMod,Result,src0,src1));
 end;
end;

//

function TEmitOp.OpFAddTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
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:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
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:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
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:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
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:TsrRegNode;src1:Single;ppLine:PPspirvOp=nil):TsrRegNode;
begin
 if (src0=nil) or (src1=0) then Exit(src0);
 Result:=OpFAddTo(src0,NewImm_s(src0.dtype,src1,_get_line(ppLine)),ppLine);
end;

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

//

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

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

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

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

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

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

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

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

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

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

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

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

//

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

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

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

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

function TEmitOp.OpFAbsTo(src:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
begin
 Result:=NewReg(src.dtype);
 _set_line(ppLine,_OpGlsl1(_get_line(ppLine),GlslOp.FAbs,Result,src));
end;

function TEmitOp.OpFNegateTo(src:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
begin
 Result:=NewReg(src.dtype);
 _set_line(ppLine,_Op1(_get_line(ppLine),Op.OpFNegate,Result,src));
end;

//

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

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

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

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

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

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

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

//

function TEmitOp.OpLogicalNotTo(src:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
begin
 Result:=NewReg(dtBool);
 _set_line(ppLine,_Op1(_get_line(ppLine),Op.OpLogicalNot,Result,src)); //post type
end;

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

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

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

function TEmitOp.OpAndTo(src0:TsrRegNode;src1:QWORD;ppLine:PPspirvOp=nil):TsrRegNode;
begin
 if (src0=nil) then Exit(src0);
 Result:=OpAndTo(src0,NewImm_q(src0.dtype,src1,_get_line(ppLine)),ppLine);
end;

//

function TEmitOp.OpLogicalOrTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
begin
 Result:=NewReg(dtBool);
 _set_line(ppLine,_Op2(_get_line(ppLine),Op.OpLogicalOr,Result,src0,src1)); //post type
end;

function TEmitOp.OpLogicalAndTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
begin
 Result:=NewReg(dtBool);
 _set_line(ppLine,_Op2(_get_line(ppLine),Op.OpLogicalAnd,Result,src0,src1)); //post type
end;

//

function TEmitOp.OpIsNanTo(src:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
begin
 Result:=NewReg(dtBool);
 _set_line(ppLine,_Op1(_get_line(ppLine),Op.OpIsNan,Result,src));
end;

function TEmitOp.OpIsSSignTo(src:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
var
 zero:TsrRegNode;
begin
 Result:=NewReg(dtBool);
 zero  :=NewImm_i(src.dtype,0,_get_line(ppLine));
 _set_line(ppLine,_Op2(_get_line(ppLine),Op.OpSLessThan,Result,src,zero)); //(x<0)
end;

function TEmitOp.OpIEqualTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
begin
 Result:=NewReg(dtBool);
 _set_line(ppLine,_Op2(_get_line(ppLine),Op.OpIEqual,Result,src0,src1));
end;

function TEmitOp.OpINotEqualTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
begin
 Result:=NewReg(dtBool);
 _set_line(ppLine,_Op2(_get_line(ppLine),Op.OpINotEqual,Result,src0,src1));
end;

function TEmitOp.OpEqualTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
begin
 Result:=NewReg(dtBool);
 _set_line(ppLine,_Op2(_get_line(ppLine),Op.OpLogicalEqual,Result,src0,src1));
end;

function TEmitOp.OpNotEqualTo(src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
begin
 Result:=NewReg(dtBool);
 _set_line(ppLine,_Op2(_get_line(ppLine),Op.OpLogicalNotEqual,Result,src0,src1));
end;

//

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

function TEmitOp.OpCmpTo(OpId:DWORD;src0,src1:TsrRegNode;ppLine:PPspirvOp=nil):TsrRegNode;
begin
 Result:=NewReg(dtBool);
 _set_line(ppLine,_Op2(_get_line(ppLine),OpId,Result,src0,src1));
end;

//

function TEmitOp.OpImageSampleImplicitLod(pLine:TspirvOp;img:TsrNode;dst,coord:TsrRegNode):TspirvOp;
Var
 node:TspirvOp;
begin
 Assert(dst.dtype.isVector,'dst must be vector');

 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:TspirvOp;img:TsrNode;dst,coord:TsrRegNode):TspirvOp;
Var
 node:TspirvOp;
begin
 Assert(dst.dtype.isVector,'dst must be vector');

 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:TspirvOp;img:TsrNode;dst,coord,pcf:TsrRegNode):TspirvOp;
Var
 node:TspirvOp;
begin
 Assert(dst.dtype.isScalar,'dst must be scalar');

 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:TspirvOp;img:TsrNode;dst,coord,pcf:TsrRegNode):TspirvOp;
Var
 node:TspirvOp;
begin
 Assert(dst.dtype.isScalar,'dst must be scalar');

 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:TspirvOp;img:TsrNode;dst,coord:TsrRegNode;id:Byte):TspirvOp;
Var
 node:TspirvOp;
 comp:TsrRegNode;
begin
 Assert(dst.dtype.isVector,'dst must be vector');

 Case id of
  0..3:;
  else
   Assert(False,'unknow id');
 end;

 comp:=NewImm_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:TspirvOp;img:TsrNode;dst,coord,pcf:TsrRegNode):TspirvOp;
Var
 node:TspirvOp;
begin
 Assert(dst.dtype.isVector,'dst must be vector');

 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:TspirvOp;Tgrp:TsrNode;dst,coord:TsrRegNode):TspirvOp;
Var
 node:TspirvOp;
begin
 Assert(dst.dtype.isVector,'dst must be vector');

 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:TspirvOp;Tgrp:TsrNode;dst,idx:TsrRegNode):TspirvOp;
Var
 node:TspirvOp;
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:TspirvOp;Tgrp:TsrNode;idx,src:TsrRegNode):TspirvOp;
Var
 node:TspirvOp;
begin
 node:=AddSpirvOp(pLine,Op.OpImageWrite); //need first

 node.AddParam(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;

function TEmitOp.OpImageQuerySizeLod(pLine:TspirvOp;img:TsrNode;dst,lod:TsrRegNode):TspirvOp;
begin
 Result:=_Op2(pLine,Op.OpImageQuerySizeLod,dst,img,lod);
end;

function TEmitOp.OpImageQueryLod(pLine:TspirvOp;img:TsrNode;dst,coord:TsrRegNode):TspirvOp;
begin
 Result:=_Op2(pLine,Op.OpImageQueryLod,dst,img,coord);
end;

end.

