Skip to content

Commit 4f9aad9

Browse files
committed
InstCombine: Fold ldexp(ldexp(x, a), b) -> ldexp(x, a + b)
The problem here is overflow or underflow which would have occurred in the inner operation, which the exponent offsetting avoids. We can do this if we know the two exponents are in the same direction, or reassoc flags allow unsafe reassociates.
1 parent d362aab commit 4f9aad9

File tree

2 files changed

+63
-19
lines changed

2 files changed

+63
-19
lines changed

llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,19 @@ static std::optional<bool> getKnownSign(Value *Op, Instruction *CxtI,
10261026
ICmpInst::ICMP_SLT, Op, Constant::getNullValue(Op->getType()), CxtI, DL);
10271027
}
10281028

1029+
/// Return true if two values \p Op0 and \p Op1 are known to have the same sign.
1030+
static bool signBitMustBeTheSame(Value *Op0, Value *Op1, Instruction *CxtI,
1031+
const DataLayout &DL, AssumptionCache *AC,
1032+
DominatorTree *DT) {
1033+
std::optional<bool> Known1 = getKnownSign(Op1, CxtI, DL, AC, DT);
1034+
if (!Known1)
1035+
return false;
1036+
std::optional<bool> Known0 = getKnownSign(Op0, CxtI, DL, AC, DT);
1037+
if (!Known0)
1038+
return false;
1039+
return *Known0 == *Known1;
1040+
}
1041+
10291042
/// Try to canonicalize min/max(X + C0, C1) as min/max(X, C1 - C0) + C0. This
10301043
/// can trigger other combines.
10311044
static Instruction *moveAddAfterMinMax(IntrinsicInst *II,
@@ -2358,6 +2371,42 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
23582371
}
23592372
break;
23602373
}
2374+
case Intrinsic::ldexp: {
2375+
// ldexp(ldexp(x, a), b) -> ldexp(x, a + b)
2376+
//
2377+
// The danger is if the first ldexp would overflow to infinity or underflow
2378+
// to zero, but the combined exponent avoids it. We ignore this with
2379+
// reassoc.
2380+
//
2381+
// It's also safe to fold if we know both exponents are >= 0 or <= 0 since
2382+
// it would just double down on the overflow/underflow which would occur
2383+
// anyway.
2384+
//
2385+
// TODO: Could do better if we had range tracking for the input value
2386+
// exponent. Also could broaden sign check to cover == 0 case.
2387+
Value *Src = II->getArgOperand(0);
2388+
Value *Exp = II->getArgOperand(1);
2389+
Value *InnerSrc;
2390+
Value *InnerExp;
2391+
if (match(Src, m_OneUse(m_Intrinsic<Intrinsic::ldexp>(
2392+
m_Value(InnerSrc), m_Value(InnerExp)))) &&
2393+
Exp->getType() == InnerExp->getType()) {
2394+
FastMathFlags FMF = II->getFastMathFlags();
2395+
FastMathFlags InnerFlags = cast<FPMathOperator>(Src)->getFastMathFlags();
2396+
2397+
if ((FMF.allowReassoc() && InnerFlags.allowReassoc()) ||
2398+
signBitMustBeTheSame(Exp, InnerExp, II, DL, &AC, &DT)) {
2399+
// TODO: Add nsw/nuw probably safe if integer type exceeds exponent
2400+
// width.
2401+
Value *NewExp = Builder.CreateAdd(InnerExp, Exp);
2402+
II->setArgOperand(1, NewExp);
2403+
II->setFastMathFlags(InnerFlags); // Or the inner flags.
2404+
return replaceOperand(*II, 0, InnerSrc);
2405+
}
2406+
}
2407+
2408+
break;
2409+
}
23612410
case Intrinsic::ptrauth_auth:
23622411
case Intrinsic::ptrauth_resign: {
23632412
// (sign|resign) + (auth|resign) can be folded by omitting the middle

llvm/test/Transforms/InstCombine/ldexp.ll

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -368,8 +368,8 @@ define float @ldexp_ldexp_reassoc(float %x, i32 %a, i32 %b) {
368368
define float @ldexp_reassoc_ldexp_reassoc(float %x, i32 %a, i32 %b) {
369369
; CHECK-LABEL: define float @ldexp_reassoc_ldexp_reassoc
370370
; CHECK-SAME: (float [[X:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) {
371-
; CHECK-NEXT: [[LDEXP0:%.*]] = call reassoc float @llvm.ldexp.f32.i32(float [[X]], i32 [[A]])
372-
; CHECK-NEXT: [[LDEXP1:%.*]] = call reassoc float @llvm.ldexp.f32.i32(float [[LDEXP0]], i32 [[B]])
371+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[A]], [[B]]
372+
; CHECK-NEXT: [[LDEXP1:%.*]] = call reassoc float @llvm.ldexp.f32.i32(float [[X]], i32 [[TMP1]])
373373
; CHECK-NEXT: ret float [[LDEXP1]]
374374
;
375375
%ldexp0 = call reassoc float @llvm.ldexp.f32.i32(float %x, i32 %a)
@@ -381,8 +381,8 @@ define float @ldexp_reassoc_ldexp_reassoc(float %x, i32 %a, i32 %b) {
381381
define float @ldexp_reassoc_ldexp_reassoc_preserve_flags(float %x, i32 %a, i32 %b) {
382382
; CHECK-LABEL: define float @ldexp_reassoc_ldexp_reassoc_preserve_flags
383383
; CHECK-SAME: (float [[X:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) {
384-
; CHECK-NEXT: [[LDEXP0:%.*]] = call reassoc ninf float @llvm.ldexp.f32.i32(float [[X]], i32 [[A]])
385-
; CHECK-NEXT: [[LDEXP1:%.*]] = call reassoc nnan float @llvm.ldexp.f32.i32(float [[LDEXP0]], i32 [[B]])
384+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[A]], [[B]]
385+
; CHECK-NEXT: [[LDEXP1:%.*]] = call reassoc nnan ninf float @llvm.ldexp.f32.i32(float [[X]], i32 [[TMP1]])
386386
; CHECK-NEXT: ret float [[LDEXP1]]
387387
;
388388
%ldexp0 = call reassoc ninf float @llvm.ldexp.f32.i32(float %x, i32 %a)
@@ -393,8 +393,8 @@ define float @ldexp_reassoc_ldexp_reassoc_preserve_flags(float %x, i32 %a, i32 %
393393
define <2 x float> @ldexp_reassoc_ldexp_reassoc_vec(<2 x float> %x, <2 x i32> %a, <2 x i32> %b) {
394394
; CHECK-LABEL: define <2 x float> @ldexp_reassoc_ldexp_reassoc_vec
395395
; CHECK-SAME: (<2 x float> [[X:%.*]], <2 x i32> [[A:%.*]], <2 x i32> [[B:%.*]]) {
396-
; CHECK-NEXT: [[LDEXP0:%.*]] = call reassoc <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> [[X]], <2 x i32> [[A]])
397-
; CHECK-NEXT: [[LDEXP1:%.*]] = call reassoc <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> [[LDEXP0]], <2 x i32> [[B]])
396+
; CHECK-NEXT: [[TMP1:%.*]] = add <2 x i32> [[A]], [[B]]
397+
; CHECK-NEXT: [[LDEXP1:%.*]] = call reassoc <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> [[X]], <2 x i32> [[TMP1]])
398398
; CHECK-NEXT: ret <2 x float> [[LDEXP1]]
399399
;
400400
%ldexp0 = call reassoc <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> %x, <2 x i32> %a)
@@ -432,8 +432,7 @@ define float @ldexp_ldexp_different_exp_type(float %x, i32 %a, i64 %b) {
432432
define float @ldexp_ldexp_constants(float %x) {
433433
; CHECK-LABEL: define float @ldexp_ldexp_constants
434434
; CHECK-SAME: (float [[X:%.*]]) {
435-
; CHECK-NEXT: [[LDEXP0:%.*]] = call reassoc float @llvm.ldexp.f32.i32(float [[X]], i32 8)
436-
; CHECK-NEXT: [[LDEXP1:%.*]] = call reassoc float @llvm.ldexp.f32.i32(float [[LDEXP0]], i32 24)
435+
; CHECK-NEXT: [[LDEXP1:%.*]] = call reassoc float @llvm.ldexp.f32.i32(float [[X]], i32 32)
437436
; CHECK-NEXT: ret float [[LDEXP1]]
438437
;
439438
%ldexp0 = call reassoc float @llvm.ldexp.f32.i32(float %x, i32 8)
@@ -444,8 +443,7 @@ define float @ldexp_ldexp_constants(float %x) {
444443
define float @ldexp_ldexp_opposite_constants(float %x) {
445444
; CHECK-LABEL: define float @ldexp_ldexp_opposite_constants
446445
; CHECK-SAME: (float [[X:%.*]]) {
447-
; CHECK-NEXT: [[LDEXP0:%.*]] = call reassoc float @llvm.ldexp.f32.i32(float [[X]], i32 8)
448-
; CHECK-NEXT: [[LDEXP1:%.*]] = call reassoc float @llvm.ldexp.f32.i32(float [[LDEXP0]], i32 -8)
446+
; CHECK-NEXT: [[LDEXP1:%.*]] = call reassoc float @llvm.ldexp.f32.i32(float [[X]], i32 0)
449447
; CHECK-NEXT: ret float [[LDEXP1]]
450448
;
451449
%ldexp0 = call reassoc float @llvm.ldexp.f32.i32(float %x, i32 8)
@@ -456,9 +454,7 @@ define float @ldexp_ldexp_opposite_constants(float %x) {
456454
define float @ldexp_ldexp_negated_variable_reassoc(float %x, i32 %a) {
457455
; CHECK-LABEL: define float @ldexp_ldexp_negated_variable_reassoc
458456
; CHECK-SAME: (float [[X:%.*]], i32 [[A:%.*]]) {
459-
; CHECK-NEXT: [[LDEXP0:%.*]] = call reassoc float @llvm.ldexp.f32.i32(float [[X]], i32 [[A]])
460-
; CHECK-NEXT: [[NEG_A:%.*]] = sub i32 0, [[A]]
461-
; CHECK-NEXT: [[LDEXP1:%.*]] = call reassoc float @llvm.ldexp.f32.i32(float [[LDEXP0]], i32 [[NEG_A]])
457+
; CHECK-NEXT: [[LDEXP1:%.*]] = call reassoc float @llvm.ldexp.f32.i32(float [[X]], i32 0)
462458
; CHECK-NEXT: ret float [[LDEXP1]]
463459
;
464460
%ldexp0 = call reassoc float @llvm.ldexp.f32.i32(float %x, i32 %a)
@@ -514,8 +510,8 @@ define float @ldexp_ldexp_both_exp_known_positive(float %x, i32 %a.arg, i32 %b.a
514510
; CHECK-SAME: (float [[X:%.*]], i32 [[A_ARG:%.*]], i32 [[B_ARG:%.*]]) {
515511
; CHECK-NEXT: [[A:%.*]] = and i32 [[A_ARG]], 127
516512
; CHECK-NEXT: [[B:%.*]] = and i32 [[B_ARG]], 127
517-
; CHECK-NEXT: [[LDEXP0:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 [[A]])
518-
; CHECK-NEXT: [[LDEXP1:%.*]] = call float @llvm.ldexp.f32.i32(float [[LDEXP0]], i32 [[B]])
513+
; CHECK-NEXT: [[TMP1:%.*]] = add nuw nsw i32 [[A]], [[B]]
514+
; CHECK-NEXT: [[LDEXP1:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 [[TMP1]])
519515
; CHECK-NEXT: ret float [[LDEXP1]]
520516
;
521517
%a = and i32 %a.arg, 127
@@ -530,8 +526,8 @@ define float @ldexp_ldexp_both_exp_known_negative(float %x, ptr %a.ptr, ptr %b.p
530526
; CHECK-SAME: (float [[X:%.*]], ptr [[A_PTR:%.*]], ptr [[B_PTR:%.*]]) {
531527
; CHECK-NEXT: [[A:%.*]] = load i32, ptr [[A_PTR]], align 4, !range [[RNG0:![0-9]+]]
532528
; CHECK-NEXT: [[B:%.*]] = load i32, ptr [[B_PTR]], align 4, !range [[RNG0]]
533-
; CHECK-NEXT: [[LDEXP0:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 [[A]])
534-
; CHECK-NEXT: [[LDEXP1:%.*]] = call float @llvm.ldexp.f32.i32(float [[LDEXP0]], i32 [[B]])
529+
; CHECK-NEXT: [[TMP1:%.*]] = add nsw i32 [[A]], [[B]]
530+
; CHECK-NEXT: [[LDEXP1:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 [[TMP1]])
535531
; CHECK-NEXT: ret float [[LDEXP1]]
536532
;
537533
%a = load i32, ptr %a.ptr, !range !0
@@ -576,8 +572,7 @@ define float @ldexp_ldexp_exp_known_positive_and_negative(float %x, ptr %a.ptr,
576572
define float @ldexp_reassoc_ldexp_reassoc_0(float %x, i32 %y) {
577573
; CHECK-LABEL: define float @ldexp_reassoc_ldexp_reassoc_0
578574
; CHECK-SAME: (float [[X:%.*]], i32 [[Y:%.*]]) {
579-
; CHECK-NEXT: [[LDEXP0:%.*]] = call reassoc float @llvm.ldexp.f32.i32(float [[X]], i32 0)
580-
; CHECK-NEXT: [[LDEXP1:%.*]] = call reassoc float @llvm.ldexp.f32.i32(float [[LDEXP0]], i32 [[Y]])
575+
; CHECK-NEXT: [[LDEXP1:%.*]] = call reassoc float @llvm.ldexp.f32.i32(float [[X]], i32 [[Y]])
581576
; CHECK-NEXT: ret float [[LDEXP1]]
582577
;
583578
%ldexp0 = call reassoc float @llvm.ldexp.f32.i32(float %x, i32 0)

0 commit comments

Comments
 (0)