[llvm] [llubi] Implement intrinsics for integer arithmetic/bit manipulation (PR #193702)

Zhige Chen via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 23 02:28:21 PDT 2026


https://github.com/nofe1248 created https://github.com/llvm/llvm-project/pull/193702

This PR implements intrinsics for integer arithmetic (from the [Standard C/C++ Library Intrinsics](https://llvm.org/docs/LangRef.html#standard-c-c-library-intrinsics)), bit manipulation, overflow arithmetic, and saturation arithmetic.

>From e7443c51889a9ab12655b19177f7393fb322f4ce Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Thu, 23 Apr 2026 17:22:10 +0800
Subject: [PATCH] [llubi] Implement integer arithmetic, bit manipulation,
 overflow/saturation arithmetic intrinsics

---
 llvm/test/tools/llubi/intr_arith_overflow.ll |  36 +++
 llvm/test/tools/llubi/intr_arith_sat.ll      |  36 +++
 llvm/test/tools/llubi/intr_bit_manip.ll      |  51 ++++
 llvm/test/tools/llubi/intr_int_arith.ll      |  36 +++
 llvm/test/tools/llubi/intr_passthrough.ll    |  15 ++
 llvm/test/tools/llubi/intr_vscale.ll         |  13 +
 llvm/tools/llubi/lib/Interpreter.cpp         | 256 +++++++++++++++++++
 7 files changed, 443 insertions(+)
 create mode 100644 llvm/test/tools/llubi/intr_arith_overflow.ll
 create mode 100644 llvm/test/tools/llubi/intr_arith_sat.ll
 create mode 100644 llvm/test/tools/llubi/intr_bit_manip.ll
 create mode 100644 llvm/test/tools/llubi/intr_int_arith.ll
 create mode 100644 llvm/test/tools/llubi/intr_passthrough.ll
 create mode 100644 llvm/test/tools/llubi/intr_vscale.ll

diff --git a/llvm/test/tools/llubi/intr_arith_overflow.ll b/llvm/test/tools/llubi/intr_arith_overflow.ll
new file mode 100644
index 0000000000000..936d3aedd67ec
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_arith_overflow.ll
@@ -0,0 +1,36 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+    call {i32, i1} @llvm.sadd.with.overflow.i32(i32 10, i32 20)
+    call {i32, i1} @llvm.sadd.with.overflow.i32(i32 -2147483647, i32 -20)
+    call {i32, i1} @llvm.uadd.with.overflow.i32(i32 10, i32 20)
+    call {i32, i1} @llvm.uadd.with.overflow.i32(i32 4294967295, i32 20)
+
+    call {i32, i1} @llvm.ssub.with.overflow.i32(i32 10, i32 20)
+    call {i32, i1} @llvm.ssub.with.overflow.i32(i32 -2147483647, i32 20)
+    call {i32, i1} @llvm.usub.with.overflow.i32(i32 20, i32 10)
+    call {i32, i1} @llvm.usub.with.overflow.i32(i32 0, i32 20)
+
+    call {i32, i1} @llvm.smul.with.overflow.i32(i32 10, i32 20)
+    call {i32, i1} @llvm.smul.with.overflow.i32(i32 -4000000, i32 4000000)
+    call {i32, i1} @llvm.umul.with.overflow.i32(i32 20, i32 10)
+    call {i32, i1} @llvm.umul.with.overflow.i32(i32 4000000, i32 4000000)
+
+    ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT:   %1 = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 10, i32 20) => { i32 30, F }
+; CHECK-NEXT:   %2 = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 -2147483647, i32 -20) => { i32 2147483629, T }
+; CHECK-NEXT:   %3 = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 10, i32 20) => { i32 30, F }
+; CHECK-NEXT:   %4 = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 -1, i32 20) => { i32 19, T }
+; CHECK-NEXT:   %5 = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 10, i32 20) => { i32 -10, F }
+; CHECK-NEXT:   %6 = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 -2147483647, i32 20) => { i32 2147483629, T }
+; CHECK-NEXT:   %7 = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 20, i32 10) => { i32 10, F }
+; CHECK-NEXT:   %8 = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 0, i32 20) => { i32 -20, T }
+; CHECK-NEXT:   %9 = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 10, i32 20) => { i32 200, F }
+; CHECK-NEXT:   %10 = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 -4000000, i32 4000000) => { i32 -1246822400, T }
+; CHECK-NEXT:   %11 = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 20, i32 10) => { i32 200, F }
+; CHECK-NEXT:   %12 = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 4000000, i32 4000000) => { i32 1246822400, T }
+; CHECK-NEXT:   ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/intr_arith_sat.ll b/llvm/test/tools/llubi/intr_arith_sat.ll
new file mode 100644
index 0000000000000..ccc1c44a488ee
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_arith_sat.ll
@@ -0,0 +1,36 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+    call i32 @llvm.sadd.sat.i32(i32 10, i32 20)
+    call i32 @llvm.sadd.sat.i32(i32 -2147483647, i32 -20)
+    call i32 @llvm.uadd.sat.i32(i32 10, i32 20)
+    call i32 @llvm.uadd.sat.i32(i32 4294967295, i32 20)
+
+    call i32 @llvm.ssub.sat.i32(i32 10, i32 20)
+    call i32 @llvm.ssub.sat.i32(i32 -2147483647, i32 20)
+    call i32 @llvm.usub.sat.i32(i32 20, i32 10)
+    call i32 @llvm.usub.sat.i32(i32 0, i32 20)
+
+    call i32 @llvm.sshl.sat.i32(i32 10, i32 2)
+    call i32 @llvm.sshl.sat.i32(i32 10, i32 40)
+    call i32 @llvm.ushl.sat.i32(i32 10, i32 2)
+    call i32 @llvm.ushl.sat.i32(i32 10, i32 40)
+
+    ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT:   %1 = call i32 @llvm.sadd.sat.i32(i32 10, i32 20) => i32 30
+; CHECK-NEXT:   %2 = call i32 @llvm.sadd.sat.i32(i32 -2147483647, i32 -20) => i32 -2147483648
+; CHECK-NEXT:   %3 = call i32 @llvm.uadd.sat.i32(i32 10, i32 20) => i32 30
+; CHECK-NEXT:   %4 = call i32 @llvm.uadd.sat.i32(i32 -1, i32 20) => i32 -1
+; CHECK-NEXT:   %5 = call i32 @llvm.ssub.sat.i32(i32 10, i32 20) => i32 -10
+; CHECK-NEXT:   %6 = call i32 @llvm.ssub.sat.i32(i32 -2147483647, i32 20) => i32 -2147483648
+; CHECK-NEXT:   %7 = call i32 @llvm.usub.sat.i32(i32 20, i32 10) => i32 10
+; CHECK-NEXT:   %8 = call i32 @llvm.usub.sat.i32(i32 0, i32 20) => i32 0
+; CHECK-NEXT:   %9 = call i32 @llvm.sshl.sat.i32(i32 10, i32 2) => i32 40
+; CHECK-NEXT:   %10 = call i32 @llvm.sshl.sat.i32(i32 10, i32 40) => i32 2147483647
+; CHECK-NEXT:   %11 = call i32 @llvm.ushl.sat.i32(i32 10, i32 2) => i32 40
+; CHECK-NEXT:   %12 = call i32 @llvm.ushl.sat.i32(i32 10, i32 40) => i32 -1
+; CHECK-NEXT:   ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/intr_bit_manip.ll b/llvm/test/tools/llubi/intr_bit_manip.ll
new file mode 100644
index 0000000000000..4e8afff00d88d
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_bit_manip.ll
@@ -0,0 +1,51 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+    call i32 @llvm.bitreverse.i32(i32 42)
+
+    call i32 @llvm.bswap.i32(i32 12345678)
+
+    call i32 @llvm.ctpop.i32(i32 42)
+
+    call i32 @llvm.ctlz.i32(i32 4200, i1 false)
+    call i32 @llvm.ctlz.i32(i32 0, i1 true)
+
+    call i32 @llvm.cttz.i32(i32 4200, i1 false)
+    call i32 @llvm.cttz.i32(i32 0, i1 true)
+
+    call i8 @llvm.fshl.i8(i8 255, i8 0, i8 15)
+    call i8 @llvm.fshl.i8(i8 15, i8 15, i8 11)
+    call i8 @llvm.fshl.i8(i8 0, i8 255, i8 8)
+
+    call i8 @llvm.fshr.i8(i8 255, i8 0, i8 15)
+    call i8 @llvm.fshr.i8(i8 15, i8 15, i8 11)
+    call i8 @llvm.fshr.i8(i8 0, i8 255, i8 8)
+
+    call i8 @llvm.clmul.i8(i8 1, i8 2)
+    call i8 @llvm.clmul.i8(i8 5, i8 6)
+    call i8 @llvm.clmul.i8(i8 -4, i8 2)
+    call i8 @llvm.clmul.i8(i8 -4, i8 -5)
+
+    ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT:   %1 = call i32 @llvm.bitreverse.i32(i32 42) => i32 1409286144
+; CHECK-NEXT:   %2 = call i32 @llvm.bswap.i32(i32 12345678) => i32 1315027968
+; CHECK-NEXT:   %3 = call i32 @llvm.ctpop.i32(i32 42) => i32 3
+; CHECK-NEXT:   %4 = call i32 @llvm.ctlz.i32(i32 4200, i1 false) => i32 19
+; CHECK-NEXT:   %5 = call i32 @llvm.ctlz.i32(i32 0, i1 true) => poison
+; CHECK-NEXT:   %6 = call i32 @llvm.cttz.i32(i32 4200, i1 false) => i32 3
+; CHECK-NEXT:   %7 = call i32 @llvm.cttz.i32(i32 0, i1 true) => poison
+; CHECK-NEXT:   %8 = call i8 @llvm.fshl.i8(i8 -1, i8 0, i8 15) => i8 -128
+; CHECK-NEXT:   %9 = call i8 @llvm.fshl.i8(i8 15, i8 15, i8 11) => i8 120
+; CHECK-NEXT:   %10 = call i8 @llvm.fshl.i8(i8 0, i8 -1, i8 8) => i8 0
+; CHECK-NEXT:   %11 = call i8 @llvm.fshr.i8(i8 -1, i8 0, i8 15) => i8 -2
+; CHECK-NEXT:   %12 = call i8 @llvm.fshr.i8(i8 15, i8 15, i8 11) => i8 -31
+; CHECK-NEXT:   %13 = call i8 @llvm.fshr.i8(i8 0, i8 -1, i8 8) => i8 -1
+; CHECK-NEXT:   %14 = call i8 @llvm.clmul.i8(i8 1, i8 2) => i8 2
+; CHECK-NEXT:   %15 = call i8 @llvm.clmul.i8(i8 5, i8 6) => i8 30
+; CHECK-NEXT:   %16 = call i8 @llvm.clmul.i8(i8 -4, i8 2) => i8 -8
+; CHECK-NEXT:   %17 = call i8 @llvm.clmul.i8(i8 -4, i8 -5) => i8 -92
+; CHECK-NEXT:   ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/intr_int_arith.ll b/llvm/test/tools/llubi/intr_int_arith.ll
new file mode 100644
index 0000000000000..f00c051791a61
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_int_arith.ll
@@ -0,0 +1,36 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+    call i32 @llvm.abs.i32(i32 -42, i1 false)
+    call i32 @llvm.abs.i32(i32 -2147483648, i1 true)
+
+    call i32 @llvm.smin.i32(i32 -10, i32 -20)
+    call i32 @llvm.umin.i32(i32 10, i32 20)
+    call i32 @llvm.smax.i32(i32 -10, i32 -20)
+    call i32 @llvm.umax.i32(i32 10, i32 20)
+
+    call i2 @llvm.scmp.i32(i32 10, i32 20)
+    call i2 @llvm.scmp.i32(i32 -10, i32 -20)
+    call i2 @llvm.scmp.i32(i32 10, i32 10)
+    call i2 @llvm.ucmp.i32(i32 10, i32 20)
+    call i2 @llvm.ucmp.i32(i32 20, i32 10)
+    call i2 @llvm.ucmp.i32(i32 10, i32 10)
+
+    ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT:   %1 = call i32 @llvm.abs.i32(i32 -42, i1 false) => i32 42
+; CHECK-NEXT:   %2 = call i32 @llvm.abs.i32(i32 -2147483648, i1 true) => poison
+; CHECK-NEXT:   %3 = call i32 @llvm.smin.i32(i32 -10, i32 -20) => i32 -20
+; CHECK-NEXT:   %4 = call i32 @llvm.umin.i32(i32 10, i32 20) => i32 10
+; CHECK-NEXT:   %5 = call i32 @llvm.smax.i32(i32 -10, i32 -20) => i32 -10
+; CHECK-NEXT:   %6 = call i32 @llvm.umax.i32(i32 10, i32 20) => i32 20
+; CHECK-NEXT:   %7 = call i2 @llvm.scmp.i2.i32(i32 10, i32 20) => i2 -1
+; CHECK-NEXT:   %8 = call i2 @llvm.scmp.i2.i32(i32 -10, i32 -20) => i2 1
+; CHECK-NEXT:   %9 = call i2 @llvm.scmp.i2.i32(i32 10, i32 10) => i2 0
+; CHECK-NEXT:   %10 = call i2 @llvm.ucmp.i2.i32(i32 10, i32 20) => i2 -1
+; CHECK-NEXT:   %11 = call i2 @llvm.ucmp.i2.i32(i32 20, i32 10) => i2 1
+; CHECK-NEXT:   %12 = call i2 @llvm.ucmp.i2.i32(i32 10, i32 10) => i2 0
+; CHECK-NEXT:   ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/intr_passthrough.ll b/llvm/test/tools/llubi/intr_passthrough.ll
new file mode 100644
index 0000000000000..ad69f80395875
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_passthrough.ll
@@ -0,0 +1,15 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  call i32 @llvm.ssa.copy(i32 42)
+  call i64 @llvm.expect(i64 -42, i64 -42)
+  call i1 @llvm.expect.with.probability(i1 true, i1 true, double 0.5)
+  ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT:   %1 = call i32 @llvm.ssa.copy.i32(i32 42) => i32 42
+; CHECK-NEXT:   %2 = call i64 @llvm.expect.i64(i64 -42, i64 -42) => i64 -42
+; CHECK-NEXT:   %3 = call i1 @llvm.expect.with.probability.i1(i1 true, i1 true, double 5.000000e-01) => T
+; CHECK-NEXT:   ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/intr_vscale.ll b/llvm/test/tools/llubi/intr_vscale.ll
new file mode 100644
index 0000000000000..d48ccf6114132
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_vscale.ll
@@ -0,0 +1,13 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  call i32 @llvm.vscale.i32()
+  call i64 @llvm.vscale.i64()
+  ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT:   %1 = call i32 @llvm.vscale.i32() => i32 4
+; CHECK-NEXT:   %2 = call i64 @llvm.vscale.i64() => i64 4
+; CHECK-NEXT:   ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 1118320c7f254..94ac8a1cc0b70 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -110,6 +110,17 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
     });
   }
 
