Пример #1
0
        private static void EmitAluBinary(
            ShaderIrBlock block,
            long opCode,
            ShaderOper oper,
            ShaderIrInst inst)
        {
            ShaderIrNode operA = opCode.Gpr8(), operB;

            switch (oper)
            {
            case ShaderOper.Cr:  operB = opCode.Cbuf34();   break;

            case ShaderOper.Imm: operB = opCode.Imm19_20(); break;

            case ShaderOper.Rr:  operB = opCode.Gpr20();    break;

            default: throw new ArgumentException(nameof(oper));
            }

            ShaderIrNode op = new ShaderIrOp(inst, operA, operB);

            block.AddNode(opCode.PredNode(new ShaderIrAsg(opCode.Gpr0(), op)));
        }
Пример #2
0
        private static void EmitAluBinary(
            ShaderIrBlock Block,
            long OpCode,
            ShaderOper Oper,
            ShaderIrInst Inst)
        {
            ShaderIrNode OperA = OpCode.Gpr8(), OperB;

            switch (Oper)
            {
            case ShaderOper.CR:  OperB = OpCode.Cbuf34();   break;

            case ShaderOper.Imm: OperB = OpCode.Imm19_20(); break;

            case ShaderOper.RR:  OperB = OpCode.Gpr20();    break;

            default: throw new ArgumentException(nameof(Oper));
            }

            ShaderIrNode Op = new ShaderIrOp(Inst, OperA, OperB);

            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
        }
Пример #3
0
 private static bool IsFlowChange(ShaderIrInst Inst)
 {
     return(Inst == ShaderIrInst.Exit);
 }
Пример #4
0
        private static void EmitTexs(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst)
        {
            //TODO: Support other formats.
            ShaderIrNode OperA = GetOperGpr8(OpCode);
            ShaderIrNode OperB = GetOperGpr20(OpCode);
            ShaderIrNode OperC = GetOperImm13_36(OpCode);

            int LutIndex;

            LutIndex  = GetOperGpr0(OpCode).Index != ShaderIrOperGpr.ZRIndex ? 1 : 0;
            LutIndex |= GetOperGpr28(OpCode).Index != ShaderIrOperGpr.ZRIndex ? 2 : 0;

            if (LutIndex == 0)
            {
                //Both registers are RZ, color is not written anywhere.
                //So, the intruction is basically a no-op.
                return;
            }

            int ChMask = MaskLut[LutIndex, (OpCode >> 50) & 7];

            for (int Ch = 0; Ch < 4; Ch++)
            {
                ShaderIrOperGpr Dst = new ShaderIrOperGpr(TempRegStart + Ch);

                ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch);

                ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB, OperC, Meta);

                Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Op), OpCode));
            }

            int RegInc = 0;

            ShaderIrOperGpr GetDst()
            {
                ShaderIrOperGpr Dst;

                switch (LutIndex)
                {
                case 1: Dst = GetOperGpr0(OpCode); break;

                case 2: Dst = GetOperGpr28(OpCode); break;

                case 3: Dst = (RegInc >> 1) != 0
                        ? GetOperGpr28(OpCode)
                        : GetOperGpr0(OpCode); break;

                default: throw new InvalidOperationException();
                }

                Dst.Index += RegInc++ & 1;

                return(Dst);
            }

            for (int Ch = 0; Ch < 4; Ch++)
            {
                if (!IsChannelUsed(ChMask, Ch))
                {
                    continue;
                }

                ShaderIrOperGpr Src = new ShaderIrOperGpr(TempRegStart + Ch);

                ShaderIrOperGpr Dst = GetDst();

                if (Dst.Index != ShaderIrOperGpr.ZRIndex)
                {
                    Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Src), OpCode));
                }
            }
        }
Пример #5
0
        private static void EmitSetp(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper)
        {
            bool Aa = ((OpCode >> 7) & 1) != 0;
            bool Np = ((OpCode >> 42) & 1) != 0;
            bool Na = ((OpCode >> 43) & 1) != 0;
            bool Ab = ((OpCode >> 44) & 1) != 0;

            ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;

            switch (Oper)
            {
            case ShaderOper.CR:   OperB = GetOperCbuf34(OpCode); break;

            case ShaderOper.Imm:  OperB = GetOperImm19_20(OpCode); break;

            case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break;

            case ShaderOper.RR:   OperB = GetOperGpr20(OpCode); break;

            default: throw new ArgumentException(nameof(Oper));
            }

            ShaderIrInst CmpInst;

            if (IsFloat)
            {
                OperA = GetAluAbsNeg(OperA, Aa, Na);
                OperB = GetAluAbs(OperB, Ab);

                CmpInst = GetCmpF(OpCode);
            }
            else
            {
                CmpInst = GetCmp(OpCode);
            }

            ShaderIrOp Op = new ShaderIrOp(CmpInst, OperA, OperB);

            ShaderIrOperPred P0Node = GetOperPred3(OpCode);
            ShaderIrOperPred P1Node = GetOperPred0(OpCode);
            ShaderIrOperPred P2Node = GetOperPred39(OpCode);

            Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode));

            ShaderIrInst LopInst = GetBLop(OpCode);

            if (LopInst == ShaderIrInst.Band && P1Node.IsConst && P2Node.IsConst)
            {
                return;
            }

            ShaderIrNode P2NNode = P2Node;

            if (Np)
            {
                P2NNode = new ShaderIrOp(ShaderIrInst.Bnot, P2NNode);
            }

            Op = new ShaderIrOp(ShaderIrInst.Bnot, P0Node);

            Op = new ShaderIrOp(LopInst, Op, P2NNode);

            Block.AddNode(GetPredNode(new ShaderIrAsg(P1Node, Op), OpCode));

            Op = new ShaderIrOp(LopInst, P0Node, P2NNode);

            Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode));
        }
