Skip to content

Commit 83914ee

Browse files
committed
[InstCombine] Remove side effect of replaced constrained intrinsics
If a constrained intrinsic call was replaced by some value, it was not removed in some cases. The dangling instruction resulted in useless instructions executed in runtime. It happened because constrained intrinsics usually have side effect, it is used to model the interaction with floating-point environment. In some cases it is correct behavior but often the side effect is actually absent or can be ignored. This change adds specific treatment of constrained intrinsics so that their side effect can be removed if it actually absents. Differential Revision: https://reviews.llvm.org/D118426
1 parent 2417de2 commit 83914ee

File tree

3 files changed

+139
-0
lines changed

3 files changed

+139
-0
lines changed

llvm/include/llvm/Analysis/InstructionSimplify.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,10 @@ Value *SimplifyBinOp(unsigned Opcode, Value *LHS, Value *RHS, FastMathFlags FMF,
299299
const SimplifyQuery &Q);
300300

301301
/// Given a callsite, fold the result or return null.
302+
///
303+
/// \note A call with declared side effect may be simplified into a value
304+
/// without such. It happens if simplification code deduces that side effect
305+
/// is actually absent.
302306
Value *SimplifyCall(CallBase *Call, const SimplifyQuery &Q);
303307

304308
/// Given an operand for a Freeze, see if we can fold the result.

llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,6 +1237,16 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
12371237
return NewCall;
12381238
}
12391239

