1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 18:54:02 +01:00

[RISCV] Add matching of codegen patterns to RISCV Bit Manipulation Zbb asm instructions

This patch provides optimization of bit manipulation operations by
enabling the +experimental-b target feature.
It adds matching of single block patterns of instructions to specific
bit-manip instructions from the base subset (zbb subextension) of the
experimental B extension of RISC-V.
It adds also the correspondent codegen tests.

This patch is based on Claire Wolf's proposal for the bit manipulation
extension of RISCV:
https://github.com/riscv/riscv-bitmanip/blob/master/bitmanip-0.92.pdf

Differential Revision: https://reviews.llvm.org/D79870
This commit is contained in:
lewis-revill 2020-07-15 11:50:03 +01:00
parent e3485c1b1b
commit f877008728
6 changed files with 2645 additions and 3 deletions

View File

@ -184,6 +184,196 @@ bool RISCVDAGToDAGISel::SelectAddrFI(SDValue Addr, SDValue &Base) {
return false;
}
// Check that it is a SLOI (Shift Left Ones Immediate). We first check that
// it is the right node tree:
//
// (OR (SHL RS1, VC2), VC1)
//
// and then we check that VC1, the mask used to fill with ones, is compatible
// with VC2, the shamt:
//
// VC1 == maskTrailingOnes<uint64_t>(VC2)
bool RISCVDAGToDAGISel::SelectSLOI(SDValue N, SDValue &RS1, SDValue &Shamt) {
MVT XLenVT = Subtarget->getXLenVT();
if (N.getOpcode() == ISD::OR) {
SDValue Or = N;
if (Or.getOperand(0).getOpcode() == ISD::SHL) {
SDValue Shl = Or.getOperand(0);
if (isa<ConstantSDNode>(Shl.getOperand(1)) &&
isa<ConstantSDNode>(Or.getOperand(1))) {
if (XLenVT == MVT::i64) {
uint64_t VC1 = Or.getConstantOperandVal(1);
uint64_t VC2 = Shl.getConstantOperandVal(1);
if (VC1 == maskTrailingOnes<uint64_t>(VC2)) {
RS1 = Shl.getOperand(0);
Shamt = CurDAG->getTargetConstant(VC2, SDLoc(N),
Shl.getOperand(1).getValueType());
return true;
}
}
if (XLenVT == MVT::i32) {
uint32_t VC1 = Or.getConstantOperandVal(1);
uint32_t VC2 = Shl.getConstantOperandVal(1);
if (VC1 == maskTrailingOnes<uint32_t>(VC2)) {
RS1 = Shl.getOperand(0);
Shamt = CurDAG->getTargetConstant(VC2, SDLoc(N),
Shl.getOperand(1).getValueType());
return true;
}
}
}
}
}
return false;
}
// Check that it is a SROI (Shift Right Ones Immediate). We first check that
// it is the right node tree:
//
// (OR (SRL RS1, VC2), VC1)
//
// and then we check that VC1, the mask used to fill with ones, is compatible
// with VC2, the shamt:
//
// VC1 == maskLeadingOnes<uint64_t>(VC2)
bool RISCVDAGToDAGISel::SelectSROI(SDValue N, SDValue &RS1, SDValue &Shamt) {
MVT XLenVT = Subtarget->getXLenVT();
if (N.getOpcode() == ISD::OR) {
SDValue Or = N;
if (Or.getOperand(0).getOpcode() == ISD::SRL) {
SDValue Srl = Or.getOperand(0);
if (isa<ConstantSDNode>(Srl.getOperand(1)) &&
isa<ConstantSDNode>(Or.getOperand(1))) {
if (XLenVT == MVT::i64) {
uint64_t VC1 = Or.getConstantOperandVal(1);
uint64_t VC2 = Srl.getConstantOperandVal(1);
if (VC1 == maskLeadingOnes<uint64_t>(VC2)) {
RS1 = Srl.getOperand(0);
Shamt = CurDAG->getTargetConstant(VC2, SDLoc(N),
Srl.getOperand(1).getValueType());
return true;
}
}
if (XLenVT == MVT::i32) {
uint32_t VC1 = Or.getConstantOperandVal(1);
uint32_t VC2 = Srl.getConstantOperandVal(1);
if (VC1 == maskLeadingOnes<uint32_t>(VC2)) {
RS1 = Srl.getOperand(0);
Shamt = CurDAG->getTargetConstant(VC2, SDLoc(N),
Srl.getOperand(1).getValueType());
return true;
}
}
}
}
}
return false;
}
// Check that it is a SLLIUW (Shift Logical Left Immediate Unsigned i32
// on RV64).
// SLLIUW is the same as SLLI except for the fact that it clears the bits
// XLEN-1:32 of the input RS1 before shifting.
// We first check that it is the right node tree:
//
// (AND (SHL RS1, VC2), VC1)
//
// We check that VC2, the shamt is less than 32, otherwise the pattern is
// exactly the same as SLLI and we give priority to that.
// Eventually we check that that VC1, the mask used to clear the upper 32 bits
// of RS1, is correct:
//
// VC1 == (0xFFFFFFFF << VC2)
bool RISCVDAGToDAGISel::SelectSLLIUW(SDValue N, SDValue &RS1, SDValue &Shamt) {
if (N.getOpcode() == ISD::AND && Subtarget->getXLenVT() == MVT::i64) {
SDValue And = N;
if (And.getOperand(0).getOpcode() == ISD::SHL) {
SDValue Shl = And.getOperand(0);
if (isa<ConstantSDNode>(Shl.getOperand(1)) &&
isa<ConstantSDNode>(And.getOperand(1))) {
uint64_t VC1 = And.getConstantOperandVal(1);
uint64_t VC2 = Shl.getConstantOperandVal(1);
if (VC2 < 32 && VC1 == ((uint64_t)0xFFFFFFFF << VC2)) {
RS1 = Shl.getOperand(0);
Shamt = CurDAG->getTargetConstant(VC2, SDLoc(N),
Shl.getOperand(1).getValueType());
return true;
}
}
}
}
return false;
}
// Check that it is a SLOIW (Shift Left Ones Immediate i32 on RV64).
// We first check that it is the right node tree:
//
// (SIGN_EXTEND_INREG (OR (SHL RS1, VC2), VC1))
//
// and then we check that VC1, the mask used to fill with ones, is compatible
// with VC2, the shamt:
//
// VC1 == maskTrailingOnes<uint32_t>(VC2)
bool RISCVDAGToDAGISel::SelectSLOIW(SDValue N, SDValue &RS1, SDValue &Shamt) {
if (Subtarget->getXLenVT() == MVT::i64 &&
N.getOpcode() == ISD::SIGN_EXTEND_INREG &&
cast<VTSDNode>(N.getOperand(1))->getVT() == MVT::i32) {
if (N.getOperand(0).getOpcode() == ISD::OR) {
SDValue Or = N.getOperand(0);
if (Or.getOperand(0).getOpcode() == ISD::SHL) {
SDValue Shl = Or.getOperand(0);
if (isa<ConstantSDNode>(Shl.getOperand(1)) &&
isa<ConstantSDNode>(Or.getOperand(1))) {
uint32_t VC1 = Or.getConstantOperandVal(1);
uint32_t VC2 = Shl.getConstantOperandVal(1);
if (VC1 == maskTrailingOnes<uint32_t>(VC2)) {
RS1 = Shl.getOperand(0);
Shamt = CurDAG->getTargetConstant(VC2, SDLoc(N),
Shl.getOperand(1).getValueType());
return true;
}
}
}
}
}
return false;
}
// Check that it is a SROIW (Shift Right Ones Immediate i32 on RV64).
// We first check that it is the right node tree:
//
// (OR (SHL RS1, VC2), VC1)
//
// and then we check that VC1, the mask used to fill with ones, is compatible
// with VC2, the shamt:
//
// VC1 == maskLeadingOnes<uint32_t>(VC2)
bool RISCVDAGToDAGISel::SelectSROIW(SDValue N, SDValue &RS1, SDValue &Shamt) {
if (N.getOpcode() == ISD::OR && Subtarget->getXLenVT() == MVT::i64) {
SDValue Or = N;
if (Or.getOperand(0).getOpcode() == ISD::SRL) {
SDValue Srl = Or.getOperand(0);
if (isa<ConstantSDNode>(Srl.getOperand(1)) &&
isa<ConstantSDNode>(Or.getOperand(1))) {
uint32_t VC1 = Or.getConstantOperandVal(1);
uint32_t VC2 = Srl.getConstantOperandVal(1);
if (VC1 == maskLeadingOnes<uint32_t>(VC2)) {
RS1 = Srl.getOperand(0);
Shamt = CurDAG->getTargetConstant(VC2, SDLoc(N),
Srl.getOperand(1).getValueType());
return true;
}
}
}
}
return false;
}
// Merge an ADDI into the offset of a load/store instruction where possible.
// (load (addi base, off1), off2) -> (load base, off1+off2)
// (store val, (addi base, off1), off2) -> (store val, base, off1+off2)