Пример #6
0
        private static void EmitSet(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper)
        {
            bool Na = ((OpCode >> 43) & 1) != 0;
            bool Ab = ((OpCode >> 44) & 1) != 0;
            bool Nb = ((OpCode >> 53) & 1) != 0;
            bool Aa = ((OpCode >> 54) & 1) != 0;

            ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;

            switch (Oper)
            {
            case ShaderOper.CR:   OperB = GetOperCbuf34(OpCode); break;

            case ShaderOper.Imm:  OperB = GetOperImm19_20(OpCode); break;

            case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break;

            case ShaderOper.RR:   OperB = GetOperGpr20(OpCode); break;

            default: throw new ArgumentException(nameof(Oper));
            }

            ShaderIrInst CmpInst;

            if (IsFloat)
            {
                OperA = GetAluAbsNeg(OperA, Aa, Na);
                OperB = GetAluAbsNeg(OperB, Ab, Nb);

                CmpInst = GetCmpF(OpCode);
            }
            else
            {
                CmpInst = GetCmp(OpCode);
            }

            ShaderIrOp Op = new ShaderIrOp(CmpInst, OperA, OperB);

            ShaderIrInst LopInst = GetBLop(OpCode);

            ShaderIrOperPred PNode = GetOperPred39(OpCode);

            ShaderIrOperImmf Imm0 = new ShaderIrOperImmf(0);
            ShaderIrOperImmf Imm1 = new ShaderIrOperImmf(1);

            ShaderIrNode Asg0 = new ShaderIrAsg(GetOperGpr0(OpCode), Imm0);
            ShaderIrNode Asg1 = new ShaderIrAsg(GetOperGpr0(OpCode), Imm1);

            if (LopInst != ShaderIrInst.Band || !PNode.IsConst)
            {
                ShaderIrOp Op2 = new ShaderIrOp(LopInst, Op, PNode);

                Asg0 = new ShaderIrCond(Op2, Asg0, Not: true);
                Asg1 = new ShaderIrCond(Op2, Asg1, Not: false);
            }
            else
            {
                Asg0 = new ShaderIrCond(Op, Asg0, Not: true);
                Asg1 = new ShaderIrCond(Op, Asg1, Not: false);
            }

            Block.AddNode(GetPredNode(Asg0, OpCode));
            Block.AddNode(GetPredNode(Asg1, OpCode));
        }
Пример #7
0
        private static void EmitTexs(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst)
        {
            //TODO: Support other formats.
            int LutIndex;

            LutIndex  = !OpCode.Gpr0().IsConst  ? 1 : 0;
            LutIndex |= !OpCode.Gpr28().IsConst ? 2 : 0;

            if (LutIndex == 0)
            {
                //Both destination registers are RZ, do nothing.
                return;
            }

            bool Fp16 = !OpCode.Read(59);

            int DstIncrement = 0;

            ShaderIrOperGpr GetDst()
            {
                ShaderIrOperGpr Dst;

                if (Fp16)
                {
                    //FP16 mode, two components are packed on the two
                    //halfs of a 32-bits register, as two half-float values.
                    int HalfPart = DstIncrement & 1;

                    switch (LutIndex)
                    {
                    case 1: Dst = OpCode.GprHalf0(HalfPart);  break;

                    case 2: Dst = OpCode.GprHalf28(HalfPart); break;

                    case 3: Dst = (DstIncrement >> 1) != 0
                            ? OpCode.GprHalf28(HalfPart)
                            : OpCode.GprHalf0(HalfPart); break;

                    default: throw new InvalidOperationException();
                    }
                }
                else
                {
                    //32-bits mode, each component uses one register.
                    //Two components uses two consecutive registers.
                    switch (LutIndex)
                    {
                    case 1: Dst = OpCode.Gpr0();  break;

                    case 2: Dst = OpCode.Gpr28(); break;

                    case 3: Dst = (DstIncrement >> 1) != 0
                            ? OpCode.Gpr28()
                            : OpCode.Gpr0(); break;

                    default: throw new InvalidOperationException();
                    }

                    Dst.Index += DstIncrement & 1;
                }

                DstIncrement++;

                return(Dst);
            }

            int ChMask = MaskLut[LutIndex, OpCode.Read(50, 7)];

            if (ChMask == 0)
            {
                //All channels are disabled, do nothing.
                return;
            }

            ShaderIrNode OperC = OpCode.Imm13_36();

            ShaderIrOperGpr Coord0 = ShaderIrOperGpr.MakeTemporary(0);
            ShaderIrOperGpr Coord1 = ShaderIrOperGpr.MakeTemporary(1);

            Block.AddNode(new ShaderIrAsg(Coord0, OpCode.Gpr8()));
            Block.AddNode(new ShaderIrAsg(Coord1, OpCode.Gpr20()));

            for (int Ch = 0; Ch < 4; Ch++)
            {
                if (!IsChannelUsed(ChMask, Ch))
                {
                    continue;
                }

                ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch);

                ShaderIrOp Op = new ShaderIrOp(Inst, Coord0, Coord1, OperC, Meta);

                ShaderIrOperGpr Dst = GetDst();

                if (Dst.IsValidRegister && !Dst.IsConst)
                {
                    Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op)));
                }
            }
        }
Пример #8
0
        private static void EmitMnmx(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper)
        {
            bool NegB = OpCode.Read(45);
            bool AbsA = OpCode.Read(46);
            bool NegA = OpCode.Read(48);
            bool AbsB = OpCode.Read(49);

            ShaderIrNode OperA = OpCode.Gpr8(), OperB;

            if (IsFloat)
            {
                OperA = GetAluFabsFneg(OperA, AbsA, NegA);
            }
            else
            {
                OperA = GetAluIabsIneg(OperA, AbsA, NegA);
            }

            switch (Oper)
            {
            case ShaderOper.CR:   OperB = OpCode.Cbuf34();    break;

            case ShaderOper.Imm:  OperB = OpCode.Imm19_20();  break;

            case ShaderOper.Immf: OperB = OpCode.Immf19_20(); break;

            case ShaderOper.RR:   OperB = OpCode.Gpr20();     break;

            default: throw new ArgumentException(nameof(Oper));
            }

            if (IsFloat)
            {
                OperB = GetAluFabsFneg(OperB, AbsB, NegB);
            }
            else
            {
                OperB = GetAluIabsIneg(OperB, AbsB, NegB);
            }

            ShaderIrOperPred Pred = OpCode.Pred39();

            ShaderIrOp Op;

            ShaderIrInst MaxInst = IsFloat ? ShaderIrInst.Fmax : ShaderIrInst.Max;
            ShaderIrInst MinInst = IsFloat ? ShaderIrInst.Fmin : ShaderIrInst.Min;

            if (Pred.IsConst)
            {
                bool IsMax = OpCode.Read(42);

                Op = new ShaderIrOp(IsMax
                    ? MaxInst
                    : MinInst, OperA, OperB);

                Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
            }
            else
            {
                ShaderIrNode PredN = OpCode.Pred39N();

                ShaderIrOp OpMax = new ShaderIrOp(MaxInst, OperA, OperB);
                ShaderIrOp OpMin = new ShaderIrOp(MinInst, OperA, OperB);

                ShaderIrAsg AsgMax = new ShaderIrAsg(OpCode.Gpr0(), OpMax);
                ShaderIrAsg AsgMin = new ShaderIrAsg(OpCode.Gpr0(), OpMin);

                Block.AddNode(OpCode.PredNode(new ShaderIrCond(PredN, AsgMax, Not: true)));
                Block.AddNode(OpCode.PredNode(new ShaderIrCond(PredN, AsgMin, Not: false)));
            }
        }
