[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