[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