Пример #9
0
        private static void EmitSetp(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper)
        {
            bool AbsA = OpCode.Read(7);
            bool NegP = OpCode.Read(42);
            bool NegA = OpCode.Read(43);
            bool AbsB = OpCode.Read(44);

            ShaderIrNode OperA = OpCode.Gpr8(), OperB;

            switch (Oper)
            {
            case ShaderOper.CR:   OperB = OpCode.Cbuf34();    break;

            case ShaderOper.Imm:  OperB = OpCode.Imm19_20();  break;

            case ShaderOper.Immf: OperB = OpCode.Immf19_20(); break;

            case ShaderOper.RR:   OperB = OpCode.Gpr20();     break;

            default: throw new ArgumentException(nameof(Oper));
            }

            ShaderIrInst CmpInst;

            if (IsFloat)
            {
                OperA = GetAluFabsFneg(OperA, AbsA, NegA);
                OperB = GetAluFabs(OperB, AbsB);

                CmpInst = OpCode.CmpF();
            }
            else
            {
                CmpInst = OpCode.Cmp();
            }

            ShaderIrOp Op = new ShaderIrOp(CmpInst, OperA, OperB);

            ShaderIrOperPred P0Node = OpCode.Pred3();
            ShaderIrOperPred P1Node = OpCode.Pred0();
            ShaderIrOperPred P2Node = OpCode.Pred39();

            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(P0Node, Op)));

            ShaderIrInst LopInst = OpCode.BLop45();

            if (LopInst == ShaderIrInst.Band && P1Node.IsConst && P2Node.IsConst)
            {
                return;
            }

            ShaderIrNode P2NNode = P2Node;

            if (NegP)
            {
                P2NNode = new ShaderIrOp(ShaderIrInst.Bnot, P2NNode);
            }

            Op = new ShaderIrOp(ShaderIrInst.Bnot, P0Node);

            Op = new ShaderIrOp(LopInst, Op, P2NNode);

            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(P1Node, Op)));

            Op = new ShaderIrOp(LopInst, P0Node, P2NNode);

            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(P0Node, Op)));
        }
Пример #10
0
        private static void EmitTex(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst)
        {
            //TODO: Support other formats.
            ShaderIrNode OperA = GetOperGpr8(OpCode);
            ShaderIrNode OperB = GetOperGpr20(OpCode);
            ShaderIrNode OperC = GetOperImm13_36(OpCode);

            bool TwoDests = GetOperGpr28(OpCode).Index != ShaderIrOperGpr.ZRIndex;

            int ChMask;

            switch ((OpCode >> 50) & 7)
            {
            case 0: ChMask = TwoDests ? 0x7 : 0x1; break;

            case 1: ChMask = TwoDests ? 0xb : 0x2; break;

            case 2: ChMask = TwoDests ? 0xd : 0x4; break;

            case 3: ChMask = TwoDests ? 0xe : 0x8; break;

            case 4: ChMask = TwoDests ? 0xf : 0x3; break;

            default: throw new InvalidOperationException();
            }

            for (int Ch = 0; Ch < 4; Ch++)
            {
                ShaderIrOperGpr Dst = new ShaderIrOperGpr(TempRegStart + Ch);

                ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch);

                ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB, OperC, Meta);

                Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Op), OpCode));
            }

            int RegInc = 0;

            for (int Ch = 0; Ch < 4; Ch++)
            {
                if (!IsChannelUsed(ChMask, Ch))
                {
                    continue;
                }

                ShaderIrOperGpr Src = new ShaderIrOperGpr(TempRegStart + Ch);

                ShaderIrOperGpr Dst = (RegInc >> 1) != 0
                    ? GetOperGpr28(OpCode)
                    : GetOperGpr0(OpCode);

                Dst.Index += RegInc++ & 1;

                if (Dst.Index >= ShaderIrOperGpr.ZRIndex)
                {
                    continue;
                }

                Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Src), OpCode));
            }
        }
Пример #11
0
        private static void EmitSet(ShaderIrBlock block, long opCode, bool isFloat, ShaderOper oper)
        {
            bool negA = opCode.Read(43);
            bool absB = opCode.Read(44);
            bool negB = opCode.Read(53);
            bool absA = opCode.Read(54);

            bool boolFloat = opCode.Read(isFloat ? 52 : 44);

            ShaderIrNode operA = opCode.Gpr8(), operB;

            switch (oper)
            {
            case ShaderOper.Cr:   operB = opCode.Cbuf34();    break;

            case ShaderOper.Imm:  operB = opCode.Imm19_20();  break;

            case ShaderOper.Immf: operB = opCode.Immf19_20(); break;

            case ShaderOper.Rr:   operB = opCode.Gpr20();     break;

            default: throw new ArgumentException(nameof(oper));
            }

            ShaderIrInst cmpInst;

            if (isFloat)
            {
                operA = GetAluFabsFneg(operA, absA, negA);
                operB = GetAluFabsFneg(operB, absB, negB);

                cmpInst = opCode.CmpF();
            }
            else
            {
                cmpInst = opCode.Cmp();
            }

            ShaderIrOp op = new ShaderIrOp(cmpInst, operA, operB);

            ShaderIrInst lopInst = opCode.BLop45();

            ShaderIrOperPred pNode = opCode.Pred39();

            ShaderIrNode imm0, imm1;

            if (boolFloat)
            {
                imm0 = new ShaderIrOperImmf(0);
                imm1 = new ShaderIrOperImmf(1);
            }
            else
            {
                imm0 = new ShaderIrOperImm(0);
                imm1 = new ShaderIrOperImm(-1);
            }

            ShaderIrNode asg0 = new ShaderIrAsg(opCode.Gpr0(), imm0);
            ShaderIrNode asg1 = new ShaderIrAsg(opCode.Gpr0(), imm1);

            if (lopInst != ShaderIrInst.Band || !pNode.IsConst)
            {
                ShaderIrOp op2 = new ShaderIrOp(lopInst, op, pNode);

                asg0 = new ShaderIrCond(op2, asg0, not: true);
                asg1 = new ShaderIrCond(op2, asg1, not: false);
            }
            else
            {
                asg0 = new ShaderIrCond(op, asg0, not: true);
                asg1 = new ShaderIrCond(op, asg1, not: false);
            }

            block.AddNode(opCode.PredNode(asg0));
            block.AddNode(opCode.PredNode(asg1));
        }
