LLVM 24.0.0git
RISCVFoldMemOffset.cpp
Go to the documentation of this file.
1//===- RISCVFoldMemOffset.cpp - Fold ADDI into memory offsets ------------===//
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// Look for ADDIs that can be removed by folding their immediate into later
10// load/store addresses. There may be other arithmetic instructions between the
11// addi and load/store that we need to reassociate through. If the final result
12// of the arithmetic is only used by load/store addresses, we can fold the
13// offset into the all the load/store as long as it doesn't create an offset
14// that is too large.
15//
16//===---------------------------------------------------------------------===//
17
18#include "RISCV.h"
19#include "RISCVSubtarget.h"
22#include <queue>
23
24using namespace llvm;
25
26#define DEBUG_TYPE "riscv-fold-mem-offset"
27#define RISCV_FOLD_MEM_OFFSET_NAME "RISC-V Fold Memory Offset"
28
29namespace {
30
31class RISCVFoldMemOffset : public MachineFunctionPass {
32public:
33 static char ID;
34
35 RISCVFoldMemOffset() : MachineFunctionPass(ID) {}
36
37 bool runOnMachineFunction(MachineFunction &MF) override;
38
39 bool foldOffset(Register OrigReg, int64_t InitialOffset,
40 const MachineRegisterInfo &MRI,
41 DenseMap<MachineInstr *, int64_t> &FoldableInstrs);
42
43 void getAnalysisUsage(AnalysisUsage &AU) const override {
44 AU.setPreservesCFG();
47 }
48
49 StringRef getPassName() const override { return RISCV_FOLD_MEM_OFFSET_NAME; }
50};
51
52// Wrapper class around a std::optional to allow accumulation.
53class FoldableOffset {
54 std::optional<int64_t> Offset;
55
56public:
57 bool hasValue() const { return Offset.has_value(); }
58 int64_t getValue() const { return *Offset; }
59
60 FoldableOffset &operator=(int64_t RHS) {
61 Offset = RHS;
62 return *this;
63 }
64
65 FoldableOffset &operator+=(int64_t RHS) {
66 if (!Offset)
67 Offset = 0;
69 return *this;
70 }
71
72 int64_t operator*() { return *Offset; }
73};
74
75} // end anonymous namespace
76
77char RISCVFoldMemOffset::ID = 0;
79 false, false)
80
82 return new RISCVFoldMemOffset();
83}
84
85// Walk forward from the ADDI looking for arithmetic instructions we can
86// analyze or memory instructions that use it as part of their address
87// calculation. For each arithmetic instruction we lookup how the offset
88// contributes to the value in that register use that information to
89// calculate the contribution to the output of this instruction.
90// Only addition and left shift are supported.
91// FIXME: Add multiplication by constant. The constant will be in a register.
92bool RISCVFoldMemOffset::foldOffset(
93 Register OrigReg, int64_t InitialOffset, const MachineRegisterInfo &MRI,
94 DenseMap<MachineInstr *, int64_t> &FoldableInstrs) {
95 // Map to hold how much the offset contributes to the value of this register.
96 DenseMap<Register, int64_t> RegToOffsetMap;
97
98 // Insert root offset into the map.
99 RegToOffsetMap[OrigReg] = InitialOffset;
100
101 std::queue<Register> Worklist;
102 Worklist.push(OrigReg);
103
104 while (!Worklist.empty()) {
105 Register Reg = Worklist.front();
106 Worklist.pop();
107
108 if (!Reg.isVirtual())
109 return false;
110
111 for (auto &User : MRI.use_nodbg_instructions(Reg)) {
112 FoldableOffset Offset;
113
114 switch (User.getOpcode()) {
115 default:
116 return false;
117 case RISCV::ADD:
118 if (auto I = RegToOffsetMap.find(User.getOperand(1).getReg());
119 I != RegToOffsetMap.end())
120 Offset = I->second;
121 if (auto I = RegToOffsetMap.find(User.getOperand(2).getReg());
122 I != RegToOffsetMap.end())
123 Offset += I->second;
124 break;
125 case RISCV::SH1ADD:
126 if (auto I = RegToOffsetMap.find(User.getOperand(1).getReg());
127 I != RegToOffsetMap.end())
128 Offset = (uint64_t)I->second << 1;
129 if (auto I = RegToOffsetMap.find(User.getOperand(2).getReg());
130 I != RegToOffsetMap.end())
131 Offset += I->second;
132 break;
133 case RISCV::SH2ADD:
134 if (auto I = RegToOffsetMap.find(User.getOperand(1).getReg());
135 I != RegToOffsetMap.end())
136 Offset = (uint64_t)I->second << 2;
137 if (auto I = RegToOffsetMap.find(User.getOperand(2).getReg());
138 I != RegToOffsetMap.end())
139 Offset += I->second;
140 break;
141 case RISCV::SH3ADD:
142 if (auto I = RegToOffsetMap.find(User.getOperand(1).getReg());
143 I != RegToOffsetMap.end())
144 Offset = (uint64_t)I->second << 3;
145 if (auto I = RegToOffsetMap.find(User.getOperand(2).getReg());
146 I != RegToOffsetMap.end())
147 Offset += I->second;
148 break;
149 case RISCV::ADD_UW:
150 case RISCV::SH1ADD_UW:
151 case RISCV::SH2ADD_UW:
152 case RISCV::SH3ADD_UW:
153 // Don't fold through the zero extended input.
154 if (User.getOperand(1).getReg() == Reg)
155 return false;
156 if (auto I = RegToOffsetMap.find(User.getOperand(2).getReg());
157 I != RegToOffsetMap.end())
158 Offset = I->second;
159 break;
160 case RISCV::SLLI: {
161 unsigned ShAmt = User.getOperand(2).getImm();
162 if (auto I = RegToOffsetMap.find(User.getOperand(1).getReg());
163 I != RegToOffsetMap.end())
164 Offset = (uint64_t)I->second << ShAmt;
165 break;
166 }
167 case RISCV::LB:
168 case RISCV::LBU:
169 case RISCV::SB:
170 case RISCV::LH:
171 case RISCV::LH_INX:
172 case RISCV::LHU:
173 case RISCV::FLH:
174 case RISCV::SH:
175 case RISCV::SH_INX:
176 case RISCV::FSH:
177 case RISCV::LW:
178 case RISCV::LW_INX:
179 case RISCV::LWU:
180 case RISCV::FLW:
181 case RISCV::SW:
182 case RISCV::SW_INX:
183 case RISCV::FSW:
184 case RISCV::LD:
185 case RISCV::LD_RV32:
186 case RISCV::FLD:
187 case RISCV::SD:
188 case RISCV::SD_RV32:
189 case RISCV::FSD: {
190 // Can't fold into store value.
191 if (User.getOperand(0).getReg() == Reg)
192 return false;
193
194 // Existing offset must be immediate.
195 if (!User.getOperand(2).isImm())
196 return false;
197
198 // Require at least one operation between the ADDI and the load/store.
199 // We have other optimizations that should handle the simple case.
200 if (User.getOperand(1).getReg() == OrigReg)
201 return false;
202
203 auto I = RegToOffsetMap.find(User.getOperand(1).getReg());
204 if (I == RegToOffsetMap.end())
205 return false;
206
207 int64_t LocalOffset = User.getOperand(2).getImm();
208 assert(isInt<12>(LocalOffset));
209 int64_t CombinedOffset = (uint64_t)LocalOffset + (uint64_t)I->second;
210 if (!isInt<12>(CombinedOffset))
211 return false;
212
213 FoldableInstrs[&User] = CombinedOffset;
214 continue;
215 }
216 }
217
218 // If we reach here we should have an accumulated offset.
219 assert(Offset.hasValue() && "Expected an offset");
220
221 // If the offset is new or changed, add the destination register to the
222 // work list.
223 int64_t OffsetVal = Offset.getValue();
224 auto P =
225 RegToOffsetMap.try_emplace(User.getOperand(0).getReg(), OffsetVal);
226 if (P.second) {
227 Worklist.push(User.getOperand(0).getReg());
228 } else if (P.first->second != OffsetVal) {
229 P.first->second = OffsetVal;
230 Worklist.push(User.getOperand(0).getReg());
231 }
232 }
233 }
234
235 return true;
236}
237
238bool RISCVFoldMemOffset::runOnMachineFunction(MachineFunction &MF) {
239 if (skipFunction(MF.getFunction()))
240 return false;
241
242 // This optimization may increase size by preventing compression.
243 if (MF.getFunction().hasOptSize())
244 return false;
245
246 MachineRegisterInfo &MRI = MF.getRegInfo();
247
248 bool MadeChange = false;
249 for (MachineBasicBlock &MBB : MF) {
250 for (MachineInstr &MI : llvm::make_early_inc_range(MBB)) {
251 // FIXME: We can support ADDIW from an LUI+ADDIW pair if the result is
252 // equivalent to LUI+ADDI.
253 if (MI.getOpcode() != RISCV::ADDI)
254 continue;
255
256 // We only want to optimize register ADDIs.
257 if (!MI.getOperand(1).isReg() || !MI.getOperand(2).isImm())
258 continue;
259
260 // Ignore 'li'.
261 if (MI.getOperand(1).getReg() == RISCV::X0)
262 continue;
263
264 int64_t Offset = MI.getOperand(2).getImm();
266
267 DenseMap<MachineInstr *, int64_t> FoldableInstrs;
268
269 if (!foldOffset(MI.getOperand(0).getReg(), Offset, MRI, FoldableInstrs))
270 continue;
271
272 if (FoldableInstrs.empty())
273 continue;
274
275 // We can fold this ADDI.
276 // Rewrite all the instructions.
277 for (auto [MemMI, NewOffset] : FoldableInstrs)
278 MemMI->getOperand(2).setImm(NewOffset);
279
280 MRI.replaceRegWith(MI.getOperand(0).getReg(), MI.getOperand(1).getReg());
281 MRI.clearKillFlags(MI.getOperand(1).getReg());
282 MI.eraseFromParent();
283 MadeChange = true;
284 }
285 }
286
287 return MadeChange;
288}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
MachineBasicBlock & MBB
#define DEBUG_TYPE
IRTranslator LLVM IR MI
static constexpr Value * getValue(Ty &ValueOrUse)
#define I(x, y, z)
Definition MD5.cpp:57
Register Reg
Promote Memory to Register
Definition Mem2Reg.cpp:110
#define P(N)
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition PassSupport.h:56
#define RISCV_FOLD_MEM_OFFSET_NAME
Value * RHS
Represent the analysis usage information of a pass.
AnalysisUsage & addPreserved()
Add the specified Pass class to the set of analyses preserved by this pass.
LLVM_ABI void setPreservesCFG()
This function should be called by the pass, iff they do not:
Definition Pass.cpp:275
iterator find(const_arg_type_t< KeyT > Val)
Definition DenseMap.h:223
std::pair< iterator, bool > try_emplace(KeyT &&Key, Ts &&...Args)
Definition DenseMap.h:299
bool empty() const
Definition DenseMap.h:171
iterator end()
Definition DenseMap.h:141
FunctionPass class - This class is used to implement most global optimizations.
Definition Pass.h:314
bool hasOptSize() const
Optimize this function for size (-Os) or minimum size (-Oz).
Definition Function.h:691
MachineFunctionPass - This class adapts the FunctionPass interface to allow convenient creation of pa...
void getAnalysisUsage(AnalysisUsage &AU) const override
getAnalysisUsage - Subclasses that override getAnalysisUsage must call this.
MachineRegisterInfo & getRegInfo()
getRegInfo - Return information about the registers currently in use.
Function & getFunction()
Return the LLVM function that this machine code represents.
MachineRegisterInfo - Keep track of information for virtual and physical registers,...
LLVM_ABI void clearKillFlags(Register Reg) const
clearKillFlags - Iterate over all the uses of the given register and clear the kill flag from the Mac...
iterator_range< use_instr_nodbg_iterator > use_nodbg_instructions(Register Reg) const
LLVM_ABI void replaceRegWith(Register FromReg, Register ToReg)
replaceRegWith - Replace all instances of FromReg with ToReg in the machine function.
Wrapper class representing virtual and physical registers.
Definition Register.h:20
constexpr bool isVirtual() const
Return true if the specified register number is in the virtual register namespace.
Definition Register.h:79
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
@ User
could "use" a pointer
This is an optimization pass for GlobalISel generic memory operations.
@ Offset
Definition DWP.cpp:578
FunctionPass * createRISCVFoldMemOffsetPass()
constexpr bool isInt(int64_t x)
Checks if an integer fits into the given bit width.
Definition MathExtras.h:166
APInt operator*(APInt a, uint64_t RHS)
Definition APInt.h:2266
LLVM_ATTRIBUTE_ALWAYS_INLINE DynamicAPInt & operator+=(DynamicAPInt &A, int64_t B)
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
Definition STLExtras.h:633