[llvm] a8b40e8 - [llubi] Implement experimental vector intrinsics (#206899)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Jul 5 02:45:00 PDT 2026
Author: Zhige Chen
Date: 2026-07-05T17:44:56+08:00
New Revision: a8b40e80344d09b9a1b2b14bde934494409db9df
URL: https://github.com/llvm/llvm-project/commit/a8b40e80344d09b9a1b2b14bde934494409db9df
DIFF: https://github.com/llvm/llvm-project/commit/a8b40e80344d09b9a1b2b14bde934494409db9df.diff
LOG: [llubi] Implement experimental vector intrinsics (#206899)
This PR implements experimental vector intrinsics.
Added:
llvm/test/tools/llubi/intr_experimental_vector.ll
Modified:
llvm/tools/llubi/lib/Interpreter.cpp
Removed:
################################################################################
diff --git a/llvm/test/tools/llubi/intr_experimental_vector.ll b/llvm/test/tools/llubi/intr_experimental_vector.ll
new file mode 100644
index 0000000000000..100e77c189c60
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_experimental_vector.ll
@@ -0,0 +1,141 @@
+; 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() {
+ %cttz_some = call i8 @llvm.experimental.cttz.elts.i8.v4i1(<4 x i1> <i1 false, i1 false, i1 true, i1 false>, i1 true)
+ %cttz_all = call i8 @llvm.experimental.cttz.elts.i8.v4i1(<4 x i1> zeroinitializer, i1 false)
+ %cttz_poison = call i8 @llvm.experimental.cttz.elts.i8.v4i1(<4 x i1> zeroinitializer, i1 true)
+
+ %evl_zero = call i32 @llvm.experimental.get.vector.length.i32(i32 0, i32 4, i1 false)
+ %evl_short = call i32 @llvm.experimental.get.vector.length.i32(i32 3, i32 4, i1 false)
+ %evl_full = call i32 @llvm.experimental.get.vector.length.i32(i32 9, i32 4, i1 false)
+ %evl_scalable = call i32 @llvm.experimental.get.vector.length.i32(i32 20, i32 4, i1 true)
+ %evl_poison = call i32 @llvm.experimental.get.vector.length.i64(i64 poison, i32 4, i1 false)
+
+ %last = call i32 @llvm.experimental.vector.extract.last.active.v4i32(<4 x i32> <i32 10, i32 20, i32 30, i32 40>, <4 x i1> <i1 true, i1 false, i1 true, i1 false>, i32 99)
+ %last_passthru = call i32 @llvm.experimental.vector.extract.last.active.v4i32(<4 x i32> <i32 10, i32 20, i32 30, i32 40>, <4 x i1> zeroinitializer, i32 99)
+ %last_poison = call i32 @llvm.experimental.vector.extract.last.active.v4i32(<4 x i32> <i32 10, i32 20, i32 30, i32 40>, <4 x i1> <i1 false, i1 poison, i1 false, i1 false>, i32 99)
+
+ %compress = call <4 x i32> @llvm.experimental.vector.compress.v4i32(<4 x i32> <i32 10, i32 20, i32 30, i32 40>, <4 x i1> <i1 false, i1 true, i1 false, i1 true>, <4 x i32> <i32 101, i32 102, i32 103, i32 104>)
+ %compress_all_false = call <4 x i32> @llvm.experimental.vector.compress.v4i32(<4 x i32> <i32 10, i32 20, i32 30, i32 40>, <4 x i1> zeroinitializer, <4 x i32> <i32 101, i32 102, i32 103, i32 104>)
+ %compress_poison = call <4 x i32> @llvm.experimental.vector.compress.v4i32(<4 x i32> <i32 10, i32 20, i32 30, i32 40>, <4 x i1> <i1 false, i1 poison, i1 false, i1 false>, <4 x i32> zeroinitializer)
+
+ %match = call <4 x i1> @llvm.experimental.vector.match.v4i1.v4i8.v3i8(<4 x i8> <i8 1, i8 2, i8 3, i8 4>, <3 x i8> <i8 4, i8 2, i8 9>, <4 x i1> <i1 true, i1 true, i1 false, i1 true>)
+ %match_poison_needles = call <4 x i1> @llvm.experimental.vector.match.v4i1.v4i8.v3i8(<4 x i8> <i8 1, i8 2, i8 3, i8 4>, <3 x i8> <i8 poison, i8 3, i8 9>, <4 x i1> <i1 true, i1 true, i1 true, i1 false>)
+
+ %buckets = alloca [8 x i32], align 4
+ %b0 = getelementptr inbounds [8 x i32], ptr %buckets, i64 0, i64 0
+ %b1 = getelementptr inbounds [8 x i32], ptr %buckets, i64 0, i64 1
+ %b2 = getelementptr inbounds [8 x i32], ptr %buckets, i64 0, i64 2
+ %b3 = getelementptr inbounds [8 x i32], ptr %buckets, i64 0, i64 3
+ store i32 10, ptr %b0, align 4
+ store i32 20, ptr %b1, align 4
+ store i32 30, ptr %b2, align 4
+ store i32 40, ptr %b3, align 4
+ %ptrs_add0 = insertelement <4 x ptr> poison, ptr %b0, i32 0
+ %ptrs_add1 = insertelement <4 x ptr> %ptrs_add0, ptr %b1, i32 1
+ %ptrs_add2 = insertelement <4 x ptr> %ptrs_add1, ptr %b0, i32 2
+ %ptrs_add3 = insertelement <4 x ptr> %ptrs_add2, ptr %b2, i32 3
+ call void @llvm.experimental.vector.histogram.add.v4p0.i32(<4 x ptr> %ptrs_add3, i32 5, <4 x i1> <i1 true, i1 true, i1 true, i1 false>)
+ %add0 = load i32, ptr %b0, align 4
+ %add1 = load i32, ptr %b1, align 4
+ %add2 = load i32, ptr %b2, align 4
+
+ store i32 4294967290, ptr %b0, align 4
+ store i32 1, ptr %b1, align 4
+ %ptrs_sat0 = insertelement <4 x ptr> poison, ptr %b0, i32 0
+ %ptrs_sat1 = insertelement <4 x ptr> %ptrs_sat0, ptr %b0, i32 1
+ %ptrs_sat2 = insertelement <4 x ptr> %ptrs_sat1, ptr %b1, i32 2
+ %ptrs_sat3 = insertelement <4 x ptr> %ptrs_sat2, ptr %b1, i32 3
+ call void @llvm.experimental.vector.histogram.uadd.sat.v4p0.i32(<4 x ptr> %ptrs_sat3, i32 4, <4 x i1> <i1 true, i1 true, i1 false, i1 true>)
+ %sat0 = load i32, ptr %b0, align 4
+ %sat1 = load i32, ptr %b1, align 4
+
+ store i32 7, ptr %b0, align 4
+ store i32 42, ptr %b1, align 4
+ call void @llvm.experimental.vector.histogram.umax.v4p0.i32(<4 x ptr> %ptrs_add3, i32 20, <4 x i1> <i1 true, i1 true, i1 true, i1 false>)
+ %max0 = load i32, ptr %b0, align 4
+ %max1 = load i32, ptr %b1, align 4
+
+ store i32 7, ptr %b0, align 4
+ store i32 42, ptr %b1, align 4
+ call void @llvm.experimental.vector.histogram.umin.v4p0.i32(<4 x ptr> %ptrs_add3, i32 20, <4 x i1> <i1 true, i1 true, i1 true, i1 false>)
+ %min0 = load i32, ptr %b0, align 4
+ %min1 = load i32, ptr %b1, align 4
+
+ %overlap = alloca [8 x i8], align 1
+ store i64 0, ptr %overlap, align 1
+ %overlap_p0 = getelementptr inbounds [8 x i8], ptr %overlap, i64 0, i64 0
+ %overlap_p2 = getelementptr inbounds i8, ptr %overlap_p0, i64 2
+ %overlap_ptrs0 = insertelement <2 x ptr> poison, ptr %overlap_p0, i32 0
+ %overlap_ptrs1 = insertelement <2 x ptr> %overlap_ptrs0, ptr %overlap_p2, i32 1
+ call void @llvm.experimental.vector.histogram.add.v2p0.i32(<2 x ptr> %overlap_ptrs1, i32 65536, <2 x i1> <i1 true, i1 true>)
+ %overlap0 = load i32, ptr %overlap_p0, align 1
+ %overlap2 = load i32, ptr %overlap_p2, align 1
+
+ ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %cttz_some = call i8 @llvm.experimental.cttz.elts.i8.v4i1(<4 x i1> <i1 false, i1 false, i1 true, i1 false>, i1 true) => i8 2
+; CHECK-NEXT: %cttz_all = call i8 @llvm.experimental.cttz.elts.i8.v4i1(<4 x i1> zeroinitializer, i1 false) => i8 4
+; CHECK-NEXT: %cttz_poison = call i8 @llvm.experimental.cttz.elts.i8.v4i1(<4 x i1> zeroinitializer, i1 true) => poison
+; CHECK-NEXT: %evl_zero = call i32 @llvm.experimental.get.vector.length.i32(i32 0, i32 4, i1 false) => i32 0
+; CHECK-NEXT: %evl_short = call i32 @llvm.experimental.get.vector.length.i32(i32 3, i32 4, i1 false) => i32 3
+; CHECK-NEXT: %evl_full = call i32 @llvm.experimental.get.vector.length.i32(i32 9, i32 4, i1 false) => i32 3
+; CHECK-NEXT: %evl_scalable = call i32 @llvm.experimental.get.vector.length.i32(i32 20, i32 4, i1 true) => i32 13
+; CHECK-NEXT: %evl_poison = call i32 @llvm.experimental.get.vector.length.i64(i64 poison, i32 4, i1 false) => poison
+; CHECK-NEXT: %last = call i32 @llvm.experimental.vector.extract.last.active.v4i32(<4 x i32> <i32 10, i32 20, i32 30, i32 40>, <4 x i1> <i1 true, i1 false, i1 true, i1 false>, i32 99) => i32 30
+; CHECK-NEXT: %last_passthru = call i32 @llvm.experimental.vector.extract.last.active.v4i32(<4 x i32> <i32 10, i32 20, i32 30, i32 40>, <4 x i1> zeroinitializer, i32 99) => i32 99
+; CHECK-NEXT: %last_poison = call i32 @llvm.experimental.vector.extract.last.active.v4i32(<4 x i32> <i32 10, i32 20, i32 30, i32 40>, <4 x i1> <i1 false, i1 poison, i1 false, i1 false>, i32 99) => poison
+; CHECK-NEXT: %compress = call <4 x i32> @llvm.experimental.vector.compress.v4i32(<4 x i32> <i32 10, i32 20, i32 30, i32 40>, <4 x i1> <i1 false, i1 true, i1 false, i1 true>, <4 x i32> <i32 101, i32 102, i32 103, i32 104>) => { i32 20, i32 40, i32 103, i32 104 }
+; CHECK-NEXT: %compress_all_false = call <4 x i32> @llvm.experimental.vector.compress.v4i32(<4 x i32> <i32 10, i32 20, i32 30, i32 40>, <4 x i1> zeroinitializer, <4 x i32> <i32 101, i32 102, i32 103, i32 104>) => { i32 101, i32 102, i32 103, i32 104 }
+; CHECK-NEXT: %compress_poison = call <4 x i32> @llvm.experimental.vector.compress.v4i32(<4 x i32> <i32 10, i32 20, i32 30, i32 40>, <4 x i1> <i1 false, i1 poison, i1 false, i1 false>, <4 x i32> zeroinitializer) => { poison, poison, poison, poison }
+; CHECK-NEXT: %match = call <4 x i1> @llvm.experimental.vector.match.v4i8.v3i8(<4 x i8> <i8 1, i8 2, i8 3, i8 4>, <3 x i8> <i8 4, i8 2, i8 9>, <4 x i1> <i1 true, i1 true, i1 false, i1 true>) => { F, T, F, T }
+; CHECK-NEXT: %match_poison_needles = call <4 x i1> @llvm.experimental.vector.match.v4i8.v3i8(<4 x i8> <i8 1, i8 2, i8 3, i8 4>, <3 x i8> <i8 poison, i8 3, i8 9>, <4 x i1> <i1 true, i1 true, i1 true, i1 false>) => { poison, poison, poison, F }
+; CHECK-NEXT: %buckets = alloca [8 x i32], align 4 => ptr 0x8 [buckets]
+; CHECK-NEXT: %b0 = getelementptr inbounds [8 x i32], ptr %buckets, i64 0, i64 0 => ptr 0x8 [buckets]
+; CHECK-NEXT: %b1 = getelementptr inbounds [8 x i32], ptr %buckets, i64 0, i64 1 => ptr 0xC [buckets + 4]
+; CHECK-NEXT: %b2 = getelementptr inbounds [8 x i32], ptr %buckets, i64 0, i64 2 => ptr 0x10 [buckets + 8]
+; CHECK-NEXT: %b3 = getelementptr inbounds [8 x i32], ptr %buckets, i64 0, i64 3 => ptr 0x14 [buckets + 12]
+; CHECK-NEXT: store i32 10, ptr %b0, align 4
+; CHECK-NEXT: store i32 20, ptr %b1, align 4
+; CHECK-NEXT: store i32 30, ptr %b2, align 4
+; CHECK-NEXT: store i32 40, ptr %b3, align 4
+; CHECK-NEXT: %ptrs_add0 = insertelement <4 x ptr> poison, ptr %b0, i32 0 => { ptr 0x8 [buckets], poison, poison, poison }
+; CHECK-NEXT: %ptrs_add1 = insertelement <4 x ptr> %ptrs_add0, ptr %b1, i32 1 => { ptr 0x8 [buckets], ptr 0xC [buckets + 4], poison, poison }
+; CHECK-NEXT: %ptrs_add2 = insertelement <4 x ptr> %ptrs_add1, ptr %b0, i32 2 => { ptr 0x8 [buckets], ptr 0xC [buckets + 4], ptr 0x8 [buckets], poison }
+; CHECK-NEXT: %ptrs_add3 = insertelement <4 x ptr> %ptrs_add2, ptr %b2, i32 3 => { ptr 0x8 [buckets], ptr 0xC [buckets + 4], ptr 0x8 [buckets], ptr 0x10 [buckets + 8] }
+; CHECK-NEXT: call void @llvm.experimental.vector.histogram.add.v4p0.i32(<4 x ptr> %ptrs_add3, i32 5, <4 x i1> <i1 true, i1 true, i1 true, i1 false>)
+; CHECK-NEXT: %add0 = load i32, ptr %b0, align 4 => i32 20
+; CHECK-NEXT: %add1 = load i32, ptr %b1, align 4 => i32 25
+; CHECK-NEXT: %add2 = load i32, ptr %b2, align 4 => i32 30
+; CHECK-NEXT: store i32 -6, ptr %b0, align 4
+; CHECK-NEXT: store i32 1, ptr %b1, align 4
+; CHECK-NEXT: %ptrs_sat0 = insertelement <4 x ptr> poison, ptr %b0, i32 0 => { ptr 0x8 [buckets], poison, poison, poison }
+; CHECK-NEXT: %ptrs_sat1 = insertelement <4 x ptr> %ptrs_sat0, ptr %b0, i32 1 => { ptr 0x8 [buckets], ptr 0x8 [buckets], poison, poison }
+; CHECK-NEXT: %ptrs_sat2 = insertelement <4 x ptr> %ptrs_sat1, ptr %b1, i32 2 => { ptr 0x8 [buckets], ptr 0x8 [buckets], ptr 0xC [buckets + 4], poison }
+; CHECK-NEXT: %ptrs_sat3 = insertelement <4 x ptr> %ptrs_sat2, ptr %b1, i32 3 => { ptr 0x8 [buckets], ptr 0x8 [buckets], ptr 0xC [buckets + 4], ptr 0xC [buckets + 4] }
+; CHECK-NEXT: call void @llvm.experimental.vector.histogram.uadd.sat.v4p0.i32(<4 x ptr> %ptrs_sat3, i32 4, <4 x i1> <i1 true, i1 true, i1 false, i1 true>)
+; CHECK-NEXT: %sat0 = load i32, ptr %b0, align 4 => i32 -1
+; CHECK-NEXT: %sat1 = load i32, ptr %b1, align 4 => i32 5
+; CHECK-NEXT: store i32 7, ptr %b0, align 4
+; CHECK-NEXT: store i32 42, ptr %b1, align 4
+; CHECK-NEXT: call void @llvm.experimental.vector.histogram.umax.v4p0.i32(<4 x ptr> %ptrs_add3, i32 20, <4 x i1> <i1 true, i1 true, i1 true, i1 false>)
+; CHECK-NEXT: %max0 = load i32, ptr %b0, align 4 => i32 20
+; CHECK-NEXT: %max1 = load i32, ptr %b1, align 4 => i32 42
+; CHECK-NEXT: store i32 7, ptr %b0, align 4
+; CHECK-NEXT: store i32 42, ptr %b1, align 4
+; CHECK-NEXT: call void @llvm.experimental.vector.histogram.umin.v4p0.i32(<4 x ptr> %ptrs_add3, i32 20, <4 x i1> <i1 true, i1 true, i1 true, i1 false>)
+; CHECK-NEXT: %min0 = load i32, ptr %b0, align 4 => i32 7
+; CHECK-NEXT: %min1 = load i32, ptr %b1, align 4 => i32 20
+; CHECK-NEXT: %overlap = alloca [8 x i8], align 1 => ptr 0x29 [overlap]
+; CHECK-NEXT: store i64 0, ptr %overlap, align 1
+; CHECK-NEXT: %overlap_p0 = getelementptr inbounds [8 x i8], ptr %overlap, i64 0, i64 0 => ptr 0x29 [overlap]
+; CHECK-NEXT: %overlap_p2 = getelementptr inbounds i8, ptr %overlap_p0, i64 2 => ptr 0x2B [overlap + 2]
+; CHECK-NEXT: %overlap_ptrs0 = insertelement <2 x ptr> poison, ptr %overlap_p0, i32 0 => { ptr 0x29 [overlap], poison }
+; CHECK-NEXT: %overlap_ptrs1 = insertelement <2 x ptr> %overlap_ptrs0, ptr %overlap_p2, i32 1 => { ptr 0x29 [overlap], ptr 0x2B [overlap + 2] }
+; CHECK-NEXT: call void @llvm.experimental.vector.histogram.add.v2p0.i32(<2 x ptr> %overlap_ptrs1, i32 65536, <2 x i1> splat (i1 true))
+; CHECK-NEXT: %overlap0 = load i32, ptr %overlap_p0, align 1 => i32 0
+; CHECK-NEXT: %overlap2 = load i32, ptr %overlap_p2, align 1 => i32 65536
+; 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 d734a9962e6d9..425215b4aee2e 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -810,6 +810,104 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
return AnyValue();
}
+ static BooleanKind getMaskLane(const AnyValue &Mask, size_t I) {
+ return Mask.asAggregate()[I].asBoolean();
+ }
+
+ AnyValue callExperimentalVectorHistogramIntrinsic(CallBase &CB,
+ ArrayRef<AnyValue> Args,
+ Intrinsic::ID IID) {
+ struct LaneUpdate {
+ MemoryObject *MO;
+ uint64_t Offset;
+ uint64_t Count;
+ AnyValue Old;
+ AnyValue New;
+ };
+
+ const auto &Ptrs = Args[0].asAggregate();
+ const AnyValue &Update = Args[1];
+ const AnyValue &Mask = Args[2];
+ Type *ElemTy = CB.getArgOperand(1)->getType();
+ const uint64_t AccessSize = Ctx.getEffectiveTypeStoreSize(ElemTy);
+
+ SmallVector<LaneUpdate, 8> Lanes;
+ Lanes.reserve(Ptrs.size());
+ for (size_t I = 0, E = Ptrs.size(); I != E; ++I) {
+ switch (getMaskLane(Mask, I)) {
+ case BooleanKind::False:
+ continue;
+ case BooleanKind::Poison:
+ reportImmediateUB()
+ << "Poison mask lane in experimental vector histogram intrinsic.";
+ return AnyValue();
+ case BooleanKind::True:
+ break;
+ }
+
+ if (Ptrs[I].isPoison()) {
+ reportImmediateUB() << "Poison pointer lane in experimental vector "
+ "histogram intrinsic.";
+ return AnyValue();
+ }
+
+ auto [MO, Offset] =
+ verifyMemAccess(Ptrs[I].asPointer(), AccessSize, Align(1),
+ /*IsStore=*/true);
+ if (!MO)
+ return AnyValue();
+
+ Lanes.push_back({MO, Offset, 0, AnyValue(), AnyValue()});
+ }
+
+ for (LaneUpdate &Lane : Lanes) {
+ Lane.Count = count_if(Lanes, [&](const LaneUpdate &Other) {
+ return Other.MO == Lane.MO && Other.Offset == Lane.Offset;
+ });
+ Lane.Old = Ctx.load(*Lane.MO, Lane.Offset, ElemTy);
+ }
+
+ for (LaneUpdate &Lane : Lanes) {
+ const AnyValue &Old = Lane.Old;
+ AnyValue &New = Lane.New;
+
+ if (Old.isPoison() || Update.isPoison()) {
+ New = AnyValue::poison();
+ } else {
+ const APInt &OldInt = Old.asInteger();
+ const APInt &UpdateInt = Update.asInteger();
+
+ switch (IID) {
+ case Intrinsic::experimental_vector_histogram_add:
+ New = OldInt + UpdateInt * APInt(UpdateInt.getBitWidth(), Lane.Count,
+ /*isSigned=*/false,
+ /*implicitTrunc=*/true);
+ break;
+ case Intrinsic::experimental_vector_histogram_uadd_sat: {
+ APInt Acc = OldInt;
+ for (uint64_t I = 0; I != Lane.Count; ++I)
+ Acc = Acc.uadd_sat(UpdateInt);
+ New = Acc;
+ break;
+ }
+ case Intrinsic::experimental_vector_histogram_umax:
+ New = APIntOps::umax(OldInt, UpdateInt);
+ break;
+ case Intrinsic::experimental_vector_histogram_umin:
+ New = APIntOps::umin(OldInt, UpdateInt);
+ break;
+ default:
+ llvm_unreachable("Unexpected histogram intrinsic ID");
+ }
+ }
+ }
+
+ for (const LaneUpdate &Lane : Lanes)
+ Ctx.store(*Lane.MO, Lane.Offset, Lane.New, ElemTy);
+
+ return AnyValue();
+ }
+
public:
InstExecutor(Context &C, EventHandler &H, Function &F,
ArrayRef<AnyValue> Args, AnyValue &RetVal)
@@ -1629,6 +1727,155 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
case Intrinsic::experimental_noalias_scope_decl:
// FIXME: Not implemented yet. Currently it acts as a noop.
return AnyValue();
+ case Intrinsic::experimental_cttz_elts: {
+ auto *IsZeroPoisonC = cast<ConstantInt>(CB.getArgOperand(1));
+ const bool IsZeroPoison = IsZeroPoisonC->isOne();
+
+ const auto &Vec = Args[0].asAggregate();
+ const unsigned RetBW = RetTy->getIntegerBitWidth();
+
+ if (!isUIntN(RetBW, Vec.size()))
+ return AnyValue::poison();
+
+ uint64_t Count = 0;
+ for (const AnyValue &V : Vec) {
+ if (V.isPoison())
+ return AnyValue::poison();
+ if (!V.asInteger().isZero())
+ break;
+ ++Count;
+ }
+
+ if (Count == Vec.size() && IsZeroPoison)
+ return AnyValue::poison();
+ return APInt(RetBW, Count);
+ }
+ case Intrinsic::experimental_get_vector_length: {
+ auto *VFC = cast<ConstantInt>(CB.getArgOperand(1));
+ auto *ScalableC = cast<ConstantInt>(CB.getArgOperand(2));
+
+ if (Args[0].isPoison())
+ return AnyValue::poison();
+
+ const APInt &Cnt = Args[0].asInteger();
+ const uint64_t VF = VFC->getZExtValue();
+ const bool Scalable = ScalableC->isOne();
+
+ const uint64_t MaxLanes = Ctx.getEVL(ElementCount::get(VF, Scalable));
+
+ uint64_t Res = 0;
+ if (!Cnt.isZero()) {
+ if (Cnt.getActiveBits() <= 64 && Cnt.getZExtValue() <= MaxLanes) {
+ Res = Cnt.getZExtValue();
+ } else {
+ APInt Max(Cnt.getBitWidth(), MaxLanes);
+ APInt NumIters =
+ APIntOps::RoundingUDiv(Cnt, Max, APInt::Rounding::UP);
+ uint64_t Lower =
+ APIntOps::RoundingUDiv(Cnt, NumIters, APInt::Rounding::UP)
+ .getZExtValue();
+ uint64_t Range = MaxLanes - Lower + 1;
+ Res = Lower + Ctx.getRandomUInt64() % Range;
+ }
+ }
+
+ if (isIntN(32, Res))
+ return APInt(32, Res);
+ return AnyValue::poison();
+ }
+
+ case Intrinsic::experimental_vector_extract_last_active: {
+ const auto &Data = Args[0].asAggregate();
+ const AnyValue &Mask = Args[1];
+
+ for (size_t I = Data.size(); I != 0; --I) {
+ switch (getMaskLane(Mask, I - 1)) {
+ case BooleanKind::True:
+ return Data[I - 1];
+ case BooleanKind::False:
+ break;
+ case BooleanKind::Poison:
+ return AnyValue::poison();
+ }
+ }
+
+ return Args[2];
+ }
+
+ case Intrinsic::experimental_vector_compress: {
+ const auto &Val = Args[0].asAggregate();
+ const AnyValue &Mask = Args[1];
+ const auto &Passthru = Args[2].asAggregate();
+
+ std::vector<AnyValue> Res;
+ Res.reserve(Val.size());
+
+ for (size_t I = 0, E = Val.size(); I != E; ++I) {
+ switch (getMaskLane(Mask, I)) {
+ case BooleanKind::True:
+ Res.push_back(Val[I]);
+ break;
+ case BooleanKind::False:
+ break;
+ case BooleanKind::Poison:
+ return AnyValue::getPoisonValue(Ctx, RetTy);
+ }
+ }
+
+ for (size_t I = Res.size(), E = Val.size(); I != E; ++I)
+ Res.push_back(Passthru[I]);
+ return std::move(Res);
+ }
+
+ case Intrinsic::experimental_vector_match: {
+ const auto &Search = Args[0].asAggregate();
+ const auto &Needles = Args[1].asAggregate();
+ const auto &Mask = Args[2].asAggregate();
+
+ std::vector<AnyValue> Res;
+ Res.reserve(Search.size());
+
+ for (size_t I = 0, E = Search.size(); I != E; ++I) {
+ switch (Mask[I].asBoolean()) {
+ case BooleanKind::False:
+ Res.push_back(AnyValue::boolean(false));
+ continue;
+ case BooleanKind::Poison:
+ Res.push_back(AnyValue::poison());
+ continue;
+ case BooleanKind::True:
+ break;
+ }
+
+ if (Search[I].isPoison()) {
+ Res.push_back(AnyValue::poison());
+ continue;
+ }
+
+ bool Found = false;
+ bool SawPoison = false;
+ for (const AnyValue &Needle : Needles) {
+ if (Needle.isPoison()) {
+ SawPoison = true;
+ break;
+ }
+ if (Search[I].asInteger() == Needle.asInteger())
+ Found = true;
+ }
+
+ if (SawPoison)
+ Res.push_back(AnyValue::poison());
+ else
+ Res.push_back(AnyValue::boolean(Found));
+ }
+
+ return std::move(Res);
+ }
+ case Intrinsic::experimental_vector_histogram_add:
+ case Intrinsic::experimental_vector_histogram_uadd_sat:
+ case Intrinsic::experimental_vector_histogram_umax:
+ case Intrinsic::experimental_vector_histogram_umin:
+ return callExperimentalVectorHistogramIntrinsic(CB, Args, IID);
default:
Handler.onUnrecognizedInstruction(CB);
setFailed();
More information about the llvm-commits
mailing list