+  AnyValue
+  visitIntUnOpWithResult(Instruction &I,
+                         function_ref<AnyValue(const APInt &)> ScalarFn) {
+    return computeUnOp(I.getType(), getValue(I.getOperand(0)),
+                       [&](const AnyValue &Operand) -> AnyValue {
+                         if (Operand.isPoison())
+                           return AnyValue::poison();
+                         return ScalarFn(Operand.asInteger());
+                       });
+  }
+
   AnyValue computeBinOp(
       Type *Ty, const AnyValue &LHS, const AnyValue &RHS,
       function_ref<AnyValue(const AnyValue &, const AnyValue &)> ScalarFn) {
@@ -142,6 +153,76 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
     });
   }
 
+  AnyValue visitIntBinOpWithResult(
+      Instruction &I,
+      function_ref<AnyValue(const APInt &, const APInt &)> ScalarFn) {
+    return computeBinOp(
+        I.getType(), getValue(I.getOperand(0)), getValue(I.getOperand(1)),
+        [&](const AnyValue &LHS, const AnyValue &RHS) -> AnyValue {
+          if (LHS.isPoison() || RHS.isPoison())
+            return AnyValue::poison();
+          return ScalarFn(LHS.asInteger(), RHS.asInteger());
+        });
+  }
+
+  AnyValue
+  computeTriOp(Type *Ty, const AnyValue &Op1, const AnyValue &Op2,
+               const AnyValue &Op3,
+               function_ref<AnyValue(const AnyValue &, const AnyValue &,
+                                     const AnyValue &)>
+                   ScalarFn) {
+    if (Ty->isVectorTy()) {
+      auto &Op1Vec = Op1.asAggregate();
+      auto &Op2Vec = Op2.asAggregate();
+      auto &Op3Vec = Op3.asAggregate();
+      std::vector<AnyValue> ResVec;
+      ResVec.reserve(Op1Vec.size());
+      for (const auto &[ScalarOp1, ScalarOp2, ScalarOp3] :
+           zip(Op1Vec, Op2Vec, Op3Vec))
+        ResVec.push_back(ScalarFn(ScalarOp1, ScalarOp2, ScalarOp3));
+      return std::move(ResVec);
+    }
+    return ScalarFn(Op1, Op2, Op3);
+  }
+
+  void visitTriOp(Instruction &I,
+                  function_ref<AnyValue(const AnyValue &, const AnyValue &,
+                                        const AnyValue &)>
+                      ScalarFn) {
+    setResult(I, computeTriOp(I.getType(), getValue(I.getOperand(0)),
+                              getValue(I.getOperand(1)),
+                              getValue(I.getOperand(2)), ScalarFn));
+  }
+
+  void visitIntTriOp(
+      Instruction &I,
+      function_ref<AnyValue(const APInt &, const APInt &, const APInt &)>
+          ScalarFn) {
+    visitTriOp(I,
+               [&](const AnyValue &Op1, const AnyValue &Op2,
+                   const AnyValue &Op3) -> AnyValue {
+                 if (Op1.isPoison() || Op2.isPoison() || Op3.isPoison())
+                   return AnyValue::poison();
+                 return ScalarFn(Op1.asInteger(), Op2.asInteger(),
+                                 Op3.asInteger());
+               });
+  }
+
+  AnyValue visitIntTriOpWithResult(
+      Instruction &I,
+      function_ref<AnyValue(const APInt &, const APInt &, const APInt &)>
+          ScalarFn) {
+    return computeTriOp(
+        I.getType(), getValue(I.getOperand(0)), getValue(I.getOperand(1)),
+        getValue(I.getOperand(2)),
+        [&](const AnyValue &Op1, const AnyValue &Op2,
+            const AnyValue &Op3) -> AnyValue {
+          if (Op1.isPoison() || Op2.isPoison() || Op3.isPoison())
+            return AnyValue::poison();
+          return ScalarFn(Op1.asInteger(), Op2.asInteger(), Op3.asInteger());
+        });
+  }
+
   void jumpTo(Instruction &Terminator, BasicBlock *DestBB) {
     if (!Handler.onBBJump(Terminator, *DestBB)) {
       setFailed();
@@ -256,6 +337,14 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
     return IdxInt.sext(IndexBitWidth);
   }
 
+  // Helper function to convert BooleanKind to bool. Report an immediate UB if
+  // a poison is found.
+  bool getBooleanNonPoison(BooleanKind Boolean) {
+    if (Boolean == BooleanKind::Poison)
+      reportImmediateUB("Unexpected poison boolean value");
+    return Boolean == BooleanKind::True;
+  }
+
 public:
   InstExecutor(Context &C, EventHandler &H, Function &F,
                ArrayRef<AnyValue> Args, AnyValue &RetVal)
@@ -353,6 +442,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
 
   AnyValue callIntrinsic(CallBase &CB) {
     Intrinsic::ID IID = CB.getIntrinsicID();
+    Type *RetTy = CB.getType();
+
     switch (IID) {
     case Intrinsic::assume:
       switch (getValue(CB.getArgOperand(0)).asBoolean()) {
@@ -381,6 +472,171 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
       }
       return AnyValue();
     }
+    case Intrinsic::ssa_copy:
+    case Intrinsic::expect:
+    case Intrinsic::expect_with_probability:
+      return getValue(CB.getArgOperand(0));
+    case Intrinsic::donothing:
+      return AnyValue();
+    case Intrinsic::vscale:
+      return APInt(RetTy->getScalarSizeInBits(), Ctx.getVScale());
+    case Intrinsic::abs: {
+      const bool IsIntMinPoison =
+          getBooleanNonPoison(getValue(CB.getArgOperand(1)).asBoolean());
+      return visitIntUnOpWithResult(CB, [&](const APInt &Operand) -> AnyValue {
+        if (IsIntMinPoison && Operand.isMinSignedValue())
+          return AnyValue::poison();
+        return Operand.abs();
+      });
+    }
+    case Intrinsic::smax: {
+      return visitIntBinOpWithResult(
+          CB, [](const APInt &LHS, const APInt &RHS) -> AnyValue {
+            return APIntOps::smax(LHS, RHS);
+          });
+    }
+    case Intrinsic::smin: {
+      return visitIntBinOpWithResult(
+          CB, [](const APInt &LHS, const APInt &RHS) -> AnyValue {
+            return APIntOps::smin(LHS, RHS);
+          });
+    }
+    case Intrinsic::umax: {
+      return visitIntBinOpWithResult(
+          CB, [](const APInt &LHS, const APInt &RHS) -> AnyValue {
+            return APIntOps::umax(LHS, RHS);
+          });
+    }
+    case Intrinsic::umin: {
+      return visitIntBinOpWithResult(
+          CB, [](const APInt &LHS, const APInt &RHS) -> AnyValue {
+            return APIntOps::umin(LHS, RHS);
+          });
+    }
+    case Intrinsic::scmp:
+    case Intrinsic::ucmp: {
+      std::uint32_t BitWidth = RetTy->getScalarSizeInBits();
+      return visitIntBinOpWithResult(
+          CB, [&](const APInt &LHS, const APInt &RHS) -> AnyValue {
+            if (LHS == RHS)
+              return APInt::getZero(BitWidth);
+            if (IID == Intrinsic::scmp)
+              return LHS.slt(RHS) ? APInt::getAllOnes(BitWidth)
+                                  : APInt(BitWidth, 1);
+            return LHS.ult(RHS) ? APInt::getAllOnes(BitWidth)
+                                : APInt(BitWidth, 1);
+          });
+    }
+    case Intrinsic::bitreverse: {
+      return visitIntUnOpWithResult(CB, [](const APInt &Operand) -> AnyValue {
+        return Operand.reverseBits();
+      });
+    }
+    case Intrinsic::bswap: {
+      return visitIntUnOpWithResult(CB, [](const APInt &Operand) -> AnyValue {
+        return Operand.byteSwap();
+      });
+    }
+    case Intrinsic::ctpop: {
+      return visitIntUnOpWithResult(CB, [](const APInt &Operand) -> AnyValue {
+        return APInt(Operand.getBitWidth(), Operand.popcount());
+      });
+    }
+    case Intrinsic::ctlz:
+    case Intrinsic::cttz: {
+      const bool IsZeroPoison =
+          getBooleanNonPoison(getValue(CB.getArgOperand(1)).asBoolean());
+      return visitIntUnOpWithResult(CB, [&](const APInt &Operand) -> AnyValue {
+        if (IsZeroPoison && Operand.isZero())
+          return AnyValue::poison();
+        if (IID == Intrinsic::ctlz)
+          return APInt(Operand.getBitWidth(), Operand.countl_zero());
+        return APInt(Operand.getBitWidth(), Operand.countr_zero());
+      });
+    }
+    case Intrinsic::fshl:
+    case Intrinsic::fshr: {
+      return visitIntTriOpWithResult(
+          CB,
+          [IID](const APInt &Op1, const APInt &Op2,
+                const APInt &Op3) -> AnyValue {
+            const std::uint32_t BitWidth = Op1.getBitWidth();
+            const std::uint32_t ShiftAmount = Op3.urem(BitWidth);
+            const bool IsFShr = IID == Intrinsic::fshr;
+            const std::uint32_t LShrAmount =
+                IsFShr ? ShiftAmount : BitWidth - ShiftAmount;
+            const std::uint32_t ShlAmount =
+                !IsFShr ? ShiftAmount : BitWidth - ShiftAmount;
+            return Op1.shl(ShlAmount) | Op2.lshr(LShrAmount);
+          });
+    }
+    case Intrinsic::clmul: {
+      return visitIntBinOpWithResult(
+          CB, [](const APInt &LHS, const APInt &RHS) -> AnyValue {
+            return APIntOps::clmul(LHS, RHS);
+          });
+    }
+    case Intrinsic::sadd_with_overflow:
+    case Intrinsic::uadd_with_overflow:
+    case Intrinsic::ssub_with_overflow:
+    case Intrinsic::usub_with_overflow:
+    case Intrinsic::smul_with_overflow:
+    case Intrinsic::umul_with_overflow: {
+      return visitIntBinOpWithResult(
+          CB, [IID](const APInt &LHS, const APInt &RHS) -> AnyValue {
+            APInt Res;
+            bool Overflow = false;
+            switch (IID) {
+            case Intrinsic::sadd_with_overflow:
+              Res = LHS.sadd_ov(RHS, Overflow);
+              break;
+            case Intrinsic::uadd_with_overflow:
+              Res = LHS.uadd_ov(RHS, Overflow);
+              break;
+            case Intrinsic::ssub_with_overflow:
+              Res = LHS.ssub_ov(RHS, Overflow);
+              break;
+            case Intrinsic::usub_with_overflow:
+              Res = LHS.usub_ov(RHS, Overflow);
+              break;
+            case Intrinsic::smul_with_overflow:
+              Res = LHS.smul_ov(RHS, Overflow);
+              break;
+            case Intrinsic::umul_with_overflow:
+              Res = LHS.umul_ov(RHS, Overflow);
+              break;
+            default:
+              llvm_unreachable("Unexpected intrinsic ID");
+            }
+            return std::vector{AnyValue(Res), AnyValue::boolean(Overflow)};
+          });
+    }
+    case Intrinsic::sadd_sat:
+    case Intrinsic::uadd_sat:
+    case Intrinsic::ssub_sat:
+    case Intrinsic::usub_sat:
+    case Intrinsic::sshl_sat:
+    case Intrinsic::ushl_sat: {
+      return visitIntBinOpWithResult(
+          CB, [IID](const APInt &LHS, const APInt &RHS) -> AnyValue {
+            switch (IID) {
+            case Intrinsic::sadd_sat:
+              return LHS.sadd_sat(RHS);
+            case Intrinsic::uadd_sat:
+              return LHS.uadd_sat(RHS);
+            case Intrinsic::ssub_sat:
+              return LHS.ssub_sat(RHS);
+            case Intrinsic::usub_sat:
+              return LHS.usub_sat(RHS);
+            case Intrinsic::sshl_sat:
+              return LHS.sshl_sat(RHS);
+            case Intrinsic::ushl_sat:
+              return LHS.ushl_sat(RHS);
+            default:
+              llvm_unreachable("Unexpected intrinsic ID");
+            }
+          });
+    }
     default:
       Handler.onUnrecognizedInstruction(CB);
       setFailed();



More information about the llvm-commits mailing list