1240+
// Unused constrained FP intrinsic calls may have declared side effect, which
1241+
// actually absent. If SimplifyCall returns a replacement for such call,
1242+
// assume side effect is absent and the call may be removed.
1243+
if (CI.use_empty() && isa<ConstrainedFPIntrinsic>(CI)) {
1244+
if (SimplifyCall(&CI, SQ.getWithInstruction(&CI))) {
1245+
eraseInstFromFunction(CI);
1246+
return nullptr;
1247+
}
1248+
}
1249+
12401250
Intrinsic::ID IID = II->getIntrinsicID();
12411251
switch (IID) {
12421252
case Intrinsic::objectsize:
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2+
; RUN: opt -S -instcombine %s | FileCheck %s
3+
4+
; Treatment of operation with unused result.
5+
6+
; If operation does not raise exceptions, it may be removed even in strict mode.
7+
define float @f_unused_precise() #0 {
8+
; CHECK-LABEL: @f_unused_precise(
9+
; CHECK-NEXT: entry:
10+
; CHECK-NEXT: ret float 1.000000e+00
11+
;
12+
entry:
13+
%result = call float @llvm.experimental.constrained.fadd.f32(float 1.0, float 1.0, metadata !"round.upward", metadata !"fpexcept.strict") #0
14+
ret float 1.0
15+
}
16+
17+
; If operation raises exceptions, it cannot be removed in strict mode.
18+
define float @f_unused_strict() #0 {
19+
; CHECK-LABEL: @f_unused_strict(
20+
; CHECK-NEXT: entry:
21+
; CHECK-NEXT: [[RESULT:%.*]] = call float @llvm.experimental.constrained.fdiv.f32(float 1.000000e+00, float 3.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0:[0-9]+]]
22+
; CHECK-NEXT: ret float 1.000000e+00
23+
;
24+
entry:
25+
%result = call float @llvm.experimental.constrained.fdiv.f32(float 1.0, float 3.0, metadata !"round.tonearest", metadata !"fpexcept.strict") #0
26+
ret float 1.0
27+
}
28+
29+
; If operation raises exceptions, it can be removed in non-strict mode.
30+
define float @f_unused_ignore() #0 {
31+
; CHECK-LABEL: @f_unused_ignore(
32+
; CHECK-NEXT: entry:
33+
; CHECK-NEXT: ret float 1.000000e+00
34+
;
35+
entry:
36+
%result = call float @llvm.experimental.constrained.fdiv.f32(float 1.0, float 3.0, metadata !"round.towardzero", metadata !"fpexcept.ignore") #0
37+
ret float 1.0
38+
}
39+
40+
; If operation raises exceptions, it can be removed in non-strict mode even if rounding mode is dynamic.
41+
define float @f_unused_dynamic_ignore() #0 {
42+
; CHECK-LABEL: @f_unused_dynamic_ignore(
43+
; CHECK-NEXT: entry:
44+
; CHECK-NEXT: ret float 1.000000e+00
45+
;
46+
entry:
47+
%result = call float @llvm.experimental.constrained.fdiv.f32(float 1.0, float 3.0, metadata !"round.dynamic", metadata !"fpexcept.ignore") #0
48+
ret float 1.0
49+
}
50+
51+
; If operation raises exceptions, it can be removed in "maytrap" mode.
52+
define float @f_unused_maytrap() #0 {
53+
; CHECK-LABEL: @f_unused_maytrap(
54+
; CHECK-NEXT: entry:
55+
; CHECK-NEXT: ret float 1.000000e+00
56+
;
57+
entry:
58+
%result = call float @llvm.experimental.constrained.fdiv.f32(float 1.0, float 3.0, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #0
59+
ret float 1.0
60+
}
61+
62+
; Constant evaluation.
63+
64+
; If operation does not raise exceptions, it may be folded even in strict mode.
65+
define float @f_eval_precise() #0 {
66+
; CHECK-LABEL: @f_eval_precise(
67+
; CHECK-NEXT: entry:
68+
; CHECK-NEXT: ret float 2.000000e+00
69+
;
70+
entry:
71+
%result = call float @llvm.experimental.constrained.fadd.f32(float 1.0, float 1.0, metadata !"round.upward", metadata !"fpexcept.strict") #0
72+
ret float %result
73+
}
74+
75+
; If operation raises exceptions, it cannot be folded in strict mode.
76+
define float @f_eval_strict() #0 {
77+
; CHECK-LABEL: @f_eval_strict(
78+
; CHECK-NEXT: entry:
79+
; CHECK-NEXT: [[RESULT:%.*]] = call float @llvm.experimental.constrained.fdiv.f32(float 1.000000e+00, float 3.000000e+00, metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR0]]
80+
; CHECK-NEXT: ret float [[RESULT]]
81+
;
82+
entry:
83+
%result = call float @llvm.experimental.constrained.fdiv.f32(float 1.0, float 3.0, metadata !"round.upward", metadata !"fpexcept.strict") #0
84+
ret float %result
85+
}
86+
87+
; If operation raises exceptions, it can be folded in non-strict mode.
88+
define float @f_eval_ignore() #0 {
89+
; CHECK-LABEL: @f_eval_ignore(
90+
; CHECK-NEXT: entry:
91+
; CHECK-NEXT: ret float 0x3FD5555540000000
92+
;
93+
entry:
94+
%result = call float @llvm.experimental.constrained.fdiv.f32(float 1.0, float 3.0, metadata !"round.downward", metadata !"fpexcept.ignore") #0
95+
ret float %result
96+
}
97+
98+
; if result is imprecise, it cannot be folded if rounding mode is dynamic.
99+
define float @f_eval_dynamic_ignore() #0 {
100+
; CHECK-LABEL: @f_eval_dynamic_ignore(
101+
; CHECK-NEXT: entry:
102+
; CHECK-NEXT: [[RESULT:%.*]] = call float @llvm.experimental.constrained.fdiv.f32(float 1.000000e+00, float 3.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.ignore") #[[ATTR0]]
103+
; CHECK-NEXT: ret float [[RESULT]]
104+
;
105+
entry:
106+
%result = call float @llvm.experimental.constrained.fdiv.f32(float 1.0, float 3.0, metadata !"round.dynamic", metadata !"fpexcept.ignore") #0
107+
ret float %result
108+
}
109+
110+
; If result is imprecise and rounding mode is not dynamic, operation can be folded in "maytrap" mode.
111+
define float @f_eval_maytrap() #0 {
112+
; CHECK-LABEL: @f_eval_maytrap(
113+
; CHECK-NEXT: entry:
114+
; CHECK-NEXT: ret float 0x3FD5555560000000
115+
;
116+
entry:
117+
%result = call float @llvm.experimental.constrained.fdiv.f32(float 1.0, float 3.0, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #0
118+
ret float %result
119+
}
120+
121+
122+
declare float @llvm.experimental.constrained.fadd.f32(float, float, metadata, metadata)
123+
declare float @llvm.experimental.constrained.fdiv.f32(float, float, metadata, metadata)
124+
125+
attributes #0 = { strictfp }

0 commit comments

Comments
 (0)