Skip to content

Commit ac182de

Browse files
committed
[RISCV][GlobalISel] Select ALU GPR instructions
Some instruction selection patterns required for ALU GPR instructions have already been automatically imported from existing TableGen descriptions - this patch simply adds testing for them. The first of the GIComplexPatternEquiv definitions required to select the shiftMaskXLen ComplexPattern has been added. Some instructions require special handling due to i32 not being a legal type on RV64 in SelectionDAG so we can't reuse SelectionDAG patterns. Co-authored-by: Lewis Revill <lewis.revill@embecosm.com> Reviewed By: nitinjohnraj Differential Revision: https://reviews.llvm.org/D76445
1 parent 6af7bf6 commit ac182de

File tree

10 files changed

+2700
-6
lines changed

10 files changed

+2700
-6
lines changed

llvm/lib/Target/RISCV/CMakeLists.txt

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ tablegen(LLVM RISCVGenAsmWriter.inc -gen-asm-writer)
77
tablegen(LLVM RISCVGenCompressInstEmitter.inc -gen-compress-inst-emitter)
88
tablegen(LLVM RISCVGenDAGISel.inc -gen-dag-isel)
99
tablegen(LLVM RISCVGenDisassemblerTables.inc -gen-disassembler)
10-
tablegen(LLVM RISCVGenGlobalISel.inc -gen-global-isel)
1110
tablegen(LLVM RISCVGenInstrInfo.inc -gen-instr-info)
1211
tablegen(LLVM RISCVGenMCCodeEmitter.inc -gen-emitter)
1312
tablegen(LLVM RISCVGenMCPseudoLowering.inc -gen-pseudo-lowering)
@@ -16,6 +15,9 @@ tablegen(LLVM RISCVGenRegisterInfo.inc -gen-register-info)
1615
tablegen(LLVM RISCVGenSearchableTables.inc -gen-searchable-tables)
1716
tablegen(LLVM RISCVGenSubtargetInfo.inc -gen-subtarget)
1817

18+
set(LLVM_TARGET_DEFINITIONS RISCVGISel.td)
19+
tablegen(LLVM RISCVGenGlobalISel.inc -gen-global-isel)
20+
1921
add_public_tablegen_target(RISCVCommonTableGen)
2022

