Skip to content

Commit 8687f7c

Browse files
authored
[RISCV] Support constant hoisting of immediate store values (#96073)
Previously getIntImmInstCost only calculated the cost of materialising the argument of a store if it was the address. This means ConstantHoisting's transformation wouldn't kick in for cases like storing two values that require multiple instructions to materialise but where one can be cheaply generated from the other (e.g. by an addition). Two key changes were needed to avoid regressions when enabling this: * Allowing constant materialisation cost to be calculated assuming zeroes are free (as might happen if you had a 2*XLEN constant and one half is zero). * Avoiding constant hoisting if we have a misaligned store that's going to be a legalised to a sequence of narrower stores. I'm seeing cases where hoisting the constant ends up with worse codegen in that case. Out of caution and so as not to unexpectedly degrade other existing hoisting logic, FreeZeroes is used only for the new cost calculations for the load instruction. It would likely make sense to revisit this later.
1 parent 3fae555 commit 8687f7c

File tree

4 files changed

+95
-15
lines changed

4 files changed

+95
-15
lines changed

llvm/lib/Target/RISCV/MCTargetDesc/RISCVMatInt.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ InstSeq generateTwoRegInstSeq(int64_t Val, const MCSubtargetInfo &STI,
499499
}
500500

501501
int getIntMatCost(const APInt &Val, unsigned Size, const MCSubtargetInfo &STI,
502-
bool CompressionCost) {
502+
bool CompressionCost, bool FreeZeroes) {
503503
bool IsRV64 = STI.hasFeature(RISCV::Feature64Bit);
504504
bool HasRVC = CompressionCost && (STI.hasFeature(RISCV::FeatureStdExtC) ||
505505
STI.hasFeature(RISCV::FeatureStdExtZca));
@@ -510,10 +510,12 @@ int getIntMatCost(const APInt &Val, unsigned Size, const MCSubtargetInfo &STI,
510510
int Cost = 0;
511511
for (unsigned ShiftVal = 0; ShiftVal < Size; ShiftVal += PlatRegSize) {
512512
APInt Chunk = Val.ashr(ShiftVal).sextOrTrunc(PlatRegSize);
513+
if (FreeZeroes && Chunk.getSExtValue() == 0)
514+
continue;
513515
InstSeq MatSeq = generateInstSeq(Chunk.getSExtValue(), STI);
514516
Cost += getInstSeqCost(MatSeq, HasRVC);
515517
}
516-
return std::max(1, Cost);
518+
return std::max(FreeZeroes ? 0 : 1, Cost);
517519
}
518520

519521
OpndKind Inst::getOpndKind() const {

llvm/lib/Target/RISCV/MCTargetDesc/RISCVMatInt.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,13 @@ InstSeq generateTwoRegInstSeq(int64_t Val, const MCSubtargetInfo &STI,
7171
// If CompressionCost is true it will use a different cost calculation if RVC is
7272
// enabled. This should be used to compare two different sequences to determine
7373
// which is more compressible.
74+
//
75+
// If FreeZeroes is true, it will be assumed free to materialize any
76+
// XLen-sized chunks that are 0. This is appropriate to use in instances when
77+
// the zero register can be used, e.g. when estimating the cost of
78+
// materializing a value used by a particular operation.
7479
int getIntMatCost(const APInt &Val, unsigned Size, const MCSubtargetInfo &STI,
75-
bool CompressionCost = false);
80+
bool CompressionCost = false, bool FreeZeroes = false);
7681
} // namespace RISCVMatInt
7782
} // namespace llvm
7883
#endif

llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,11 @@ RISCVTTIImpl::getRISCVInstructionCost(ArrayRef<unsigned> OpCodes, MVT VT,
109109
return Cost;
110110
}
111111

112-
InstructionCost RISCVTTIImpl::getIntImmCost(const APInt &Imm, Type *Ty,
113-
TTI::TargetCostKind CostKind) {
112+
static InstructionCost getIntImmCostImpl(const DataLayout &DL,
113+
const RISCVSubtarget *ST,
114+
const APInt &Imm, Type *Ty,
115+
TTI::TargetCostKind CostKind,
116+
bool FreeZeroes) {
114117
assert(Ty->isIntegerTy() &&
115118
"getIntImmCost can only estimate cost of materialising integers");
116119

@@ -119,8 +122,13 @@ InstructionCost RISCVTTIImpl::getIntImmCost(const APInt &Imm, Type *Ty,
119122
return TTI::TCC_Free;
120123

121124
// Otherwise, we check how many instructions it will take to materialise.
122-
const DataLayout &DL = getDataLayout();
123-
return RISCVMatInt::getIntMatCost(Imm, DL.getTypeSizeInBits(Ty), *getST());
125+
return RISCVMatInt::getIntMatCost(Imm, DL.getTypeSizeInBits(Ty), *ST,
126+
/*CompressionCost=*/false, FreeZeroes);
127+
}
128+
129+
InstructionCost RISCVTTIImpl::getIntImmCost(const APInt &Imm, Type *Ty,
130+
TTI::TargetCostKind CostKind) {
131+
return getIntImmCostImpl(getDataLayout(), getST(), Imm, Ty, CostKind, false);
124132
}
125133

126134
// Look for patterns of shift followed by AND that can be turned into a pair of
@@ -172,11 +180,24 @@ InstructionCost RISCVTTIImpl::getIntImmCostInst(unsigned Opcode, unsigned Idx,
172180
// split up large offsets in GEP into better parts than ConstantHoisting
173181
// can.
174182
return TTI::TCC_Free;
175-
case Instruction::Store:
176-
// If the address is a constant, use the materialization cost.
177-
if (Idx == 1)
178-
return getIntImmCost(Imm, Ty, CostKind);
179-
return TTI::TCC_Free;
183+
case Instruction::Store: {
184+
// Use the materialization cost regardless of if it's the address or the
185+
// value that is constant, except for if the store is misaligned and
186+
// misaligned accesses are not legal (experience shows constant hoisting
187+
// can sometimes be harmful in such cases).
188+
if (Idx == 1 || !Inst)
189+
return getIntImmCostImpl(getDataLayout(), getST(), Imm, Ty, CostKind,
190+
/*FreeZeroes=*/true);
191+
192+
StoreInst *ST = cast<StoreInst>(Inst);
193+
if (!getTLI()->allowsMemoryAccessForAlignment(
194+
Ty->getContext(), DL, getTLI()->getValueType(DL, Ty),
195+
ST->getPointerAddressSpace(), ST->getAlign()))
196+
return TTI::TCC_Free;
197+
198+
return getIntImmCostImpl(getDataLayout(), getST(), Imm, Ty, CostKind,
199+
/*FreeZeroes=*/true);
200+
}
180201
case Instruction::Load:
181202
// If the address is a constant, use the materialization cost.
182203
return getIntImmCost(Imm, Ty, CostKind);

llvm/test/Transforms/ConstantHoisting/RISCV/immediates.ll

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,12 +211,64 @@ exit:
211211

212212
; Check that we use a common base for immediates needed by a store if the
213213
; constants require more than 1 instruction.
214-
; TODO: This doesn't trigger currently.
215214
define void @test20(ptr %p1, ptr %p2) {
216215
; CHECK-LABEL: test20
217-
; CHECK: store i32 15111111, ptr %p1
218-
; CHECK: store i32 15111112, ptr %p2
216+
; CHECK: %const = bitcast i32 15111111 to i32
217+
; CHECK: store i32 %const, ptr %p1, align 4
218+
; CHECK: %const_mat = add i32 %const, 1
219+
; CHECK: store i32 %const_mat, ptr %p2, align 4
219220
store i32 15111111, ptr %p1, align 4
220221
store i32 15111112, ptr %p2, align 4
221222
ret void
222223
}
224+
225+
define void @test21(ptr %p1, ptr %p2) {
226+
; CHECK-LABEL: define void @test21(
227+
; CHECK-SAME: ptr [[P1:%.*]], ptr [[P2:%.*]]) {
228+
; CHECK-NEXT: store i32 15111111, ptr [[P1]], align 1
229+
; CHECK-NEXT: store i32 15111112, ptr [[P2]], align 1
230+
; CHECK-NEXT: ret void
231+
;
232+
store i32 15111111, ptr %p1, align 1
233+
store i32 15111112, ptr %p2, align 1
234+
ret void
235+
}
236+
237+
; 0 immediates shouldn't be hoisted.
238+
define void @test22(ptr %p1, ptr %p2) {
239+
; CHECK-LABEL: define void @test22(
240+
; CHECK-SAME: ptr [[P1:%.*]], ptr [[P2:%.*]]) {
241+
; CHECK-NEXT: store i64 0, ptr [[P1]], align 8
242+
; CHECK-NEXT: store i64 -1, ptr [[P2]], align 8
243+
; CHECK-NEXT: ret void
244+
;
245+
store i64 0, ptr %p1, align 8
246+
store i64 -1, ptr %p2, align 8
247+
ret void
248+
}
249+
250+
; 0 immediates shouldn't be hoisted.
251+
define void @test23(ptr %p1, ptr %p2) {
252+
; CHECK-LABEL: define void @test23(
253+
; CHECK-SAME: ptr [[P1:%.*]], ptr [[P2:%.*]]) {
254+
; CHECK-NEXT: store i127 0, ptr [[P1]], align 8
255+
; CHECK-NEXT: store i127 -1, ptr [[P2]], align 8
256+
; CHECK-NEXT: ret void
257+
;
258+
store i127 0, ptr %p1, align 8
259+
store i127 -1, ptr %p2, align 8
260+
ret void
261+
}
262+
263+
; Hoisting doesn't happen for types that aren't legal.
264+
define void @test24(ptr %p1, ptr %p2) {
265+
; CHECK-LABEL: define void @test24(
266+
; CHECK-SAME: ptr [[P1:%.*]], ptr [[P2:%.*]]) {
267+
; CHECK-NEXT: store i128 15111111, ptr [[P1]], align 4
268+
; CHECK-NEXT: store i128 15111112, ptr [[P2]], align 4
269+
; CHECK-NEXT: ret void
270+
;
271+
store i128 15111111, ptr %p1, align 4
272+
store i128 15111112, ptr %p2, align 4
273+
ret void
274+
}

0 commit comments

Comments
 (0)