Пример #12
0
        private static void EmitMnmx(ShaderIrBlock block, long opCode, bool isFloat, ShaderOper oper)
        {
            bool negB = opCode.Read(45);
            bool absA = opCode.Read(46);
            bool negA = opCode.Read(48);
            bool absB = opCode.Read(49);

            ShaderIrNode operA = opCode.Gpr8(), operB;

            if (isFloat)
            {
                operA = GetAluFabsFneg(operA, absA, negA);
            }
            else
            {
                operA = GetAluIabsIneg(operA, absA, negA);
            }

            switch (oper)
            {
            case ShaderOper.Cr:   operB = opCode.Cbuf34();    break;

            case ShaderOper.Imm:  operB = opCode.Imm19_20();  break;

            case ShaderOper.Immf: operB = opCode.Immf19_20(); break;

            case ShaderOper.Rr:   operB = opCode.Gpr20();     break;

            default: throw new ArgumentException(nameof(oper));
            }

            if (isFloat)
            {
                operB = GetAluFabsFneg(operB, absB, negB);
            }
            else
            {
                operB = GetAluIabsIneg(operB, absB, negB);
            }

            ShaderIrOperPred pred = opCode.Pred39();

            ShaderIrOp op;

            ShaderIrInst maxInst = isFloat ? ShaderIrInst.Fmax : ShaderIrInst.Max;
            ShaderIrInst minInst = isFloat ? ShaderIrInst.Fmin : ShaderIrInst.Min;

            if (pred.IsConst)
            {
                bool isMax = opCode.Read(42);

                op = new ShaderIrOp(isMax
                    ? maxInst
                    : minInst, operA, operB);

                block.AddNode(opCode.PredNode(new ShaderIrAsg(opCode.Gpr0(), op)));
            }
            else
            {
                ShaderIrNode predN = opCode.Pred39N();

                ShaderIrOp opMax = new ShaderIrOp(maxInst, operA, operB);
                ShaderIrOp opMin = new ShaderIrOp(minInst, operA, operB);

                ShaderIrAsg asgMax = new ShaderIrAsg(opCode.Gpr0(), opMax);
                ShaderIrAsg asgMin = new ShaderIrAsg(opCode.Gpr0(), opMin);

                block.AddNode(opCode.PredNode(new ShaderIrCond(predN, asgMax, not: true)));
                block.AddNode(opCode.PredNode(new ShaderIrCond(predN, asgMin, not: false)));
            }
        }
Пример #13
0
        private static void EmitXmad(ShaderIrBlock block, long opCode, ShaderOper oper)
        {
            bool signedA = opCode.Read(48);
            bool signedB = opCode.Read(49);
            bool highB   = opCode.Read(52);
            bool highA   = opCode.Read(53);

            int mode = opCode.Read(50, 7);

            ShaderIrNode operA = opCode.Gpr8(), operB, operC;

            switch (oper)
            {
            case ShaderOper.Cr:  operB = opCode.Cbuf34();    break;

            case ShaderOper.Imm: operB = opCode.ImmU16_20(); break;

            case ShaderOper.Rc:  operB = opCode.Gpr39();     break;

            case ShaderOper.Rr:  operB = opCode.Gpr20();     break;

            default: throw new ArgumentException(nameof(oper));
            }

            ShaderIrNode operB2 = operB;

            if (oper == ShaderOper.Imm)
            {
                int imm = ((ShaderIrOperImm)operB2).Value;

                if (!highB)
                {
                    imm <<= 16;
                }

                if (signedB)
                {
                    imm >>= 16;
                }
                else
                {
                    imm = (int)((uint)imm >> 16);
                }

                operB2 = new ShaderIrOperImm(imm);
            }

            ShaderIrOperImm imm16 = new ShaderIrOperImm(16);

            //If we are working with the lower 16-bits of the A/B operands,
            //we need to shift the lower 16-bits to the top 16-bits. Later,
            //they will be right shifted. For U16 types, this will be a logical
            //right shift, and for S16 types, a arithmetic right shift.
            if (!highA)
            {
                operA = new ShaderIrOp(ShaderIrInst.Lsl, operA, imm16);
            }

            if (!highB && oper != ShaderOper.Imm)
            {
                operB2 = new ShaderIrOp(ShaderIrInst.Lsl, operB2, imm16);
            }

            ShaderIrInst shiftA = signedA ? ShaderIrInst.Asr : ShaderIrInst.Lsr;
            ShaderIrInst shiftB = signedB ? ShaderIrInst.Asr : ShaderIrInst.Lsr;

            operA = new ShaderIrOp(shiftA, operA, imm16);

            if (oper != ShaderOper.Imm)
            {
                operB2 = new ShaderIrOp(shiftB, operB2, imm16);
            }

            bool productShiftLeft = false;
            bool merge            = false;

            if (oper == ShaderOper.Rc)
            {
                operC = opCode.Cbuf34();
            }
            else
            {
                operC = opCode.Gpr39();

                productShiftLeft = opCode.Read(36);
                merge            = opCode.Read(37);
            }

            ShaderIrOp mulOp = new ShaderIrOp(ShaderIrInst.Mul, operA, operB2);

            if (productShiftLeft)
            {
                mulOp = new ShaderIrOp(ShaderIrInst.Lsl, mulOp, imm16);
            }

            switch ((XmadMode)mode)
            {
            case XmadMode.Clo: operC = ExtendTo32(operC, signed: false, size: 16); break;

            case XmadMode.Chi: operC = new ShaderIrOp(ShaderIrInst.Lsr, operC, imm16); break;

            case XmadMode.Cbcc:
            {
                ShaderIrOp operBLsh16 = new ShaderIrOp(ShaderIrInst.Lsl, operB, imm16);

                operC = new ShaderIrOp(ShaderIrInst.Add, operC, operBLsh16);

                break;
            }

            case XmadMode.Csfu:
            {
                ShaderIrOperImm imm31 = new ShaderIrOperImm(31);

                ShaderIrOp signAdjustA = new ShaderIrOp(ShaderIrInst.Lsr, operA, imm31);
                ShaderIrOp signAdjustB = new ShaderIrOp(ShaderIrInst.Lsr, operB2, imm31);

                signAdjustA = new ShaderIrOp(ShaderIrInst.Lsl, signAdjustA, imm16);
                signAdjustB = new ShaderIrOp(ShaderIrInst.Lsl, signAdjustB, imm16);

                ShaderIrOp signAdjust = new ShaderIrOp(ShaderIrInst.Add, signAdjustA, signAdjustB);

                operC = new ShaderIrOp(ShaderIrInst.Sub, operC, signAdjust);

                break;
            }
            }

            ShaderIrOp addOp = new ShaderIrOp(ShaderIrInst.Add, mulOp, operC);

            if (merge)
            {
                ShaderIrOperImm imm16Mask = new ShaderIrOperImm(0xffff);

                addOp = new ShaderIrOp(ShaderIrInst.And, addOp, imm16Mask);
                operB = new ShaderIrOp(ShaderIrInst.Lsl, operB, imm16);
                addOp = new ShaderIrOp(ShaderIrInst.Or, addOp, operB);
            }

            block.AddNode(opCode.PredNode(new ShaderIrAsg(opCode.Gpr0(), addOp)));
        }