2123
add_llvm_target(RISCVCodeGen

llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp

+101-4
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ class RISCVInstructionSelector : public InstructionSelector {
5050
bool selectConstant(MachineInstr &MI, MachineIRBuilder &MIB,
5151
MachineRegisterInfo &MRI) const;
5252

53+
bool earlySelectShift(unsigned Opc, MachineInstr &I, MachineIRBuilder &MIB,
54+
const MachineRegisterInfo &MRI);
55+
56+
ComplexRendererFns selectShiftMask(MachineOperand &Root) const;
57+
58+
// Custom renderers for tablegen
59+
void renderNegImm(MachineInstrBuilder &MIB, const MachineInstr &MI,
60+
int OpIdx) const;
61+
5362
const RISCVSubtarget &STI;
5463
const RISCVInstrInfo &TII;
5564
const RISCVRegisterInfo &TRI;
@@ -89,12 +98,43 @@ RISCVInstructionSelector::RISCVInstructionSelector(
8998
{
9099
}
91100

101+
InstructionSelector::ComplexRendererFns
102+
RISCVInstructionSelector::selectShiftMask(MachineOperand &Root) const {
103+
// TODO: Also check if we are seeing the result of an AND operation which
104+
// could be bypassed since we only check the lower log2(xlen) bits.
105+
return {{[=](MachineInstrBuilder &MIB) { MIB.add(Root); }}};
106+
}
107+
108+
// Tablegen doesn't allow us to write SRLIW/SRAIW/SLLIW patterns because the
109+
// immediate Operand has type XLenVT. GlobalISel wants it to be i32.
110+
bool RISCVInstructionSelector::earlySelectShift(
111+
unsigned Opc, MachineInstr &I, MachineIRBuilder &MIB,
112+
const MachineRegisterInfo &MRI) {
113+
if (!Subtarget->is64Bit())
114+
return false;
115+
116+
LLT Ty = MRI.getType(I.getOperand(0).getReg());
117+
if (!Ty.isScalar() || Ty.getSizeInBits() != 32)
118+
return false;
119+
120+
std::optional<int64_t> CstVal =
121+
getIConstantVRegSExtVal(I.getOperand(2).getReg(), MRI);
122+
if (!CstVal || !isUInt<5>(*CstVal))
123+
return false;
124+
125+
auto NewI = MIB.buildInstr(Opc, {I.getOperand(0).getReg()},
126+
{I.getOperand(1).getReg()})
127+
.addImm(*CstVal);
128+
I.eraseFromParent();
129+
return constrainSelectedInstRegOperands(*NewI, TII, TRI, RBI);
130+
}
131+
92132
bool RISCVInstructionSelector::select(MachineInstr &MI) {
93133
unsigned Opc = MI.getOpcode();
94134
MachineBasicBlock &MBB = *MI.getParent();
95135
MachineFunction &MF = *MBB.getParent();
96136
MachineRegisterInfo &MRI = MF.getRegInfo();
97-
MachineIRBuilder MIB(MF);
137+
MachineIRBuilder MIB(MI);
98138

99139
if (!isPreISelGenericOpcode(Opc)) {
100140
// Certain non-generic instructions also need some special handling.
@@ -104,13 +144,61 @@ bool RISCVInstructionSelector::select(MachineInstr &MI) {
104144
return true;
105145
}
106146

147+
switch (Opc) {
148+
case TargetOpcode::G_ADD: {
149+
// Tablegen doesn't pick up the ADDIW pattern because i32 isn't a legal
150+
// type for RV64 in SelectionDAG. Manually select it here.
151+
LLT Ty = MRI.getType(MI.getOperand(0).getReg());
152+
if (Subtarget->is64Bit() && Ty.isScalar() && Ty.getSizeInBits() == 32) {
153+
std::optional<int64_t> CstVal =
154+
getIConstantVRegSExtVal(MI.getOperand(2).getReg(), MRI);
155+
if (CstVal && isInt<12>(*CstVal)) {
156+
auto NewI = MIB.buildInstr(RISCV::ADDIW, {MI.getOperand(0).getReg()},
157+
{MI.getOperand(1).getReg()})
158+
.addImm(*CstVal);
159+
MI.eraseFromParent();
160+
return constrainSelectedInstRegOperands(*NewI, TII, TRI, RBI);
161+
}
162+
}
163+
break;
164+
}
165+
case TargetOpcode::G_SUB: {
166+
// Tablegen doesn't pick up the ADDIW pattern because i32 isn't a legal
167+
// type for RV64 in SelectionDAG. Manually select it here.
168+
LLT Ty = MRI.getType(MI.getOperand(0).getReg());
169+
if (Subtarget->is64Bit() && Ty.isScalar() && Ty.getSizeInBits() == 32) {
170+
std::optional<int64_t> CstVal =
171+
getIConstantVRegSExtVal(MI.getOperand(2).getReg(), MRI);
172+
if (CstVal && ((isInt<12>(*CstVal) && *CstVal != -2048) || *CstVal == 2048)) {
173+
auto NewI = MIB.buildInstr(RISCV::ADDIW, {MI.getOperand(0).getReg()},
174+
{MI.getOperand(1).getReg()})
175+
.addImm(-*CstVal);
176+
MI.eraseFromParent();
177+
return constrainSelectedInstRegOperands(*NewI, TII, TRI, RBI);
178+
}
179+
}
180+
break;
181+
}
182+
case TargetOpcode::G_ASHR:
183+
if (earlySelectShift(RISCV::SRAIW, MI, MIB, MRI))
184+
return true;
185+
break;
186+
case TargetOpcode::G_LSHR:
187+
if (earlySelectShift(RISCV::SRLIW, MI, MIB, MRI))
188+
return true;
189+
break;
190+
case TargetOpcode::G_SHL:
191+
if (earlySelectShift(RISCV::SLLIW, MI, MIB, MRI))
192+
return true;
193+
break;
194+
}
195+
107196
if (selectImpl(MI, *CoverageInfo))
108197
return true;
109198

110-
MIB.setInstrAndDebugLoc(MI);
111-
112199
switch (Opc) {
113200
case TargetOpcode::G_ANYEXT:
201+
case TargetOpcode::G_TRUNC:
114202
MI.setDesc(TII.get(TargetOpcode::COPY));
115203
return true;
116204
case TargetOpcode::G_CONSTANT:
@@ -126,10 +214,19 @@ bool RISCVInstructionSelector::select(MachineInstr &MI) {
126214
return true;
127215
}
128216

217+
void RISCVInstructionSelector::renderNegImm(MachineInstrBuilder &MIB,
218+
const MachineInstr &MI,
219+
int OpIdx) const {
220+
assert(MI.getOpcode() == TargetOpcode::G_CONSTANT && OpIdx == -1 &&
221+
"Expected G_CONSTANT");
222+
int64_t CstVal = MI.getOperand(1).getCImm()->getSExtValue();
223+
MIB.addImm(-CstVal);
224+
}
225+
129226
const TargetRegisterClass *RISCVInstructionSelector::getRegClassForTypeOnBank(
130227
LLT Ty, const RegisterBank &RB, bool GetAllRegSet) const {
131228
if (RB.getID() == RISCV::GPRRegBankID) {
132-
if (Ty.getSizeInBits() == (STI.is64Bit() ? 64 : 32))
229+
if (Ty.getSizeInBits() <= 32 || (STI.is64Bit() && Ty.getSizeInBits() == 64))
133230
return &RISCV::GPRRegClass;
134231
}
135232

llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ RISCVLegalizerInfo::RISCVLegalizerInfo(const RISCVSubtarget &ST) {
4747
.lowerFor({{XLenLLT, s1}});
4848

4949
getActionDefinitionsBuilder({G_ASHR, G_LSHR, G_SHL})
50-
.legalFor({{s32, s32}, {s32, XLenLLT}, {XLenLLT, XLenLLT}})
50+
.legalFor({{s32, s32}, {XLenLLT, XLenLLT}})
5151
.widenScalarToNextPow2(0)
5252
.clampScalar(1, s32, XLenLLT)
5353
.clampScalar(0, s32, XLenLLT);

llvm/lib/Target/RISCV/RISCVGISel.td

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//===-- RISCVGIsel.td - RISCV GlobalISel Patterns ----------*- tablegen -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
/// \file
10+
/// This file contains patterns that are relevant to GlobalISel, including
11+
/// GIComplexOperandMatcher definitions for equivalent SelectionDAG
12+
/// ComplexPatterns.
13+
//
14+
//===----------------------------------------------------------------------===//
15+
16+
include "RISCV.td"
17+
18+
def simm12Plus1 : ImmLeaf<XLenVT, [{
19+
return (isInt<12>(Imm) && Imm != -2048) || Imm == 2048;}]>;
20+
21+
def GINegImm : GICustomOperandRenderer<"renderNegImm">,
22+
GISDNodeXFormEquiv<NegImm>;
23+
24+
// FIXME: This is labelled as handling 's32', however the ComplexPattern it
25+
// refers to handles both i32 and i64 based on the HwMode. Currently this LLT
26+
// parameter appears to be ignored so this pattern works for both, however we
27+
// should add a LowLevelTypeByHwMode, and use that to define our XLenLLT instead
28+
// here.
29+
def ShiftMaskGI :
30+
GIComplexOperandMatcher<s32, "selectShiftMask">,
31+
GIComplexPatternEquiv<shiftMaskXLen>;
32+
33+
// FIXME: Canonicalize (sub X, C) -> (add X, -C) earlier.
34+
def : Pat<(XLenVT (sub GPR:$rs1, simm12Plus1:$imm)),
35+
(ADDI GPR:$rs1, (NegImm simm12Plus1:$imm))>;
36+
37+
let Predicates = [IsRV64] in {
38+
def : Pat<(i32 (add GPR:$rs1, GPR:$rs2)), (ADDW GPR:$rs1, GPR:$rs2)>;
39+
def : Pat<(i32 (sub GPR:$rs1, GPR:$rs2)), (SUBW GPR:$rs1, GPR:$rs2)>;
40+
41+
def : Pat<(i32 (shl GPR:$rs1, (i32 GPR:$rs2))), (SLLW GPR:$rs1, GPR:$rs2)>;
42+
def : Pat<(i32 (sra GPR:$rs1, (i32 GPR:$rs2))), (SRAW GPR:$rs1, GPR:$rs2)>;
43+
def : Pat<(i32 (srl GPR:$rs1, (i32 GPR:$rs2))), (SRLW GPR:$rs1, GPR:$rs2)>;
44+
}
45+
46+
let Predicates = [HasStdExtMOrZmmul, IsRV64] in {
47+
def : Pat<(i32 (mul GPR:$rs1, GPR:$rs2)), (MULW GPR:$rs1, GPR:$rs2)>;
48+
}
49+
50+
let Predicates = [HasStdExtM, IsRV64] in {
51+
def : Pat<(i32 (sdiv GPR:$rs1, GPR:$rs2)), (DIVW GPR:$rs1, GPR:$rs2)>;
52+
def : Pat<(i32 (srem GPR:$rs1, GPR:$rs2)), (REMW GPR:$rs1, GPR:$rs2)>;
53+
def : Pat<(i32 (udiv GPR:$rs1, GPR:$rs2)), (DIVUW GPR:$rs1, GPR:$rs2)>;
54+
def : Pat<(i32 (urem GPR:$rs1, GPR:$rs2)), (REMUW GPR:$rs1, GPR:$rs2)>;
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2+
; RUN: llc -mtriple=riscv64 -mattr=+m -global-isel -verify-machineinstrs < %s \
3+
; RUN: | FileCheck %s --check-prefix=RV64IM
4+
5+
define i64 @sll_i64(i64 %a, i64 %b) {
6+
; RV64IM-LABEL: sll_i64:
7+
; RV64IM: # %bb.0: # %entry
8+
; RV64IM-NEXT: sll a0, a0, a1
9+
; RV64IM-NEXT: ret
10+
entry:
11+
%0 = shl i64 %a, %b
12+
ret i64 %0
13+
}
14+
15+
define i64 @slli_i64(i64 %a) {
16+
; RV64IM-LABEL: slli_i64:
17+
; RV64IM: # %bb.0: # %entry
18+
; RV64IM-NEXT: slli a0, a0, 33
19+
; RV64IM-NEXT: ret
20+
entry:
21+
%0 = shl i64 %a, 33
22+
ret i64 %0
23+
}
24+
25+
define i64 @sra_i64(i64 %a, i64 %b) {
26+
; RV64IM-LABEL: sra_i64:
27+
; RV64IM: # %bb.0: # %entry
28+
; RV64IM-NEXT: sra a0, a0, a1
29+
; RV64IM-NEXT: ret
30+
entry:
31+
%0 = ashr i64 %a, %b
32+
ret i64 %0
33+
}
34+
35+
define i64 @srai_i64(i64 %a) {
36+
; RV64IM-LABEL: srai_i64:
37+
; RV64IM: # %bb.0: # %entry
38+
; RV64IM-NEXT: srai a0, a0, 47
39+
; RV64IM-NEXT: ret
40+
entry:
41+
%0 = ashr i64 %a, 47
42+
ret i64 %0
43+
}
44+
45+
define i64 @srl_i64(i64 %a, i64 %b) {
46+
; RV64IM-LABEL: srl_i64:
47+
; RV64IM: # %bb.0: # %entry
48+
; RV64IM-NEXT: srl a0, a0, a1
49+
; RV64IM-NEXT: ret
50+
entry:
51+
%0 = lshr i64 %a, %b
52+
ret i64 %0
53+
}
54+
55+
define i64 @srli_i64(i64 %a, i64 %b) {
56+
; RV64IM-LABEL: srli_i64:
57+
; RV64IM: # %bb.0: # %entry
58+
; RV64IM-NEXT: srli a0, a0, 55
59+
; RV64IM-NEXT: ret
60+
entry:
61+
%0 = lshr i64 %a, 55
62+
ret i64 %0
63+
}
64+
65+
define i64 @sdiv_i64(i64 %a, i64 %b) {
66+
; RV64IM-LABEL: sdiv_i64:
67+
; RV64IM: # %bb.0: # %entry
68+
; RV64IM-NEXT: div a0, a0, a1
69+
; RV64IM-NEXT: ret
70+
entry:
71+
%0 = sdiv i64 %a, %b
72+
ret i64 %0
73+
}
74+
75+
define i64 @srem_i64(i64 %a, i64 %b) {
76+
; RV64IM-LABEL: srem_i64:
77+
; RV64IM: # %bb.0: # %entry
78+
; RV64IM-NEXT: rem a0, a0, a1
79+
; RV64IM-NEXT: ret
80+
entry:
81+
%0 = srem i64 %a, %b
82+
ret i64 %0
83+
}
84+
85+
define i64 @udiv_i64(i64 %a, i64 %b) {
86+
; RV64IM-LABEL: udiv_i64:
87+
; RV64IM: # %bb.0: # %entry
88+
; RV64IM-NEXT: divu a0, a0, a1
89+
; RV64IM-NEXT: ret
90+
entry:
91+
%0 = udiv i64 %a, %b
92+
ret i64 %0
93+
}
94+
95+
define i64 @urem_i64(i64 %a, i64 %b) {
96+
; RV64IM-LABEL: urem_i64:
97+
; RV64IM: # %bb.0: # %entry
98+
; RV64IM-NEXT: remu a0, a0, a1
99+
; RV64IM-NEXT: ret
100+
entry:
101+
%0 = urem i64 %a, %b
102+
ret i64 %0
103+
}

0 commit comments

Comments
 (0)