[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 04:18:28 PDT 2026
https://github.com/nofe1248 updated https://github.com/llvm/llvm-project/pull/193702
>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 1/4] [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();
>From 4ab4679cbbd4ebf8872ef3a0c837a9e995560ee2 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Thu, 23 Apr 2026 18:43:46 +0800
Subject: [PATCH 2/4] [llubi] Small fixes and test cases update
---
llvm/test/tools/llubi/intr_arith_overflow.ll | 36 ++++++++-----
llvm/test/tools/llubi/intr_arith_sat.ll | 44 +++++++++++-----
llvm/test/tools/llubi/intr_bit_manip.ll | 54 ++++++++++++++------
llvm/test/tools/llubi/intr_int_arith.ll | 38 +++++++++-----
llvm/tools/llubi/lib/Interpreter.cpp | 51 ++++++++++--------
5 files changed, 148 insertions(+), 75 deletions(-)
diff --git a/llvm/test/tools/llubi/intr_arith_overflow.ll b/llvm/test/tools/llubi/intr_arith_overflow.ll
index 936d3aedd67ec..6e5ff40b4b93e 100644
--- a/llvm/test/tools/llubi/intr_arith_overflow.ll
+++ b/llvm/test/tools/llubi/intr_arith_overflow.ll
@@ -2,35 +2,47 @@
; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
define void @main() {
+ call {i32, i1} @llvm.sadd.with.overflow.i32(i32 poison, i32 20)
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 poison)
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 poison, 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 poison)
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 poison, 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 poison)
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: %1 = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 poison, i32 20) => poison
+; CHECK-NEXT: %2 = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 10, i32 20) => { i32 30, F }
+; CHECK-NEXT: %3 = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 -2147483647, i32 -20) => { i32 2147483629, T }
+; CHECK-NEXT: %4 = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 10, i32 poison) => poison
+; CHECK-NEXT: %5 = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 10, i32 20) => { i32 30, F }
+; CHECK-NEXT: %6 = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 -1, i32 20) => { i32 19, T }
+; CHECK-NEXT: %7 = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 poison, i32 20) => poison
+; CHECK-NEXT: %8 = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 10, i32 20) => { i32 -10, F }
+; CHECK-NEXT: %9 = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 -2147483647, i32 20) => { i32 2147483629, T }
+; CHECK-NEXT: %10 = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 20, i32 poison) => poison
+; CHECK-NEXT: %11 = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 20, i32 10) => { i32 10, F }
+; CHECK-NEXT: %12 = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 0, i32 20) => { i32 -20, T }
+; CHECK-NEXT: %13 = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 poison, i32 20) => poison
+; CHECK-NEXT: %14 = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 10, i32 20) => { i32 200, F }
+; CHECK-NEXT: %15 = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 -4000000, i32 4000000) => { i32 -1246822400, T }
+; CHECK-NEXT: %16 = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 20, i32 poison) => poison
+; CHECK-NEXT: %17 = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 20, i32 10) => { i32 200, F }
+; CHECK-NEXT: %18 = 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
index ccc1c44a488ee..e327b2741689e 100644
--- a/llvm/test/tools/llubi/intr_arith_sat.ll
+++ b/llvm/test/tools/llubi/intr_arith_sat.ll
@@ -2,35 +2,51 @@
; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
define void @main() {
+ call i32 @llvm.sadd.sat.i32(i32 poison, i32 20)
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 poison)
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 poison, 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 poison)
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 poison, i32 2)
call i32 @llvm.sshl.sat.i32(i32 10, i32 2)
- call i32 @llvm.sshl.sat.i32(i32 10, i32 40)
+ call i32 @llvm.sshl.sat.i32(i32 10, i32 31)
+ call i32 @llvm.sshl.sat.i32(i32 10, i32 32)
+ call i32 @llvm.ushl.sat.i32(i32 10, i32 poison)
call i32 @llvm.ushl.sat.i32(i32 10, i32 2)
- call i32 @llvm.ushl.sat.i32(i32 10, i32 40)
+ call i32 @llvm.ushl.sat.i32(i32 10, i32 31)
+ call i32 @llvm.ushl.sat.i32(i32 10, i32 32)
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: %1 = call i32 @llvm.sadd.sat.i32(i32 poison, i32 20) => poison
+; CHECK-NEXT: %2 = call i32 @llvm.sadd.sat.i32(i32 10, i32 20) => i32 30
+; CHECK-NEXT: %3 = call i32 @llvm.sadd.sat.i32(i32 -2147483647, i32 -20) => i32 -2147483648
+; CHECK-NEXT: %4 = call i32 @llvm.uadd.sat.i32(i32 10, i32 poison) => poison
+; CHECK-NEXT: %5 = call i32 @llvm.uadd.sat.i32(i32 10, i32 20) => i32 30
+; CHECK-NEXT: %6 = call i32 @llvm.uadd.sat.i32(i32 -1, i32 20) => i32 -1
+; CHECK-NEXT: %7 = call i32 @llvm.ssub.sat.i32(i32 poison, i32 20) => poison
+; CHECK-NEXT: %8 = call i32 @llvm.ssub.sat.i32(i32 10, i32 20) => i32 -10
+; CHECK-NEXT: %9 = call i32 @llvm.ssub.sat.i32(i32 -2147483647, i32 20) => i32 -2147483648
+; CHECK-NEXT: %10 = call i32 @llvm.usub.sat.i32(i32 20, i32 poison) => poison
+; CHECK-NEXT: %11 = call i32 @llvm.usub.sat.i32(i32 20, i32 10) => i32 10
+; CHECK-NEXT: %12 = call i32 @llvm.usub.sat.i32(i32 0, i32 20) => i32 0
+; CHECK-NEXT: %13 = call i32 @llvm.sshl.sat.i32(i32 poison, i32 2) => poison
+; CHECK-NEXT: %14 = call i32 @llvm.sshl.sat.i32(i32 10, i32 2) => i32 40
+; CHECK-NEXT: %15 = call i32 @llvm.sshl.sat.i32(i32 10, i32 31) => i32 2147483647
+; CHECK-NEXT: %16 = call i32 @llvm.sshl.sat.i32(i32 10, i32 32) => poison
+; CHECK-NEXT: %17 = call i32 @llvm.ushl.sat.i32(i32 10, i32 poison) => poison
+; CHECK-NEXT: %18 = call i32 @llvm.ushl.sat.i32(i32 10, i32 2) => i32 40
+; CHECK-NEXT: %19 = call i32 @llvm.ushl.sat.i32(i32 10, i32 31) => i32 -1
+; CHECK-NEXT: %20 = call i32 @llvm.ushl.sat.i32(i32 10, i32 32) => poison
; 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
index 4e8afff00d88d..e9459433317d4 100644
--- a/llvm/test/tools/llubi/intr_bit_manip.ll
+++ b/llvm/test/tools/llubi/intr_bit_manip.ll
@@ -2,26 +2,36 @@
; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
define void @main() {
+ call i32 @llvm.bitreverse.i32(i32 poison)
call i32 @llvm.bitreverse.i32(i32 42)
+ call i32 @llvm.bswap.i32(i32 poison)
call i32 @llvm.bswap.i32(i32 12345678)
+ call i32 @llvm.ctpop.i32(i32 poison)
call i32 @llvm.ctpop.i32(i32 42)
+ call i32 @llvm.ctlz.i32(i32 poison, i1 false)
call i32 @llvm.ctlz.i32(i32 4200, i1 false)
call i32 @llvm.ctlz.i32(i32 0, i1 true)
+ call i32 @llvm.cttz.i32(i32 poison, i1 false)
call i32 @llvm.cttz.i32(i32 4200, i1 false)
call i32 @llvm.cttz.i32(i32 0, i1 true)
+ call i8 @llvm.fshl.i8(i8 poison, i8 0, i8 15)
+ call i8 @llvm.fshl.i8(i8 255, i8 0, i8 15)
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 poison)
call i8 @llvm.fshr.i8(i8 255, i8 0, i8 15)
+ call i8 @llvm.fshr.i8(i8 255, i8 0, i8 0)
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 poison, i8 2)
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)
@@ -30,22 +40,32 @@ define void @main() {
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: %1 = call i32 @llvm.bitreverse.i32(i32 poison) => poison
+; CHECK-NEXT: %2 = call i32 @llvm.bitreverse.i32(i32 42) => i32 1409286144
+; CHECK-NEXT: %3 = call i32 @llvm.bswap.i32(i32 poison) => poison
+; CHECK-NEXT: %4 = call i32 @llvm.bswap.i32(i32 12345678) => i32 1315027968
+; CHECK-NEXT: %5 = call i32 @llvm.ctpop.i32(i32 poison) => poison
+; CHECK-NEXT: %6 = call i32 @llvm.ctpop.i32(i32 42) => i32 3
+; CHECK-NEXT: %7 = call i32 @llvm.ctlz.i32(i32 poison, i1 false) => poison
+; CHECK-NEXT: %8 = call i32 @llvm.ctlz.i32(i32 4200, i1 false) => i32 19
+; CHECK-NEXT: %9 = call i32 @llvm.ctlz.i32(i32 0, i1 true) => poison
+; CHECK-NEXT: %10 = call i32 @llvm.cttz.i32(i32 poison, i1 false) => poison
+; CHECK-NEXT: %11 = call i32 @llvm.cttz.i32(i32 4200, i1 false) => i32 3
+; CHECK-NEXT: %12 = call i32 @llvm.cttz.i32(i32 0, i1 true) => poison
+; CHECK-NEXT: %13 = call i8 @llvm.fshl.i8(i8 poison, i8 0, i8 15) => poison
+; CHECK-NEXT: %14 = call i8 @llvm.fshl.i8(i8 -1, i8 0, i8 15) => i8 -128
+; CHECK-NEXT: %15 = call i8 @llvm.fshl.i8(i8 -1, i8 0, i8 15) => i8 -128
+; CHECK-NEXT: %16 = call i8 @llvm.fshl.i8(i8 15, i8 15, i8 11) => i8 120
+; CHECK-NEXT: %17 = call i8 @llvm.fshl.i8(i8 0, i8 -1, i8 8) => i8 0
+; CHECK-NEXT: %18 = call i8 @llvm.fshr.i8(i8 -1, i8 0, i8 poison) => poison
+; CHECK-NEXT: %19 = call i8 @llvm.fshr.i8(i8 -1, i8 0, i8 15) => i8 -2
+; CHECK-NEXT: %20 = call i8 @llvm.fshr.i8(i8 -1, i8 0, i8 0) => i8 -1
+; CHECK-NEXT: %21 = call i8 @llvm.fshr.i8(i8 15, i8 15, i8 11) => i8 -31
+; CHECK-NEXT: %22 = call i8 @llvm.fshr.i8(i8 0, i8 -1, i8 8) => i8 0
+; CHECK-NEXT: %23 = call i8 @llvm.clmul.i8(i8 poison, i8 2) => poison
+; CHECK-NEXT: %24 = call i8 @llvm.clmul.i8(i8 1, i8 2) => i8 2
+; CHECK-NEXT: %25 = call i8 @llvm.clmul.i8(i8 5, i8 6) => i8 30
+; CHECK-NEXT: %26 = call i8 @llvm.clmul.i8(i8 -4, i8 2) => i8 -8
+; CHECK-NEXT: %27 = 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
index f00c051791a61..bc996e6e4b49a 100644
--- a/llvm/test/tools/llubi/intr_int_arith.ll
+++ b/llvm/test/tools/llubi/intr_int_arith.ll
@@ -2,35 +2,49 @@
; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
define void @main() {
+ call i32 @llvm.abs.i32(i32 poison, i1 false)
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.smin.i32(i32 poison, i32 -20)
call i32 @llvm.umin.i32(i32 10, i32 20)
+ call i32 @llvm.umin.i32(i32 10, i32 poison)
call i32 @llvm.smax.i32(i32 -10, i32 -20)
+ call i32 @llvm.smax.i32(i32 poison, i32 -20)
call i32 @llvm.umax.i32(i32 10, i32 20)
+ call i32 @llvm.umax.i32(i32 10, i32 poison)
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.scmp.i32(i32 poison, 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)
+ call i2 @llvm.ucmp.i32(i32 10, i32 poison)
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: %1 = call i32 @llvm.abs.i32(i32 poison, i1 false) => poison
+; CHECK-NEXT: %2 = call i32 @llvm.abs.i32(i32 -42, i1 false) => i32 42
+; CHECK-NEXT: %3 = call i32 @llvm.abs.i32(i32 -2147483648, i1 true) => poison
+; CHECK-NEXT: %4 = call i32 @llvm.smin.i32(i32 -10, i32 -20) => i32 -20
+; CHECK-NEXT: %5 = call i32 @llvm.smin.i32(i32 poison, i32 -20) => poison
+; CHECK-NEXT: %6 = call i32 @llvm.umin.i32(i32 10, i32 20) => i32 10
+; CHECK-NEXT: %7 = call i32 @llvm.umin.i32(i32 10, i32 poison) => poison
+; CHECK-NEXT: %8 = call i32 @llvm.smax.i32(i32 -10, i32 -20) => i32 -10
+; CHECK-NEXT: %9 = call i32 @llvm.smax.i32(i32 poison, i32 -20) => poison
+; CHECK-NEXT: %10 = call i32 @llvm.umax.i32(i32 10, i32 20) => i32 20
+; CHECK-NEXT: %11 = call i32 @llvm.umax.i32(i32 10, i32 poison) => poison
+; CHECK-NEXT: %12 = call i2 @llvm.scmp.i2.i32(i32 10, i32 20) => i2 -1
+; CHECK-NEXT: %13 = call i2 @llvm.scmp.i2.i32(i32 -10, i32 -20) => i2 1
+; CHECK-NEXT: %14 = call i2 @llvm.scmp.i2.i32(i32 10, i32 10) => i2 0
+; CHECK-NEXT: %15 = call i2 @llvm.scmp.i2.i32(i32 poison, i32 10) => poison
+; CHECK-NEXT: %16 = call i2 @llvm.ucmp.i2.i32(i32 10, i32 20) => i2 -1
+; CHECK-NEXT: %17 = call i2 @llvm.ucmp.i2.i32(i32 20, i32 10) => i2 1
+; CHECK-NEXT: %18 = call i2 @llvm.ucmp.i2.i32(i32 10, i32 10) => i2 0
+; CHECK-NEXT: %19 = call i2 @llvm.ucmp.i2.i32(i32 10, i32 poison) => poison
; 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 94ac8a1cc0b70..013e299fef01d 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -440,13 +440,13 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
++CurrentFrame->PC;
}
- AnyValue callIntrinsic(CallBase &CB) {
+ AnyValue callIntrinsic(CallBase &CB, ArrayRef<AnyValue> Args) {
Intrinsic::ID IID = CB.getIntrinsicID();
Type *RetTy = CB.getType();
switch (IID) {
case Intrinsic::assume:
- switch (getValue(CB.getArgOperand(0)).asBoolean()) {
+ switch (Args[0].asBoolean()) {
case BooleanKind::True:
break;
case BooleanKind::False:
@@ -458,10 +458,10 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
return AnyValue();
case Intrinsic::lifetime_start:
case Intrinsic::lifetime_end: {
- auto *Ptr = CB.getArgOperand(0);
- if (isa<PoisonValue>(Ptr))
+ auto Ptr = Args[0];
+ if (Ptr.isPoison())
return AnyValue();
- auto *MO = getValue(Ptr).asPointer().getMemoryObject();
+ auto *MO = Ptr.asPointer().getMemoryObject();
assert(MO && "Memory object accessed by lifetime intrinsic should be "
"always valid.");
if (IID == Intrinsic::lifetime_start) {
@@ -475,14 +475,18 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
case Intrinsic::ssa_copy:
case Intrinsic::expect:
case Intrinsic::expect_with_probability:
- return getValue(CB.getArgOperand(0));
+ return Args[0];
case Intrinsic::donothing:
return AnyValue();
- case Intrinsic::vscale:
- return APInt(RetTy->getScalarSizeInBits(), Ctx.getVScale());
+ case Intrinsic::vscale: {
+ const unsigned BitWidth = RetTy->getScalarSizeInBits();
+ const APInt VScale(64, Ctx.getVScale());
+ if (!VScale.isIntN(BitWidth))
+ return AnyValue::poison();
+ return VScale.zextOrTrunc(BitWidth);
+ }
case Intrinsic::abs: {
- const bool IsIntMinPoison =
- getBooleanNonPoison(getValue(CB.getArgOperand(1)).asBoolean());
+ const bool IsIntMinPoison = getBooleanNonPoison(Args[1].asBoolean());
return visitIntUnOpWithResult(CB, [&](const APInt &Operand) -> AnyValue {
if (IsIntMinPoison && Operand.isMinSignedValue())
return AnyValue::poison();
@@ -515,7 +519,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
case Intrinsic::scmp:
case Intrinsic::ucmp: {
- std::uint32_t BitWidth = RetTy->getScalarSizeInBits();
+ const unsigned BitWidth = RetTy->getScalarSizeInBits();
return visitIntBinOpWithResult(
CB, [&](const APInt &LHS, const APInt &RHS) -> AnyValue {
if (LHS == RHS)
@@ -544,8 +548,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
case Intrinsic::ctlz:
case Intrinsic::cttz: {
- const bool IsZeroPoison =
- getBooleanNonPoison(getValue(CB.getArgOperand(1)).asBoolean());
+ const bool IsZeroPoison = getBooleanNonPoison(Args[1].asBoolean());
return visitIntUnOpWithResult(CB, [&](const APInt &Operand) -> AnyValue {
if (IsZeroPoison && Operand.isZero())
return AnyValue::poison();
@@ -560,12 +563,14 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
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 unsigned BitWidth = Op1.getBitWidth();
+ const std::uint64_t ShiftAmount = Op3.urem(BitWidth);
const bool IsFShr = IID == Intrinsic::fshr;
- const std::uint32_t LShrAmount =
+ if (ShiftAmount == 0)
+ return IsFShr ? Op2 : Op1;
+ const std::uint64_t LShrAmount =
IsFShr ? ShiftAmount : BitWidth - ShiftAmount;
- const std::uint32_t ShlAmount =
+ const std::uint64_t ShlAmount =
!IsFShr ? ShiftAmount : BitWidth - ShiftAmount;
return Op1.shl(ShlAmount) | Op2.lshr(LShrAmount);
});
@@ -628,10 +633,16 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
return LHS.ssub_sat(RHS);
case Intrinsic::usub_sat:
return LHS.usub_sat(RHS);
- case Intrinsic::sshl_sat:
+ case Intrinsic::sshl_sat: {
+ if (RHS.uge(LHS.getBitWidth()))
+ return AnyValue::poison();
return LHS.sshl_sat(RHS);
- case Intrinsic::ushl_sat:
+ }
+ case Intrinsic::ushl_sat: {
+ if (RHS.uge(LHS.getBitWidth()))
+ return AnyValue::poison();
return LHS.ushl_sat(RHS);
+ }
default:
llvm_unreachable("Unexpected intrinsic ID");
}
@@ -715,7 +726,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
"Expected the callee function type to match the call site signature.");
CurrentFrame->ResolvedCallee = Callee;
if (Callee->isIntrinsic()) {
- CurrentFrame->CalleeRetVal = callIntrinsic(CB);
+ CurrentFrame->CalleeRetVal = callIntrinsic(CB, CalleeArgs);
returnFromCallee();
return;
} else if (Callee->isDeclaration()) {
>From b7288f0322869116c6917e07bc279a926cdc0581 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Thu, 23 Apr 2026 19:06:00 +0800
Subject: [PATCH 3/4] [llubi] Tests for vectors, also fix the implementation of
overflow arithmetic intrinsics
---
llvm/test/tools/llubi/intr_arith_overflow.ll | 42 +++++++++------
llvm/test/tools/llubi/intr_arith_sat.ll | 46 ++++++++++------
llvm/test/tools/llubi/intr_bit_manip.ll | 56 +++++++++++---------
llvm/test/tools/llubi/intr_int_arith.ll | 46 ++++++++++------
llvm/tools/llubi/lib/Interpreter.cpp | 41 ++++++++++++--
5 files changed, 156 insertions(+), 75 deletions(-)
diff --git a/llvm/test/tools/llubi/intr_arith_overflow.ll b/llvm/test/tools/llubi/intr_arith_overflow.ll
index 6e5ff40b4b93e..dff9b05125bde 100644
--- a/llvm/test/tools/llubi/intr_arith_overflow.ll
+++ b/llvm/test/tools/llubi/intr_arith_overflow.ll
@@ -5,23 +5,29 @@ define void @main() {
call {i32, i1} @llvm.sadd.with.overflow.i32(i32 poison, i32 20)
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 {<2 x i32>, <2 x i1>} @llvm.sadd.with.overflow.v2i32(<2 x i32> <i32 10, i32 poison>, <2 x i32> <i32 20, i32 1>)
call {i32, i1} @llvm.uadd.with.overflow.i32(i32 10, i32 poison)
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 {<2 x i32>, <2 x i1>} @llvm.uadd.with.overflow.v2i32(<2 x i32> <i32 10, i32 poison>, <2 x i32> <i32 20, i32 1>)
call {i32, i1} @llvm.ssub.with.overflow.i32(i32 poison, 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 {<2 x i32>, <2 x i1>} @llvm.ssub.with.overflow.v2i32(<2 x i32> <i32 10, i32 poison>, <2 x i32> <i32 20, i32 1>)
call {i32, i1} @llvm.usub.with.overflow.i32(i32 20, i32 poison)
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 {<2 x i32>, <2 x i1>} @llvm.usub.with.overflow.v2i32(<2 x i32> <i32 20, i32 poison>, <2 x i32> <i32 10, i32 1>)
call {i32, i1} @llvm.smul.with.overflow.i32(i32 poison, 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 {<2 x i32>, <2 x i1>} @llvm.smul.with.overflow.v2i32(<2 x i32> <i32 10, i32 poison>, <2 x i32> <i32 20, i32 1>)
call {i32, i1} @llvm.umul.with.overflow.i32(i32 20, i32 poison)
call {i32, i1} @llvm.umul.with.overflow.i32(i32 20, i32 10)
call {i32, i1} @llvm.umul.with.overflow.i32(i32 4000000, i32 4000000)
+ call {<2 x i32>, <2 x i1>} @llvm.umul.with.overflow.v2i32(<2 x i32> <i32 20, i32 poison>, <2 x i32> <i32 10, i32 1>)
ret void
}
@@ -29,20 +35,26 @@ define void @main() {
; CHECK-NEXT: %1 = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 poison, i32 20) => poison
; CHECK-NEXT: %2 = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 10, i32 20) => { i32 30, F }
; CHECK-NEXT: %3 = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 -2147483647, i32 -20) => { i32 2147483629, T }
-; CHECK-NEXT: %4 = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 10, i32 poison) => poison
-; CHECK-NEXT: %5 = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 10, i32 20) => { i32 30, F }
-; CHECK-NEXT: %6 = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 -1, i32 20) => { i32 19, T }
-; CHECK-NEXT: %7 = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 poison, i32 20) => poison
-; CHECK-NEXT: %8 = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 10, i32 20) => { i32 -10, F }
-; CHECK-NEXT: %9 = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 -2147483647, i32 20) => { i32 2147483629, T }
-; CHECK-NEXT: %10 = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 20, i32 poison) => poison
-; CHECK-NEXT: %11 = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 20, i32 10) => { i32 10, F }
-; CHECK-NEXT: %12 = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 0, i32 20) => { i32 -20, T }
-; CHECK-NEXT: %13 = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 poison, i32 20) => poison
-; CHECK-NEXT: %14 = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 10, i32 20) => { i32 200, F }
-; CHECK-NEXT: %15 = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 -4000000, i32 4000000) => { i32 -1246822400, T }
-; CHECK-NEXT: %16 = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 20, i32 poison) => poison
-; CHECK-NEXT: %17 = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 20, i32 10) => { i32 200, F }
-; CHECK-NEXT: %18 = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 4000000, i32 4000000) => { i32 1246822400, T }
+; CHECK-NEXT: %4 = call { <2 x i32>, <2 x i1> } @llvm.sadd.with.overflow.v2i32(<2 x i32> <i32 10, i32 poison>, <2 x i32> <i32 20, i32 1>) => { { i32 30, poison }, { F, poison } }
+; CHECK-NEXT: %5 = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 10, i32 poison) => poison
+; CHECK-NEXT: %6 = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 10, i32 20) => { i32 30, F }
+; CHECK-NEXT: %7 = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 -1, i32 20) => { i32 19, T }
+; CHECK-NEXT: %8 = call { <2 x i32>, <2 x i1> } @llvm.uadd.with.overflow.v2i32(<2 x i32> <i32 10, i32 poison>, <2 x i32> <i32 20, i32 1>) => { { i32 30, poison }, { F, poison } }
+; CHECK-NEXT: %9 = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 poison, i32 20) => poison
+; CHECK-NEXT: %10 = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 10, i32 20) => { i32 -10, F }
+; CHECK-NEXT: %11 = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 -2147483647, i32 20) => { i32 2147483629, T }
+; CHECK-NEXT: %12 = call { <2 x i32>, <2 x i1> } @llvm.ssub.with.overflow.v2i32(<2 x i32> <i32 10, i32 poison>, <2 x i32> <i32 20, i32 1>) => { { i32 -10, poison }, { F, poison } }
+; CHECK-NEXT: %13 = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 20, i32 poison) => poison
+; CHECK-NEXT: %14 = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 20, i32 10) => { i32 10, F }
+; CHECK-NEXT: %15 = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 0, i32 20) => { i32 -20, T }
+; CHECK-NEXT: %16 = call { <2 x i32>, <2 x i1> } @llvm.usub.with.overflow.v2i32(<2 x i32> <i32 20, i32 poison>, <2 x i32> <i32 10, i32 1>) => { { i32 10, poison }, { F, poison } }
+; CHECK-NEXT: %17 = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 poison, i32 20) => poison
+; CHECK-NEXT: %18 = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 10, i32 20) => { i32 200, F }
+; CHECK-NEXT: %19 = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 -4000000, i32 4000000) => { i32 -1246822400, T }
+; CHECK-NEXT: %20 = call { <2 x i32>, <2 x i1> } @llvm.smul.with.overflow.v2i32(<2 x i32> <i32 10, i32 poison>, <2 x i32> <i32 20, i32 1>) => { { i32 200, poison }, { F, poison } }
+; CHECK-NEXT: %21 = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 20, i32 poison) => poison
+; CHECK-NEXT: %22 = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 20, i32 10) => { i32 200, F }
+; CHECK-NEXT: %23 = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 4000000, i32 4000000) => { i32 1246822400, T }
+; CHECK-NEXT: %24 = call { <2 x i32>, <2 x i1> } @llvm.umul.with.overflow.v2i32(<2 x i32> <i32 20, i32 poison>, <2 x i32> <i32 10, i32 1>) => { { i32 200, poison }, { F, poison } }
; 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
index e327b2741689e..a1b22929f671b 100644
--- a/llvm/test/tools/llubi/intr_arith_sat.ll
+++ b/llvm/test/tools/llubi/intr_arith_sat.ll
@@ -5,25 +5,31 @@ define void @main() {
call i32 @llvm.sadd.sat.i32(i32 poison, i32 20)
call i32 @llvm.sadd.sat.i32(i32 10, i32 20)
call i32 @llvm.sadd.sat.i32(i32 -2147483647, i32 -20)
+ call <2 x i32> @llvm.sadd.sat.v2i32(<2 x i32> <i32 -1, i32 poison>, <2 x i32> <i32 2, i32 3>)
call i32 @llvm.uadd.sat.i32(i32 10, i32 poison)
call i32 @llvm.uadd.sat.i32(i32 10, i32 20)
call i32 @llvm.uadd.sat.i32(i32 4294967295, i32 20)
+ call <2 x i32> @llvm.uadd.sat.v2i32(<2 x i32> <i32 1, i32 poison>, <2 x i32> <i32 2, i32 3>)
call i32 @llvm.ssub.sat.i32(i32 poison, i32 20)
call i32 @llvm.ssub.sat.i32(i32 10, i32 20)
call i32 @llvm.ssub.sat.i32(i32 -2147483647, i32 20)
+ call <2 x i32> @llvm.ssub.sat.v2i32(<2 x i32> <i32 -1, i32 poison>, <2 x i32> <i32 2, i32 3>)
call i32 @llvm.usub.sat.i32(i32 20, i32 poison)
call i32 @llvm.usub.sat.i32(i32 20, i32 10)
call i32 @llvm.usub.sat.i32(i32 0, i32 20)
+ call <2 x i32> @llvm.usub.sat.v2i32(<2 x i32> <i32 1, i32 poison>, <2 x i32> <i32 2, i32 3>)
call i32 @llvm.sshl.sat.i32(i32 poison, i32 2)
call i32 @llvm.sshl.sat.i32(i32 10, i32 2)
call i32 @llvm.sshl.sat.i32(i32 10, i32 31)
call i32 @llvm.sshl.sat.i32(i32 10, i32 32)
+ call <2 x i32> @llvm.sshl.sat.v2i32(<2 x i32> <i32 10, i32 10>, <2 x i32> <i32 2, i32 poison>)
call i32 @llvm.ushl.sat.i32(i32 10, i32 poison)
call i32 @llvm.ushl.sat.i32(i32 10, i32 2)
call i32 @llvm.ushl.sat.i32(i32 10, i32 31)
call i32 @llvm.ushl.sat.i32(i32 10, i32 32)
+ call <2 x i32> @llvm.ushl.sat.v2i32(<2 x i32> <i32 10, i32 10>, <2 x i32> <i32 2, i32 poison>)
ret void
}
@@ -31,22 +37,28 @@ define void @main() {
; CHECK-NEXT: %1 = call i32 @llvm.sadd.sat.i32(i32 poison, i32 20) => poison
; CHECK-NEXT: %2 = call i32 @llvm.sadd.sat.i32(i32 10, i32 20) => i32 30
; CHECK-NEXT: %3 = call i32 @llvm.sadd.sat.i32(i32 -2147483647, i32 -20) => i32 -2147483648
-; CHECK-NEXT: %4 = call i32 @llvm.uadd.sat.i32(i32 10, i32 poison) => poison
-; CHECK-NEXT: %5 = call i32 @llvm.uadd.sat.i32(i32 10, i32 20) => i32 30
-; CHECK-NEXT: %6 = call i32 @llvm.uadd.sat.i32(i32 -1, i32 20) => i32 -1
-; CHECK-NEXT: %7 = call i32 @llvm.ssub.sat.i32(i32 poison, i32 20) => poison
-; CHECK-NEXT: %8 = call i32 @llvm.ssub.sat.i32(i32 10, i32 20) => i32 -10
-; CHECK-NEXT: %9 = call i32 @llvm.ssub.sat.i32(i32 -2147483647, i32 20) => i32 -2147483648
-; CHECK-NEXT: %10 = call i32 @llvm.usub.sat.i32(i32 20, i32 poison) => poison
-; CHECK-NEXT: %11 = call i32 @llvm.usub.sat.i32(i32 20, i32 10) => i32 10
-; CHECK-NEXT: %12 = call i32 @llvm.usub.sat.i32(i32 0, i32 20) => i32 0
-; CHECK-NEXT: %13 = call i32 @llvm.sshl.sat.i32(i32 poison, i32 2) => poison
-; CHECK-NEXT: %14 = call i32 @llvm.sshl.sat.i32(i32 10, i32 2) => i32 40
-; CHECK-NEXT: %15 = call i32 @llvm.sshl.sat.i32(i32 10, i32 31) => i32 2147483647
-; CHECK-NEXT: %16 = call i32 @llvm.sshl.sat.i32(i32 10, i32 32) => poison
-; CHECK-NEXT: %17 = call i32 @llvm.ushl.sat.i32(i32 10, i32 poison) => poison
-; CHECK-NEXT: %18 = call i32 @llvm.ushl.sat.i32(i32 10, i32 2) => i32 40
-; CHECK-NEXT: %19 = call i32 @llvm.ushl.sat.i32(i32 10, i32 31) => i32 -1
-; CHECK-NEXT: %20 = call i32 @llvm.ushl.sat.i32(i32 10, i32 32) => poison
+; CHECK-NEXT: %4 = call <2 x i32> @llvm.sadd.sat.v2i32(<2 x i32> <i32 -1, i32 poison>, <2 x i32> <i32 2, i32 3>) => { i32 1, poison }
+; CHECK-NEXT: %5 = call i32 @llvm.uadd.sat.i32(i32 10, i32 poison) => poison
+; CHECK-NEXT: %6 = call i32 @llvm.uadd.sat.i32(i32 10, i32 20) => i32 30
+; CHECK-NEXT: %7 = call i32 @llvm.uadd.sat.i32(i32 -1, i32 20) => i32 -1
+; CHECK-NEXT: %8 = call <2 x i32> @llvm.uadd.sat.v2i32(<2 x i32> <i32 1, i32 poison>, <2 x i32> <i32 2, i32 3>) => { i32 3, poison }
+; CHECK-NEXT: %9 = call i32 @llvm.ssub.sat.i32(i32 poison, i32 20) => poison
+; CHECK-NEXT: %10 = call i32 @llvm.ssub.sat.i32(i32 10, i32 20) => i32 -10
+; CHECK-NEXT: %11 = call i32 @llvm.ssub.sat.i32(i32 -2147483647, i32 20) => i32 -2147483648
+; CHECK-NEXT: %12 = call <2 x i32> @llvm.ssub.sat.v2i32(<2 x i32> <i32 -1, i32 poison>, <2 x i32> <i32 2, i32 3>) => { i32 -3, poison }
+; CHECK-NEXT: %13 = call i32 @llvm.usub.sat.i32(i32 20, i32 poison) => poison
+; CHECK-NEXT: %14 = call i32 @llvm.usub.sat.i32(i32 20, i32 10) => i32 10
+; CHECK-NEXT: %15 = call i32 @llvm.usub.sat.i32(i32 0, i32 20) => i32 0
+; CHECK-NEXT: %16 = call <2 x i32> @llvm.usub.sat.v2i32(<2 x i32> <i32 1, i32 poison>, <2 x i32> <i32 2, i32 3>) => { i32 0, poison }
+; CHECK-NEXT: %17 = call i32 @llvm.sshl.sat.i32(i32 poison, i32 2) => poison
+; CHECK-NEXT: %18 = call i32 @llvm.sshl.sat.i32(i32 10, i32 2) => i32 40
+; CHECK-NEXT: %19 = call i32 @llvm.sshl.sat.i32(i32 10, i32 31) => i32 2147483647
+; CHECK-NEXT: %20 = call i32 @llvm.sshl.sat.i32(i32 10, i32 32) => poison
+; CHECK-NEXT: %21 = call <2 x i32> @llvm.sshl.sat.v2i32(<2 x i32> splat (i32 10), <2 x i32> <i32 2, i32 poison>) => { i32 40, poison }
+; CHECK-NEXT: %22 = call i32 @llvm.ushl.sat.i32(i32 10, i32 poison) => poison
+; CHECK-NEXT: %23 = call i32 @llvm.ushl.sat.i32(i32 10, i32 2) => i32 40
+; CHECK-NEXT: %24 = call i32 @llvm.ushl.sat.i32(i32 10, i32 31) => i32 -1
+; CHECK-NEXT: %25 = call i32 @llvm.ushl.sat.i32(i32 10, i32 32) => poison
+; CHECK-NEXT: %26 = call <2 x i32> @llvm.ushl.sat.v2i32(<2 x i32> splat (i32 10), <2 x i32> <i32 2, i32 poison>) => { i32 40, poison }
; 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
index e9459433317d4..c63e1987567f5 100644
--- a/llvm/test/tools/llubi/intr_bit_manip.ll
+++ b/llvm/test/tools/llubi/intr_bit_manip.ll
@@ -4,6 +4,7 @@
define void @main() {
call i32 @llvm.bitreverse.i32(i32 poison)
call i32 @llvm.bitreverse.i32(i32 42)
+ call <2 x i32> @llvm.bitreverse.v2i32(<2 x i32> <i32 42, i32 poison>)
call i32 @llvm.bswap.i32(i32 poison)
call i32 @llvm.bswap.i32(i32 12345678)
@@ -24,48 +25,55 @@ define void @main() {
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 <2 x i8> @llvm.fshl.v2i8(<2 x i8> <i8 poison, i8 15>, <2 x i8> <i8 0, i8 15>, <2 x i8> <i8 15, i8 11>)
call i8 @llvm.fshr.i8(i8 255, i8 0, i8 poison)
call i8 @llvm.fshr.i8(i8 255, i8 0, i8 15)
call i8 @llvm.fshr.i8(i8 255, i8 0, i8 0)
call i8 @llvm.fshr.i8(i8 15, i8 15, i8 11)
call i8 @llvm.fshr.i8(i8 0, i8 255, i8 8)
+ call <2 x i8> @llvm.fshr.v2i8(<2 x i8> <i8 255, i8 0>, <2 x i8> <i8 0, i8 15>, <2 x i8> <i8 0, i8 0>)
call i8 @llvm.clmul.i8(i8 poison, i8 2)
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)
+ call <2 x i8> @llvm.clmul.v2i8(<2 x i8> <i8 1, i8 poison>, <2 x i8> <i8 2, i8 3>)
ret void
}
; CHECK: Entering function: main
; CHECK-NEXT: %1 = call i32 @llvm.bitreverse.i32(i32 poison) => poison
; CHECK-NEXT: %2 = call i32 @llvm.bitreverse.i32(i32 42) => i32 1409286144
-; CHECK-NEXT: %3 = call i32 @llvm.bswap.i32(i32 poison) => poison
-; CHECK-NEXT: %4 = call i32 @llvm.bswap.i32(i32 12345678) => i32 1315027968
-; CHECK-NEXT: %5 = call i32 @llvm.ctpop.i32(i32 poison) => poison
-; CHECK-NEXT: %6 = call i32 @llvm.ctpop.i32(i32 42) => i32 3
-; CHECK-NEXT: %7 = call i32 @llvm.ctlz.i32(i32 poison, i1 false) => poison
-; CHECK-NEXT: %8 = call i32 @llvm.ctlz.i32(i32 4200, i1 false) => i32 19
-; CHECK-NEXT: %9 = call i32 @llvm.ctlz.i32(i32 0, i1 true) => poison
-; CHECK-NEXT: %10 = call i32 @llvm.cttz.i32(i32 poison, i1 false) => poison
-; CHECK-NEXT: %11 = call i32 @llvm.cttz.i32(i32 4200, i1 false) => i32 3
-; CHECK-NEXT: %12 = call i32 @llvm.cttz.i32(i32 0, i1 true) => poison
-; CHECK-NEXT: %13 = call i8 @llvm.fshl.i8(i8 poison, i8 0, i8 15) => poison
-; CHECK-NEXT: %14 = call i8 @llvm.fshl.i8(i8 -1, i8 0, i8 15) => i8 -128
+; CHECK-NEXT: %3 = call <2 x i32> @llvm.bitreverse.v2i32(<2 x i32> <i32 42, i32 poison>) => { i32 1409286144, poison }
+; CHECK-NEXT: %4 = call i32 @llvm.bswap.i32(i32 poison) => poison
+; CHECK-NEXT: %5 = call i32 @llvm.bswap.i32(i32 12345678) => i32 1315027968
+; CHECK-NEXT: %6 = call i32 @llvm.ctpop.i32(i32 poison) => poison
+; CHECK-NEXT: %7 = call i32 @llvm.ctpop.i32(i32 42) => i32 3
+; CHECK-NEXT: %8 = call i32 @llvm.ctlz.i32(i32 poison, i1 false) => poison
+; CHECK-NEXT: %9 = call i32 @llvm.ctlz.i32(i32 4200, i1 false) => i32 19
+; CHECK-NEXT: %10 = call i32 @llvm.ctlz.i32(i32 0, i1 true) => poison
+; CHECK-NEXT: %11 = call i32 @llvm.cttz.i32(i32 poison, i1 false) => poison
+; CHECK-NEXT: %12 = call i32 @llvm.cttz.i32(i32 4200, i1 false) => i32 3
+; CHECK-NEXT: %13 = call i32 @llvm.cttz.i32(i32 0, i1 true) => poison
+; CHECK-NEXT: %14 = call i8 @llvm.fshl.i8(i8 poison, i8 0, i8 15) => poison
; CHECK-NEXT: %15 = call i8 @llvm.fshl.i8(i8 -1, i8 0, i8 15) => i8 -128
-; CHECK-NEXT: %16 = call i8 @llvm.fshl.i8(i8 15, i8 15, i8 11) => i8 120
-; CHECK-NEXT: %17 = call i8 @llvm.fshl.i8(i8 0, i8 -1, i8 8) => i8 0
-; CHECK-NEXT: %18 = call i8 @llvm.fshr.i8(i8 -1, i8 0, i8 poison) => poison
-; CHECK-NEXT: %19 = call i8 @llvm.fshr.i8(i8 -1, i8 0, i8 15) => i8 -2
-; CHECK-NEXT: %20 = call i8 @llvm.fshr.i8(i8 -1, i8 0, i8 0) => i8 -1
-; CHECK-NEXT: %21 = call i8 @llvm.fshr.i8(i8 15, i8 15, i8 11) => i8 -31
-; CHECK-NEXT: %22 = call i8 @llvm.fshr.i8(i8 0, i8 -1, i8 8) => i8 0
-; CHECK-NEXT: %23 = call i8 @llvm.clmul.i8(i8 poison, i8 2) => poison
-; CHECK-NEXT: %24 = call i8 @llvm.clmul.i8(i8 1, i8 2) => i8 2
-; CHECK-NEXT: %25 = call i8 @llvm.clmul.i8(i8 5, i8 6) => i8 30
-; CHECK-NEXT: %26 = call i8 @llvm.clmul.i8(i8 -4, i8 2) => i8 -8
-; CHECK-NEXT: %27 = call i8 @llvm.clmul.i8(i8 -4, i8 -5) => i8 -92
+; CHECK-NEXT: %16 = call i8 @llvm.fshl.i8(i8 -1, i8 0, i8 15) => i8 -128
+; CHECK-NEXT: %17 = call i8 @llvm.fshl.i8(i8 15, i8 15, i8 11) => i8 120
+; CHECK-NEXT: %18 = call i8 @llvm.fshl.i8(i8 0, i8 -1, i8 8) => i8 0
+; CHECK-NEXT: %19 = call <2 x i8> @llvm.fshl.v2i8(<2 x i8> <i8 poison, i8 15>, <2 x i8> <i8 0, i8 15>, <2 x i8> <i8 15, i8 11>) => { poison, i8 120 }
+; CHECK-NEXT: %20 = call i8 @llvm.fshr.i8(i8 -1, i8 0, i8 poison) => poison
+; CHECK-NEXT: %21 = call i8 @llvm.fshr.i8(i8 -1, i8 0, i8 15) => i8 -2
+; CHECK-NEXT: %22 = call i8 @llvm.fshr.i8(i8 -1, i8 0, i8 0) => i8 0
+; CHECK-NEXT: %23 = call i8 @llvm.fshr.i8(i8 15, i8 15, i8 11) => i8 -31
+; CHECK-NEXT: %24 = call i8 @llvm.fshr.i8(i8 0, i8 -1, i8 8) => i8 -1
+; CHECK-NEXT: %25 = call <2 x i8> @llvm.fshr.v2i8(<2 x i8> <i8 -1, i8 0>, <2 x i8> <i8 0, i8 15>, <2 x i8> zeroinitializer) => { i8 0, i8 15 }
+; CHECK-NEXT: %26 = call i8 @llvm.clmul.i8(i8 poison, i8 2) => poison
+; CHECK-NEXT: %27 = call i8 @llvm.clmul.i8(i8 1, i8 2) => i8 2
+; CHECK-NEXT: %28 = call i8 @llvm.clmul.i8(i8 5, i8 6) => i8 30
+; CHECK-NEXT: %29 = call i8 @llvm.clmul.i8(i8 -4, i8 2) => i8 -8
+; CHECK-NEXT: %30 = call i8 @llvm.clmul.i8(i8 -4, i8 -5) => i8 -92
+; CHECK-NEXT: %31 = call <2 x i8> @llvm.clmul.v2i8(<2 x i8> <i8 1, i8 poison>, <2 x i8> <i8 2, i8 3>) => { i8 2, poison }
; 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
index bc996e6e4b49a..a601200a65d82 100644
--- a/llvm/test/tools/llubi/intr_int_arith.ll
+++ b/llvm/test/tools/llubi/intr_int_arith.ll
@@ -5,24 +5,31 @@ define void @main() {
call i32 @llvm.abs.i32(i32 poison, i1 false)
call i32 @llvm.abs.i32(i32 -42, i1 false)
call i32 @llvm.abs.i32(i32 -2147483648, i1 true)
+ call <2 x i32> @llvm.abs.v2i32(<2 x i32> <i32 poison, i32 -42>, i1 false)
call i32 @llvm.smin.i32(i32 -10, i32 -20)
call i32 @llvm.smin.i32(i32 poison, i32 -20)
+ call <2 x i32> @llvm.smin.v2i32(<2 x i32> <i32 poison, i32 -10>, <2 x i32> <i32 -20, i32 -20>)
call i32 @llvm.umin.i32(i32 10, i32 20)
call i32 @llvm.umin.i32(i32 10, i32 poison)
+ call <2 x i32> @llvm.umin.v2i32(<2 x i32> <i32 10, i32 poison>, <2 x i32> <i32 20, i32 20>)
call i32 @llvm.smax.i32(i32 -10, i32 -20)
call i32 @llvm.smax.i32(i32 poison, i32 -20)
+ call <2 x i32> @llvm.smax.v2i32(<2 x i32> <i32 poison, i32 -10>, <2 x i32> <i32 -20, i32 -20>)
call i32 @llvm.umax.i32(i32 10, i32 20)
call i32 @llvm.umax.i32(i32 10, i32 poison)
+ call <2 x i32> @llvm.umax.v2i32(<2 x i32> <i32 10, i32 poison>, <2 x i32> <i32 20, 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.scmp.i32(i32 poison, i32 10)
+ call <2 x i2> @llvm.scmp.v2i2.v2i32(<2 x i32> <i32 10, i32 poison>, <2 x i32> <i32 20, 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)
call i2 @llvm.ucmp.i32(i32 10, i32 poison)
+ call <2 x i2> @llvm.ucmp.v2i2.v2i32(<2 x i32> <i32 10, i32 poison>, <2 x i32> <i32 20, i32 10>)
ret void
}
@@ -30,21 +37,28 @@ define void @main() {
; CHECK-NEXT: %1 = call i32 @llvm.abs.i32(i32 poison, i1 false) => poison
; CHECK-NEXT: %2 = call i32 @llvm.abs.i32(i32 -42, i1 false) => i32 42
; CHECK-NEXT: %3 = call i32 @llvm.abs.i32(i32 -2147483648, i1 true) => poison
-; CHECK-NEXT: %4 = call i32 @llvm.smin.i32(i32 -10, i32 -20) => i32 -20
-; CHECK-NEXT: %5 = call i32 @llvm.smin.i32(i32 poison, i32 -20) => poison
-; CHECK-NEXT: %6 = call i32 @llvm.umin.i32(i32 10, i32 20) => i32 10
-; CHECK-NEXT: %7 = call i32 @llvm.umin.i32(i32 10, i32 poison) => poison
-; CHECK-NEXT: %8 = call i32 @llvm.smax.i32(i32 -10, i32 -20) => i32 -10
-; CHECK-NEXT: %9 = call i32 @llvm.smax.i32(i32 poison, i32 -20) => poison
-; CHECK-NEXT: %10 = call i32 @llvm.umax.i32(i32 10, i32 20) => i32 20
-; CHECK-NEXT: %11 = call i32 @llvm.umax.i32(i32 10, i32 poison) => poison
-; CHECK-NEXT: %12 = call i2 @llvm.scmp.i2.i32(i32 10, i32 20) => i2 -1
-; CHECK-NEXT: %13 = call i2 @llvm.scmp.i2.i32(i32 -10, i32 -20) => i2 1
-; CHECK-NEXT: %14 = call i2 @llvm.scmp.i2.i32(i32 10, i32 10) => i2 0
-; CHECK-NEXT: %15 = call i2 @llvm.scmp.i2.i32(i32 poison, i32 10) => poison
-; CHECK-NEXT: %16 = call i2 @llvm.ucmp.i2.i32(i32 10, i32 20) => i2 -1
-; CHECK-NEXT: %17 = call i2 @llvm.ucmp.i2.i32(i32 20, i32 10) => i2 1
-; CHECK-NEXT: %18 = call i2 @llvm.ucmp.i2.i32(i32 10, i32 10) => i2 0
-; CHECK-NEXT: %19 = call i2 @llvm.ucmp.i2.i32(i32 10, i32 poison) => poison
+; CHECK-NEXT: %4 = call <2 x i32> @llvm.abs.v2i32(<2 x i32> <i32 poison, i32 -42>, i1 false) => { poison, i32 42 }
+; CHECK-NEXT: %5 = call i32 @llvm.smin.i32(i32 -10, i32 -20) => i32 -20
+; CHECK-NEXT: %6 = call i32 @llvm.smin.i32(i32 poison, i32 -20) => poison
+; CHECK-NEXT: %7 = call <2 x i32> @llvm.smin.v2i32(<2 x i32> <i32 poison, i32 -10>, <2 x i32> splat (i32 -20)) => { poison, i32 -20 }
+; CHECK-NEXT: %8 = call i32 @llvm.umin.i32(i32 10, i32 20) => i32 10
+; CHECK-NEXT: %9 = call i32 @llvm.umin.i32(i32 10, i32 poison) => poison
+; CHECK-NEXT: %10 = call <2 x i32> @llvm.umin.v2i32(<2 x i32> <i32 10, i32 poison>, <2 x i32> splat (i32 20)) => { i32 10, poison }
+; CHECK-NEXT: %11 = call i32 @llvm.smax.i32(i32 -10, i32 -20) => i32 -10
+; CHECK-NEXT: %12 = call i32 @llvm.smax.i32(i32 poison, i32 -20) => poison
+; CHECK-NEXT: %13 = call <2 x i32> @llvm.smax.v2i32(<2 x i32> <i32 poison, i32 -10>, <2 x i32> splat (i32 -20)) => { poison, i32 -10 }
+; CHECK-NEXT: %14 = call i32 @llvm.umax.i32(i32 10, i32 20) => i32 20
+; CHECK-NEXT: %15 = call i32 @llvm.umax.i32(i32 10, i32 poison) => poison
+; CHECK-NEXT: %16 = call <2 x i32> @llvm.umax.v2i32(<2 x i32> <i32 10, i32 poison>, <2 x i32> splat (i32 20)) => { i32 20, poison }
+; CHECK-NEXT: %17 = call i2 @llvm.scmp.i2.i32(i32 10, i32 20) => i2 -1
+; CHECK-NEXT: %18 = call i2 @llvm.scmp.i2.i32(i32 -10, i32 -20) => i2 1
+; CHECK-NEXT: %19 = call i2 @llvm.scmp.i2.i32(i32 10, i32 10) => i2 0
+; CHECK-NEXT: %20 = call i2 @llvm.scmp.i2.i32(i32 poison, i32 10) => poison
+; CHECK-NEXT: %21 = call <2 x i2> @llvm.scmp.v2i2.v2i32(<2 x i32> <i32 10, i32 poison>, <2 x i32> <i32 20, i32 10>) => { i2 -1, poison }
+; CHECK-NEXT: %22 = call i2 @llvm.ucmp.i2.i32(i32 10, i32 20) => i2 -1
+; CHECK-NEXT: %23 = call i2 @llvm.ucmp.i2.i32(i32 20, i32 10) => i2 1
+; CHECK-NEXT: %24 = call i2 @llvm.ucmp.i2.i32(i32 10, i32 10) => i2 0
+; CHECK-NEXT: %25 = call i2 @llvm.ucmp.i2.i32(i32 10, i32 poison) => poison
+; CHECK-NEXT: %26 = call <2 x i2> @llvm.ucmp.v2i2.v2i32(<2 x i32> <i32 10, i32 poison>, <2 x i32> <i32 20, i32 10>) => { i2 -1, poison }
; 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 013e299fef01d..3bec36f353ce2 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -165,6 +165,40 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
});
}
+ AnyValue visitOverflowIntBinOpWithResult(
+ CallBase &CB,
+ function_ref<std::pair<APInt, bool>(const APInt &, const APInt &)>
+ ScalarFn) {
+ const AnyValue &LHS = getValue(CB.getOperand(0));
+ const AnyValue &RHS = getValue(CB.getOperand(1));
+ if (!LHS.isAggregate())
+ return visitIntBinOpWithResult(
+ CB, [&](const APInt &ScalarLHS, const APInt &ScalarRHS) -> AnyValue {
+ auto [Res, Overflow] = ScalarFn(ScalarLHS, ScalarRHS);
+ return std::vector{AnyValue(Res), AnyValue::boolean(Overflow)};
+ });
+
+ auto &LHSVec = LHS.asAggregate();
+ auto &RHSVec = RHS.asAggregate();
+ std::vector<AnyValue> ResVec;
+ std::vector<AnyValue> OverflowVec;
+ ResVec.reserve(LHSVec.size());
+ OverflowVec.reserve(LHSVec.size());
+ for (const auto &[ScalarLHS, ScalarRHS] : zip(LHSVec, RHSVec)) {
+ if (ScalarLHS.isPoison() || ScalarRHS.isPoison()) {
+ ResVec.push_back(AnyValue::poison());
+ OverflowVec.push_back(AnyValue::poison());
+ continue;
+ }
+ auto [Res, Overflow] =
+ ScalarFn(ScalarLHS.asInteger(), ScalarRHS.asInteger());
+ ResVec.push_back(AnyValue(Res));
+ OverflowVec.push_back(AnyValue::boolean(Overflow));
+ }
+ return std::vector{AnyValue(std::move(ResVec)),
+ AnyValue(std::move(OverflowVec))};
+ }
+
AnyValue
computeTriOp(Type *Ty, const AnyValue &Op1, const AnyValue &Op2,
const AnyValue &Op3,
@@ -587,8 +621,9 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
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 {
+ return visitOverflowIntBinOpWithResult(
+ CB,
+ [IID](const APInt &LHS, const APInt &RHS) -> std::pair<APInt, bool> {
APInt Res;
bool Overflow = false;
switch (IID) {
@@ -613,7 +648,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
default:
llvm_unreachable("Unexpected intrinsic ID");
}
- return std::vector{AnyValue(Res), AnyValue::boolean(Overflow)};
+ return {Res, Overflow};
});
}
case Intrinsic::sadd_sat:
>From 3e815257ccf2f3714fb7db42408809ae39139466 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Thu, 23 Apr 2026 19:18:10 +0800
Subject: [PATCH 4/4] [llubi] Add test for vscale overflow
---
llvm/test/tools/llubi/intr_vscale_poison.ll | 13 +++++++++++++
1 file changed, 13 insertions(+)
create mode 100644 llvm/test/tools/llubi/intr_vscale_poison.ll
diff --git a/llvm/test/tools/llubi/intr_vscale_poison.ll b/llvm/test/tools/llubi/intr_vscale_poison.ll
new file mode 100644
index 0000000000000..2fe406d532254
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_vscale_poison.ll
@@ -0,0 +1,13 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --vscale=257 --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+ call i8 @llvm.vscale.i8()
+ call i16 @llvm.vscale.i16()
+ ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %1 = call i8 @llvm.vscale.i8() => poison
+; CHECK-NEXT: %2 = call i16 @llvm.vscale.i16() => i16 257
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
More information about the llvm-commits
mailing list