Пример #14
0
        private static void EmitSetp(ShaderIrBlock block, long opCode, bool isFloat, ShaderOper oper)
        {
            bool absA = opCode.Read(7);
            bool negP = opCode.Read(42);
            bool negA = opCode.Read(43);
            bool absB = opCode.Read(44);

            ShaderIrNode operA = opCode.Gpr8(), operB;

            switch (oper)
            {
            case ShaderOper.Cr:   operB = opCode.Cbuf34();    break;

            case ShaderOper.Imm:  operB = opCode.Imm19_20();  break;

            case ShaderOper.Immf: operB = opCode.Immf19_20(); break;

            case ShaderOper.Rr:   operB = opCode.Gpr20();     break;

            default: throw new ArgumentException(nameof(oper));
            }

            ShaderIrInst cmpInst;

            if (isFloat)
            {
                operA = GetAluFabsFneg(operA, absA, negA);
                operB = GetAluFabs(operB, absB);

                cmpInst = opCode.CmpF();
            }
            else
            {
                cmpInst = opCode.Cmp();
            }

            ShaderIrOp op = new ShaderIrOp(cmpInst, operA, operB);

            ShaderIrOperPred p0Node = opCode.Pred3();
            ShaderIrOperPred p1Node = opCode.Pred0();
            ShaderIrOperPred p2Node = opCode.Pred39();

            block.AddNode(opCode.PredNode(new ShaderIrAsg(p0Node, op)));

            ShaderIrInst lopInst = opCode.BLop45();

            if (lopInst == ShaderIrInst.Band && p1Node.IsConst && p2Node.IsConst)
            {
                return;
            }

            ShaderIrNode p2NNode = p2Node;

            if (negP)
            {
                p2NNode = new ShaderIrOp(ShaderIrInst.Bnot, p2NNode);
            }

            op = new ShaderIrOp(ShaderIrInst.Bnot, p0Node);

            op = new ShaderIrOp(lopInst, op, p2NNode);

            block.AddNode(opCode.PredNode(new ShaderIrAsg(p1Node, op)));

            op = new ShaderIrOp(lopInst, p0Node, p2NNode);

            block.AddNode(opCode.PredNode(new ShaderIrAsg(p0Node, op)));
        }
Пример #15
0
        private static void EmitF2I(ShaderIrBlock block, long opCode, ShaderOper oper)
        {
            IntType type = GetIntType(opCode);

            if (type == IntType.U64 ||
                type == IntType.S64)
            {
                //TODO: 64-bits support.
                //Note: GLSL doesn't support 64-bits integers.
                throw new NotImplementedException();
            }

            bool negA = opCode.Read(45);
            bool absA = opCode.Read(49);

            ShaderIrNode operA;

            switch (oper)
            {
            case ShaderOper.Cr:   operA = opCode.Cbuf34();    break;

            case ShaderOper.Immf: operA = opCode.Immf19_20(); break;

            case ShaderOper.Rr:   operA = opCode.Gpr20();     break;

            default: throw new ArgumentException(nameof(oper));
            }

            operA = GetAluFabsFneg(operA, absA, negA);

            ShaderIrInst roundInst = GetRoundInst(opCode);

            if (roundInst != ShaderIrInst.Invalid)
            {
                operA = new ShaderIrOp(roundInst, operA);
            }

            bool signed = type >= IntType.S8;

            int size = 8 << ((int)type & 3);

            if (size < 32)
            {
                uint mask = uint.MaxValue >> (32 - size);

                float cMin = 0;
                float cMax = mask;

                if (signed)
                {
                    uint halfMask = mask >> 1;

                    cMin -= halfMask + 1;
                    cMax  = halfMask;
                }

                ShaderIrOperImmf min = new ShaderIrOperImmf(cMin);
                ShaderIrOperImmf max = new ShaderIrOperImmf(cMax);

                operA = new ShaderIrOp(ShaderIrInst.Fclamp, operA, min, max);
            }

            ShaderIrInst inst = signed
                ? ShaderIrInst.Ftos
                : ShaderIrInst.Ftou;

            ShaderIrNode op = new ShaderIrOp(inst, operA);

            block.AddNode(opCode.PredNode(new ShaderIrAsg(opCode.Gpr0(), op)));
        }
