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:
parent
e3485c1b1b
commit
f877008728
@ -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)
|
||||
|
@ -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"
|
||||
|
||||
|
@ -152,9 +152,12 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
|
||||
setOperationAction(ISD::ROTL, XLenVT, Expand);
|
||||
setOperationAction(ISD::ROTR, XLenVT, Expand);
|
||||
setOperationAction(ISD::BSWAP, 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,
|
||||
|
@ -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]
|
||||
|
1218
test/CodeGen/RISCV/rv32Zbb.ll
Normal file
1218
test/CodeGen/RISCV/rv32Zbb.ll
Normal file
File diff suppressed because it is too large
Load Diff
1149
test/CodeGen/RISCV/rv64Zbb.ll
Normal file
1149
test/CodeGen/RISCV/rv64Zbb.ll
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user