unit emit_post_op; {$mode ObjFPC}{$H+} interface uses sysutils, math, bittype, Half16, spirv, srCFGParser, srNode, srType, srTypes, srRefId, srConst, srReg, srPrivate, srLiteral, srOp, srOpInternal, srOpUtils, emit_fetch; type TEmitPostOp=class(TEmitFetch) function PostForward1(node:TSpirvOp):Integer; function PostForward2(node:TSpirvOp):Integer; // function OpConvert1(node:TSpirvOp):Integer; function OnCompositeExtract1(node:TSpirvOp):Integer; function OnFDiv1(node:TSpirvOp):Integer; function OnIAdd1(node:TSpirvOp):Integer; function OnISub1(node:TSpirvOp):Integer; function OnShr1(node:TSpirvOp):Integer; function _OnShr_ext1(node,pOp:TSpirvOp;pShrVal:TsrConst):Integer; function _OnShr_ext_and(node:TSpirvOp;pShrVal,pAndVal:TsrConst):Integer; function _OnShr_ext_add(node,pOp0,pOp1:TSpirvOp;pShrVal:TsrConst):Integer; function OnAbsDiff1(node:TSpirvOp):Integer; function OnWQM32__1(node:TSpirvOp):Integer; function OnPackOfs1(node:TSpirvOp):Integer; function _Fetch_PackAnc(node:TsrRegNode;index,count:Byte):TsrRegNode; function OnBFE_32_1(node:TSpirvOp):Integer; function OnBFIB32_1(node:TSpirvOp):Integer; // function OnBitwiseAnd1(node:TSpirvOp):Integer; function OnLogicalAnd1(node:TSpirvOp):Integer; function OnBitwiseOr1(node:TSpirvOp):Integer; function OnLogicalOr1(node:TSpirvOp):Integer; function OnNot1(node:TSpirvOp):Integer; function OnLogicalNot1(node:TSpirvOp):Integer; function OnBranchConditional1(node:TSpirvOp):Integer; // function OpBitCount1(node:TSpirvOp):Integer; function OpBitReverse1(node:TSpirvOp):Integer; // function OnSelect1(node:TSpirvOp):Integer; // procedure MakeVecConst(rtype:TsrDataType;dst:TsrRegNode;src:PPsrRegNode); procedure MakeVecOne(dst:TsrRegNode;src:PPsrRegNode); function MakeVecComp(pLine:TSpirvOp;rtype:TsrDataType;dst:TsrRegNode;src:PPsrRegNode):TSpirvOp; // function OnMakeVec2(node:TSpirvOp):Integer; function OnReturn_2(node:TSpirvOp):Integer; function OnMakeExp2(node:TSpirvOp):Integer; function OnIAddExt2(node:TSpirvOp):Integer; function OnISubExt2(node:TSpirvOp):Integer; function OnPackAnc2(node:TSpirvOp):Integer; // function OnImageSample2(node:TSpirvOp):Integer; end; implementation function TEmitPostOp.PostForward1(node:TSpirvOp):Integer; begin Result:=0; Case node.OpId of Op.OpFConvert, Op.OpConvertFToU, Op.OpConvertFToS, Op.OpConvertSToF, Op.OpConvertUToF :Result:=OpConvert1(node); Op.OpCompositeExtract :Result:=OnCompositeExtract1(node); Op.OpFDiv :Result:=OnFDiv1(node); Op.OpIAdd :Result:=OnIAdd1(node); Op.OpISub :Result:=OnISub1(node); Op.OpShiftRightLogical, Op.OpShiftRightArithmetic:Result:=OnShr1(node); srOpInternal.OpAbsDiff :Result:=OnAbsDiff1(node); srOpInternal.OpWQM32 :Result:=OnWQM32__1(node); srOpInternal.OpPackOfs :Result:=OnPackOfs1(node); srOpInternal.OpBFE_32 :Result:=OnBFE_32_1(node); srOpInternal.OpBFIB32 :Result:=OnBFIB32_1(node); Op.OpSelect :Result:=OnSelect1(node); Op.OpBitwiseAnd :Result:=OnBitwiseAnd1(node); Op.OpLogicalAnd :Result:=OnLogicalAnd1(node); Op.OpBitwiseOr :Result:=OnBitwiseOr1(node); Op.OpLogicalOr :Result:=OnLogicalOr1(node); Op.OpNot :Result:=OnNot1(node); Op.OpLogicalNot :Result:=OnLogicalNot1(node); Op.OpBranchConditional:Result:=OnBranchConditional1(node); Op.OpBitCount :Result:=OpBitCount1(node); Op.OpBitReverse :Result:=OpBitReverse1(node); else; end; end; function TEmitPostOp.PostForward2(node:TSpirvOp):Integer; begin Result:=0; Case node.OpId of srOpInternal.OpIAddExt:Result:=OnIAddExt2(node); srOpInternal.OpISubExt:Result:=OnISubExt2(node); srOpInternal.OpMakeVec:Result:=OnMakeVec2(node); srOpInternal.OpPackAnc:Result:=OnPackAnc2(node); Op.OpReturn:Result:=OnReturn_2(node); OpMakeExp :Result:=OnMakeExp2(node); Op.OpImageSampleImplicitLod, Op.OpImageSampleExplicitLod, Op.OpImageSampleDrefImplicitLod, Op.OpImageSampleDrefExplicitLod:Result:=OnImageSample2(node); end; end; function _classif_const(dtype:TsrDataType;value:QWORD):Integer; begin if (value=0) then //always false begin Result:=0; end else if (value=dtype.High) then //always true begin Result:=1; end else begin Result:=-1; end; end; function _classif_const(p:TsrConst):Integer; begin Result:=_classif_const(p.dtype,p.GetData); end; function BinType(t:TsrDataType):TsrDataType; begin Case t of dtBool :Result:=dtBool; dtInt32, dtUint32:Result:=dtUint32; dtInt64, dtUint64:Result:=dtUint64; else Result:=dtUnknow; end; end; function TEmitPostOp.OnBitwiseAnd1(node:TSpirvOp):Integer; var dtype:TsrDataType; dst:TsrRegNode; src:array[0..1] of TsrRegNode; data:array[0..1] of QWORD; procedure _SetConst(dtype:TsrDataType;value:QWORD); begin dst.pWriter:=NewImm_q(dtype,value,node); node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; procedure _SetReg(src:TsrRegNode); begin dst.pWriter:=src; node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; procedure _SetOpType(OpId:DWORD;dtype:TsrDataType); begin Result:=0; if (node.OpId<>OpId) then begin node.OpId:=OpId; Inc(Result); end; // dst:=node.pDst.specialize AsType; Result:=Result+PrepTypeDst(dst,dtype,False); node.pDst:=dst; // dtype:=dst.dtype; if (dtype=dtBool) then dst.dweak:=False; node.pType:=TypeList.Fetch(dtype); end; procedure _SetType(dtype:TsrDataType); begin dst:=node.pDst.specialize AsType; Result:=Result+PrepTypeDst(dst,dtype,False); node.pDst:=dst; // dtype:=dst.dtype; //if (dtype=dtBool) then dst.dweak:=False; node.pType:=TypeList.Fetch(dtype); end; begin Result:=0; dst:=node.pDst.specialize AsType; src[0]:=RegDown(node.ParamNode(0).AsReg); src[1]:=RegDown(node.ParamNode(1).AsReg); if (dst=nil) or (src[0]=nil) or (src[1]=nil) then Exit; if src[0].is_const and src[1].is_const then begin //need a const calc data[0]:=src[0].AsConst.GetData; data[1]:=src[1].AsConst.GetData; dtype:=LazyType3(dst.dtype,src[0].dtype,src[1].dtype); dtype:=LazyType2(dtype,dtUint32); _SetConst(dtype,data[0] and data[1]); Exit; end; if (dst.is_bool) or ((src[0].is_bool_or_const_bool) and (src[1].is_bool_or_const_bool)) then begin if (src[0].is_const) then begin Case src[0].AsConst.AsBool of True :_SetReg(src[1]); False:_SetConst(dtBool,0); end; Exit; end; if (src[1].is_const) then begin Case src[1].AsConst.AsBool of True :_SetReg(src[0]); False:_SetConst(dtBool,0); end; Exit; end; _SetOpType(Op.OpLogicalAnd,dtBool); end else begin dtype:=LazyType3(BinType(dst.dtype),BinType(src[0].dtype),BinType(src[1].dtype)); dtype:=LazyType2(dtype,dtUint32); if (src[0].is_const) then begin case _classif_const(src[0].AsConst) of 0:_SetConst(dtype,0); //always false 1:_SetReg(src[1]); //always true end; end; if (src[1].is_const) then begin case _classif_const(src[1].AsConst) of 0:_SetConst(dtype,0); //always false 1:_SetReg(src[0]); //always true end; end; if (Result<>0) then Exit; //_SetConst/_SetReg _SetType(dtype); end; Result:=Result+PrepTypeParam(node.ParamNode(0),dst.dtype); Result:=Result+PrepTypeParam(node.ParamNode(1),dst.dtype); end; Function _FindNest_LAnd(node,src:TsrRegNode):Boolean; var p:TSpirvOp; tmp:TsrRegNode; begin Result:=False; if (node=nil) or (src=nil) then Exit; repeat p:=node.pWriter.specialize AsType; if (p<>nil) then if (p.OpId=Op.OpLogicalAnd) then begin tmp:=RegDown(p.ParamNode(0).AsReg); if (tmp=src) then Exit(True); Result:=_FindNest_LAnd(tmp,src); //recursion if Result then Exit(True); tmp:=RegDown(p.ParamNode(1).AsReg); if (tmp=src) then Exit(True); node:=tmp; Continue; //cycle end; Exit; until false; end; function TEmitPostOp.OnLogicalAnd1(node:TSpirvOp):Integer; var dst:TsrRegNode; src:array[0..1] of TsrRegNode; data:array[0..1] of QWORD; procedure _SetConst(dtype:TsrDataType;value:QWORD); begin dst.pWriter:=NewImm_q(dtype,value,node); node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; procedure _SetReg(src:TsrRegNode); begin dst.pWriter:=src; node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; begin Result:=0; dst:=node.pDst.specialize AsType; src[0]:=RegDown(node.ParamNode(0).AsReg); src[1]:=RegDown(node.ParamNode(1).AsReg); if (dst=nil) or (src[0]=nil) or (src[1]=nil) then Exit; Assert(dst.dtype=dtBool); if src[0].is_const and src[1].is_const then begin //need a const calc data[0]:=src[0].AsConst.GetData; data[1]:=src[1].AsConst.GetData; _SetConst(dtBool,data[0] and data[1]); Exit; end; if (src[0].is_const) then begin Case src[0].AsConst.AsBool of True :_SetReg(src[1]); False:_SetConst(dtBool,0); end; Exit; end; if (src[1].is_const) then begin Case src[1].AsConst.AsBool of True :_SetReg(src[0]); False:_SetConst(dtBool,0); end; Exit; end; if _FindNest_LAnd(src[1],src[0]) then //Find src[0] in src[1] begin _SetReg(src[1]); Exit; end; if _FindNest_LAnd(src[0],src[1]) then //Find src[1] in src[0] begin _SetReg(src[0]); Exit; end; Result:=Result+PrepTypeParam(node.ParamNode(0),dtBool); Result:=Result+PrepTypeParam(node.ParamNode(1),dtBool); end; function _Fetch_BitwiseOr_Const(node:TsrRegNode):TsrConst; var pLine:TSpirvOp; src:array[0..1] of TsrRegNode; begin Result:=nil; if (node=nil) then Exit; pLine:=node.pWriter.specialize AsType; if (pLine=nil) then Exit; if (pLine.OpId<>Op.OpBitwiseOr) then Exit; src[0]:=RegDown(pLine.ParamNode(0).AsReg); src[1]:=RegDown(pLine.ParamNode(1).AsReg); if (src[0]=nil) or (src[1]=nil) then Exit; if src[0].is_const and src[1].is_const then Exit; if src[0].is_const then begin Result:=src[0].AsConst; end else if src[1].is_const then begin Result:=src[1].AsConst; end; end; function _Fetch_BitwiseOr_Value(node:TsrRegNode):TsrRegNode; var pLine:TSpirvOp; src:array[0..1] of TsrRegNode; begin Result:=nil; if (node=nil) then Exit; pLine:=node.pWriter.specialize AsType; if (pLine=nil) then Exit; if (pLine.OpId<>Op.OpBitwiseOr) then Exit; src[0]:=RegDown(pLine.ParamNode(0).AsReg); src[1]:=RegDown(pLine.ParamNode(1).AsReg); if (src[0]=nil) or (src[1]=nil) then Exit; if src[0].is_const and src[1].is_const then Exit; if src[0].is_const then begin Result:=src[1]; end else if src[1].is_const then begin Result:=src[0]; end; end; // function TEmitPostOp.OnBitwiseOr1(node:TSpirvOp):Integer; var dtype:TsrDataType; dst:TsrRegNode; src:array[0..1] of TsrRegNode; data:array[0..1] of QWORD; pConst:TsrConst; procedure _SetConst(dtype:TsrDataType;value:QWORD); begin dst.pWriter:=NewImm_q(dtype,value,node); node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; procedure _SetReg(src:TsrRegNode); begin dst.pWriter:=src; node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; procedure _SetOpType(OpId:DWORD;dtype:TsrDataType); begin Result:=0; if (node.OpId<>OpId) then begin node.OpId:=OpId; Inc(Result); end; // dst:=node.pDst.specialize AsType; Result:=Result+PrepTypeDst(dst,dtype,False); node.pDst:=dst; // dtype:=dst.dtype; if (dtype=dtBool) then dst.dweak:=False; node.pType:=TypeList.Fetch(dtype); end; procedure _SetType(dtype:TsrDataType); begin dst:=node.pDst.specialize AsType; Result:=Result+PrepTypeDst(dst,dtype,False); node.pDst:=dst; // dtype:=dst.dtype; //if (dtype=dtBool) then dst.dweak:=False; node.pType:=TypeList.Fetch(dtype); end; begin Result:=0; dst:=node.pDst.specialize AsType; src[0]:=RegDown(node.ParamNode(0).AsReg); src[1]:=RegDown(node.ParamNode(1).AsReg); if (dst=nil) or (src[0]=nil) or (src[1]=nil) then Exit; if src[0].is_const and src[1].is_const then begin //need a const calc data[0]:=src[0].AsConst.GetData; data[1]:=src[1].AsConst.GetData; dtype:=LazyType3(dst.dtype,src[0].dtype,src[1].dtype); dtype:=LazyType2(dtype,dtUint32); _SetConst(dtype,data[0] or data[1]); Exit; end; if (dst.is_bool) or ((src[0].is_bool_or_const_bool) and (src[1].is_bool_or_const_bool)) then begin if (src[0].is_const) then begin Case src[0].AsConst.AsBool of True :_SetConst(dtBool,1); False:_SetReg(src[1]); end; Exit; end; if (src[1].is_const) then begin Case src[1].AsConst.AsBool of True :_SetConst(dtBool,1); False:_SetReg(src[0]); end; Exit; end; _SetOpType(Op.OpLogicalOr,dtBool); Result:=Result+PrepTypeParam(node.ParamNode(0),dst.dtype); Result:=Result+PrepTypeParam(node.ParamNode(1),dst.dtype); Exit; end; // pConst:=_Fetch_BitwiseOr_Const(src[0]); if (pConst<>nil) and src[1].is_const then begin //need a const calc data[0]:= pConst.GetData; data[1]:=src[1].AsConst.GetData; dtype:=LazyType3(dst.dtype,src[0].dtype,src[1].dtype); src[1]:=NewImm_q(dtype,data[0] or data[1],node); src[0]:=_Fetch_BitwiseOr_Value(src[0]); Assert(src[0]<>nil); node.ParamNode(0).Value:=src[0]; node.ParamNode(1).Value:=src[1]; end; // pConst:=_Fetch_BitwiseOr_Const(src[1]); if src[0].is_const and (pConst<>nil) then begin //need a const calc data[0]:=src[0].AsConst.GetData; data[1]:= pConst.GetData; dtype:=LazyType3(dst.dtype,src[0].dtype,src[1].dtype); src[0]:=NewImm_q(dtype,data[0] or data[1],node); src[1]:=_Fetch_BitwiseOr_Value(src[1]); Assert(src[1]<>nil); node.ParamNode(0).Value:=src[0]; node.ParamNode(1).Value:=src[1]; end; //else begin dtype:=LazyType3(BinType(dst.dtype),BinType(src[0].dtype),BinType(src[1].dtype)); dtype:=LazyType2(dtype,dtUint32); if (src[0].is_const) then begin case _classif_const(src[0].AsConst) of 0:_SetReg(src[1]); 1:_SetConst(dtype,dtype.High); //is high end; end; if (src[1].is_const) then begin case _classif_const(src[1].AsConst) of 0:_SetReg(src[0]); 1:_SetConst(dtype,dtype.High); //is high end; end; if (Result<>0) then Exit; //_SetConst/_SetReg _SetType(dtype); end; Result:=Result+PrepTypeParam(node.ParamNode(0),dst.dtype); Result:=Result+PrepTypeParam(node.ParamNode(1),dst.dtype); end; function TEmitPostOp.OnLogicalOr1(node:TSpirvOp):Integer; var dst:TsrRegNode; src:array[0..1] of TsrRegNode; data:array[0..1] of QWORD; procedure _SetConst(dtype:TsrDataType;value:QWORD); begin dst.pWriter:=NewImm_q(dtype,value,node); node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; procedure _SetReg(src:TsrRegNode); begin dst.pWriter:=src; node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; begin Result:=0; dst:=node.pDst.specialize AsType; src[0]:=RegDown(node.ParamNode(0).AsReg); src[1]:=RegDown(node.ParamNode(1).AsReg); if (dst=nil) or (src[0]=nil) or (src[1]=nil) then Exit; Assert(dst.dtype=dtBool); if src[0].is_const and src[1].is_const then begin //need a const calc data[0]:=src[0].AsConst.GetData; data[1]:=src[1].AsConst.GetData; _SetConst(dtBool,data[0] or data[1]); Exit; end; if (src[0].is_const) then begin Case src[0].AsConst.AsBool of True :_SetConst(dtBool,1); False:_SetReg(src[1]); end; Exit; end; if (src[1].is_const) then begin Case src[1].AsConst.AsBool of True :_SetConst(dtBool,1); False:_SetReg(src[0]); end; Exit; end; Result:=Result+PrepTypeParam(node.ParamNode(0),dtBool); Result:=Result+PrepTypeParam(node.ParamNode(1),dtBool); end; function TEmitPostOp.OnNot1(node:TSpirvOp):Integer; var dtype:TsrDataType; dst:TsrRegNode; src:TsrRegNode; data:array[0..1] of QWORD; procedure _SetConst(dtype:TsrDataType;value:QWORD); begin dst.pWriter:=NewImm_q(dtype,value,node); node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; procedure _SetOpType(OpId:DWORD;dtype:TsrDataType); begin Result:=0; if (node.OpId<>OpId) then begin node.OpId:=OpId; Inc(Result); end; // dst:=node.pDst.specialize AsType; Result:=Result+PrepTypeDst(dst,dtype,False); node.pDst:=dst; // dtype:=dst.dtype; if (dtype=dtBool) then dst.dweak:=False; node.pType:=TypeList.Fetch(dtype); end; procedure _SetType(dtype:TsrDataType); begin dst:=node.pDst.specialize AsType; Result:=Result+PrepTypeDst(dst,dtype,False); node.pDst:=dst; // dtype:=dst.dtype; //if (dtype=dtBool) then dst.dweak:=False; node.pType:=TypeList.Fetch(dtype); end; begin Result:=0; dst:=node.pDst.specialize AsType; src:=RegDown(node.ParamNode(0).AsReg); if (dst=nil) or (src=nil) then Exit; if src.is_const then begin dtype:=LazyType2(dst.dtype,src.dtype); dtype:=LazyType2(dtype,dtUint32); //need a const calc data[0]:=src.AsConst.GetData; data[1]:=dtype.High; _SetConst(dtype,(not data[0]) and data[1]); Exit; end; if (dst.is_bool) or (src.is_bool_or_const_bool) then begin if (src.is_const) then begin Case src.AsConst.AsBool of True :_SetConst(dtBool,0); False:_SetConst(dtBool,1); end; Exit; end; _SetOpType(Op.OpLogicalNot,dtBool); end else begin dtype:=LazyType2(BinType(dst.dtype),BinType(src.dtype)); dtype:=LazyType2(dtype,dtUint32); _SetType(dtype); end; Result:=Result+PrepTypeParam(node.ParamNode(0),dst.dtype); end; function TEmitPostOp.OnLogicalNot1(node:TSpirvOp):Integer; var dst:TsrRegNode; src:TsrRegNode; dst2:TsrRegNode; srp:array[0..1] of TsrRegNode; pop:TSpirvOp; cmp:DWORD; procedure _SetReg(src:TsrRegNode); begin dst.pWriter:=src; node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; begin Result:=0; dst:=node.pDst.specialize AsType; src:=RegDown(node.ParamNode(0).AsReg); if (dst=nil) or (src=nil) then Exit; Assert(dst.dtype=dtBool); if (src.read_count>1) then Exit; pop:=src.pWriter.specialize AsType; if (pop=nil) then Exit; cmp:=pop.OpId; cmp:=get_inverse_not_cmp_op(cmp); if (cmp=0) then Exit; srp[0]:=pop.ParamNode(0).AsReg; srp[1]:=pop.ParamNode(1).AsReg; if (srp[0]=nil) or (srp[1]=nil) then Exit; dst2:=NewReg(dtBool); _Op2(pop,cmp,dst2,srp[0],srp[1]); _SetReg(dst2); end; Procedure mark_not_used_branch_op(pBlock:TsrOpBlock); var node:TSpirvOp; begin node:=pBlock.First; While (node<>nil) do begin if node.IsType(ntOp) then begin // Case node.OpId of Op.OpLabel, Op.OpSelectionMerge, Op.OpBranch, Op.OpBranchConditional:node.mark([soNotUsed,soForce]); else; end; // end; node:=node.Next; end; end; procedure _restore(var vctx:TsrVolatileContext); var node:TsrVolatileNode; V:TsrVolatile; N:TsrRegNode; begin node:=vctx.FList.pHead; while (node<>nil) do begin V:=TsrVolatile(node.V); N:=TsrRegNode(node.N); if (N.pWriter<>V) then begin Assert(false,'_restore:1'); end; if (V.FBase=nil) then begin Assert(false,'_restore:2'); end; //Preventing circular markings V.mark_read(nil); N.pWriter:=V.FBase; N.pWriter.PrepType(ord(N.dtype)); V.mark_unread(nil); // node:=node.pNext; end; end; function TEmitPostOp.OnBranchConditional1(node:TSpirvOp):Integer; var src,prv:TsrRegNode; pOpNot:TSpirvOp; pLabel:array[0..1] of TsrRefNode; pCond:TsrOpBlock; pMerg:TsrOpBlock; cst :TsrConst; begin Result:=0; //exit; src:=RegDown(node.ParamNode(0).AsReg); if (src=nil) then Exit; if (src.is_const) then begin cst:=src.AsConst; if (cst<>nil) then begin pCond:=TsrOpBlock(node.Parent).FindUpCond; if (pCond<>nil) then if (pCond.pElse=nil) then //no else if (RegDown(pCond.Cond.pReg)=src) then if ((cst.AsBool=True) and (pCond.Cond.FNormalOrder=True )) or //if (true) ((cst.AsBool=False) and (pCond.Cond.FNormalOrder=False)) then //if (!false) begin //Remove the condition block //Clear ref in BranchConditional node.ParamNode(0).Value:=nil; //Get merge block pMerg:=pCond.Parent; Assert(pMerg.bType=btMerg); //set type pMerg.bType:=btOther; _restore(pCond.vctx); //simplification of connections //PrivateList.build_volatile_ctrue(pCond.pAfter,pCond.Regs.orig,pCond.Regs.prev,pCond.Regs.next); //set type pCond.bType:=btOther; //clear instructions mark_not_used_branch_op(pMerg); mark_not_used_branch_op(pCond); Exit(1); end; end; end; pOpNot:=src.pWriter.specialize AsType; if (pOpNot=nil) then Exit; Case pOpNot.OpId of Op.OpLogicalNot:; else Exit; end; prv:=pOpNot.ParamNode(0).AsReg; if (prv=nil) then Exit; node.ParamNode(0).Value:=prv; //set new pLabel[0]:=node.ParamNode(1).Value.specialize AsType; //read pLabel[1]:=node.ParamNode(2).Value.specialize AsType; //read node.ParamNode(1).Value:=pLabel[1]; //swap node.ParamNode(2).Value:=pLabel[0]; //swap pCond:=TsrOpBlock(node.Parent).FindUpCond; if (pCond<>nil) then if (RegDown(pCond.Cond.pReg)=src) then begin //broken? //set new pCond.Cond.pReg:=prv; //invert mark pCond.Cond.FNormalOrder:=not pCond.Cond.FNormalOrder; end; Exit(1); end; function TEmitPostOp.OpBitCount1(node:TSpirvOp):Integer; var dst,src:TsrRegNode; data:QWORD; procedure _SetConst(dtype:TsrDataType;value:QWORD); begin dst.pWriter:=NewImm_q(dtype,value,node); node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; begin Result:=0; dst:=node.pDst.specialize AsType; src:=RegDown(node.ParamNode(0).AsReg); if (dst=nil) or (src=nil) then Exit; if src.is_const then begin //need a const calc data:=src.AsConst.GetData; data:=PopCnt(data); //BitCount _SetConst(dst.dtype,data); Exit; end; end; Function ReverseBits(src:QWORD;count:Byte):QWORD; var v:QWORD; i:Byte; begin Result:=0; Assert(count<>0); dec(count); For i:=0 to count do begin v:=((src shr i) and 1); //get Result:=Result or (v shl (count-i)); //set end; end; function TEmitPostOp.OpBitReverse1(node:TSpirvOp):Integer; var dst,src:TsrRegNode; data:QWORD; procedure _SetConst(dtype:TsrDataType;value:QWORD); begin dst.pWriter:=NewImm_q(dtype,value,node); node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; begin Result:=0; dst:=node.pDst.specialize AsType; src:=RegDown(node.ParamNode(0).AsReg); if (dst=nil) or (src=nil) then Exit; if src.is_const then begin //need a const calc data:=src.AsConst.GetData; data:=ReverseBits(data,src.dtype.BitSize); _SetConst(dst.dtype,data); Exit; end; end; function try_get_comp_bridge(var src:TsrRegNode):Integer; forward; function TEmitPostOp.OpConvert1(node:TSpirvOp):Integer; var i:Int64; dst,src,tmp:TsrRegNode; pc:TsrConst; pLine:TSpirvOp; procedure _SetConst(dtype:TsrDataType;value:QWORD); begin dst.pWriter:=NewImm_q(dtype,value,node); node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; procedure _SetConst_s(dtype:TsrDataType;value:Single); begin Assert(dtype=dtFloat32); dst.pWriter:=NewImm_s(dtype,value,node); node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; procedure _SetReg(src:TsrRegNode); begin dst.pWriter:=src; node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; function minz(i:Int64):QWORD; begin if (i>0) then Result:=i else Result:=0; end; begin Result:=0; dst:=node.pDst.specialize AsType; src:=RegDown(node.ParamNode(0).AsReg); if (dst=nil) or (src=nil) then Exit; tmp:=src; While try_get_comp_bridge(tmp)<>0 do begin tmp:=RegDown(tmp); end; if (tmp<>src) then begin node.ParamNode(0).Value:=tmp; src:=tmp; Inc(Result); end; i:=0; if src.is_const then begin pc:=src.AsConst; Case node.OpId of Op.OpFConvert: case src.dtype of dtFloat32: case dst.dtype of dtHalf16:_SetConst(dst.dtype,WORD(THalf16(pc.AsFloat32))); else; end; dtHalf16: case dst.dtype of dtFloat32:_SetConst_s(dst.dtype,Single(pc.AsHalf16)); else; end; else; end; Op.OpConvertFToU: case src.dtype of dtFloat32: if TryTruncInt64(pc.AsFloat32,i) then begin _SetConst(dst.dtype,minz(i)); end; else; end; Op.OpConvertFToS: case src.dtype of dtFloat32: if TryTruncInt64(pc.AsFloat32,i) then begin _SetConst(dst.dtype,i); end; else; end; Op.OpConvertSToF: case src.dtype of dtInt32 :_SetConst_s(dst.dtype,pc.AsInt32); dtUint32:_SetConst_s(dst.dtype,pc.AsInt32); dtInt64 :_SetConst_s(dst.dtype,pc.AsInt64); dtUint64:_SetConst_s(dst.dtype,pc.AsInt64); else; end; Op.OpConvertUToF: case src.dtype of dtInt32 :_SetConst_s(dst.dtype,pc.AsUint32); dtUint32:_SetConst_s(dst.dtype,pc.AsUint32); dtInt64 :_SetConst_s(dst.dtype,pc.AsUint64); dtUint64:_SetConst_s(dst.dtype,pc.AsUint64); else; end; end; end else begin pLine:=src.pWriter.specialize AsType; if (pLine=nil) then Exit; Case pLine.OpId of Op.OpFConvert: begin tmp:=RegDown(pLine.ParamNode(0).AsReg); Case node.OpId of Op.OpFConvert: if (tmp.dtype=dst.dtype) then begin _SetReg(tmp); end; else; end; end; else; end; end; end; function TEmitPostOp.OnCompositeExtract1(node:TSpirvOp):Integer; var pc:TsrConst; dst,org,src:TsrRegNode; pos:PtrUint; begin Result:=0; dst:=node.pDst.specialize AsType; org:=node.ParamNode(0).AsReg; src:=RegDown(org); if (dst=nil) or (src=nil) then Exit; pos:=0; if not node.ParamNode(1).TryGetValue(pos) then Exit; if not src.is_const then Exit; pc:=src.AsConst; pc:=ConstList.Bitcast(org.dtype,pc); if (pos>=pc.Count) then Exit; pc:=pc.GetConst(pos); if (pc=nil) then Exit; dst.pWriter:=pc; node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; { function TEmitPostOp.OnSAbs(node:TSpirvOp):Integer; var dst:TsrRegNode; src:TsrRegNode; procedure _SetConst(dtype:TsrDataType;value:QWORD); begin dst^.SetConst(FConsts.Fetch(dtype,value)); node^.OpId:=OpLinks; //mark remove node^.dst:=Default(TOpParamSingle); Inc(Result); end; begin Result:=0; dst:=node^.dst.pData; src:=node^.Param(2).pData; if src^.is_const then begin Case dst^.dtype of dtInt32, dtUint32:_SetConst(dtUint32,Abs(src^.AsConst^.AsInt)); dtInt64, dtUint64:_SetConst(dtUint64,Abs(src^.AsConst^.AsInt64)); else; end; end; end; } function TEmitPostOp.OnSelect1(node:TSpirvOp):Integer; var dtype:TsrDataType; dst:TsrRegNode; src:array[0..1] of TsrRegNode; cst:array[0..1] of TsrConst; pLine:TSpirvOp; procedure _SetConst(dtype:TsrDataType;value:QWORD); begin dst.pWriter:=NewImm_q(dtype,value,node); node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; procedure _SetReg(src:TsrRegNode); begin dst.pWriter:=src; node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; begin Result:=0; dst:=node.pDst.specialize AsType; src[0]:=node.ParamNode(1).AsReg; src[1]:=node.ParamNode(2).AsReg; if (dst=nil) or (src[0]=nil) or (src[1]=nil) then Exit; cst[0]:=RegDown(src[0]).AsConst; cst[1]:=RegDown(src[1]).AsConst; if (cst[0]<>nil) and (cst[1]<>nil) then begin if (cst[0].GetData=cst[1].GetData) then begin _SetConst(dst.dtype,cst[0].GetData); Exit; end; if (dst.dtype=dtBool) and (cst[0].dtype=dtBool) and (cst[1].dtype=dtBool) then begin if (cst[0].AsBool=True) and (cst[1].AsBool=False) then begin src[0]:=node.ParamNode(0).AsReg; _SetReg(src[0]); Exit; end else if (cst[0].AsBool=False) and (cst[1].AsBool=True) then begin src[0]:=node.ParamNode(0).AsReg; pLine:=src[0].pLine; src[1]:=OpNotTo(src[0],@pLine); src[1].PrepType(ord(dtBool)); _SetReg(src[1]); Exit; end; end; end; dtype:=LazyType3(dst.dtype,src[0].dtype,src[1].dtype); if (dtype<>dtUnknow) and (node.pType.dtype<>dtype) then begin node.pType:=TypeList.Fetch(dtype); if (node.pType.dtype=dtype) then begin Inc(Result); end; end; if (dtype<>dtUnknow) and (dst.dtype<>dtype) then begin Result:=Result+PrepTypeDst(dst,dtype,False); node.pDst:=dst; end; Result:=Result+PrepTypeParam(node.ParamNode(1),dst.dtype); Result:=Result+PrepTypeParam(node.ParamNode(2),dst.dtype); end; function TEmitPostOp.OnFDiv1(node:TSpirvOp):Integer; var dst:TsrRegNode; src:array[0..1] of TsrRegNode; pCon:array[0..1] of TsrConst; s:Single; procedure _SetConst_s(dtype:TsrDataType;value:Single); begin Assert(dtype=dtFloat32); dst.pWriter:=NewImm_s(dtype,value,node); node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; begin Result:=0; dst:=node.pDst.specialize AsType; src[0]:=RegDown(node.ParamNode(0).AsReg); src[1]:=RegDown(node.ParamNode(1).AsReg); if (dst=nil) or (src[0]=nil) or (src[1]=nil) then Exit; if (src[0].is_const) and (src[1].is_const) then if (src[0].dtype=src[1].dtype) then begin //need a const calc pCon[0]:=src[0].AsConst; pCon[1]:=src[1].AsConst; Case src[0].dtype of dtFloat32: begin s:=pCon[1].AsFloat32; if IsZero(s) or IsNan(s) or IsInfinite(s) then Exit(0); _SetConst_s(dst.dtype,pCon[0].AsFloat32/s); end; else; end; end; end; function TEmitPostOp.OnIAdd1(node:TSpirvOp):Integer; var dst:TsrRegNode; src:array[0..1] of TsrRegNode; data:array[0..1] of QWORD; procedure _SetConst(dtype:TsrDataType;value:QWORD); begin dst.pWriter:=NewImm_q(dtype,value,node); node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; procedure _SetReg(src:TsrRegNode); begin dst.pWriter:=src; node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; begin Result:=0; dst:=node.pDst.specialize AsType; src[0]:=RegDown(node.ParamNode(0).AsReg); src[1]:=RegDown(node.ParamNode(1).AsReg); if (dst=nil) or (src[0]=nil) or (src[1]=nil) then Exit; if (src[0].is_const) and (src[1].is_const) then begin //need a const calc data[0]:=src[0].AsConst.GetData; data[1]:=src[1].AsConst.GetData; _SetConst(dst.dtype,data[0]+data[1]); end else if (src[0].is_const) then begin if (src[0].AsConst.GetData=0) then begin _SetReg(src[1]); end; end else if (src[1].is_const) then begin if (src[1].AsConst.GetData=0) then begin _SetReg(src[0]); end; end; end; function TEmitPostOp.OnISub1(node:TSpirvOp):Integer; var dst:TsrRegNode; src:array[0..1] of TsrRegNode; data:array[0..1] of QWORD; procedure _SetConst(dtype:TsrDataType;value:QWORD); begin dst.pWriter:=NewImm_q(dtype,value,node); node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; begin Result:=0; dst:=node.pDst.specialize AsType; src[0]:=RegDown(node.ParamNode(0).AsReg); src[1]:=RegDown(node.ParamNode(1).AsReg); if (dst=nil) or (src[0]=nil) or (src[1]=nil) then Exit; if src[0].is_const and src[1].is_const then begin //need a const calc data[0]:=src[0].AsConst.GetData; data[1]:=src[1].AsConst.GetData; _SetConst(dst.dtype,data[0]-data[1]); end; end; function TEmitPostOp.OnShr1(node:TSpirvOp):Integer; var dst:TsrRegNode; src:array[0..1] of TsrRegNode; data:array[0..1] of QWORD; mask:Byte; procedure _SetConst(dtype:TsrDataType;value:QWORD); begin dst.pWriter:=NewImm_q(dtype,value,node); node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; begin Result:=0; dst:=node.pDst.specialize AsType; src[0]:=RegDown(node.ParamNode(0).AsReg); src[1]:=RegDown(node.ParamNode(1).AsReg); if (dst=nil) or (src[0]=nil) or (src[1]=nil) then Exit; if (src[0].is_const) and (src[1].is_const) then begin //need a const calc data[0]:=src[0].AsConst.GetData; data[1]:=src[1].AsConst.GetData; case src[0].dtype.BitSize of 32:mask:=31; 64:mask:=63; else Assert(false); end; Case node.OpId of Op.OpShiftRightLogical :_SetConst(dst.dtype,data[0] shr (data[1] and mask)); Op.OpShiftRightArithmetic:_SetConst(dst.dtype,SarInt64(Int64(data[0]),(data[1] and mask))); else Assert(false); end; end else if (src[1].is_const) then begin Result:=0; //TODO: Restrict optimizations //Result:=_OnShr_ext1(node,src[0].pWriter.specialize AsType,src[1].AsConst); end; end; function TEmitPostOp._OnShr_ext1(node,pOp:TSpirvOp;pShrVal:TsrConst):Integer; var src:array[0..1] of TsrRegNode; begin Result:=0; if (pOp=nil) then Exit; Case pOp.OpId of Op.OpBitwiseAnd: begin src[0]:=RegDown(pOp.ParamNode(0).AsReg); src[1]:=RegDown(pOp.ParamNode(1).AsReg); if (src[0]=nil) or (src[1]=nil) then Exit; if (src[0].is_const) then begin Result:=_OnShr_ext_and(node,pShrVal,src[0].AsConst); end else if (src[1].is_const) then begin Result:=_OnShr_ext_and(node,pShrVal,src[1].AsConst); end; end; Op.OpIAdd: begin src[0]:=RegDown(pOp.ParamNode(0).AsReg); src[1]:=RegDown(pOp.ParamNode(1).AsReg); if (src[0]=nil) or (src[1]=nil) then Exit; Result:=_OnShr_ext_add(node,src[0].pWriter.specialize AsType,src[1].pWriter.specialize AsType,pShrVal); end; else; end; end; function TEmitPostOp._OnShr_ext_and(node:TSpirvOp;pShrVal,pAndVal:TsrConst):Integer; var dst:TsrRegNode; data:array[0..1] of QWORD; procedure _SetConst(dtype:TsrDataType;value:QWORD); begin dst.pWriter:=NewImm_q(dtype,value,node); node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; begin Result:=0; data[0]:=pShrVal.GetData; data[1]:=pAndVal.GetData; data[0]:=High(QWORD) shl data[0]; if (data[0] and data[1]=0) then begin dst:=node.pDst.specialize AsType; _SetConst(dst.dtype,0); end; end; 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; function _OpCanBeShrOpt(pOp:TSpirvOp;ShrVal:QWORD;var Delta:QWORD):Boolean; var src:array[0..1] of TsrRegNode; data:QWORD; begin Result:=False; if (pOp=nil) then Exit; Case pOp.OpId of Op.OpIMul: begin src[0]:=RegDown(pOp.ParamNode(0).AsReg); src[1]:=RegDown(pOp.ParamNode(1).AsReg); if (src[0]=nil) or (src[1]=nil) then Exit; if (src[0].is_const) then begin data:=src[0].AsConst.GetData; end else if (src[1].is_const) then begin data:=src[1].AsConst.GetData; end else begin Exit; end; if isPowerOfTwo(data) then begin data:=fastIntLog2(data); Result:=(data>=ShrVal); Delta:=data-ShrVal; end; end; Op.OpShiftLeftLogical: begin src[0]:=RegDown(pOp.ParamNode(0).AsReg); src[1]:=RegDown(pOp.ParamNode(1).AsReg); if (src[0]=nil) or (src[1]=nil) then Exit; if (src[1].is_const) then begin data:=src[1].AsConst.GetData; Result:=(data>=ShrVal); Delta:=data-ShrVal; end; end; else; end; end; function _GetShrOptReg(pOp:TSpirvOp):TsrRegNode; var src:array[0..1] of TsrRegNode; begin Result:=nil; if (pOp=nil) then Exit; Case pOp.OpId of Op.OpIMul: begin src[0]:=RegDown(pOp.ParamNode(0).AsReg); src[1]:=RegDown(pOp.ParamNode(1).AsReg); if (src[0]=nil) or (src[1]=nil) then Exit; if (src[0].is_const) then begin Result:=src[1]; end else if (src[1].is_const) then begin Result:=src[0]; end; end; Op.OpShiftLeftLogical: begin src[0]:=RegDown(pOp.ParamNode(0).AsReg); src[1]:=RegDown(pOp.ParamNode(1).AsReg); if (src[0]=nil) or (src[1]=nil) then Exit; Result:=src[0]; end; else; end; end; function TEmitPostOp._OnShr_ext_add(node,pOp0,pOp1:TSpirvOp;pShrVal:TsrConst):Integer; var dst,src:TsrRegNode; dst_shr:array[0..1] of TsrRegNode; data:array[0..1] of QWORD; procedure _SetReg(src:TsrRegNode); begin dst.pWriter:=src; node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; begin Result:=0; data[0]:=0; data[1]:=0; if (not _OpCanBeShrOpt(pOp0,pShrVal.GetData,data[0])) and (not _OpCanBeShrOpt(pOp1,pShrVal.GetData,data[1])) then Exit; if (data[0]=0) then begin dst_shr[0]:=_GetShrOptReg(pOp0); end else begin dst:=pOp0.pDst.specialize AsType; src:=_GetShrOptReg(pOp0); PrepTypeNode(src,dtUInt32); dst_shr[0]:=OpShlTo(src,data[0],@pOp0) end; if (data[1]=0) then begin dst_shr[1]:=_GetShrOptReg(pOp1); end else begin dst:=pOp1.pDst.specialize AsType; src:=_GetShrOptReg(pOp1); PrepTypeNode(src,dtUInt32); dst_shr[1]:=OpShlTo(src,data[1],@pOp0) end; dst:=node.pDst.specialize AsType; pOp0:=dst.pWriter.specialize AsType; //OpIAdd PrepTypeNode(dst_shr[0],dtUInt32); PrepTypeNode(dst_shr[1],dtUInt32); src:=OpIAddTo(dst_shr[0],dst_shr[1],@pOp0); _SetReg(src); //Writeln(data[0],' ',data[1]); //writeln; Result:=1; end; function TEmitPostOp.OnAbsDiff1(node:TSpirvOp):Integer; var dst:TsrRegNode; src:array[0..1] of TsrRegNode; data:array[0..1] of QWORD; rmax,rmin:TsrRegNode; procedure _SetConst(dtype:TsrDataType;value:QWORD); begin dst.pWriter:=NewImm_q(dtype,value,node); node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; procedure _SetReg(src:TsrRegNode); begin dst.pWriter:=src; node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; begin Result:=0; dst:=node.pDst.specialize AsType; src[0]:=RegDown(node.ParamNode(0).AsReg); src[1]:=RegDown(node.ParamNode(1).AsReg); if (dst=nil) or (src[0]=nil) or (src[1]=nil) then Exit; if src[0].is_const and src[1].is_const then begin //need a const calc data[0]:=src[0].AsConst.GetData; data[1]:=src[1].AsConst.GetData; if (data[0]>data[1]) then _SetConst(dst.dtype,data[0]-data[1]) else _SetConst(dst.dtype,data[1]-data[0]); Exit; end else if src[0].is_const then begin if src[0].AsConst.isZeroVal then begin //src[1]-0 src[1]:=node.ParamNode(1).AsReg; //get original _SetReg(src[1]); Exit; end; end else if src[1].is_const then begin if src[1].AsConst.isZeroVal then begin //src[0]-0 src[0]:=node.ParamNode(0).AsReg; //get original _SetReg(src[0]); Exit; end; end; //else node.mark([soNotUsed]); node.pDst:=nil; rmax:=OpUMaxTo(src[0],src[1],@node); //update line rmin:=OpUMinTo(src[0],src[1],@node); //update line _Op2(node,Op.OpISub,dst,rmax,rmin); 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; function TEmitPostOp.OnWQM32__1(node:TSpirvOp):Integer; var dst:TsrRegNode; src:TsrRegNode; data:QWORD; procedure _SetConst(dtype:TsrDataType;value:QWORD); begin dst.pWriter:=NewImm_q(dtype,value,node); node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; procedure _SetReg(src:TsrRegNode); begin dst.pWriter:=src; node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; begin Result:=0; dst:=node.pDst.specialize AsType; src:=RegDown(node.ParamNode(0).AsReg); if (dst=nil) or (src=nil) then Exit; if src.is_const then begin //need a const calc data:=src.AsConst.GetData; data:=F_WQM_32(data); _SetConst(dst.dtype,data); end else if (src.dtype=dtBool) then begin _SetReg(src); end else begin //TODO: WQM32 _SetReg(src); end; end; type Ppacked_offset=^Tpacked_offset; Tpacked_offset=bitpacked record x:bit6; //0..5 (int6) a1:bit2; y:bit6; //8..13 (int6) a2:bit2; z:bit6; //16..21 (int6) a3:bit10; end; function int6(b:Byte):Integer; inline; const shift=BitSizeOf(Integer)-6; begin Result:=SarLongint((Integer(b) shl shift),shift); end; function TEmitPostOp.OnPackOfs1(node:TSpirvOp):Integer; var dst:TsrRegNode; src:TsrRegNode; data:QWORD; P:Ppacked_offset; count:PtrUint; cret:TsrConst; cvec:array[0..2] of TsrConst; pLine:TSpirvOp; vint6:TsrRegNode; rvec:array[0..2] of TsrRegNode; begin Result:=0; dst:=node.pDst.specialize AsType; if (dst=nil) then Exit; count:=0; if not node.ParamNode(0).TryGetValue(count) then Exit; src:=RegDown(node.ParamNode(1).AsReg); if (src=nil) then Exit; if src.is_const then begin //need a const calc data:=src.AsConst.GetData; P:=@data; cret:=nil; Case count of 1: begin cret:=ConstList.Fetch_i(dtInt32,int6(P^.x)); end; 2: begin cvec[0]:=ConstList.Fetch_i(dtInt32,int6(P^.x)); cvec[1]:=ConstList.Fetch_i(dtInt32,int6(P^.y)); cret:=ConstList.FetchVector(dtVec2i,@cvec,true); end; 3: begin cvec[0]:=ConstList.Fetch_i(dtInt32,int6(P^.x)); cvec[1]:=ConstList.Fetch_i(dtInt32,int6(P^.y)); cvec[2]:=ConstList.Fetch_i(dtInt32,int6(P^.z)); cret:=ConstList.FetchVector(dtVec3i,@cvec,true); end; else Assert(False); end; dst.pWriter:=cret; node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end else begin src:=node.ParamNode(1).AsReg; pLine:=src.pLine; vint6:=NewImm_i(dtInt32,6); Case count of 1: begin src:=OpBFSETo(src,NewImm_i(dtInt32,0),vint6,@pLine); end; 2: begin rvec[0]:=OpBFSETo(src,NewImm_i(dtInt32,0),vint6,@pLine); rvec[1]:=OpBFSETo(src,NewImm_i(dtInt32,8),vint6,@pLine); src:=NewReg(dtVec2i); pLine:=OpMakeCon(pLine,src,@rvec); end; 3: begin rvec[0]:=OpBFSETo(src,NewImm_i(dtInt32, 0),vint6,@pLine); rvec[1]:=OpBFSETo(src,NewImm_i(dtInt32, 8),vint6,@pLine); rvec[2]:=OpBFSETo(src,NewImm_i(dtInt32,16),vint6,@pLine); src:=NewReg(dtVec3i); pLine:=OpMakeCon(pLine,src,@rvec); end; else Assert(False); end; dst.dtype :=src.dtype; dst.pWriter:=src; node.mark([soNotUsed]); node.pDst:=nil; Inc(Result); end; end; function TEmitPostOp.OnImageSample2(node:TSpirvOp):Integer; var i,param,count_ofs,count_dim,count_query:Integer; L:TsrLiteral; pLine:TSpirvOp; roffset:TsrRegNode; coffset:TsrConst; cvec:array[0..2] of TsrConst; img:TsrRegSampledImage; coord:TsrRegNode; sizes:TsrRegNode; dvec:TsrDataType; begin Result:=0; param:=0; Case node.OpId of Op.OpImageSampleImplicitLod :param:=2; Op.OpImageSampleExplicitLod :param:=2; Op.OpImageSampleDrefImplicitLod:param:=3; Op.OpImageSampleDrefExplicitLod:param:=3; end; L:=node.ParamNode(param).Value.specialize AsType; if (L=nil) then Exit; i:=L.AsInt32; //ConstOffset is used? if (i and ImageOperands.ConstOffset)=0 then Exit; //get position if (i and ImageOperands.Bias)<>0 then begin Inc(param); end; // if (i and ImageOperands.Lod)<>0 then begin Inc(param); end; // if (i and ImageOperands.Grad)<>0 then begin Inc(param); end; // Inc(param); //get position roffset:=RegDown(node.ParamNode(param).AsReg); if (roffset=nil) then Exit; coffset:=roffset.pWriter.specialize AsType; if (coffset<>nil) then Exit; //valid const offset pLine:=roffset.pWriter.specialize AsType; if (pLine<>nil) then begin if (pLine.OpId=srOpInternal.OpPackOfs) then Exit; //skip end; // count_ofs:=roffset.dtype.Count; //get zero offset coffset:=nil; Case count_ofs of 1: begin coffset:=ConstList.Fetch_i(dtInt32,0); end; 2: begin cvec[0]:=ConstList.Fetch_i(dtInt32,0); cvec[1]:=ConstList.Fetch_i(dtInt32,0); coffset:=ConstList.FetchVector(dtVec2i,@cvec,true); end; 3: begin cvec[0]:=ConstList.Fetch_i(dtInt32,0); cvec[1]:=ConstList.Fetch_i(dtInt32,0); cvec[2]:=ConstList.Fetch_i(dtInt32,0); coffset:=ConstList.FetchVector(dtVec3i,@cvec,true); end; else Assert(False); end; //set zero to ConstOffset node.ParamNode(param).Value:=coffset; //get image/coord img :=node.ParamNode(0).Value.specialize AsType; coord:=node.ParamNode(1).AsReg; Assert(img<>nil); Assert(coord<>nil); pLine:=node.pPrev; Assert(pLine<>nil); Case img.info.Dim of Dim.Dim2D:count_dim:=2; Dim.Cube :count_dim:=2; Dim.Dim3D:count_dim:=3; else count_dim:=1; end; count_query:=count_dim; if (img.info.Arrayed<>0) then begin Inc(count_query); end; dvec:=TsrDataType(dtUint32).AsVector(count_query); sizes:=NewReg(dvec); pLine:=OpImageQuerySizeLod(pLine,img.Tgrp,sizes,NewImm_s(dtUint32,0)); //first lod???? if (count_ofs<>count_query) then begin Assert(false,'TODO:count_ofs<>count_query'); end; dvec:=TsrDataType(dtFloat32).AsVector(count_ofs); sizes :=OpUToF(sizes ,dvec,@pLine); roffset:=OpSToF(roffset,dvec,@pLine); roffset:=OpFDivTo(roffset,sizes,@pLine); //(offset.x/WIDTH,offset.y/HEIGHT) coord :=OpFAddTo(coord,roffset,@pLine); //coord + (offset.x/WIDTH,offset.y/HEIGHT) //replace node.ParamNode(1).Value:=coord; Result:=1; end; //////// function TEmitPostOp._Fetch_PackAnc(node:TsrRegNode;index,count:Byte):TsrRegNode; var pLine:TSpirvOp; src:array[0..2] of TsrRegNode; prim,smid,rtid:Boolean; begin Result:=nil; if (node=nil) then Exit; if (count=0) then Exit; pLine:=node.pWriter.specialize AsType; if (pLine=nil) then Exit; if (pLine.OpId<>srOpInternal.OpPackAnc) then Exit; src[0]:=pLine.ParamNode(0).AsReg; src[1]:=pLine.ParamNode(1).AsReg; src[2]:=pLine.ParamNode(2).AsReg; if (src[0]=nil) or (src[1]=nil) or (src[2]=nil) then Exit; prim:= (index+count<= 2); //[ 1: 0] smid:=(index>= 8) and (index+count<=12); //[11: 8] rtid:=(index>=16) and (index+count<=27); //[26:16] count:=ord(prim)+ord(smid)+ord(rtid); if (count=0) then begin Result:=NewImm_q(node.dtype,0,node); end else if (count=1) then begin if prim then begin Result:=src[0]; end else if smid then begin Result:=src[1]; end else if rtid then begin Result:=src[2]; end; end; end; function TEmitPostOp.OnBFE_32_1(node:TSpirvOp):Integer; var pLine:TSpirvOp; dst:TsrRegNode; rBase,rIndex,rCount:TsrRegNode; rsl:TsrRegNode; num_31:TsrRegNode; data:array[0..1] of QWORD; index,count:Byte; dtype:TsrDataType; begin Result:=0; dst:=node.pDst.specialize AsType; if (dst=nil) then Exit; rBase :=node.ParamNode(0).AsReg; rIndex:=RegDown(node.ParamNode(1).AsReg); rCount:=RegDown(node.ParamNode(2).AsReg); if (rBase=nil) or (rIndex=nil) or (rCount=nil) then Exit; dtype:=rBase.dtype; //else pLine:=node; if (rIndex.is_const) and (rCount.is_const) then begin data[0]:=rIndex.AsConst.GetData; data[1]:=rCount.AsConst.GetData; // index:=Byte(data[0] and 31); count:=Byte(data[1] and 31); // rsl:=_Fetch_PackAnc(rBase,index,count); if (rsl<>nil) then begin pLine:=rsl.pLine; rBase:=RegDown(rsl); if (rBase.is_const) then begin data[0]:=rBase.AsConst.GetData; data[1]:=(QWORD(1) shl count)-1; data[0]:=data[0] and data[1]; // if (data[0]<>rBase.AsConst.GetData) then begin rsl:=NewImm_q(dtUInt32,data[0],pLine); end; end else begin data[1]:=(QWORD(1) shl count)-1; num_31:=NewImm_q(dtUInt32,data[1],pLine); // PrepTypeNode(rsl,dtype); rsl:=OpAndTo(rsl,num_31,@pLine); rsl.PrepType(ord(dtype)); end; dst.pWriter:=rsl; node.mark([soNotUsed]); node.pDst:=nil; Exit; end; end; num_31:=nil; // if (rIndex.is_const) then begin data[0]:=rIndex.AsConst.GetData; data[0]:=data[0] and 31; // if (data[0]<>rIndex.AsConst.GetData) then begin rIndex:=NewImm_q(dtUInt32,data[0],pLine); end else begin rIndex:=node.ParamNode(1).AsReg; //orig end; end else begin num_31:=NewImm_q(dtUInt32,31,pLine); // rIndex:=node.ParamNode(1).AsReg; //orig PrepTypeNode(rIndex,dtUInt32); rIndex:=OpAndTo(rIndex,num_31,@pLine); end; // if (rCount.is_const) then begin data[1]:=rCount.AsConst.GetData; data[1]:=data[1] and 31; // if (data[1]<>rCount.AsConst.GetData) then begin rCount:=NewImm_q(dtUInt32,data[1],pLine); end else begin rCount:=node.ParamNode(2).AsReg; //orig end; end else begin if (num_31<>nil) then begin num_31:=NewImm_q(dtUInt32,31,pLine); end; // rCount:=node.ParamNode(2).AsReg; //orig PrepTypeNode(rCount,dtUInt32); rCount:=OpAndTo(rCount,num_31,@pLine); end; PrepTypeNode(rIndex,dtUInt32); PrepTypeNode(rCount,dtUInt32); case dtype of dtUint32:_Op3(pLine,Op.OpBitFieldUExtract,dst,rBase,rIndex,rCount); dtInt32 :_Op3(pLine,Op.OpBitFieldSExtract,dst,rBase,rIndex,rCount); else Assert(False); end; node.mark([soNotUsed]); node.pDst:=nil; end; function TEmitPostOp.OnBFIB32_1(node:TSpirvOp):Integer; var pLine:TSpirvOp; dst:TsrRegNode; bitmsk:TsrRegNode; src:array[0..1] of TsrRegNode; rIndex,rCount:TsrRegNode; data:array[0..1] of QWORD; index,count:DWORD; begin Result:=0; dst:=node.pDst.specialize AsType; if (dst=nil) then Exit; pLine:=node; bitmsk:=RegDown(node.ParamNode(0).AsReg); src[0]:=node.ParamNode(1).AsReg; src[1]:=node.ParamNode(2).AsReg; if (bitmsk=nil) or (src[0]=nil) or (src[1]=nil) then Exit; if bitmsk.is_const then begin data[0]:=bitmsk.AsConst.GetData; index:=BsfQWord(data[0]); count:=PopCnt (data[0]); data[1]:=((QWORD(1) shl count)-1) shl index; if (data[0]=data[1]) then begin rIndex:=NewImm_q(dtUint32,index,pLine); rCount:=NewImm_q(dtUint32,count,pLine); _Op4(pLine,Op.OpBitFieldInsert,dst,src[1],src[0],rIndex,rCount); node.mark([soNotUsed]); node.pDst:=nil; Exit; end; end; //else src[0]:=OpAndTo(src[0],bitmsk,@pLine); src[0].PrepType(ord(dtUInt32)); bitmsk:=OpNotTo(bitmsk,@pLine); bitmsk.PrepType(ord(dtUInt32)); src[1]:=OpAndTo(src[1],bitmsk,@pLine); src[1].PrepType(ord(dtUInt32)); _Op2(pLine,Op.OpBitwiseOr,dst,src[0],src[1]); node.mark([soNotUsed]); node.pDst:=nil; end; function _IsFma(node:TSpirvOp):Boolean; var OpId:PtrUint; begin Result:=False; if (node=nil) then Exit; if (node.OpId<>Op.OpExtInst) then Exit; OpId:=0; node.ParamNode(1).TryGetValue(OpId); if (OpId<>GlslOp.Fma) then Exit; Result:=True; end; function _GetMadLegacyFma(node:TSpirvOp):TSpirvOp; var reg:TsrRegNode; fma:TSpirvOp; begin Result:=node; if (node=nil) then Exit; if (node.OpId<>Op.OpSelect) then Exit; reg:=RegDown(node.ParamNode(1).AsReg); fma:=reg.pWriter.specialize AsType; if _IsFma(fma) then begin Exit(fma); end; reg:=RegDown(node.ParamNode(2).AsReg); fma:=reg.pWriter.specialize AsType; if _IsFma(fma) then begin Exit(fma); end; end; function _IsOp(node:TSpirvOp;OpId:DWORD):Boolean; begin Result:=False; if (node=nil) then Exit; Result:=(node.OpId=OpId); end; function _IsConstFloat_1_0(pReg:TsrRegNode):Boolean; var pConst:TsrConst; begin Result:=False; if (pReg=nil) then Exit; pConst:=pReg.AsConst; if (pConst=nil) then Exit; if (pConst.dtype<>dtFloat32) then Exit; Result:=(Round(pConst.AsFloat32*10)=10); end; function _IsConstFloat_1_5(pReg:TsrRegNode):Boolean; var pConst:TsrConst; begin Result:=False; if (pReg=nil) then Exit; pConst:=pReg.AsConst; if (pConst=nil) then Exit; if (pConst.dtype<>dtFloat32) then Exit; Result:=(Round(pConst.AsFloat32*10)=15); end; function _Fetch_FAbs_Value(node:TSpirvOp):TSpirvOp; var OpId:PtrUint; pReg:TsrRegNode; begin Result:=nil; if (node=nil) then Exit; if (node.OpId<>Op.OpExtInst) then Exit; OpId:=0; node.ParamNode(1).TryGetValue(OpId); if (OpId<>GlslOp.FAbs) then Exit; pReg:=RegDown(node.ParamNode(2).AsReg); if (pReg=nil) then Exit; Result:=pReg.pWriter.specialize AsType; end; function is_all_const(src:PPsrRegNode;count:byte):Boolean; var i:Byte; begin Result:=True; For i:=0 to count-1 do if not src[i].is_const then Exit(false); end; function is_all_in_one_comp(src:PPsrRegNode;rtype:TsrDataType;count:byte):Boolean; var i:Byte; pos:PtrUint; pLine:TSpirvOp; pReg,tmp:TsrRegNode; begin pReg:=nil; Result:=True; For i:=0 to count-1 do begin pLine:=src[i].pWriter.specialize AsType; if (pLine=nil) then Exit(false); if (pLine.OpId<>Op.OpCompositeExtract) then Exit(false); pos:=0; if not pLine.ParamNode(1).TryGetValue(pos) then Exit; if (pos<>i) then Exit(false); tmp:=RegDown(pLine.ParamNode(0).AsReg); if (tmp=nil) then Exit(false); if (tmp.dtype<>rtype) then Exit(false); if (i=0) then begin pReg:=tmp; end else begin if (pReg<>tmp) then Exit(false); end; end; end; function try_get_comp_bridge(var src:TsrRegNode):Integer; var pLine:TSpirvOp; pos:PtrUint; pReg:TsrRegNode; begin Result:=0; pLine:=src.pWriter.specialize AsType; if (pLine=nil) then Exit; if (pLine.OpId<>Op.OpCompositeExtract) then Exit; pos:=0; if not pLine.ParamNode(1).TryGetValue(pos) then Exit; pReg:=RegDown(pLine.ParamNode(0).AsReg); if (pReg=nil) then Exit; pLine:=pReg.pWriter.specialize AsType; if (pLine=nil) then Exit; if (pLine.OpId<>Op.OpCompositeConstruct) then Exit; pReg:=RegDown(pLine.ParamNode(pos).AsReg); if (pReg=nil) then Exit; src:=pReg; Result:=1; end; procedure TEmitPostOp.MakeVecConst(rtype:TsrDataType;dst:TsrRegNode;src:PPsrRegNode); var nodes:array[0..3] of TsrConst; h:TsrConst; i:Byte; begin For i:=0 to rtype.Count-1 do begin nodes[i]:=src[i].AsConst; end; h:=ConstList.FetchVector(rtype,@nodes,true); dst.pWriter:=h; end; procedure TEmitPostOp.MakeVecOne(dst:TsrRegNode;src:PPsrRegNode); var pLine:TSpirvOp; rsrc:TsrRegNode; begin pLine:=src[0].pWriter.specialize AsType; rsrc:=RegDown(pLine.ParamNode(0).AsReg); dst.pWriter:=rsrc; end; function TEmitPostOp.MakeVecComp(pLine:TSpirvOp;rtype:TsrDataType;dst:TsrRegNode;src:PPsrRegNode):TSpirvOp; var r:Integer; i:Byte; begin Result:=pLine; repeat r:=0; For i:=0 to rtype.Count-1 do begin r:=r+try_get_comp_bridge(src[i]); end; if is_all_const(src,rtype.Count) then begin MakeVecConst(rtype,dst,src); Exit; // end; if is_all_in_one_comp(src,rtype,rtype.Count) then begin MakeVecOne(dst,src); Exit; // end; until (r=0); Result:=OpMakeCon(pLine,dst,src); end; function TEmitPostOp.OnMakeVec2(node:TSpirvOp):Integer; var pParam:POpParamNode; dst:TsrRegNode; src:array[0..3] of TsrRegNode; rtype:TsrDataType; i:Byte; begin Result:=1; dst:=node.pDst.specialize AsType; if (dst=nil) then Exit; pParam:=node.ParamFirst; rtype:=dst.dtype; For i:=0 to rtype.Count-1 do begin src[i]:=pParam.AsReg; pParam:=pParam.Next; if (src[i]=nil) then Assert(false,'OnMakeVec2'); end; MakeVecComp(node,rtype,dst,@src); node.mark([soNotUsed]); node.pDst:=nil; end; function TEmitPostOp.OnReturn_2(node:TSpirvOp):Integer; begin Result:=0; if is_term_op(flow_down_prev_up(node)) then begin node.mark([soNotUsed]); Inc(Result); end; end; function TEmitPostOp.OnMakeExp2(node:TSpirvOp):Integer; var pLine:TSpirvOp; pOpBlock:TsrOpBlock; pChild:TsrOpBlock; pBegOp,pEndOp,pMrgOp:TspirvOp; exc:TsrRegNode; begin Result:=1; pOpBlock:=node.Parent; exc:=RegDown(node.ParamNode(0).AsReg); if (exc=nil) then Exit; if exc.is_const then begin node.mark([soNotUsed]); Case exc.AsConst.AsBool of True : //is always store begin pLine:=node.Next; if (pLine<>nil) then begin pChild:=pLine.specialize AsType; if (pChild<>nil) then begin //up pOpBlock.Remove(pChild); pLine:=pOpBlock; pLine.InsertAfter(pChild); pChild.UpdateLevel; end; end; end; False: //is always kill begin AddSpirvOp(pOpBlock.dummy,Op.OpKill); //add kill //clear all node:=pOpBlock.First; While (node<>nil) do begin if node.IsType(ntOpBlock) then begin pChild:=node.specialize AsType; node:=pChild.First; Continue; end else begin Case node.OpId of Op.OpNop:; Op.OpKill:; else node.mark([soNotUsed,soForce]); end; end; node:=node.Next; end; end; end; Exit; end else begin //reread exc:=node.ParamNode(0).AsReg; node.mark([soNotUsed]); pLine:=node.Next; if (pLine=nil) then //kill or nop begin pBegOp:=NewLabelOp(False); //current pEndOp:=NewLabelOp(False); //end pMrgOp:=pEndOp; //merge pOpBlock.SetLabels(pBegOp,pEndOp,pMrgOp); pOpBlock.bType:=btCond; pOpBlock.SetCond(exc,false); //reverse pLine:=node; pLine:=OpCondMerge (pLine,pMrgOp); pLine:=OpBranchCond(pLine,pEndOp,pBegOp,exc); //reverse pLine:=AddSpirvOp (pLine,pBegOp); pChild:=AllocBlockOp; //create new pChild.SetInfo(btOther); pChild.dummy.OpId:=Op.OpKill; //set kill to dummy pOpBlock.pBody:=pChild; pLine:=InsertBlockOp(pLine,pChild); //OpBranch not need from kill pLine:=AddSpirvOp(pLine,pMrgOp); end else begin //kill or store Assert(pLine.IsType(ntOpBlock)); pBegOp:=NewLabelOp(False); //current pEndOp:=NewLabelOp(False); //end pMrgOp:=NewLabelOp(False); //merge pOpBlock.SetLabels(pBegOp,pEndOp,pMrgOp); pOpBlock.bType:=btCond; pOpBlock.SetCond(exc,false); //reverse pOpBlock.pElse.bType:=btElse; pLine:=node; pLine:=OpCondMerge (pLine,pMrgOp); pLine:=OpBranchCond(pLine,pEndOp,pBegOp,exc); //reverse pLine:=AddSpirvOp (pLine,pBegOp); pChild:=AllocBlockOp; //create new pChild.SetInfo(btOther); pChild.dummy.OpId:=Op.OpKill; //set kill to dummy pOpBlock.pBody:=pChild; pLine:=InsertBlockOp(pLine,pChild); //OpBranch not need from kill pLine:=AddSpirvOp(pLine,pEndOp); //OpStore child pLine:=pOpBlock.Last; pLine:=OpBranch (pLine,pMrgOp); pLine:=AddSpirvOp(pLine,pMrgOp); //end end; end; end; function TEmitPostOp.OnIAddExt2(node:TSpirvOp):Integer; var rsl:TsrRegPair; dst,car:TsrRegNode; src:array[0..1] of TsrRegNode; begin Result:=1; rsl:=node.pDst.specialize AsType; if (rsl=nil) then Exit; dst:=rsl.pDst0.specialize AsType; car:=rsl.pDst1.specialize AsType; if (dst=nil) or (car=nil) then Exit; src[0]:=node.ParamNode(0).AsReg; src[1]:=node.ParamNode(1).AsReg; if (src[0]=nil) or (src[1]=nil) then Exit; node.mark([soNotUsed]); node.pDst:=nil; if (car.IsUsed) then //carry is use begin OpIAddCar(node,dst,car,src[0],src[1]); end else begin _Op2(node,Op.OpIAdd,dst,src[0],src[1]); end; end; function TEmitPostOp.OnISubExt2(node:TSpirvOp):Integer; var rsl:TsrRegPair; dst,bor:TsrRegNode; src:array[0..1] of TsrRegNode; begin Result:=1; rsl:=node.pDst.specialize AsType; if (rsl=nil) then Exit; dst:=rsl.pDst0.specialize AsType; bor:=rsl.pDst1.specialize AsType; if (dst=nil) or (bor=nil) then Exit; src[0]:=node.ParamNode(0).AsReg; src[1]:=node.ParamNode(1).AsReg; if (src[0]=nil) or (src[1]=nil) then Exit; node.mark([soNotUsed]); node.pDst:=nil; if (bor.IsUsed) then //borrow is use begin OpISubBor(node,dst,bor,src[0],src[1]); end else begin _Op2(node,Op.OpISub,dst,src[0],src[1]); end; end; function TEmitPostOp.OnPackAnc2(node:TSpirvOp):Integer; var dst:TsrRegNode; src:array[0..2] of TsrRegNode; num4 :TsrRegNode; num8 :TsrRegNode; num11:TsrRegNode; num16:TsrRegNode; begin Result:=0; dst:=node.pDst.specialize AsType; if (dst=nil) then Exit; src[0]:=node.ParamNode(0).AsReg; //prim src[1]:=node.ParamNode(1).AsReg; //smid src[2]:=node.ParamNode(2).AsReg; //rtid if (src[0]=nil) or (src[0]=nil) or (src[1]=nil) then Exit; node.mark([soNotUsed]); node.pDst:=nil; num4 :=NewImm_q(dtUint32, 4,node); num8 :=NewImm_q(dtUint32, 8,node); num11:=NewImm_q(dtUint32,11,node); num16:=NewImm_q(dtUint32,16,node); //Base,Insert,Offset,Count src[0]:=OpBFITo(src[0],src[1],num8 ,num4 ,@node); src[0]:=OpBFITo(src[0],src[2],num16,num11,@node); dst.pWriter:=src[0]; end; // end.