Пример #16
0
        private static void EmitF2i(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
        {
            IntType Type = GetIntType(OpCode);

            if (Type == IntType.U64 ||
                Type == IntType.S64)
            {
                //TODO: 64-bits support.
                //Note: GLSL doesn't support 64-bits integers.
                throw new NotImplementedException();
            }

            bool NegA = ((OpCode >> 45) & 1) != 0;
            bool AbsA = ((OpCode >> 49) & 1) != 0;

            ShaderIrNode OperA;

            switch (Oper)
            {
            case ShaderOper.CR:   OperA = GetOperCbuf34(OpCode); break;

            case ShaderOper.Immf: OperA = GetOperImmf19_20(OpCode); break;

            case ShaderOper.RR:   OperA = GetOperGpr20(OpCode); break;

            default: throw new ArgumentException(nameof(Oper));
            }

            OperA = GetAluFabsFneg(OperA, AbsA, NegA);

            ShaderIrInst RoundInst = GetRoundInst(OpCode);

            if (RoundInst != ShaderIrInst.Invalid)
            {
                OperA = new ShaderIrOp(RoundInst, OperA);
            }

            bool Signed = Type >= IntType.S8;

            int Size = 8 << ((int)Type & 3);

            if (Size < 32)
            {
                uint Mask = uint.MaxValue >> (32 - Size);

                float CMin = 0;
                float CMax = Mask;

                if (Signed)
                {
                    uint HalfMask = Mask >> 1;

                    CMin -= HalfMask + 1;
                    CMax  = HalfMask;
                }

                ShaderIrOperImmf IMin = new ShaderIrOperImmf(CMin);
                ShaderIrOperImmf IMax = new ShaderIrOperImmf(CMax);

                OperA = new ShaderIrOp(ShaderIrInst.Fclamp, OperA, IMin, IMax);
            }

            ShaderIrInst Inst = Signed
                ? ShaderIrInst.Ftos
                : ShaderIrInst.Ftou;

            ShaderIrNode Op = new ShaderIrOp(Inst, OperA);

            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
        }
Пример #17
0
        private static void EmitTex(ShaderIrBlock block, long opCode, TextureInstructionSuffix textureInstructionSuffix, bool gprHandle)
        {
            bool isArray = opCode.HasArray();

            GalTextureTarget textureTarget = TexToTextureTarget(opCode.Read(28, 6), isArray);

            bool hasDepthCompare = opCode.Read(0x32);

            if (hasDepthCompare)
            {
                textureInstructionSuffix |= TextureInstructionSuffix.Dc;
            }

            ShaderIrOperGpr[] coords = new ShaderIrOperGpr[ImageUtils.GetCoordsCountTextureTarget(textureTarget)];

            int indexExtraCoord = 0;

            if (isArray)
            {
                indexExtraCoord++;

                coords[coords.Length - 1] = opCode.Gpr8();
            }


            for (int index = 0; index < coords.Length - indexExtraCoord; index++)
            {
                ShaderIrOperGpr coordReg = opCode.Gpr8();

                coordReg.Index += index;

                coordReg.Index += indexExtraCoord;

                if (!coordReg.IsValidRegister)
                {
                    coordReg.Index = ShaderIrOperGpr.ZrIndex;
                }

                coords[index] = coordReg;
            }

            int chMask = opCode.Read(31, 0xf);

            ShaderIrOperGpr levelOfDetail = null;
            ShaderIrOperGpr offset        = null;
            ShaderIrOperGpr depthCompare  = null;

            // TODO: determine first argument when TEX.B is used
            int operBIndex = gprHandle ? 1 : 0;

            if ((textureInstructionSuffix & TextureInstructionSuffix.Ll) != 0 ||
                (textureInstructionSuffix & TextureInstructionSuffix.Lb) != 0 ||
                (textureInstructionSuffix & TextureInstructionSuffix.Lba) != 0 ||
                (textureInstructionSuffix & TextureInstructionSuffix.Lla) != 0)
            {
                levelOfDetail        = opCode.Gpr20();
                levelOfDetail.Index += operBIndex;

                operBIndex++;
            }

            if ((textureInstructionSuffix & TextureInstructionSuffix.AOffI) != 0)
            {
                offset        = opCode.Gpr20();
                offset.Index += operBIndex;

                operBIndex++;
            }

            if ((textureInstructionSuffix & TextureInstructionSuffix.Dc) != 0)
            {
                depthCompare        = opCode.Gpr20();
                depthCompare.Index += operBIndex;

                operBIndex++;
            }

            // ???
            ShaderIrNode operC = gprHandle
                ? (ShaderIrNode)opCode.Gpr20()
                : (ShaderIrNode)opCode.Imm13_36();

            ShaderIrInst inst = gprHandle ? ShaderIrInst.Texb : ShaderIrInst.Texs;

            coords = CoordsRegistersToTempRegisters(block, coords);

            int regInc = 0;

            for (int ch = 0; ch < 4; ch++)
            {
                if (!IsChannelUsed(chMask, ch))
                {
                    continue;
                }

                ShaderIrOperGpr dst = opCode.Gpr0();

                dst.Index += regInc++;

                if (!dst.IsValidRegister || dst.IsConst)
                {
                    continue;
                }

                ShaderIrMetaTex meta = new ShaderIrMetaTex(ch, textureTarget, textureInstructionSuffix, coords)
                {
                    LevelOfDetail = levelOfDetail,
                    Offset        = offset,
                    DepthCompare  = depthCompare
                };

                ShaderIrOp op = new ShaderIrOp(inst, coords[0], coords.Length > 1 ? coords[1] : null, operC, meta);

                block.AddNode(opCode.PredNode(new ShaderIrAsg(dst, op)));
            }
        }
Пример #18
0
        private static void EmitXmad(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
        {
            //TODO: Confirm SignAB/C, it is just a guess.
            //TODO: Implement Mode 3 (CSFU), what it does?
            bool SignAB = OpCode.Read(48);
            bool SignC  = OpCode.Read(49);
            bool HighB  = OpCode.Read(52);
            bool HighA  = OpCode.Read(53);

            int Mode = OpCode.Read(50, 7);

            ShaderIrNode OperA = OpCode.Gpr8(), OperB, OperC;

            ShaderIrOperImm Imm16  = new ShaderIrOperImm(16);
            ShaderIrOperImm ImmMsk = new ShaderIrOperImm(0xffff);

            ShaderIrInst ShiftAB = SignAB ? ShaderIrInst.Asr : ShaderIrInst.Lsr;
            ShaderIrInst ShiftC  = SignC  ? ShaderIrInst.Asr : ShaderIrInst.Lsr;

            if (HighA)
            {
                OperA = new ShaderIrOp(ShiftAB, OperA, Imm16);
            }

            switch (Oper)
            {
            case ShaderOper.CR:  OperB = OpCode.Cbuf34();   break;

            case ShaderOper.Imm: OperB = OpCode.Imm19_20(); break;

            case ShaderOper.RC:  OperB = OpCode.Gpr39();    break;

            case ShaderOper.RR:  OperB = OpCode.Gpr20();    break;

            default: throw new ArgumentException(nameof(Oper));
            }

            bool ProductShiftLeft = false, Merge = false;

            if (Oper == ShaderOper.RC)
            {
                OperC = OpCode.Cbuf34();
            }
            else
            {
                OperC = OpCode.Gpr39();

                ProductShiftLeft = OpCode.Read(36);
                Merge            = OpCode.Read(37);
            }

            switch (Mode)
            {
            //CLO.
            case 1: OperC = ExtendTo32(OperC, SignC, 16); break;

            //CHI.
            case 2: OperC = new ShaderIrOp(ShiftC, OperC, Imm16); break;
            }

            ShaderIrNode OperBH = OperB;

            if (HighB)
            {
                OperBH = new ShaderIrOp(ShiftAB, OperBH, Imm16);
            }

            ShaderIrOp MulOp = new ShaderIrOp(ShaderIrInst.Mul, OperA, OperBH);

            if (ProductShiftLeft)
            {
                MulOp = new ShaderIrOp(ShaderIrInst.Lsl, MulOp, Imm16);
            }

            ShaderIrOp AddOp = new ShaderIrOp(ShaderIrInst.Add, MulOp, OperC);

            if (Merge)
            {
                AddOp = new ShaderIrOp(ShaderIrInst.And, AddOp, ImmMsk);
                OperB = new ShaderIrOp(ShaderIrInst.Lsl, OperB, Imm16);
                AddOp = new ShaderIrOp(ShaderIrInst.Or, AddOp, OperB);
            }

            if (Mode == 4)
            {
                OperB = new ShaderIrOp(ShaderIrInst.Lsl, OperB, Imm16);
                AddOp = new ShaderIrOp(ShaderIrInst.Or, AddOp, OperB);
            }

            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), AddOp)));
        }
