//===-- X86InstrShiftRotate.td - Shift and Rotate Instrs ---*- tablegen -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file describes the shift and rotate instructions. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// // Shift/Rotate instructions //===----------------------------------------------------------------------===// multiclass ShiftRotate uses = []> { let Uses = uses in { let isConvertibleToThreeAddress = !if(!eq(m, "shl"), 1, 0) in { let Predicates = [NoNDD] in { def 8ri : BinOpRI8U_R, Sched<[ri]>, DefEFLAGS; def 16ri : BinOpRI8U_R, Sched<[ri]>, DefEFLAGS, OpSize16; def 32ri : BinOpRI8U_R, Sched<[ri]>, DefEFLAGS, OpSize32; def 64ri : BinOpRI8U_R, Sched<[ri]>, DefEFLAGS; } let Predicates = [HasNDD, In64BitMode] in { def 8ri_ND : BinOpRI8U_R, Sched<[ri]>, DefEFLAGS; def 16ri_ND : BinOpRI8U_R, Sched<[ri]>, DefEFLAGS, PD; def 32ri_ND : BinOpRI8U_R, Sched<[ri]>, DefEFLAGS; def 64ri_ND : BinOpRI8U_R, Sched<[ri]>, DefEFLAGS; } let Predicates = [In64BitMode] in { def 8ri_EVEX : BinOpRI8U_R, Sched<[ri]>, DefEFLAGS, PL; def 16ri_EVEX : BinOpRI8U_R, Sched<[ri]>, DefEFLAGS, PL, PD; def 32ri_EVEX : BinOpRI8U_R, Sched<[ri]>, DefEFLAGS, PL; def 64ri_EVEX : BinOpRI8U_R, Sched<[ri]>, DefEFLAGS, PL; } } def 8mi : BinOpMI8U_M, Sched<[mi, WriteRMW]>, DefEFLAGS; def 16mi : BinOpMI8U_M, Sched<[mi, WriteRMW]>, DefEFLAGS, OpSize16; def 32mi : BinOpMI8U_M, Sched<[mi, WriteRMW]>, DefEFLAGS, OpSize32; def 64mi : BinOpMI8U_M, Sched<[mi, WriteRMW]>, DefEFLAGS, Requires<[In64BitMode]>; let Predicates = [HasNDD, In64BitMode] in { def 8mi_ND : BinOpMI8U_R, Sched<[mi, ri]>, DefEFLAGS; def 16mi_ND : BinOpMI8U_R, Sched<[mi, ri]>, DefEFLAGS, PD; def 32mi_ND : BinOpMI8U_R, Sched<[mi, ri]>, DefEFLAGS; def 64mi_ND : BinOpMI8U_R, Sched<[mi, ri]>, DefEFLAGS; } let Predicates = [In64BitMode] in { def 8mi_EVEX : BinOpMI8U_M, Sched<[mi, WriteRMW]>, DefEFLAGS, PL; def 16mi_EVEX : BinOpMI8U_M, Sched<[mi, WriteRMW]>, DefEFLAGS, PL, PD; def 32mi_EVEX : BinOpMI8U_M, Sched<[mi, WriteRMW]>, DefEFLAGS, PL; def 64mi_EVEX : BinOpMI8U_M, Sched<[mi, WriteRMW]>, DefEFLAGS, PL; } let SchedRW = [ri] in { def 8r1 : UnaryOpR_RF<0xD1, RegMRM, m, Xi8>; def 16r1 : UnaryOpR_RF<0xD1, RegMRM, m, Xi16>, OpSize16; def 32r1 : UnaryOpR_RF<0xD1, RegMRM, m, Xi32>, OpSize32; def 64r1 : UnaryOpR_RF<0xD1, RegMRM, m, Xi64>; // FIXME: Assembler can't tell whether it's 8r1_ND or 8rCL when the source register is cl, e.g. // // shlb %cl, %al // // GNU binutils distinguish them by adding an explicit $1 to asm string of 8r1_ND. But we haven't support // constant immediate in asm string for X86 in TD. So we add DisassembleOnly for 8r1_ND for the time being. let Predicates = [In64BitMode] in { def 8r1_ND : UnaryOpR_RF<0xD1, RegMRM, m, Xi8, null_frag, 1>, DisassembleOnly; def 16r1_ND : UnaryOpR_RF<0xD1, RegMRM, m, Xi16, null_frag, 1>, PD; def 32r1_ND : UnaryOpR_RF<0xD1, RegMRM, m, Xi32, null_frag, 1>; def 64r1_ND : UnaryOpR_RF<0xD1, RegMRM, m, Xi64, null_frag, 1>; def 8r1_EVEX : UnaryOpR_RF<0xD1, RegMRM, m, Xi8>, PL; def 16r1_EVEX : UnaryOpR_RF<0xD1, RegMRM, m, Xi16>, PL, PD; def 32r1_EVEX : UnaryOpR_RF<0xD1, RegMRM, m, Xi32>, PL; def 64r1_EVEX : UnaryOpR_RF<0xD1, RegMRM, m, Xi64>, PL; } } let SchedRW = [mi, WriteRMW] in { def 8m1 : UnaryOpM_MF<0xD1, MemMRM, m, Xi8>; def 16m1 : UnaryOpM_MF<0xD1, MemMRM, m, Xi16>, OpSize16; def 32m1 : UnaryOpM_MF<0xD1, MemMRM, m, Xi32>, OpSize32; def 64m1 : UnaryOpM_MF<0xD1, MemMRM, m, Xi64>, Requires<[In64BitMode]>; let Predicates = [In64BitMode] in { def 8m1_EVEX : UnaryOpM_MF<0xD1, MemMRM, m, Xi8>, PL; def 16m1_EVEX : UnaryOpM_MF<0xD1, MemMRM, m, Xi16>, PL, PD; def 32m1_EVEX : UnaryOpM_MF<0xD1, MemMRM, m, Xi32>, PL; def 64m1_EVEX : UnaryOpM_MF<0xD1, MemMRM, m, Xi64>, PL; } } let SchedRW = [mi, ri], Predicates = [In64BitMode] in { def 8m1_ND : UnaryOpM_RF<0xD1, MemMRM, m, Xi8>; def 16m1_ND : UnaryOpM_RF<0xD1, MemMRM, m, Xi16>, PD; def 32m1_ND : UnaryOpM_RF<0xD1, MemMRM, m, Xi32>; def 64m1_ND : UnaryOpM_RF<0xD1, MemMRM, m, Xi64>; } } let Uses = !listconcat([CL], uses), Defs = [EFLAGS] in { let Predicates = [NoNDD] in { def 8rCL : BinOpRC_R, Sched<[rCL]>; def 16rCL : BinOpRC_R, Sched<[rCL]>, OpSize16; def 32rCL : BinOpRC_R, Sched<[rCL]>, OpSize32; def 64rCL : BinOpRC_R, Sched<[rCL]>; } let Predicates = [HasNDD, In64BitMode] in { def 8rCL_ND : BinOpRC_R, Sched<[rCL]>; def 16rCL_ND : BinOpRC_R, Sched<[rCL]>, PD; def 32rCL_ND : BinOpRC_R, Sched<[rCL]>; def 64rCL_ND : BinOpRC_R, Sched<[rCL]>; } let Predicates = [In64BitMode] in { def 8rCL_EVEX : BinOpRC_R, Sched<[rCL]>, PL; def 16rCL_EVEX : BinOpRC_R, Sched<[rCL]>, PL, PD; def 32rCL_EVEX : BinOpRC_R, Sched<[rCL]>, PL; def 64rCL_EVEX : BinOpRC_R, Sched<[rCL]>, PL; } def 8mCL : BinOpMC_M, Sched<[mCL, WriteRMW]>; def 16mCL : BinOpMC_M, Sched<[mCL, WriteRMW]>, OpSize16; def 32mCL : BinOpMC_M, Sched<[mCL, WriteRMW]>, OpSize32; def 64mCL : BinOpMC_M, Sched<[mCL, WriteRMW]>, Requires<[In64BitMode]>; let Predicates = [HasNDD, In64BitMode] in { def 8mCL_ND : BinOpMC_R, Sched<[mCL, rCL]>; def 16mCL_ND : BinOpMC_R, Sched<[mCL, rCL]>, PD; def 32mCL_ND : BinOpMC_R, Sched<[mCL, rCL]>; def 64mCL_ND : BinOpMC_R, Sched<[mCL, rCL]>; } let Predicates = [In64BitMode] in { def 8mCL_EVEX : BinOpMC_M, Sched<[mCL, WriteRMW]>, PL; def 16mCL_EVEX : BinOpMC_M, Sched<[mCL, WriteRMW]>, PL, PD; def 32mCL_EVEX : BinOpMC_M, Sched<[mCL, WriteRMW]>, PL; def 64mCL_EVEX : BinOpMC_M, Sched<[mCL, WriteRMW]>, PL; } } } multiclass ShiftRotate_NF { let Predicates = [In64BitMode] in { let isConvertibleToThreeAddress = !if(!eq(m, "shl"), 1, 0) in { def 8ri_NF : BinOpRI8U_R, Sched<[ri]>, NF; def 16ri_NF : BinOpRI8U_R, Sched<[ri]>, NF, PD; def 32ri_NF : BinOpRI8U_R, Sched<[ri]>, NF; def 64ri_NF : BinOpRI8U_R, Sched<[ri]>, NF; def 8ri_NF_ND : BinOpRI8U_R, Sched<[ri]>, EVEX_NF; def 16ri_NF_ND : BinOpRI8U_R, Sched<[ri]>, EVEX_NF, PD; def 32ri_NF_ND : BinOpRI8U_R, Sched<[ri]>, EVEX_NF; def 64ri_NF_ND : BinOpRI8U_R, Sched<[ri]>, EVEX_NF; } def 8mi_NF : BinOpMI8U_M, Sched<[mi, WriteRMW]>, NF; def 16mi_NF : BinOpMI8U_M, Sched<[mi, WriteRMW]>, NF, PD; def 32mi_NF : BinOpMI8U_M, Sched<[mi, WriteRMW]>, NF; def 64mi_NF : BinOpMI8U_M, Sched<[mi, WriteRMW]>, NF; def 8mi_NF_ND : BinOpMI8U_R, Sched<[mi, ri]>, EVEX_NF; def 16mi_NF_ND : BinOpMI8U_R, Sched<[mi, ri]>, EVEX_NF, PD; def 32mi_NF_ND : BinOpMI8U_R, Sched<[mi, ri]>, EVEX_NF; def 64mi_NF_ND : BinOpMI8U_R, Sched<[mi, ri]>, EVEX_NF; let SchedRW = [ri] in { // FIXME: Assembler can't tell whether it's 8r1_NF_ND or 8rCL_NF when the source register is cl, e.g. // // {nf} shlb %cl, %al // // GNU binutils distinguish them by adding an explicit $1 to asm string of 8r1_NF_ND. But we haven't support // constant immediate in asm string for X86 in TD. So we add DisassembleOnly for 8r1_NF_ND for the time being. def 8r1_NF : UnaryOpR_R<0xD1, RegMRM, m, Xi8>, NF; def 16r1_NF : UnaryOpR_R<0xD1, RegMRM, m, Xi16>, NF, PD; def 32r1_NF : UnaryOpR_R<0xD1, RegMRM, m, Xi32>, NF; def 64r1_NF : UnaryOpR_R<0xD1, RegMRM, m, Xi64>, NF; def 8r1_NF_ND : UnaryOpR_R<0xD1, RegMRM, m, Xi8, null_frag, 1>, EVEX_NF, DisassembleOnly; def 16r1_NF_ND : UnaryOpR_R<0xD1, RegMRM, m, Xi16, null_frag, 1>, EVEX_NF, PD; def 32r1_NF_ND : UnaryOpR_R<0xD1, RegMRM, m, Xi32, null_frag, 1>, EVEX_NF; def 64r1_NF_ND : UnaryOpR_R<0xD1, RegMRM, m, Xi64, null_frag, 1>, EVEX_NF; } let SchedRW = [mi, WriteRMW] in { def 8m1_NF : UnaryOpM_M<0xD1, MemMRM, m, Xi8>, NF; def 16m1_NF : UnaryOpM_M<0xD1, MemMRM, m, Xi16>, NF, PD; def 32m1_NF : UnaryOpM_M<0xD1, MemMRM, m, Xi32>, NF; def 64m1_NF : UnaryOpM_M<0xD1, MemMRM, m, Xi64>, NF; } let SchedRW = [mi, ri] in { def 8m1_NF_ND : UnaryOpM_R<0xD1, MemMRM, m, Xi8>, EVEX_NF; def 16m1_NF_ND : UnaryOpM_R<0xD1, MemMRM, m, Xi16>, EVEX_NF, PD; def 32m1_NF_ND : UnaryOpM_R<0xD1, MemMRM, m, Xi32>, EVEX_NF; def 64m1_NF_ND : UnaryOpM_R<0xD1, MemMRM, m, Xi64>, EVEX_NF; } let Uses = [CL] in { def 8rCL_NF : BinOpRC_R, Sched<[rCL]>, NF; def 16rCL_NF : BinOpRC_R, Sched<[rCL]>, NF, PD; def 32rCL_NF : BinOpRC_R, Sched<[rCL]>, NF; def 64rCL_NF : BinOpRC_R, Sched<[rCL]>, NF; def 8rCL_NF_ND : BinOpRC_R, Sched<[rCL]>, EVEX_NF; def 16rCL_NF_ND : BinOpRC_R, Sched<[rCL]>, EVEX_NF, PD; def 32rCL_NF_ND : BinOpRC_R, Sched<[rCL]>, EVEX_NF; def 64rCL_NF_ND : BinOpRC_R, Sched<[rCL]>, EVEX_NF; def 8mCL_NF : BinOpMC_M, Sched<[mCL, WriteRMW]>, NF; def 16mCL_NF : BinOpMC_M, Sched<[mCL, WriteRMW]>, NF, PD; def 32mCL_NF : BinOpMC_M, Sched<[mCL, WriteRMW]>, NF; def 64mCL_NF : BinOpMC_M, Sched<[mCL, WriteRMW]>, NF; def 8mCL_NF_ND : BinOpMC_R, Sched<[mCL, rCL]>, EVEX_NF; def 16mCL_NF_ND : BinOpMC_R, Sched<[mCL, rCL]>, EVEX_NF, PD; def 32mCL_NF_ND : BinOpMC_R, Sched<[mCL, rCL]>, EVEX_NF; def 64mCL_NF_ND : BinOpMC_R, Sched<[mCL, rCL]>, EVEX_NF; } } } defm SHL: ShiftRotate<"shl", MRM4r, MRM4m, shl, WriteShiftCL, WriteShift, WriteShiftCLLd, WriteShiftLd>; defm SHR: ShiftRotate<"shr", MRM5r, MRM5m, srl, WriteShiftCL, WriteShift, WriteShiftCLLd, WriteShiftLd>; defm SAR: ShiftRotate<"sar", MRM7r, MRM7m, sra, WriteShiftCL, WriteShift, WriteShiftCLLd, WriteShiftLd>; defm ROL: ShiftRotate<"rol", MRM0r, MRM0m, rotl, WriteRotateCL, WriteRotate, WriteRotateCLLd, WriteRotateLd>; defm ROR: ShiftRotate<"ror", MRM1r, MRM1m, rotr, WriteRotateCL, WriteRotate, WriteRotateCLLd, WriteRotateLd>; defm RCL: ShiftRotate<"rcl", MRM2r, MRM2m, null_frag, WriteRotateCL, WriteRotate, WriteRotateCLLd, WriteRotateLd, [EFLAGS]>; defm RCR: ShiftRotate<"rcr", MRM3r, MRM3m, null_frag, WriteRotateCL, WriteRotate, WriteRotateCLLd, WriteRotateLd, [EFLAGS]>; defm SHL: ShiftRotate_NF<"shl", MRM4r, MRM4m, WriteShiftCL, WriteShift, WriteShiftCLLd, WriteShiftLd>; defm SHR: ShiftRotate_NF<"shr", MRM5r, MRM5m, WriteShiftCL, WriteShift, WriteShiftCLLd, WriteShiftLd>; defm SAR: ShiftRotate_NF<"sar", MRM7r, MRM7m, WriteShiftCL, WriteShift, WriteShiftCLLd, WriteShiftLd>; defm ROL: ShiftRotate_NF<"rol", MRM0r, MRM0m, WriteRotateCL, WriteRotate, WriteRotateCLLd, WriteRotateLd>; defm ROR: ShiftRotate_NF<"ror", MRM1r, MRM1m, WriteRotateCL, WriteRotate, WriteRotateCLLd, WriteRotateLd>; // Use the opposite rotate if allows us to use the rotate by 1 instruction. let Predicates = [NoNDD] in { def : Pat<(rotl GR8:$src1, (i8 7)), (ROR8r1 GR8:$src1)>; def : Pat<(rotl GR16:$src1, (i8 15)), (ROR16r1 GR16:$src1)>; def : Pat<(rotl GR32:$src1, (i8 31)), (ROR32r1 GR32:$src1)>; def : Pat<(rotl GR64:$src1, (i8 63)), (ROR64r1 GR64:$src1)>; def : Pat<(rotr GR8:$src1, (i8 7)), (ROL8r1 GR8:$src1)>; def : Pat<(rotr GR16:$src1, (i8 15)), (ROL16r1 GR16:$src1)>; def : Pat<(rotr GR32:$src1, (i8 31)), (ROL32r1 GR32:$src1)>; def : Pat<(rotr GR64:$src1, (i8 63)), (ROL64r1 GR64:$src1)>; } let Predicates = [HasNDD] in { def : Pat<(rotl GR8:$src1, (i8 7)), (ROR8r1_ND GR8:$src1)>; def : Pat<(rotl GR16:$src1, (i8 15)), (ROR16r1_ND GR16:$src1)>; def : Pat<(rotl GR32:$src1, (i8 31)), (ROR32r1_ND GR32:$src1)>; def : Pat<(rotl GR64:$src1, (i8 63)), (ROR64r1_ND GR64:$src1)>; def : Pat<(rotr GR8:$src1, (i8 7)), (ROL8r1_ND GR8:$src1)>; def : Pat<(rotr GR16:$src1, (i8 15)), (ROL16r1_ND GR16:$src1)>; def : Pat<(rotr GR32:$src1, (i8 31)), (ROL32r1_ND GR32:$src1)>; def : Pat<(rotr GR64:$src1, (i8 63)), (ROL64r1_ND GR64:$src1)>; } def : Pat<(store (rotl (loadi8 addr:$dst), (i8 7)), addr:$dst), (ROR8m1 addr:$dst)>; def : Pat<(store (rotl (loadi16 addr:$dst), (i8 15)), addr:$dst), (ROR16m1 addr:$dst)>; def : Pat<(store (rotl (loadi32 addr:$dst), (i8 31)), addr:$dst), (ROR32m1 addr:$dst)>; def : Pat<(store (rotl (loadi64 addr:$dst), (i8 63)), addr:$dst), (ROR64m1 addr:$dst)>, Requires<[In64BitMode]>; def : Pat<(store (rotr (loadi8 addr:$dst), (i8 7)), addr:$dst), (ROL8m1 addr:$dst)>; def : Pat<(store (rotr (loadi16 addr:$dst), (i8 15)), addr:$dst), (ROL16m1 addr:$dst)>; def : Pat<(store (rotr (loadi32 addr:$dst), (i8 31)), addr:$dst), (ROL32m1 addr:$dst)>; def : Pat<(store (rotr (loadi64 addr:$dst), (i8 63)), addr:$dst), (ROL64m1 addr:$dst)>, Requires<[In64BitMode]>; let Predicates = [HasNDD] in { def : Pat<(rotl (loadi8 addr:$src), (i8 7)), (ROR8m1_ND addr:$src)>; def : Pat<(rotl (loadi16 addr:$src), (i8 15)), (ROR16m1_ND addr:$src)>; def : Pat<(rotl (loadi32 addr:$src), (i8 31)), (ROR32m1_ND addr:$src)>; def : Pat<(rotl (loadi64 addr:$src), (i8 63)), (ROR64m1_ND addr:$src)>; def : Pat<(rotr (loadi8 addr:$src), (i8 7)), (ROL8m1_ND addr:$src)>; def : Pat<(rotr (loadi16 addr:$src), (i8 15)), (ROL16m1_ND addr:$src)>; def : Pat<(rotr (loadi32 addr:$src), (i8 31)), (ROL32m1_ND addr:$src)>; def : Pat<(rotr (loadi64 addr:$src), (i8 63)), (ROL64m1_ND addr:$src)>; } // Patterns for rotate with relocImm for the immediate field. let Predicates = [NoNDD] in { def : Pat<(rotl GR8:$src1, (i8 relocImm:$src2)), (ROL8ri GR8:$src1, relocImm:$src2)>; def : Pat<(rotl GR16:$src1, (i8 relocImm:$src2)), (ROL16ri GR16:$src1, relocImm:$src2)>; def : Pat<(rotl GR32:$src1, (i8 relocImm:$src2)), (ROL32ri GR32:$src1, relocImm:$src2)>; def : Pat<(rotl GR64:$src1, (i8 relocImm:$src2)), (ROL64ri GR64:$src1, relocImm:$src2)>; def : Pat<(rotr GR8:$src1, (i8 relocImm:$src2)), (ROR8ri GR8:$src1, relocImm:$src2)>; def : Pat<(rotr GR16:$src1, (i8 relocImm:$src2)), (ROR16ri GR16:$src1, relocImm:$src2)>; def : Pat<(rotr GR32:$src1, (i8 relocImm:$src2)), (ROR32ri GR32:$src1, relocImm:$src2)>; def : Pat<(rotr GR64:$src1, (i8 relocImm:$src2)), (ROR64ri GR64:$src1, relocImm:$src2)>; } let Predicates = [HasNDD] in { def : Pat<(rotl GR8:$src1, (i8 relocImm:$src2)), (ROL8ri_ND GR8:$src1, relocImm:$src2)>; def : Pat<(rotl GR16:$src1, (i8 relocImm:$src2)), (ROL16ri_ND GR16:$src1, relocImm:$src2)>; def : Pat<(rotl GR32:$src1, (i8 relocImm:$src2)), (ROL32ri_ND GR32:$src1, relocImm:$src2)>; def : Pat<(rotl GR64:$src1, (i8 relocImm:$src2)), (ROL64ri_ND GR64:$src1, relocImm:$src2)>; def : Pat<(rotr GR8:$src1, (i8 relocImm:$src2)), (ROR8ri_ND GR8:$src1, relocImm:$src2)>; def : Pat<(rotr GR16:$src1, (i8 relocImm:$src2)), (ROR16ri_ND GR16:$src1, relocImm:$src2)>; def : Pat<(rotr GR32:$src1, (i8 relocImm:$src2)), (ROR32ri_ND GR32:$src1, relocImm:$src2)>; def : Pat<(rotr GR64:$src1, (i8 relocImm:$src2)), (ROR64ri_ND GR64:$src1, relocImm:$src2)>; } //===----------------------------------------------------------------------===// // Double precision shift instructions (generalizations of rotate) //===----------------------------------------------------------------------===// class ShlrdOpRRI8U_R o, string m, X86TypeInfo t, SDPatternOperator node = null_frag, bit ndd = 0> : ITy, NDD { let isCommutable = 1; let ImmT = Imm8; let SchedRW = [WriteSHDrri]; let Pattern = !if(!eq(m, "shld"), [(set t.RegClass:$dst, (node t.RegClass:$src1, t.RegClass:$src2, (i8 imm:$src3)))], [(set t.RegClass:$dst, (node t.RegClass:$src2, t.RegClass:$src1, (i8 imm:$src3)))]); } class ShlrdOpRRC_R o, string m, X86TypeInfo t, SDPatternOperator node = null_frag, bit ndd = 0> : BinOpRR, NDD { let Uses = [CL]; let SchedRW = [WriteSHDrrcl]; let Pattern = !if(!eq(m, "shld"), [(set t.RegClass:$dst, (node t.RegClass:$src1, t.RegClass:$src2, CL))], [(set t.RegClass:$dst, (node t.RegClass:$src2, t.RegClass:$src1, CL))]); } class ShlrdOpMRI8U_M o, string m, X86TypeInfo t, SDPatternOperator node = null_frag> : ITy, TB { let ImmT = Imm8; let SchedRW = [WriteSHDmri]; let mayLoad = 1; let mayStore = 1; let Pattern = !if(!eq(m, "shld"), [(store (node (t.LoadNode addr:$src1), t.RegClass:$src2, (i8 imm:$src3)), addr:$src1)], [(store (node t.RegClass:$src2, (t.LoadNode addr:$src1), (i8 imm:$src3)), addr:$src1)]); } class ShlrdOpMRC_M o, string m, X86TypeInfo t, SDPatternOperator node = null_frag> : BinOpMR, TB { let Uses = [CL]; let SchedRW = [WriteSHDmrcl]; let mayStore = 1; let Pattern = !if(!eq(m, "shld"), [(store (node (t.LoadNode addr:$src1), t.RegClass:$src2, CL), addr:$src1)], [(store (node t.RegClass:$src2, (t.LoadNode addr:$src1), CL), addr:$src1)]); } class ShlrdOpMRI8U_R o, string m, X86TypeInfo t, SDPatternOperator node = null_frag> : ITy, NDD<1> { let ImmT = Imm8; let SchedRW = [WriteSHDmri]; let mayLoad = 1; let Pattern = !if(!eq(m, "shld"), [(set t.RegClass:$dst, (node (t.LoadNode addr:$src1), t.RegClass:$src2, (i8 imm:$src3)))], [(set t.RegClass:$dst, (node t.RegClass:$src2, (t.LoadNode addr:$src1), (i8 imm:$src3)))]); } class ShlrdOpMRC_R o, string m, X86TypeInfo t, SDPatternOperator node = null_frag> : BinOpMR, NDD<1> { let Uses = [CL]; let SchedRW = [WriteSHDmrcl]; let Pattern = !if(!eq(m, "shld"), [(set t.RegClass:$dst, (node (t.LoadNode addr:$src1), t.RegClass:$src2, CL))], [(set t.RegClass:$dst, (node t.RegClass:$src2, (t.LoadNode addr:$src1), CL))]); } multiclass Shlrd o1, bits<8> o2, bits<8> o3, string m, SDPatternOperator node, SDPatternOperator t_node> { let Predicates = [NoNDD] in { def 16rri8 : ShlrdOpRRI8U_R, TB, DefEFLAGS, OpSize16; def 32rri8 : ShlrdOpRRI8U_R, TB, DefEFLAGS, OpSize32; def 64rri8 : ShlrdOpRRI8U_R, TB, DefEFLAGS; def 16rrCL : ShlrdOpRRC_R, TB, DefEFLAGS, OpSize16; def 32rrCL : ShlrdOpRRC_R, TB, DefEFLAGS, OpSize32; def 64rrCL : ShlrdOpRRC_R, TB, DefEFLAGS; } let Predicates = [HasNDD, In64BitMode] in { def 16rri8_ND : ShlrdOpRRI8U_R, DefEFLAGS, PD; def 32rri8_ND : ShlrdOpRRI8U_R, DefEFLAGS; def 64rri8_ND : ShlrdOpRRI8U_R, DefEFLAGS; def 16rrCL_ND : ShlrdOpRRC_R, DefEFLAGS, PD; def 32rrCL_ND : ShlrdOpRRC_R, DefEFLAGS; def 64rrCL_ND : ShlrdOpRRC_R, DefEFLAGS; } let Predicates = [In64BitMode] in { def 16rri8_NF : ShlrdOpRRI8U_R, NF, PD; def 32rri8_NF : ShlrdOpRRI8U_R, NF; def 64rri8_NF : ShlrdOpRRI8U_R, NF; def 16rrCL_NF : ShlrdOpRRC_R, NF, PD; def 32rrCL_NF : ShlrdOpRRC_R, NF; def 64rrCL_NF : ShlrdOpRRC_R, NF; def 16rri8_NF_ND : ShlrdOpRRI8U_R, EVEX_NF, PD; def 32rri8_NF_ND : ShlrdOpRRI8U_R, EVEX_NF; def 64rri8_NF_ND : ShlrdOpRRI8U_R, EVEX_NF; def 16rrCL_NF_ND : ShlrdOpRRC_R, EVEX_NF, PD; def 32rrCL_NF_ND : ShlrdOpRRC_R, EVEX_NF; def 64rrCL_NF_ND : ShlrdOpRRC_R, EVEX_NF; def 16rri8_EVEX : ShlrdOpRRI8U_R, DefEFLAGS, PL, PD; def 32rri8_EVEX : ShlrdOpRRI8U_R, DefEFLAGS, PL; def 64rri8_EVEX : ShlrdOpRRI8U_R, DefEFLAGS, PL; def 16rrCL_EVEX : ShlrdOpRRC_R, DefEFLAGS, PL, PD; def 32rrCL_EVEX : ShlrdOpRRC_R, DefEFLAGS, PL; def 64rrCL_EVEX : ShlrdOpRRC_R, DefEFLAGS, PL; } def 16mri8 : ShlrdOpMRI8U_M, DefEFLAGS, OpSize16; def 32mri8 : ShlrdOpMRI8U_M, DefEFLAGS, OpSize32; def 64mri8 : ShlrdOpMRI8U_M, DefEFLAGS; def 16mrCL : ShlrdOpMRC_M, DefEFLAGS, OpSize16; def 32mrCL : ShlrdOpMRC_M, DefEFLAGS, OpSize32; def 64mrCL : ShlrdOpMRC_M, DefEFLAGS; let Predicates = [HasNDD, In64BitMode] in { def 16mri8_ND : ShlrdOpMRI8U_R, DefEFLAGS, PD; def 32mri8_ND : ShlrdOpMRI8U_R, DefEFLAGS; def 64mri8_ND : ShlrdOpMRI8U_R, DefEFLAGS; def 16mrCL_ND : ShlrdOpMRC_R, DefEFLAGS, PD; def 32mrCL_ND : ShlrdOpMRC_R, DefEFLAGS; def 64mrCL_ND : ShlrdOpMRC_R, DefEFLAGS; } let Predicates = [In64BitMode] in { def 16mri8_NF : ShlrdOpMRI8U_M, NF, PD; def 32mri8_NF : ShlrdOpMRI8U_M, NF; def 64mri8_NF : ShlrdOpMRI8U_M, NF; def 16mrCL_NF : ShlrdOpMRC_M, NF, PD; def 32mrCL_NF : ShlrdOpMRC_M, NF; def 64mrCL_NF : ShlrdOpMRC_M, NF; def 16mri8_NF_ND : ShlrdOpMRI8U_R, EVEX_NF, PD; def 32mri8_NF_ND : ShlrdOpMRI8U_R, EVEX_NF; def 64mri8_NF_ND : ShlrdOpMRI8U_R, EVEX_NF; def 16mrCL_NF_ND : ShlrdOpMRC_R, EVEX_NF, PD; def 32mrCL_NF_ND : ShlrdOpMRC_R, EVEX_NF; def 64mrCL_NF_ND : ShlrdOpMRC_R, EVEX_NF; def 16mri8_EVEX : ShlrdOpMRI8U_M, DefEFLAGS, PL, PD; def 32mri8_EVEX : ShlrdOpMRI8U_M, DefEFLAGS, PL; def 64mri8_EVEX : ShlrdOpMRI8U_M, DefEFLAGS, PL; def 16mrCL_EVEX : ShlrdOpMRC_M, DefEFLAGS, PL, PD; def 32mrCL_EVEX : ShlrdOpMRC_M, DefEFLAGS, PL; def 64mrCL_EVEX : ShlrdOpMRC_M, DefEFLAGS, PL; } } defm SHLD : Shlrd<0xA4, 0xA5, 0x24, "shld", fshl, X86fshl>; defm SHRD : Shlrd<0xAC, 0xAD, 0x2C, "shrd", fshr, X86fshr>; // Sandy Bridge and newer Intel processors support faster rotates using // SHLD to avoid a partial flag update on the normal rotate instructions. // Use a pseudo so that TwoInstructionPass and register allocation will see // this as unary instruction. let Predicates = [HasFastSHLDRotate], AddedComplexity = 5, Defs = [EFLAGS], isPseudo = 1, SchedRW = [WriteSHDrri], Constraints = "$src1 = $dst" in { def SHLDROT32ri : I<0, Pseudo, (outs GR32:$dst), (ins GR32:$src1, u8imm:$shamt), "", [(set GR32:$dst, (rotl GR32:$src1, (i8 imm:$shamt)))]>; def SHLDROT64ri : I<0, Pseudo, (outs GR64:$dst), (ins GR64:$src1, u8imm:$shamt), "", [(set GR64:$dst, (rotl GR64:$src1, (i8 imm:$shamt)))]>; def SHRDROT32ri : I<0, Pseudo, (outs GR32:$dst), (ins GR32:$src1, u8imm:$shamt), "", [(set GR32:$dst, (rotr GR32:$src1, (i8 imm:$shamt)))]>; def SHRDROT64ri : I<0, Pseudo, (outs GR64:$dst), (ins GR64:$src1, u8imm:$shamt), "", [(set GR64:$dst, (rotr GR64:$src1, (i8 imm:$shamt)))]>; } //===----------------------------------------------------------------------===// // BMI Shift/Rotate instructions //===----------------------------------------------------------------------===// def ROT32L2R_imm8 : SDNodeXFormgetZExtValue(), SDLoc(N)); }]>; def ROT64L2R_imm8 : SDNodeXFormgetZExtValue(), SDLoc(N)); }]>; // NOTE: We use WriteShift for these rotates as they avoid the stalls // of many of the older x86 rotate instructions. class RorXri : ITy<0xF0, MRMSrcReg, t, (outs t.RegClass:$dst), (ins t.RegClass:$src1, u8imm:$src2), "rorx", binop_ndd_args, []>, TA, XD, Sched<[WriteShift]> { let ImmT = Imm8; } class RorXmi : ITy<0xF0, MRMSrcMem, t, (outs t.RegClass:$dst), (ins t.MemOperand:$src1, u8imm:$src2), "rorx", binop_ndd_args, []>, TA, XD, Sched<[WriteShiftLd]> { let ImmT = Imm8; let mayLoad = 1; } multiclass RorX { let Predicates = [HasBMI2, NoEGPR] in { def ri : RorXri, VEX; def mi : RorXmi, VEX; } let Predicates = [HasBMI2, HasEGPR, In64BitMode] in { def ri_EVEX : RorXri, EVEX; def mi_EVEX : RorXmi, EVEX; } } defm RORX32: RorX; defm RORX64: RorX; class ShiftXrr : ITy<0xF7, MRMSrcReg4VOp3, t, (outs t.RegClass:$dst), (ins t.RegClass:$src1, t.RegClass:$src2), m, binop_ndd_args, []>, T8, Sched<[WriteShift]>; class ShiftXrm : ITy<0xF7, MRMSrcMem4VOp3, t, (outs t.RegClass:$dst), (ins t.MemOperand:$src1, t.RegClass:$src2), m, binop_ndd_args, []>, T8, Sched<[WriteShift.Folded, // x86memop:$src1 ReadDefault, ReadDefault, ReadDefault, ReadDefault, ReadDefault, // RC:$src2 WriteShift.ReadAfterFold]> { let mayLoad = 1; } multiclass ShiftX { let Predicates = [HasBMI2, NoEGPR] in { def rr : ShiftXrr, VEX; def rm : ShiftXrm, VEX; } let Predicates = [HasBMI2, HasEGPR, In64BitMode] in { def rr_EVEX : ShiftXrr, EVEX; def rm_EVEX : ShiftXrm, EVEX; } } defm SARX32: ShiftX<"sarx", Xi32>, XS; defm SARX64: ShiftX<"sarx", Xi64>, XS; defm SHRX32: ShiftX<"shrx", Xi32>, XD; defm SHRX64: ShiftX<"shrx", Xi64>, XD; defm SHLX32: ShiftX<"shlx", Xi32>, PD; defm SHLX64: ShiftX<"shlx", Xi64>, PD; multiclass RORX_Pats { // Prefer RORX which is non-destructive and doesn't update EFLAGS. let AddedComplexity = 10 in { def : Pat<(rotr GR32:$src, (i8 imm:$shamt)), (!cast(RORX32ri#suffix) GR32:$src, imm:$shamt)>; def : Pat<(rotr GR64:$src, (i8 imm:$shamt)), (!cast(RORX64ri#suffix) GR64:$src, imm:$shamt)>; def : Pat<(rotl GR32:$src, (i8 imm:$shamt)), (!cast(RORX32ri#suffix) GR32:$src, (ROT32L2R_imm8 imm:$shamt))>; def : Pat<(rotl GR64:$src, (i8 imm:$shamt)), (!cast(RORX64ri#suffix) GR64:$src, (ROT64L2R_imm8 imm:$shamt))>; } def : Pat<(rotr (loadi32 addr:$src), (i8 imm:$shamt)), (!cast(RORX32mi#suffix) addr:$src, imm:$shamt)>; def : Pat<(rotr (loadi64 addr:$src), (i8 imm:$shamt)), (!cast(RORX64mi#suffix) addr:$src, imm:$shamt)>; def : Pat<(rotl (loadi32 addr:$src), (i8 imm:$shamt)), (!cast(RORX32mi#suffix) addr:$src, (ROT32L2R_imm8 imm:$shamt))>; def : Pat<(rotl (loadi64 addr:$src), (i8 imm:$shamt)), (!cast(RORX64mi#suffix) addr:$src, (ROT64L2R_imm8 imm:$shamt))>; } multiclass ShiftX_Pats { // Prefer SARX/SHRX/SHLX over SAR/SHR/SHL with variable shift BUT not // immediate shift, i.e. the following code is considered better // // mov %edi, %esi // shl $imm, %esi // ... %edi, ... // // than // // movb $imm, %sil // shlx %sil, %edi, %esi // ... %edi, ... // let AddedComplexity = 1 in { def : Pat<(op GR32:$src1, GR8:$src2), (!cast(NAME#"32rr"#suffix) GR32:$src1, (INSERT_SUBREG (i32 (IMPLICIT_DEF)), GR8:$src2, sub_8bit))>; def : Pat<(op GR64:$src1, GR8:$src2), (!cast(NAME#"64rr"#suffix) GR64:$src1, (INSERT_SUBREG (i64 (IMPLICIT_DEF)), GR8:$src2, sub_8bit))>; def : Pat<(op GR32:$src1, (shiftMask32 GR8:$src2)), (!cast(NAME#"32rr"#suffix) GR32:$src1, (INSERT_SUBREG (i32 (IMPLICIT_DEF)), GR8:$src2, sub_8bit))>; def : Pat<(op GR64:$src1, (shiftMask64 GR8:$src2)), (!cast(NAME#"64rr"#suffix) GR64:$src1, (INSERT_SUBREG (i64 (IMPLICIT_DEF)), GR8:$src2, sub_8bit))>; } // We prefer to use // mov (%ecx), %esi // shl $imm, $esi // // over // // movb $imm, %al // shlx %al, (%ecx), %esi // // This priority is enforced by IsProfitableToFoldLoad. def : Pat<(op (loadi32 addr:$src1), GR8:$src2), (!cast(NAME#"32rm"#suffix) addr:$src1, (INSERT_SUBREG (i32 (IMPLICIT_DEF)), GR8:$src2, sub_8bit))>; def : Pat<(op (loadi64 addr:$src1), GR8:$src2), (!cast(NAME#"64rm"#suffix) addr:$src1, (INSERT_SUBREG (i64 (IMPLICIT_DEF)), GR8:$src2, sub_8bit))>; def : Pat<(op (loadi32 addr:$src1), (shiftMask32 GR8:$src2)), (!cast(NAME#"32rm"#suffix) addr:$src1, (INSERT_SUBREG (i32 (IMPLICIT_DEF)), GR8:$src2, sub_8bit))>; def : Pat<(op (loadi64 addr:$src1), (shiftMask64 GR8:$src2)), (!cast(NAME#"64rm"#suffix) addr:$src1, (INSERT_SUBREG (i64 (IMPLICIT_DEF)), GR8:$src2, sub_8bit))>; } let Predicates = [HasBMI2, NoEGPR] in { defm : RORX_Pats<"">; defm SARX : ShiftX_Pats; defm SHRX : ShiftX_Pats; defm SHLX : ShiftX_Pats; } let Predicates = [HasBMI2, HasEGPR] in { defm : RORX_Pats<"_EVEX">; defm SARX : ShiftX_Pats; defm SHRX : ShiftX_Pats; defm SHLX : ShiftX_Pats; }