Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit b6e102e

Browse files
authoredMay 20, 2024
[SCEV] Don't use non-deterministic constant folding for trip counts (#90942)
When calculating the exit count exhaustively, if any of the involved operations is non-deterministic, the exit count we compute at compile-time and the exit count at run-time may differ. Using these non-deterministic constant folding results is only correct if we actually replace all uses of the instruction with the value. SCEV (or its consumers) generally don't do this. Handle this by adding a new AllowNonDeterministic flag to the constant folding API, and disabling it in SCEV. If non-deterministic results are not allowed, do not fold FP lib calls in general, and FP operations returning NaNs in particular. This could be made more precise (some FP libcalls like fabs are fully deterministic), but I don't think this that precise handling here is worthwhile. Fixes the interesting part of #89885.
1 parent d316a0b commit b6e102e

File tree

4 files changed

+208
-16
lines changed

4 files changed

+208
-16
lines changed
 

‎llvm/include/llvm/Analysis/ConstantFolding.h

+12-3
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,16 @@ Constant *ConstantFoldConstant(const Constant *C, const DataLayout &DL,
6868
/// fold instructions like loads and stores, which have no constant expression
6969
/// form.
7070
///
71+
/// In some cases, constant folding may return one value chosen from a set of
72+
/// multiple legal return values. For example, the exact bit pattern of NaN
73+
/// results is not guaranteed. Using such a result is usually only valid if
74+
/// all uses of the original operation are replaced by the constant-folded
75+
/// result. The \p AllowNonDeterministic parameter controls whether this is
76+
/// allowed.
7177
Constant *ConstantFoldInstOperands(Instruction *I, ArrayRef<Constant *> Ops,
7278
const DataLayout &DL,
73-
const TargetLibraryInfo *TLI = nullptr);
79+
const TargetLibraryInfo *TLI = nullptr,
80+
bool AllowNonDeterministic = true);
7481

7582
/// Attempt to constant fold a compare instruction (icmp/fcmp) with the
7683
/// specified operands. Returns null or a constant expression of the specified
@@ -95,7 +102,8 @@ Constant *ConstantFoldBinaryOpOperands(unsigned Opcode, Constant *LHS,
95102
/// Returns null or a constant expression of the specified operands on failure.
96103
Constant *ConstantFoldFPInstOperands(unsigned Opcode, Constant *LHS,
97104
Constant *RHS, const DataLayout &DL,
98-
const Instruction *I);
105+
const Instruction *I,
106+
bool AllowNonDeterministic = true);
99107

100108
/// Attempt to flush float point constant according to denormal mode set in the
101109
/// instruction's parent function attributes. If so, return a zero with the
@@ -190,7 +198,8 @@ bool canConstantFoldCallTo(const CallBase *Call, const Function *F);
190198
/// with the specified arguments, returning null if unsuccessful.
191199
Constant *ConstantFoldCall(const CallBase *Call, Function *F,
192200
ArrayRef<Constant *> Operands,
193-
const TargetLibraryInfo *TLI = nullptr);
201+
const TargetLibraryInfo *TLI = nullptr,
202+
bool AllowNonDeterministic = true);
194203