Пример #19
0
        private static void EmitTexs(ShaderIrBlock block,
                                     long opCode,
                                     ShaderIrInst inst,
                                     GalTextureTarget textureTarget,
                                     TextureInstructionSuffix textureInstructionSuffix)
        {
            if (inst == ShaderIrInst.Txlf && textureTarget == GalTextureTarget.CubeArray)
            {
                throw new InvalidOperationException("TLDS instructions cannot use CUBE modifier!");
            }

            bool isArray = ImageUtils.IsArray(textureTarget);

            ShaderIrOperGpr[] coords = new ShaderIrOperGpr[ImageUtils.GetCoordsCountTextureTarget(textureTarget)];

            ShaderIrOperGpr operA = opCode.Gpr8();
            ShaderIrOperGpr operB = opCode.Gpr20();

            ShaderIrOperGpr suffixExtra = opCode.Gpr20();

            suffixExtra.Index += 1;

            int coordStartIndex = 0;

            if (isArray)
            {
                coordStartIndex++;
                coords[coords.Length - 1] = opCode.Gpr8();
            }

            switch (coords.Length - coordStartIndex)
            {
            case 1:
                coords[0] = opCode.Gpr8();

                break;

            case 2:
                coords[0]        = opCode.Gpr8();
                coords[0].Index += coordStartIndex;

                break;

            case 3:
                coords[0]        = opCode.Gpr8();
                coords[0].Index += coordStartIndex;

                coords[1]        = opCode.Gpr8();
                coords[1].Index += 1 + coordStartIndex;

                break;

            default:
                throw new NotSupportedException($"{coords.Length - coordStartIndex} coords textures aren't supported in TEXS");
            }

            int operBIndex = 0;

            ShaderIrOperGpr levelOfDetail = null;
            ShaderIrOperGpr offset        = null;
            ShaderIrOperGpr depthCompare  = null;

            // OperB is always the last value
            // Not applicable to 1d textures
            if (coords.Length - coordStartIndex != 1)
            {
                coords[coords.Length - coordStartIndex - 1] = operB;
                operBIndex++;
            }

            // Encoding of TEXS/TLDS is a bit special and change for 2d textures
            // NOTE: OperA seems to hold at best two args.
            // On 2D textures, if no suffix need an additional values, Y is stored in OperB, otherwise coords are in OperA and the additional values is in OperB.
            if (textureInstructionSuffix != TextureInstructionSuffix.None && textureInstructionSuffix != TextureInstructionSuffix.Lz && textureTarget == GalTextureTarget.TwoD)
            {
                coords[coords.Length - coordStartIndex - 1]        = opCode.Gpr8();
                coords[coords.Length - coordStartIndex - 1].Index += coords.Length - coordStartIndex - 1;
                operBIndex--;
            }

            // TODO: Find what MZ does and what changes about the encoding (Maybe Multisample?)
            if ((textureInstructionSuffix & TextureInstructionSuffix.Ll) != 0)
            {
                levelOfDetail        = opCode.Gpr20();
                levelOfDetail.Index += operBIndex;
                operBIndex++;
            }

            if ((textureInstructionSuffix & TextureInstructionSuffix.AOffI) != 0)
            {
                offset        = opCode.Gpr20();
                offset.Index += operBIndex;
                operBIndex++;
            }

            if ((textureInstructionSuffix & TextureInstructionSuffix.Dc) != 0)
            {
                depthCompare        = opCode.Gpr20();
                depthCompare.Index += operBIndex;
                operBIndex++;
            }

            int lutIndex;

            lutIndex  = !opCode.Gpr0().IsConst  ? 1 : 0;
            lutIndex |= !opCode.Gpr28().IsConst ? 2 : 0;

            if (lutIndex == 0)
            {
                //Both destination registers are RZ, do nothing.
                return;
            }

            bool fp16 = !opCode.Read(59);

            int dstIncrement = 0;

            ShaderIrOperGpr GetDst()
            {
                ShaderIrOperGpr dst;

                if (fp16)
                {
                    //FP16 mode, two components are packed on the two
                    //halfs of a 32-bits register, as two half-float values.
                    int halfPart = dstIncrement & 1;

                    switch (lutIndex)
                    {
                    case 1: dst = opCode.GprHalf0(halfPart);  break;

                    case 2: dst = opCode.GprHalf28(halfPart); break;

                    case 3: dst = (dstIncrement >> 1) != 0
                            ? opCode.GprHalf28(halfPart)
                            : opCode.GprHalf0(halfPart); break;

                    default: throw new InvalidOperationException();
                    }
                }
                else
                {
                    //32-bits mode, each component uses one register.
                    //Two components uses two consecutive registers.
                    switch (lutIndex)
                    {
                    case 1: dst = opCode.Gpr0();  break;

                    case 2: dst = opCode.Gpr28(); break;

                    case 3: dst = (dstIncrement >> 1) != 0
                            ? opCode.Gpr28()
                            : opCode.Gpr0(); break;

                    default: throw new InvalidOperationException();
                    }

                    dst.Index += dstIncrement & 1;
                }

                dstIncrement++;

                return(dst);
            }

            int chMask = _maskLut[lutIndex, opCode.Read(50, 7)];

            if (chMask == 0)
            {
                //All channels are disabled, do nothing.
                return;
            }

            ShaderIrNode operC = opCode.Imm13_36();

            coords = CoordsRegistersToTempRegisters(block, coords);

            for (int ch = 0; ch < 4; ch++)
            {
                if (!IsChannelUsed(chMask, ch))
                {
                    continue;
                }

                ShaderIrMetaTex meta = new ShaderIrMetaTex(ch, textureTarget, textureInstructionSuffix, coords)
                {
                    LevelOfDetail = levelOfDetail,
                    Offset        = offset,
                    DepthCompare  = depthCompare
                };
                ShaderIrOp op = new ShaderIrOp(inst, operA, operB, operC, meta);

                ShaderIrOperGpr dst = GetDst();

                if (dst.IsValidRegister && !dst.IsConst)
                {
                    block.AddNode(opCode.PredNode(new ShaderIrAsg(dst, op)));
                }
            }
        }
