[RISCV] Teach RISCVCodeGenPrepare to optimize (zext (abs(i32 X, i1 1))).
authorCraig Topper <craig.topper@sifive.com>
Mon, 25 Jul 2022 16:13:21 +0000 (09:13 -0700)
committerCraig Topper <craig.topper@sifive.com>
Mon, 25 Jul 2022 16:36:41 +0000 (09:36 -0700)
(abs(i32 X, i1 1) always produces a positive result. The 'i1 1'
means INT_MIN input produces poison. If the result is sign extended,
InstCombine will convert it to zext. This does not produce ideal
code for RISCV.

This patch reverses the zext back to sext which can be folded
into a subw or negw. Ideally we'd do this in SelectionDAG, but
we lose the INT_MIN poison flag when llvm.abs becomes ISD::ABS.

Reviewed By: reames

Differential Revision: https://reviews.llvm.org/D130412

llvm/lib/Target/RISCV/RISCVCodeGenPrepare.cpp
llvm/test/CodeGen/RISCV/iabs.ll

index b700a9e..a19253d 100644 (file)
@@ -18,6 +18,7 @@
 #include "llvm/ADT/Statistic.h"
 #include "llvm/Analysis/ValueTracking.h"
 #include "llvm/CodeGen/TargetPassConfig.h"
+#include "llvm/IR/PatternMatch.h"
 #include "llvm/InitializePasses.h"
 #include "llvm/Pass.h"
 
@@ -81,6 +82,20 @@ bool RISCVCodeGenPrepare::optimizeZExt(ZExtInst *ZExt) {
     return true;
   }
 
+  // Convert (zext (abs(i32 X, i1 1))) -> (sext (abs(i32 X, i1 1))). If abs of
+  // INT_MIN is poison, the sign bit is zero.
+  using namespace PatternMatch;
+  if (match(Src, m_Intrinsic<Intrinsic::abs>(m_Value(), m_One()))) {
+    auto *SExt = new SExtInst(Src, ZExt->getType(), "", ZExt);
+    SExt->takeName(ZExt);
+    SExt->setDebugLoc(ZExt->getDebugLoc());
+
+    ZExt->replaceAllUsesWith(SExt);
+    ZExt->eraseFromParent();
+    ++NumZExtToSExt;
+    return true;
+  }
+
   return false;
 }
 
index 749887d..8434f88 100644 (file)
@@ -694,3 +694,51 @@ define i128 @select_abs128(i128 %x) {
   ret i128 %3
 }
 
+define i64 @zext_abs32(i32 %x) {
+; RV32I-LABEL: zext_abs32:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    srai a1, a0, 31
+; RV32I-NEXT:    xor a0, a0, a1
+; RV32I-NEXT:    sub a0, a0, a1
+; RV32I-NEXT:    li a1, 0
+; RV32I-NEXT:    ret
+;
+; RV32ZBB-LABEL: zext_abs32:
+; RV32ZBB:       # %bb.0:
+; RV32ZBB-NEXT:    neg a1, a0
+; RV32ZBB-NEXT:    max a0, a0, a1
+; RV32ZBB-NEXT:    li a1, 0
+; RV32ZBB-NEXT:    ret
+;
+; RV32ZBT-LABEL: zext_abs32:
+; RV32ZBT:       # %bb.0:
+; RV32ZBT-NEXT:    srai a1, a0, 31
+; RV32ZBT-NEXT:    xor a0, a0, a1
+; RV32ZBT-NEXT:    sub a0, a0, a1
+; RV32ZBT-NEXT:    li a1, 0
+; RV32ZBT-NEXT:    ret
+;
+; RV64I-LABEL: zext_abs32:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    sraiw a1, a0, 31
+; RV64I-NEXT:    xor a0, a0, a1
+; RV64I-NEXT:    subw a0, a0, a1
+; RV64I-NEXT:    ret
+;
+; RV64ZBB-LABEL: zext_abs32:
+; RV64ZBB:       # %bb.0:
+; RV64ZBB-NEXT:    sext.w a0, a0
+; RV64ZBB-NEXT:    negw a1, a0
+; RV64ZBB-NEXT:    max a0, a0, a1
+; RV64ZBB-NEXT:    ret
+;
+; RV64ZBT-LABEL: zext_abs32:
+; RV64ZBT:       # %bb.0:
+; RV64ZBT-NEXT:    sraiw a1, a0, 31
+; RV64ZBT-NEXT:    xor a0, a0, a1
+; RV64ZBT-NEXT:    subw a0, a0, a1
+; RV64ZBT-NEXT:    ret
+  %abs = tail call i32 @llvm.abs.i32(i32 %x, i1 true)
+  %zext = zext i32 %abs to i64
+  ret i64 %zext
+}