195204
Constant *ConstantFoldBinaryIntrinsic(Intrinsic::ID ID, Constant *LHS,
196205
Constant *RHS, Type *Ty,

‎llvm/lib/Analysis/ConstantFolding.cpp

+40-11
Original file line numberDiff line numberDiff line change
@@ -992,7 +992,8 @@ Constant *SymbolicallyEvaluateGEP(const GEPOperator *GEP,
992992
Constant *ConstantFoldInstOperandsImpl(const Value *InstOrCE, unsigned Opcode,
993993
ArrayRef<Constant *> Ops,
994994
const DataLayout &DL,
995-
const TargetLibraryInfo *TLI) {
995+
const TargetLibraryInfo *TLI,
996+
bool AllowNonDeterministic) {
996997
Type *DestTy = InstOrCE->getType();
997998

998999
if (Instruction::isUnaryOp(Opcode))
@@ -1011,7 +1012,8 @@ Constant *ConstantFoldInstOperandsImpl(const Value *InstOrCE, unsigned Opcode,
10111012
// TODO: If a constant expression is being folded rather than an
10121013
// instruction, denormals will not be flushed/treated as zero
10131014
if (const auto *I = dyn_cast<Instruction>(InstOrCE)) {
1014-
return ConstantFoldFPInstOperands(Opcode, Ops[0], Ops[1], DL, I);
1015+
return ConstantFoldFPInstOperands(Opcode, Ops[0], Ops[1], DL, I,
1016+
AllowNonDeterministic);
10151017
}
10161018
}
10171019
return ConstantFoldBinaryOpOperands(Opcode, Ops[0], Ops[1], DL);
@@ -1053,7 +1055,8 @@ Constant *ConstantFoldInstOperandsImpl(const Value *InstOrCE, unsigned Opcode,
10531055
if (auto *F = dyn_cast<Function>(Ops.back())) {
10541056
const auto *Call = cast<CallBase>(InstOrCE);
10551057
if (canConstantFoldCallTo(Call, F))
1056-
return ConstantFoldCall(Call, F, Ops.slice(0, Ops.size() - 1), TLI);
1058+
return ConstantFoldCall(Call, F, Ops.slice(0, Ops.size() - 1), TLI,
1059+
AllowNonDeterministic);
10571060
}
10581061
return nullptr;
10591062
case Instruction::Select:
@@ -1114,8 +1117,8 @@ ConstantFoldConstantImpl(const Constant *C, const DataLayout &DL,
11141117
}
11151118

11161119
if (auto *CE = dyn_cast<ConstantExpr>(C)) {
1117-
if (Constant *Res =
1118-
ConstantFoldInstOperandsImpl(CE, CE->getOpcode(), Ops, DL, TLI))
1120+
if (Constant *Res = ConstantFoldInstOperandsImpl(
1121+
CE, CE->getOpcode(), Ops, DL, TLI, /*AllowNonDeterministic=*/true))
11191122
return Res;
11201123
return const_cast<Constant *>(C);
11211124
}
@@ -1183,8 +1186,10 @@ Constant *llvm::ConstantFoldConstant(const Constant *C, const DataLayout &DL,
11831186
Constant *llvm::ConstantFoldInstOperands(Instruction *I,
11841187
ArrayRef<Constant *> Ops,
11851188
const DataLayout &DL,
1186-
const TargetLibraryInfo *TLI) {
1187-
return ConstantFoldInstOperandsImpl(I, I->getOpcode(), Ops, DL, TLI);
1189+
const TargetLibraryInfo *TLI,
1190+
bool AllowNonDeterministic) {
1191+
return ConstantFoldInstOperandsImpl(I, I->getOpcode(), Ops, DL, TLI,
1192+
AllowNonDeterministic);
11881193
}
11891194

11901195
Constant *llvm::ConstantFoldCompareInstOperands(
@@ -1357,7 +1362,8 @@ Constant *llvm::FlushFPConstant(Constant *Operand, const Instruction *I,
13571362

13581363
Constant *llvm::ConstantFoldFPInstOperands(unsigned Opcode, Constant *LHS,
13591364
Constant *RHS, const DataLayout &DL,
1360-
const Instruction *I) {
1365+
const Instruction *I,
1366+
bool AllowNonDeterministic) {
13611367
if (Instruction::isBinaryOp(Opcode)) {
13621368
// Flush denormal inputs if needed.
13631369
Constant *Op0 = FlushFPConstant(LHS, I, /* IsOutput */ false);
@@ -1367,13 +1373,30 @@ Constant *llvm::ConstantFoldFPInstOperands(unsigned Opcode, Constant *LHS,
13671373
if (!Op1)
13681374
return nullptr;
13691375

1376+
// If nsz or an algebraic FMF flag is set, the result of the FP operation
1377+
// may change due to future optimization. Don't constant fold them if
1378+
// non-deterministic results are not allowed.
1379+
if (!AllowNonDeterministic)
1380+
if (auto *FP = dyn_cast_or_null<FPMathOperator>(I))
1381+
if (FP->hasNoSignedZeros() || FP->hasAllowReassoc() ||
1382+
FP->hasAllowContract() || FP->hasAllowReciprocal())
1383+
return nullptr;
1384+
13701385
// Calculate constant result.
13711386
Constant *C = ConstantFoldBinaryOpOperands(Opcode, Op0, Op1, DL);
13721387
if (!C)
13731388
return nullptr;
13741389

13751390
// Flush denormal output if needed.
1376-
return FlushFPConstant(C, I, /* IsOutput */ true);
1391+
C = FlushFPConstant(C, I, /* IsOutput */ true);
1392+
if (!C)
1393+
return nullptr;
1394+
1395+
// The precise NaN value is non-deterministic.
1396+
if (!AllowNonDeterministic && C->isNaN())
1397+
return nullptr;
1398+
1399+
return C;
13771400
}
13781401
// If instruction lacks a parent/function and the denormal mode cannot be
13791402
// determined, use the default (IEEE).
@@ -3401,7 +3424,8 @@ Constant *llvm::ConstantFoldBinaryIntrinsic(Intrinsic::ID ID, Constant *LHS,
34013424

34023425
Constant *llvm::ConstantFoldCall(const CallBase *Call, Function *F,
34033426
ArrayRef<Constant *> Operands,
3404-
const TargetLibraryInfo *TLI) {
3427+
const TargetLibraryInfo *TLI,
3428+
bool AllowNonDeterministic) {
34053429
if (Call->isNoBuiltin())
34063430
return nullptr;
34073431
if (!F->hasName())
@@ -3417,8 +3441,13 @@ Constant *llvm::ConstantFoldCall(const CallBase *Call, Function *F,
34173441
return nullptr;
34183442
}
34193443

3420-
StringRef Name = F->getName();
3444+
// Conservatively assume that floating-point libcalls may be
3445+
// non-deterministic.
34213446
Type *Ty = F->getReturnType();
3447+
if (!AllowNonDeterministic && Ty->isFPOrFPVectorTy())
3448+
return nullptr;
3449+
3450+
StringRef Name = F->getName();
34223451
if (auto *FVTy = dyn_cast<FixedVectorType>(Ty))
34233452
return ConstantFoldFixedVectorCall(
34243453
Name, IID, FVTy, Operands, F->getParent()->getDataLayout(), TLI, Call);

‎llvm/lib/Analysis/ScalarEvolution.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -9540,7 +9540,8 @@ static Constant *EvaluateExpression(Value *V, const Loop *L,
95409540
Operands[i] = C;
95419541
}
95429542

9543-
return ConstantFoldInstOperands(I, Operands, DL, TLI);
9543+
return ConstantFoldInstOperands(I, Operands, DL, TLI,
9544+
/*AllowNonDeterministic=*/false);
95449545
}
95459546

95469547

@@ -10031,7 +10032,8 @@ const SCEV *ScalarEvolution::computeSCEVAtScope(const SCEV *V, const Loop *L) {
1003110032

1003210033
Constant *C = nullptr;
1003310034
const DataLayout &DL = getDataLayout();
10034-
C = ConstantFoldInstOperands(I, Operands, DL, &TLI);
10035+
C = ConstantFoldInstOperands(I, Operands, DL, &TLI,
10036+
/*AllowNonDeterministic=*/false);
1003510037
if (!C)
1003610038
return V;
1003710039
return getSCEV(C);

‎llvm/test/Analysis/ScalarEvolution/exhaustive-trip-counts.ll

+152
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,156 @@ for.cond.cleanup:
2727
ret void
2828
}
2929

30+
; Do not compute exhaustive trip count based on FP libcalls, as their exact
31+
; return value may not be specified.
32+
define i64 @test_fp_libcall() {
33+
; CHECK-LABEL: 'test_fp_libcall'
34+
; CHECK-NEXT: Determining loop execution counts for: @test_fp_libcall
35+
; CHECK-NEXT: Loop %loop: Unpredictable backedge-taken count.
36+
; CHECK-NEXT: Loop %loop: Unpredictable constant max backedge-taken count.
37+
; CHECK-NEXT: Loop %loop: Unpredictable symbolic max backedge-taken count.
38+
;
39+
entry:
40+
br label %loop
41+
42+
loop:
43+
%iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
44+
%fv = phi double [ 1.000000e+00, %entry ], [ %fv.next, %loop ]
45+
call void @use(double %fv)
46+
%fv.next = call double @llvm.sin.f64(double %fv)
47+
%iv.next = add i64 %iv, 1
48+
%fcmp = fcmp une double %fv, 0x3FC6BA15EE8460B0
49+
br i1 %fcmp, label %loop, label %exit
50+
51+
exit:
52+
ret i64 %iv
53+
}
54+
55+
; Do not compute exhaustive trip count based on FP constant folding resulting
56+
; in NaN values, as we don't specify which NaN exactly is returned.
57+
define i64 @test_nan_sign() {
58+
; CHECK-LABEL: 'test_nan_sign'
59+
; CHECK-NEXT: Determining loop execution counts for: @test_nan_sign
60+
; CHECK-NEXT: Loop %loop: Unpredictable backedge-taken count.
61+
; CHECK-NEXT: Loop %loop: Unpredictable constant max backedge-taken count.
62+
; CHECK-NEXT: Loop %loop: Unpredictable symbolic max backedge-taken count.
63+
;
64+
entry:
65+
br label %loop
66+
67+
loop:
68+
%iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
69+
%fv = phi double [ -1.000000e+00, %entry ], [ %fv.next, %loop ]
70+
call void @use(double %fv)
71+
%a = fsub double %fv, 0x7F86C16C16C16C16
72+
%b = fadd double %a, %a
73+
%fv.next = fsub double %b, %a
74+
%iv.next = add i64 %iv, 1
75+
%fv.bc = bitcast double %fv to i64
76+
%icmp = icmp slt i64 %fv.bc, 0
77+
br i1 %icmp, label %loop, label %exit
78+
79+
exit:
80+
ret i64 %iv
81+
}
82+
83+
; Do not compute exhaustive trip count based on FP constant folding if the
84+
; involved operation has nsz or one of the algebraic FMF flags (reassoc, arcp,
85+
; contract) set. The examples in the following are dummies and don't illustrate
86+
; real cases where FMF transforms could cause issues.
87+
88+
define i64 @test_fp_nsz() {
89+
; CHECK-LABEL: 'test_fp_nsz'
90+
; CHECK-NEXT: Determining loop execution counts for: @test_fp_nsz
91+
; CHECK-NEXT: Loop %loop: Unpredictable backedge-taken count.
92+
; CHECK-NEXT: Loop %loop: Unpredictable constant max backedge-taken count.
93+
; CHECK-NEXT: Loop %loop: Unpredictable symbolic max backedge-taken count.
94+
;
95+
entry:
96+
br label %loop
97+
98+
loop:
99+
%iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
100+
%fv = phi double [ 1.000000e+00, %entry ], [ %fv.next, %loop ]
101+
call void @use(double %fv)
102+
%fv.next = fadd nsz double %fv, 1.0
103+
%iv.next = add i64 %iv, 1
104+
%fcmp = fcmp une double %fv, 100.0
105+
br i1 %fcmp, label %loop, label %exit
106+
107+
exit:
108+
ret i64 %iv
109+
}
110+
111+
define i64 @test_fp_reassoc() {
112+
; CHECK-LABEL: 'test_fp_reassoc'
113+
; CHECK-NEXT: Determining loop execution counts for: @test_fp_reassoc
114+
; CHECK-NEXT: Loop %loop: Unpredictable backedge-taken count.
115+
; CHECK-NEXT: Loop %loop: Unpredictable constant max backedge-taken count.
116+
; CHECK-NEXT: Loop %loop: Unpredictable symbolic max backedge-taken count.
117+
;
118+
entry:
119+
br label %loop
120+
121+
loop:
122+
%iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
123+
%fv = phi double [ 1.000000e+00, %entry ], [ %fv.next, %loop ]
124+
call void @use(double %fv)
125+
%fv.next = fadd reassoc double %fv, 1.0
126+
%iv.next = add i64 %iv, 1
127+
%fcmp = fcmp une double %fv, 100.0
128+
br i1 %fcmp, label %loop, label %exit
129+
130+
exit:
131+
ret i64 %iv
132+
}
133+
134+
define i64 @test_fp_arcp() {
135+
; CHECK-LABEL: 'test_fp_arcp'
136+
; CHECK-NEXT: Determining loop execution counts for: @test_fp_arcp
137+
; CHECK-NEXT: Loop %loop: Unpredictable backedge-taken count.
138+
; CHECK-NEXT: Loop %loop: Unpredictable constant max backedge-taken count.
139+
; CHECK-NEXT: Loop %loop: Unpredictable symbolic max backedge-taken count.
140+
;
141+
entry:
142+
br label %loop
143+
144+
loop:
145+
%iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
146+
%fv = phi double [ 1.000000e+00, %entry ], [ %fv.next, %loop ]
147+
call void @use(double %fv)
148+
%fv.next = fadd arcp double %fv, 1.0
149+
%iv.next = add i64 %iv, 1
150+
%fcmp = fcmp une double %fv, 100.0
151+
br i1 %fcmp, label %loop, label %exit
152+
153+
exit:
154+
ret i64 %iv
155+
}
156+
157+
define i64 @test_fp_contract() {
158+
; CHECK-LABEL: 'test_fp_contract'
159+
; CHECK-NEXT: Determining loop execution counts for: @test_fp_contract
160+
; CHECK-NEXT: Loop %loop: Unpredictable backedge-taken count.
161+
; CHECK-NEXT: Loop %loop: Unpredictable constant max backedge-taken count.
162+
; CHECK-NEXT: Loop %loop: Unpredictable symbolic max backedge-taken count.
163+
;
164+
entry:
165+
br label %loop
166+
167+
loop:
168+
%iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
169+
%fv = phi double [ 1.000000e+00, %entry ], [ %fv.next, %loop ]
170+
call void @use(double %fv)
171+
%fv.next = fadd contract double %fv, 1.0
172+
%iv.next = add i64 %iv, 1
173+
%fcmp = fcmp une double %fv, 100.0
174+
br i1 %fcmp, label %loop, label %exit
175+
176+
exit:
177+
ret i64 %iv
178+
}
179+
30180
declare void @dummy()
181+
declare void @use(double %i)
182+
declare double @llvm.sin.f64(double)

0 commit comments

Comments
 (0)
Failed to load comments.