Пример #20
0
        private static void EmitSet(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper)
        {
            bool NegA = OpCode.Read(43);
            bool AbsB = OpCode.Read(44);
            bool NegB = OpCode.Read(53);
            bool AbsA = OpCode.Read(54);

            bool BoolFloat = OpCode.Read(IsFloat ? 52 : 44);

            ShaderIrNode OperA = OpCode.Gpr8(), OperB;

            switch (Oper)
            {
            case ShaderOper.CR:   OperB = OpCode.Cbuf34();    break;

            case ShaderOper.Imm:  OperB = OpCode.Imm19_20();  break;

            case ShaderOper.Immf: OperB = OpCode.Immf19_20(); break;

            case ShaderOper.RR:   OperB = OpCode.Gpr20();     break;

            default: throw new ArgumentException(nameof(Oper));
            }

            ShaderIrInst CmpInst;

            if (IsFloat)
            {
                OperA = GetAluFabsFneg(OperA, AbsA, NegA);
                OperB = GetAluFabsFneg(OperB, AbsB, NegB);

                CmpInst = OpCode.CmpF();
            }
            else
            {
                CmpInst = OpCode.Cmp();
            }

            ShaderIrOp Op = new ShaderIrOp(CmpInst, OperA, OperB);

            ShaderIrInst LopInst = OpCode.BLop45();

            ShaderIrOperPred PNode = OpCode.Pred39();

            ShaderIrNode Imm0, Imm1;

            if (BoolFloat)
            {
                Imm0 = new ShaderIrOperImmf(0);
                Imm1 = new ShaderIrOperImmf(1);
            }
            else
            {
                Imm0 = new ShaderIrOperImm(0);
                Imm1 = new ShaderIrOperImm(-1);
            }

            ShaderIrNode Asg0 = new ShaderIrAsg(OpCode.Gpr0(), Imm0);
            ShaderIrNode Asg1 = new ShaderIrAsg(OpCode.Gpr0(), Imm1);

            if (LopInst != ShaderIrInst.Band || !PNode.IsConst)
            {
                ShaderIrOp Op2 = new ShaderIrOp(LopInst, Op, PNode);

                Asg0 = new ShaderIrCond(Op2, Asg0, Not: true);
                Asg1 = new ShaderIrCond(Op2, Asg1, Not: false);
            }
            else
            {
                Asg0 = new ShaderIrCond(Op, Asg0, Not: true);
                Asg1 = new ShaderIrCond(Op, Asg1, Not: false);
            }

            Block.AddNode(OpCode.PredNode(Asg0));
            Block.AddNode(OpCode.PredNode(Asg1));
        }
Пример #21
0
        private static void EmitXmad(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
        {
            bool SignedA = OpCode.Read(48);
            bool SignedB = OpCode.Read(49);
            bool HighB   = OpCode.Read(52);
            bool HighA   = OpCode.Read(53);

            int Mode = OpCode.Read(50, 7);

            ShaderIrNode OperA = OpCode.Gpr8(), OperB, OperC;

            switch (Oper)
            {
            case ShaderOper.CR:  OperB = OpCode.Cbuf34();    break;

            case ShaderOper.Imm: OperB = OpCode.ImmU16_20(); break;

            case ShaderOper.RC:  OperB = OpCode.Gpr39();     break;

            case ShaderOper.RR:  OperB = OpCode.Gpr20();     break;

            default: throw new ArgumentException(nameof(Oper));
            }

            ShaderIrNode OperB2 = OperB;

            if (Oper == ShaderOper.Imm)
            {
                int Imm = ((ShaderIrOperImm)OperB2).Value;

                if (!HighB)
                {
                    Imm <<= 16;
                }

                if (SignedB)
                {
                    Imm >>= 16;
                }
                else
                {
                    Imm = (int)((uint)Imm >> 16);
                }

                OperB2 = new ShaderIrOperImm(Imm);
            }

            ShaderIrOperImm Imm16 = new ShaderIrOperImm(16);

            //If we are working with the lower 16-bits of the A/B operands,
            //we need to shift the lower 16-bits to the top 16-bits. Later,
            //they will be right shifted. For U16 types, this will be a logical
            //right shift, and for S16 types, a arithmetic right shift.
            if (!HighA)
            {
                OperA = new ShaderIrOp(ShaderIrInst.Lsl, OperA, Imm16);
            }

            if (!HighB && Oper != ShaderOper.Imm)
            {
                OperB2 = new ShaderIrOp(ShaderIrInst.Lsl, OperB2, Imm16);
            }

            ShaderIrInst ShiftA = SignedA ? ShaderIrInst.Asr : ShaderIrInst.Lsr;
            ShaderIrInst ShiftB = SignedB ? ShaderIrInst.Asr : ShaderIrInst.Lsr;

            OperA = new ShaderIrOp(ShiftA, OperA, Imm16);

            if (Oper != ShaderOper.Imm)
            {
                OperB2 = new ShaderIrOp(ShiftB, OperB2, Imm16);
            }

            bool ProductShiftLeft = false;
            bool Merge            = false;

            if (Oper == ShaderOper.RC)
            {
                OperC = OpCode.Cbuf34();
            }
            else
            {
                OperC = OpCode.Gpr39();

                ProductShiftLeft = OpCode.Read(36);
                Merge            = OpCode.Read(37);
            }

            ShaderIrOp MulOp = new ShaderIrOp(ShaderIrInst.Mul, OperA, OperB2);

            if (ProductShiftLeft)
            {
                MulOp = new ShaderIrOp(ShaderIrInst.Lsl, MulOp, Imm16);
            }

            switch ((XmadMode)Mode)
            {
            case XmadMode.Clo: OperC = ExtendTo32(OperC, Signed: false, Size: 16); break;

            case XmadMode.Chi: OperC = new ShaderIrOp(ShaderIrInst.Lsr, OperC, Imm16); break;

            case XmadMode.Cbcc:
            {
                ShaderIrOp OperBLsh16 = new ShaderIrOp(ShaderIrInst.Lsl, OperB, Imm16);

                OperC = new ShaderIrOp(ShaderIrInst.Add, OperC, OperBLsh16);

                break;
            }

            case XmadMode.Csfu:
            {
                ShaderIrOperImm Imm31 = new ShaderIrOperImm(31);

                ShaderIrOp SignAdjustA = new ShaderIrOp(ShaderIrInst.Lsr, OperA, Imm31);
                ShaderIrOp SignAdjustB = new ShaderIrOp(ShaderIrInst.Lsr, OperB2, Imm31);

                SignAdjustA = new ShaderIrOp(ShaderIrInst.Lsl, SignAdjustA, Imm16);
                SignAdjustB = new ShaderIrOp(ShaderIrInst.Lsl, SignAdjustB, Imm16);

                ShaderIrOp SignAdjust = new ShaderIrOp(ShaderIrInst.Add, SignAdjustA, SignAdjustB);

                OperC = new ShaderIrOp(ShaderIrInst.Sub, OperC, SignAdjust);

                break;
            }
            }

            ShaderIrOp AddOp = new ShaderIrOp(ShaderIrInst.Add, MulOp, OperC);

            if (Merge)
            {
                ShaderIrOperImm Imm16Mask = new ShaderIrOperImm(0xffff);

                AddOp = new ShaderIrOp(ShaderIrInst.And, AddOp, Imm16Mask);
                OperB = new ShaderIrOp(ShaderIrInst.Lsl, OperB, Imm16);
                AddOp = new ShaderIrOp(ShaderIrInst.Or, AddOp, OperB);
            }

            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), AddOp)));
        }