Skip to content

Commit 16a0629

Browse files
authored
[ValueTracking] Compute known FPClass from signbit idiom (#80740)
This patch improves `computeKnownFPClass` by using context-sensitive information from `DomConditionCache`. The motivation of this patch is to optimize the following case found in [fmt/format.h](https://github.com/fmtlib/fmt/blob/e17bc67547a66cdd378ca6a90c56b865d30d6168/include/fmt/format.h#L3555-L3566): ``` define float @test(float %x, i1 %cond) { %i32 = bitcast float %x to i32 %cmp = icmp slt i32 %i32, 0 br i1 %cmp, label %if.then1, label %if.else if.then1: %fneg = fneg float %x br label %if.end if.else: br i1 %cond, label %if.then2, label %if.end if.then2: br label %if.end if.end: %value = phi float [ %fneg, %if.then1 ], [ %x, %if.then2 ], [ %x, %if.else ] %ret = call float @llvm.fabs.f32(float %value) ret float %ret } ``` We can prove the sign bit of %value is always zero. Then the fabs can be eliminated. This pattern also exists in cpython/duckdb/oiio/openexr. Compile-time impact: https://llvm-compile-time-tracker.com/compare.php?from=f82e0809ba12170e2f648f8a1ac01e78ef06c958&to=041218bf5491996edd828cc15b3aec5a59ddc636&stat=instructions:u |stage1-O3|stage1-ReleaseThinLTO|stage1-ReleaseLTO-g|stage1-O0-g|stage2-O3|stage2-O0-g|stage2-clang| |--|--|--|--|--|--|--| |-0.00%|+0.01%|+0.00%|-0.03%|+0.00%|+0.00%|+0.02%|
1 parent c5e1384 commit 16a0629

File tree

3 files changed

+130
-1
lines changed

3 files changed

+130
-1
lines changed

llvm/lib/Analysis/DomConditionCache.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ static void findAffectedValues(Value *Cond,
6666
// A > C3 && A < C4.
6767
if (match(A, m_Add(m_Value(X), m_ConstantInt())))
6868
AddAffected(X);
69+
// Handle icmp slt/sgt (bitcast X to int), 0/-1, which is supported by
70+
// computeKnownFPClass().
71+
if ((Pred == ICmpInst::ICMP_SLT || Pred == ICmpInst::ICMP_SGT) &&
72+
match(A, m_ElementWiseBitCast(m_Value(X))))
73+
Affected.push_back(X);
6974
}
7075
} else if (match(Cond, m_CombineOr(m_FCmp(Pred, m_Value(A), m_Constant()),
7176
m_Intrinsic<Intrinsic::is_fpclass>(

llvm/lib/Analysis/ValueTracking.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4272,7 +4272,7 @@ static void computeKnownFPClassFromCond(const Value *V, Value *Cond,
42724272
Value *LHS;
42734273
uint64_t ClassVal = 0;
42744274
const APFloat *CRHS;
4275-
// TODO: handle sign-bit check idiom
4275+
const APInt *RHS;
42764276
if (match(Cond, m_FCmp(Pred, m_Value(LHS), m_APFloat(CRHS)))) {
42774277
auto [CmpVal, MaskIfTrue, MaskIfFalse] = fcmpImpliesClass(
42784278
Pred, *CxtI->getParent()->getParent(), LHS, *CRHS, LHS != V);
@@ -4282,6 +4282,15 @@ static void computeKnownFPClassFromCond(const Value *V, Value *Cond,
42824282
m_Value(LHS), m_ConstantInt(ClassVal)))) {
42834283
FPClassTest Mask = static_cast<FPClassTest>(ClassVal);
42844284
KnownFromContext.knownNot(CondIsTrue ? ~Mask : Mask);
4285+
} else if (match(Cond, m_ICmp(Pred, m_ElementWiseBitCast(m_Value(LHS)),
4286+
m_APInt(RHS)))) {
4287+
bool TrueIfSigned;
4288+
if (!isSignBitCheck(Pred, *RHS, TrueIfSigned))
4289+
return;
4290+
if (TrueIfSigned == CondIsTrue)
4291+
KnownFromContext.signBitMustBeOne();
4292+
else
4293+
KnownFromContext.signBitMustBeZero();
42854294
}
42864295
}
42874296

llvm/test/Transforms/InstCombine/fpclass-from-dom-cond.ll

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,3 +320,118 @@ if.else:
320320
%ret = call i1 @llvm.is.fpclass.f32(float %x, i32 783)
321321
ret i1 %ret
322322
}
323+
324+
define float @test_signbit_check(float %x, i1 %cond) {
325+
; CHECK-LABEL: define float @test_signbit_check(
326+
; CHECK-SAME: float [[X:%.*]], i1 [[COND:%.*]]) {
327+
; CHECK-NEXT: [[I32:%.*]] = bitcast float [[X]] to i32
328+
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[I32]], 0
329+
; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN1:%.*]], label [[IF_ELSE:%.*]]
330+
; CHECK: if.then1:
331+
; CHECK-NEXT: [[FNEG:%.*]] = fneg float [[X]]
332+
; CHECK-NEXT: br label [[IF_END:%.*]]
333+
; CHECK: if.else:
334+
; CHECK-NEXT: br i1 [[COND]], label [[IF_THEN2:%.*]], label [[IF_END]]
335+
; CHECK: if.then2:
336+
; CHECK-NEXT: br label [[IF_END]]
337+
; CHECK: if.end:
338+
; CHECK-NEXT: [[VALUE:%.*]] = phi float [ [[FNEG]], [[IF_THEN1]] ], [ [[X]], [[IF_THEN2]] ], [ [[X]], [[IF_ELSE]] ]
339+
; CHECK-NEXT: ret float [[VALUE]]
340+
;
341+
%i32 = bitcast float %x to i32
342+
%cmp = icmp slt i32 %i32, 0
343+
br i1 %cmp, label %if.then1, label %if.else
344+
345+
if.then1:
346+
%fneg = fneg float %x
347+
br label %if.end
348+
349+
if.else:
350+
br i1 %cond, label %if.then2, label %if.end
351+
352+
if.then2:
353+
br label %if.end
354+
355+
if.end:
356+
%value = phi float [ %fneg, %if.then1 ], [ %x, %if.then2 ], [ %x, %if.else ]
357+
%ret = call float @llvm.fabs.f32(float %value)
358+
ret float %ret
359+
}
360+
361+
define float @test_signbit_check_fail(float %x, i1 %cond) {
362+
; CHECK-LABEL: define float @test_signbit_check_fail(
363+
; CHECK-SAME: float [[X:%.*]], i1 [[COND:%.*]]) {
364+
; CHECK-NEXT: [[I32:%.*]] = bitcast float [[X]] to i32
365+
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[I32]], 0
366+
; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN1:%.*]], label [[IF_ELSE:%.*]]
367+
; CHECK: if.then1:
368+
; CHECK-NEXT: [[FNEG:%.*]] = fneg float [[X]]
369+
; CHECK-NEXT: br label [[IF_END:%.*]]
370+
; CHECK: if.else:
371+
; CHECK-NEXT: br i1 [[COND]], label [[IF_THEN2:%.*]], label [[IF_END]]
372+
; CHECK: if.then2:
373+
; CHECK-NEXT: [[FNEG2:%.*]] = fneg float [[X]]
374+
; CHECK-NEXT: br label [[IF_END]]
375+
; CHECK: if.end:
376+
; CHECK-NEXT: [[VALUE:%.*]] = phi float [ [[FNEG]], [[IF_THEN1]] ], [ [[FNEG2]], [[IF_THEN2]] ], [ [[X]], [[IF_ELSE]] ]
377+
; CHECK-NEXT: [[RET:%.*]] = call float @llvm.fabs.f32(float [[VALUE]])
378+
; CHECK-NEXT: ret float [[RET]]
379+
;
380+
%i32 = bitcast float %x to i32
381+
%cmp = icmp slt i32 %i32, 0
382+
br i1 %cmp, label %if.then1, label %if.else
383+
384+
if.then1:
385+
%fneg = fneg float %x
386+
br label %if.end
387+
388+
if.else:
389+
br i1 %cond, label %if.then2, label %if.end
390+
391+
if.then2:
392+
%fneg2 = fneg float %x
393+
br label %if.end
394+
395+
if.end:
396+
%value = phi float [ %fneg, %if.then1 ], [ %fneg2, %if.then2 ], [ %x, %if.else ]
397+
%ret = call float @llvm.fabs.f32(float %value)
398+
ret float %ret
399+
}
400+
401+
define <2 x float> @test_signbit_check_wrong_type(<2 x float> %x, i1 %cond) {
402+
; CHECK-LABEL: define <2 x float> @test_signbit_check_wrong_type(
403+
; CHECK-SAME: <2 x float> [[X:%.*]], i1 [[COND:%.*]]) {
404+
; CHECK-NEXT: [[I32:%.*]] = bitcast <2 x float> [[X]] to i64
405+
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i64 [[I32]], 0
406+
; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN1:%.*]], label [[IF_ELSE:%.*]]
407+
; CHECK: if.then1:
408+
; CHECK-NEXT: [[FNEG:%.*]] = fneg <2 x float> [[X]]
409+
; CHECK-NEXT: br label [[IF_END:%.*]]
410+
; CHECK: if.else:
411+
; CHECK-NEXT: br i1 [[COND]], label [[IF_THEN2:%.*]], label [[IF_END]]
412+
; CHECK: if.then2:
413+
; CHECK-NEXT: br label [[IF_END]]
414+
; CHECK: if.end:
415+
; CHECK-NEXT: [[VALUE:%.*]] = phi <2 x float> [ [[FNEG]], [[IF_THEN1]] ], [ [[X]], [[IF_THEN2]] ], [ [[X]], [[IF_ELSE]] ]
416+
; CHECK-NEXT: [[RET:%.*]] = call <2 x float> @llvm.fabs.v2f32(<2 x float> [[VALUE]])
417+
; CHECK-NEXT: ret <2 x float> [[RET]]
418+
;
419+
%i32 = bitcast <2 x float> %x to i64
420+
%cmp = icmp slt i64 %i32, 0
421+
br i1 %cmp, label %if.then1, label %if.else
422+
423+
if.then1:
424+
%fneg = fneg <2 x float> %x
425+
br label %if.end
426+
427+
if.else:
428+
br i1 %cond, label %if.then2, label %if.end
429+
430+
if.then2:
431+
br label %if.end
432+
433+
if.end:
434+
%value = phi <2 x float> [ %fneg, %if.then1 ], [ %x, %if.then2 ], [ %x, %if.else ]
435+
%ret = call <2 x float> @llvm.fabs.v2f32(<2 x float> %value)
436+
ret <2 x float> %ret
437+
}

0 commit comments

Comments
 (0)