View File

@ -45,6 +45,12 @@ public:
bool SelectAddrFI(SDValue Addr, SDValue &Base);
bool SelectSLOI(SDValue N, SDValue &RS1, SDValue &Shamt);
bool SelectSROI(SDValue N, SDValue &RS1, SDValue &Shamt);
bool SelectSLLIUW(SDValue N, SDValue &RS1, SDValue &Shamt);
bool SelectSLOIW(SDValue N, SDValue &RS1, SDValue &Shamt);
bool SelectSROIW(SDValue N, SDValue &RS1, SDValue &Shamt);
// Include the pieces autogenerated from the target description.
#include "RISCVGenDAGISel.inc"

View File

@ -152,9 +152,12 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
setOperationAction(ISD::ROTL, XLenVT, Expand);
setOperationAction(ISD::ROTR, XLenVT, Expand);
setOperationAction(ISD::BSWAP, XLenVT, Expand);
setOperationAction(ISD::CTTZ, XLenVT, Expand);
setOperationAction(ISD::CTLZ, XLenVT, Expand);
setOperationAction(ISD::CTPOP, XLenVT, Expand);
if (!Subtarget.hasStdExtZbb()) {
setOperationAction(ISD::CTTZ, XLenVT, Expand);
setOperationAction(ISD::CTLZ, XLenVT, Expand);
setOperationAction(ISD::CTPOP, XLenVT, Expand);
}
ISD::CondCode FPCCToExtend[] = {
ISD::SETOGT, ISD::SETOGE, ISD::SETONE, ISD::SETUEQ, ISD::SETUGT,

View File

@ -632,3 +632,79 @@ let Predicates = [HasStdExtZbproposedc, HasStdExtZbbOrZbp, HasStdExtC, IsRV64] i
def : CompressPat<(PACK GPRC:$rs1, GPRC:$rs1, X0),
(C_ZEXTW GPRC:$rs1)>;
} // Predicates = [HasStdExtZbproposedc, HasStdExtC, IsRV64]
//===----------------------------------------------------------------------===//
// Codegen patterns
//===----------------------------------------------------------------------===//
def SLOIPat : ComplexPattern<XLenVT, 2, "SelectSLOI", [or]>;
def SROIPat : ComplexPattern<XLenVT, 2, "SelectSROI", [or]>;
def SLLIUWPat : ComplexPattern<i64, 2, "SelectSLLIUW", [and]>;
def SLOIWPat : ComplexPattern<i64, 2, "SelectSLOIW", [sext_inreg]>;
def SROIWPat : ComplexPattern<i64, 2, "SelectSROIW", [or]>;
let Predicates = [HasStdExtZbb] in {
def : Pat<(xor (shl (xor GPR:$rs1, -1), GPR:$rs2), -1),
(SLO GPR:$rs1, GPR:$rs2)>;
def : Pat<(xor (srl (xor GPR:$rs1, -1), GPR:$rs2), -1),
(SRO GPR:$rs1, GPR:$rs2)>;
def : Pat<(SLOIPat GPR:$rs1, uimmlog2xlen:$shamt),
(SLOI GPR:$rs1, uimmlog2xlen:$shamt)>;
def : Pat<(SROIPat GPR:$rs1, uimmlog2xlen:$shamt),
(SROI GPR:$rs1, uimmlog2xlen:$shamt)>;
def : Pat<(ctlz GPR:$rs1), (CLZ GPR:$rs1)>;
def : Pat<(cttz GPR:$rs1), (CTZ GPR:$rs1)>;
def : Pat<(ctpop GPR:$rs1), (PCNT GPR:$rs1)>;
} // Predicates = [HasStdExtZbb]
let Predicates = [HasStdExtZbb, IsRV32] in
def : Pat<(sra (shl GPR:$rs1, (i32 24)), (i32 24)), (SEXTB GPR:$rs1)>;
let Predicates = [HasStdExtZbb, IsRV64] in
def : Pat<(sra (shl GPR:$rs1, (i64 56)), (i64 56)), (SEXTB GPR:$rs1)>;
let Predicates = [HasStdExtZbb, IsRV32] in
def : Pat<(sra (shl GPR:$rs1, (i32 16)), (i32 16)), (SEXTH GPR:$rs1)>;
let Predicates = [HasStdExtZbb, IsRV64] in
def : Pat<(sra (shl GPR:$rs1, (i64 48)), (i64 48)), (SEXTH GPR:$rs1)>;
let Predicates = [HasStdExtZbb] in {
def : Pat<(smin GPR:$rs1, GPR:$rs2), (MIN GPR:$rs1, GPR:$rs2)>;
def : Pat<(riscv_selectcc GPR:$rs1, GPR:$rs2, (XLenVT 20), GPR:$rs1, GPR:$rs2),
(MIN GPR:$rs1, GPR:$rs2)>;
def : Pat<(smax GPR:$rs1, GPR:$rs2), (MAX GPR:$rs1, GPR:$rs2)>;
def : Pat<(riscv_selectcc GPR:$rs2, GPR:$rs1, (XLenVT 20), GPR:$rs1, GPR:$rs2),
(MAX GPR:$rs1, GPR:$rs2)>;
def : Pat<(umin GPR:$rs1, GPR:$rs2), (MINU GPR:$rs1, GPR:$rs2)>;
def : Pat<(riscv_selectcc GPR:$rs1, GPR:$rs2, (XLenVT 12), GPR:$rs1, GPR:$rs2),
(MINU GPR:$rs1, GPR:$rs2)>;
def : Pat<(umax GPR:$rs1, GPR:$rs2), (MAXU GPR:$rs1, GPR:$rs2)>;
def : Pat<(riscv_selectcc GPR:$rs2, GPR:$rs1, (XLenVT 12), GPR:$rs1, GPR:$rs2),
(MAXU GPR:$rs1, GPR:$rs2)>;
} // Predicates = [HasStdExtZbb]
let Predicates = [HasStdExtZbb, IsRV64] in {
def : Pat<(and (add GPR:$rs, simm12:$simm12), (i64 0xFFFFFFFF)),
(ADDIWU GPR:$rs, simm12:$simm12)>;
def : Pat<(SLLIUWPat GPR:$rs1, uimmlog2xlen:$shamt),
(SLLIUW GPR:$rs1, uimmlog2xlen:$shamt)>;
def : Pat<(and (add GPR:$rs1, GPR:$rs2), (i64 0xFFFFFFFF)),
(ADDWU GPR:$rs1, GPR:$rs2)>;
def : Pat<(and (sub GPR:$rs1, GPR:$rs2), (i64 0xFFFFFFFF)),
(SUBWU GPR:$rs1, GPR:$rs2)>;
def : Pat<(add GPR:$rs1, (and GPR:$rs2, (i64 0xFFFFFFFF))),
(ADDUW GPR:$rs1, GPR:$rs2)>;
def : Pat<(sub GPR:$rs1, (and GPR:$rs2, (i64 0xFFFFFFFF))),
(SUBUW GPR:$rs1, GPR:$rs2)>;
def : Pat<(xor (riscv_sllw (xor GPR:$rs1, -1), GPR:$rs2), -1),
(SLOW GPR:$rs1, GPR:$rs2)>;
def : Pat<(xor (riscv_srlw (xor GPR:$rs1, -1), GPR:$rs2), -1),
(SROW GPR:$rs1, GPR:$rs2)>;
def : Pat<(SLOIWPat GPR:$rs1, uimmlog2xlen:$shamt),
(SLOIW GPR:$rs1, uimmlog2xlen:$shamt)>;
def : Pat<(SROIWPat GPR:$rs1, uimmlog2xlen:$shamt),
(SROIW GPR:$rs1, uimmlog2xlen:$shamt)>;
def : Pat<(add (ctlz (and GPR:$rs1, (i64 0xFFFFFFFF))), (i64 -32)),
(CLZW GPR:$rs1)>;
// We don't pattern-match CTZW here as it has the same pattern and result as
// RV64 CTZ
def : Pat<(ctpop (and GPR:$rs1, (i64 0xFFFFFFFF))), (PCNTW GPR:$rs1)>;
} // Predicates = [HasStdExtZbb, IsRV64]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff