[llvm] [llubi] Implement experimental vector intrinsics (PR #206899)
Nikita Popov via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 1 01:14:37 PDT 2026
================
@@ -1629,6 +1725,150 @@ 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 (APInt::getMaxValue(RetBW).ult(Vec.size())) {
+ reportImmediateUB() << "experimental.cttz.elts return type too narrow.";
+ 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 =
+ Scalable ? Ctx.getEVL(ElementCount::getScalable(VF)) : VF;
+
+ uint64_t Res = 0;
+ if (!Cnt.isZero()) {
+ APInt Max(Cnt.getBitWidth(), MaxLanes);
+ Res = Cnt.ugt(Max) ? MaxLanes : Cnt.getZExtValue();
+ }
+
+ return APInt(32, Res);
+ }
+
+ 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()) {
----------------
nikic wrote:
I think the poison behavior of this intrinsic needs clarification in LangRef. It looks like you are assuming short-circuiting semantics, where if there is a matching element, the result is true, even if there is poison later. But one could also interpret this without short-circuiting, where any poison element in the needles vector results in poison output.
It looks like the SDAG expansion at least always propagates poison.
https://github.com/llvm/llvm-project/pull/206899
More information about the llvm-commits
mailing list