Peter Collingbourne b3c54914ef
InstCombine: Stop transforming EQ/NE of SHR to 0 to ULT/UGT if >1 use
This is a small code size optimization that lets us avoid both shifting
and comparing to a constant if we need the shifted value anyway. On most
architectures the zero comparison is cheaper than a constant comparison
(or free if the shift sets flags).

Although this change appears to remove the optimization entirely, we
continue to do this transform if there is one use because of the code
below the removed code that transforms the shift into an and, followed
by the PR10267 case in InstCombinerImpl::foldICmpAndConstConst that
transforms the and into a ult/ugt. Added a test case to verify this
explicitly.

Per [1] reduces clang .text size by 0.09% and dynamic instruction count
by 0.01%.

[1] https://llvm-compile-time-tracker.com/compare.php?from=1f38d49ebe96417e368a567efa4d650b8a9ac30f&to=0873787a12b8f2eab019d8211ace4bccc1807343&stat=size-text

Reviewers: nikic, dtcxzyw

Reviewed By: dtcxzyw

Pull Request: https://github.com/llvm/llvm-project/pull/168007
2025-11-17 19:39:20 -08:00

70 lines
2.6 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -O1 -S -mattr=+lzcnt | FileCheck %s
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
; This test ensures we are able to optimize the following loop to an llvm.abs
; followed by an llvm.ctlz.
; FIXME: LoopIdiom recongize is not forming llvm.ctlz.
; int ctlz_zero_check(int n)
; {
; n = n >= 0 ? n : -n;
; int i = 0;
; while(n) {
; n >>= 1;
; i++;
; }
; return i;
; }
define i32 @ctlz_loop_with_abs(i32 %n) {
; CHECK-LABEL: @ctlz_loop_with_abs(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TOBOOL_NOT1:%.*]] = icmp eq i32 [[N:%.*]], 0
; CHECK-NEXT: br i1 [[TOBOOL_NOT1]], label [[WHILE_END:%.*]], label [[WHILE_BODY_PREHEADER:%.*]]
; CHECK: while.body.preheader:
; CHECK-NEXT: [[TMP0:%.*]] = tail call i32 @llvm.abs.i32(i32 [[N]], i1 true)
; CHECK-NEXT: br label [[WHILE_BODY:%.*]]
; CHECK: while.body:
; CHECK-NEXT: [[N_ADDR_03:%.*]] = phi i32 [ [[TMP1:%.*]], [[WHILE_BODY]] ], [ [[TMP0]], [[WHILE_BODY_PREHEADER]] ]
; CHECK-NEXT: [[I_02:%.*]] = phi i32 [ [[INC:%.*]], [[WHILE_BODY]] ], [ 0, [[WHILE_BODY_PREHEADER]] ]
; CHECK-NEXT: [[TMP1]] = lshr i32 [[N_ADDR_03]], 1
; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_02]], 1
; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 [[TMP1]], 0
; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label [[WHILE_END]], label [[WHILE_BODY]]
; CHECK: while.end:
; CHECK-NEXT: [[I_0_LCSSA:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC]], [[WHILE_BODY]] ]
; CHECK-NEXT: ret i32 [[I_0_LCSSA]]
;
entry:
%cmp = icmp sge i32 %n, 0
br i1 %cmp, label %cond.true, label %cond.false
cond.true: ; preds = %entry
br label %cond.end
cond.false: ; preds = %entry
%sub = sub nsw i32 0, %n
br label %cond.end
cond.end: ; preds = %cond.false, %cond.true
%cond = phi i32 [ %n, %cond.true ], [ %sub, %cond.false ]
br label %while.cond
while.cond: ; preds = %while.body, %cond.end
%i.0 = phi i32 [ 0, %cond.end ], [ %inc, %while.body ]
%n.addr.0 = phi i32 [ %cond, %cond.end ], [ %shr, %while.body ]
%tobool = icmp ne i32 %n.addr.0, 0
br i1 %tobool, label %while.body, label %while.end
while.body: ; preds = %while.cond
%shr = ashr i32 %n.addr.0, 1
%inc = add nsw i32 %i.0, 1
br label %while.cond
while.end: ; preds = %while.cond
ret i32 %i.0
}