[llvm] [IR][RFC] Add @llvm.mask.beforefirst intrinsic (PR #203874)
Luke Lau via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 29 01:56:23 PDT 2026
https://github.com/lukel97 updated https://github.com/llvm/llvm-project/pull/203874
>From bc2497b5279529e3602e01afc7bb94b362fcfced Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Mon, 15 Jun 2026 18:07:27 +0800
Subject: [PATCH 01/10] [IR][RFC] Add @llvm.mask.beforefirst intrinsic
---
llvm/docs/LangRef.rst | 40 ++++++
llvm/include/llvm/CodeGen/ISDOpcodes.h | 4 +
llvm/include/llvm/IR/Intrinsics.td | 5 +
.../include/llvm/Target/TargetSelectionDAG.td | 5 +
.../SelectionDAG/LegalizeIntegerTypes.cpp | 17 +--
llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h | 3 +-
.../SelectionDAG/LegalizeVectorOps.cpp | 29 ++++
.../SelectionDAG/SelectionDAGBuilder.cpp | 6 +
.../SelectionDAG/SelectionDAGDumper.cpp | 2 +
llvm/lib/CodeGen/TargetLoweringBase.cpp | 2 +
llvm/lib/IR/Verifier.cpp | 5 +
.../Target/AArch64/AArch64ISelLowering.cpp | 1 +
.../lib/Target/AArch64/AArch64SVEInstrInfo.td | 9 ++
llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 2 +
.../Target/RISCV/RISCVInstrInfoVSDPatterns.td | 4 +
.../CodeGen/AArch64/mask-beforefirst-sve.ll | 121 ++++++++++++++++
llvm/test/CodeGen/AArch64/mask-beforefirst.ll | 90 ++++++++++++
.../rvv/fixed-vectors-mask-beforefirst.ll | 134 ++++++++++++++++++
.../CodeGen/RISCV/rvv/mask-beforefirst.ll | 80 +++++++++++
llvm/test/Verifier/mask-beforefirst.ll | 9 ++
20 files changed, 554 insertions(+), 14 deletions(-)
create mode 100644 llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll
create mode 100644 llvm/test/CodeGen/AArch64/mask-beforefirst.ll
create mode 100644 llvm/test/CodeGen/RISCV/rvv/fixed-vectors-mask-beforefirst.ll
create mode 100644 llvm/test/CodeGen/RISCV/rvv/mask-beforefirst.ll
create mode 100644 llvm/test/Verifier/mask-beforefirst.ll
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index e570c9a385e8f..519ef6db68c75 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -22004,6 +22004,46 @@ Examples:
; because if any more lanes were active the load would be dependent on the
; completion of the store.
+.. _int_mask_beforefirst
+
+'``llvm.mask.beforefirst.*``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+This is an overloaded intrinsic.
+
+::
+
+ declare <4 x i1> @llvm.mask.beforefirst.v4i1(<4 x i1> %mask)
+ declare <vscale x 8 x i1> @llvm.mask.beforefirst.nxv8i1(<vscale x 8 x i1> %mask)
+
+
+Overview:
+"""""""""
+
+Given a vector mask, returns a new mask with all elements before the first active element in the input set to 1, and every element afterwards set to 0.
+
+Arguments:
+""""""""""
+
+Takes one argument which must be an i1 vector, and returns a vector of the same type.
+
+Semantics:
+""""""""""
+
+When the input is all zeroes, the result is all ones.
+
+Examples:
+"""""""""
+
+.. code-block:: llvm
+ @llvm.mask.beforefirst(<0,0,1,1>); ==> <1,1,0,0>
+ @llvm.mask.beforefirst(<0,0,0,0>); ==> <1,1,1,1>
+ @llvm.mask.beforefirst(<0,1,0,1>); ==> <1,0,0,0>
+ @llvm.mask.beforefirst(<1,0,0,1>); ==> <0,0,0,0>
+
+
Experimental Vector Intrinsics
------------------------------
diff --git a/llvm/include/llvm/CodeGen/ISDOpcodes.h b/llvm/include/llvm/CodeGen/ISDOpcodes.h
index 11099df775e38..c9ac660d1bcd0 100644
--- a/llvm/include/llvm/CodeGen/ISDOpcodes.h
+++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h
@@ -1603,6 +1603,10 @@ enum NodeType {
/// bits conform to getBooleanContents similar to the SETCC operator.
GET_ACTIVE_LANE_MASK,
+ /// Has one mask vector operand. Returns a mask with all ones up to but not
+ /// including the first active element in the input, followed by zeroes.
+ MASK_BEFOREFIRST,
+
/// The `llvm.loop.dependence.{war, raw}.mask` intrinsics
/// Operands: Load pointer, Store pointer, Element size, Lane offset
/// Output: Mask
diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index 7ae8e3d513ed0..323b8894f0517 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -2615,6 +2615,11 @@ def int_get_active_lane_mask:
[llvm_anyint_ty, LLVMMatchType<1>],
[IntrNoMem, IntrSpeculatable]>;
+def int_mask_beforefirst:
+ DefaultAttrsIntrinsic<[llvm_anyvector_ty],
+ [LLVMMatchType<0>],
+ [IntrNoMem, IntrSpeculatable]>;
+
def int_experimental_get_vector_length:
DefaultAttrsIntrinsic<[llvm_i32_ty],
[llvm_anyint_ty, llvm_i32_ty, llvm_i1_ty],
diff --git a/llvm/include/llvm/Target/TargetSelectionDAG.td b/llvm/include/llvm/Target/TargetSelectionDAG.td
index 8ed2698f12b1b..936bc58a137f8 100644
--- a/llvm/include/llvm/Target/TargetSelectionDAG.td
+++ b/llvm/include/llvm/Target/TargetSelectionDAG.td
@@ -919,6 +919,11 @@ def get_active_lane_mask
SDTypeProfile<1, 2, [SDTCisVec<0>, SDTCisInt<1>, SDTCisSameAs<1, 2>]>,
[]>;
+def mask_beforefirst
+ : SDNode<"ISD::MASK_BEFOREFIRST",
+ SDTypeProfile<1, 1, [SDTCisVec<0>, SDTCisSameAs<0, 1>]>,
+ []>;
+
// Nodes for intrinsics, you should use the intrinsic itself and let tblgen use
// these internally. Don't reference these directly.
def intrinsic_void : SDNode<"ISD::INTRINSIC_VOID",
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp
index a5faff4f038cd..3472842ed5d58 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp
@@ -162,11 +162,9 @@ void DAGTypeLegalizer::PromoteIntegerResult(SDNode *N, unsigned ResNo) {
Res = PromoteIntRes_EXTEND_VECTOR_INREG(N); break;
case ISD::VECTOR_FIND_LAST_ACTIVE:
- Res = PromoteIntRes_VECTOR_FIND_LAST_ACTIVE(N);
- break;
-
+ case ISD::MASK_BEFOREFIRST:
case ISD::GET_ACTIVE_LANE_MASK:
- Res = PromoteIntRes_GET_ACTIVE_LANE_MASK(N);
+ Res = PromoteIntRes_BooleanVector(N);
break;
case ISD::PARTIAL_REDUCE_UMLA:
@@ -2195,6 +2193,7 @@ bool DAGTypeLegalizer::PromoteIntegerOperand(SDNode *N, unsigned OpNo) {
case ISD::VECTOR_FIND_LAST_ACTIVE:
case ISD::CTTZ_ELTS:
case ISD::CTTZ_ELTS_ZERO_POISON:
+ case ISD::MASK_BEFOREFIRST:
Res = PromoteIntOp_UnaryBooleanVectorOp(N, OpNo);
break;
case ISD::GET_ACTIVE_LANE_MASK:
@@ -6482,16 +6481,10 @@ SDValue DAGTypeLegalizer::PromoteIntRes_EXTEND_VECTOR_INREG(SDNode *N) {
return DAG.getNode(N->getOpcode(), dl, NVT, N->getOperand(0));
}
-SDValue DAGTypeLegalizer::PromoteIntRes_VECTOR_FIND_LAST_ACTIVE(SDNode *N) {
- EVT VT = N->getValueType(0);
- EVT NVT = TLI.getTypeToTransformTo(*DAG.getContext(), VT);
- return DAG.getNode(ISD::VECTOR_FIND_LAST_ACTIVE, SDLoc(N), NVT, N->ops());
-}
-
-SDValue DAGTypeLegalizer::PromoteIntRes_GET_ACTIVE_LANE_MASK(SDNode *N) {
+SDValue DAGTypeLegalizer::PromoteIntRes_BooleanVector(SDNode *N) {
EVT VT = N->getValueType(0);
EVT NVT = TLI.getTypeToTransformTo(*DAG.getContext(), VT);
- return DAG.getNode(ISD::GET_ACTIVE_LANE_MASK, SDLoc(N), NVT, N->ops());
+ return DAG.getNode(N->getOpcode(), SDLoc(N), NVT, N->ops());
}
SDValue DAGTypeLegalizer::PromoteIntRes_PARTIAL_REDUCE_MLA(SDNode *N) {
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
index 450eba435cc0b..5a81c96320c3c 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
@@ -362,8 +362,7 @@ class LLVM_LIBRARY_VISIBILITY DAGTypeLegalizer {
SDValue PromoteIntRes_IS_FPCLASS(SDNode *N);
SDValue PromoteIntRes_PATCHPOINT(SDNode *N);
SDValue PromoteIntRes_READ_REGISTER(SDNode *N);
- SDValue PromoteIntRes_VECTOR_FIND_LAST_ACTIVE(SDNode *N);
- SDValue PromoteIntRes_GET_ACTIVE_LANE_MASK(SDNode *N);
+ SDValue PromoteIntRes_BooleanVector(SDNode *N);
SDValue PromoteIntRes_PARTIAL_REDUCE_MLA(SDNode *N);
SDValue PromoteIntRes_LOOP_DEPENDENCE_MASK(SDNode *N);
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp
index 03e36b3ea75e8..75ffe8f7e5b90 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp
@@ -29,6 +29,7 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/Analysis/ValueTracking.h"
#include "llvm/Analysis/VectorUtils.h"
#include "llvm/CodeGen/ISDOpcodes.h"
#include "llvm/CodeGen/SelectionDAG.h"
@@ -139,6 +140,7 @@ class VectorLegalizer {
SDValue ExpandVP_FABS(SDNode *Node);
SDValue ExpandVP_FCOPYSIGN(SDNode *Node);
SDValue ExpandLOOP_DEPENDENCE_MASK(SDNode *N);
+ SDValue ExpandMASK_BEFOREFIRST(SDNode *N);
SDValue ExpandMaskedBinOp(SDNode *N);
SDValue ExpandSELECT(SDNode *Node);
std::pair<SDValue, SDValue> ExpandLoad(SDNode *N);
@@ -484,6 +486,7 @@ SDValue VectorLegalizer::LegalizeOp(SDValue Op) {
case ISD::UCMP:
case ISD::LOOP_DEPENDENCE_WAR_MASK:
case ISD::LOOP_DEPENDENCE_RAW_MASK:
+ case ISD::MASK_BEFOREFIRST:
case ISD::MASKED_UDIV:
case ISD::MASKED_SDIV:
case ISD::MASKED_UREM:
@@ -1398,6 +1401,9 @@ void VectorLegalizer::Expand(SDNode *Node, SmallVectorImpl<SDValue> &Results) {
case ISD::LOOP_DEPENDENCE_RAW_MASK:
Results.push_back(ExpandLOOP_DEPENDENCE_MASK(Node));
return;
+ case ISD::MASK_BEFOREFIRST:
+ Results.push_back(ExpandMASK_BEFOREFIRST(Node));
+ return;
case ISD::FADD:
case ISD::FMUL:
@@ -1917,6 +1923,29 @@ SDValue VectorLegalizer::ExpandLOOP_DEPENDENCE_MASK(SDNode *N) {
return TLI.expandLoopDependenceMask(N, DAG);
}
+SDValue VectorLegalizer::ExpandMASK_BEFOREFIRST(SDNode *N) {
+ // Expand to (setcc ult (step-vector), (cttz_elts x))
+ SDLoc DL(N);
+ EVT VT = N->getValueType(0);
+
+ // Compute the type for the cttz_elts.
+ ConstantRange VScaleRange(1, /*isFullSet=*/true); // Fixed length default.
+ if (VT.isScalableVector())
+ VScaleRange = getVScaleRange(&DAG.getMachineFunction().getFunction(), 64);
+ const TargetLowering &TLI = DAG.getTargetLoweringInfo();
+ uint64_t EltWidth = TLI.getBitWidthForCttzElements(
+ EVT(TLI.getVectorIdxTy(DAG.getDataLayout())), VT.getVectorElementCount(),
+ /*ZeroIsPoison=*/false, &VScaleRange);
+
+ EVT EltVT = EVT::getIntegerVT(*DAG.getContext(), EltWidth);
+ EVT CttzEltsVT =
+ EVT::getVectorVT(*DAG.getContext(), EltVT, VT.getVectorElementCount());
+ SDValue CttzElts = DAG.getSplatVector(
+ CttzEltsVT, DL, DAG.getNode(ISD::CTTZ_ELTS, DL, EltVT, N->getOperand(0)));
+ SDValue StepVector = DAG.getStepVector(DL, CttzEltsVT);
+ return DAG.getSetCC(DL, VT, StepVector, CttzElts, ISD::CondCode::SETULT);
+}
+
SDValue VectorLegalizer::ExpandMaskedBinOp(SDNode *N) {
// Masked bin ops don't have undefined behaviour when dividing by zero
// on disabled lanes and produce poison instead. Replace the divisor on the
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 52af36014c5b3..fb12d254e9010 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -8302,6 +8302,12 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
setValue(&I, SetCC);
return;
}
+ case Intrinsic::mask_beforefirst: {
+ SDValue Op = getValue(I.getOperand(0));
+ setValue(&I,
+ DAG.getNode(ISD::MASK_BEFOREFIRST, sdl, Op.getValueType(), Op));
+ return;
+ }
case Intrinsic::experimental_get_vector_length: {
assert(cast<ConstantInt>(I.getOperand(1))->getSExtValue() > 0 &&
"Expected positive VF");
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
index 7b500fc863b72..8ae7f521e45ad 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
@@ -602,6 +602,8 @@ std::string SDNode::getOperationName(const SelectionDAG *G) const {
case ISD::GET_ACTIVE_LANE_MASK:
return "get_active_lane_mask";
+ case ISD::MASK_BEFOREFIRST:
+ return "mask_beforefirst";
case ISD::PARTIAL_REDUCE_UMLA:
return "partial_reduce_umla";
diff --git a/llvm/lib/CodeGen/TargetLoweringBase.cpp b/llvm/lib/CodeGen/TargetLoweringBase.cpp
index 83ad156e770f4..0f8592dc17a2a 100644
--- a/llvm/lib/CodeGen/TargetLoweringBase.cpp
+++ b/llvm/lib/CodeGen/TargetLoweringBase.cpp
@@ -1257,6 +1257,8 @@ void TargetLoweringBase::initActions() {
setOperationAction(ISD::LOOP_DEPENDENCE_RAW_MASK, VT, Expand);
setOperationAction(ISD::LOOP_DEPENDENCE_WAR_MASK, VT, Expand);
+ setOperationAction(ISD::MASK_BEFOREFIRST, VT, Expand);
+
// FP environment operations default to expand.
setOperationAction(ISD::GET_FPENV, VT, Expand);
setOperationAction(ISD::SET_FPENV, VT, Expand);
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 6d900ce0d917f..e2a78549d563c 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -6675,6 +6675,11 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
Call);
break;
}
+ case Intrinsic::mask_beforefirst: {
+ Check(Call.getType()->getScalarType()->isIntegerTy(1),
+ "mask.beforefirst element type must be i1", Call);
+ break;
+ }
case Intrinsic::experimental_get_vector_length: {
ConstantInt *VF = cast<ConstantInt>(Call.getArgOperand(1));
Check(!VF->isNegative() && !VF->isZero(),
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index faf102f300659..94f12fa183160 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -1594,6 +1594,7 @@ AArch64TargetLowering::AArch64TargetLowering(const TargetMachine &TM,
Custom);
setOperationAction(ISD::VECTOR_FIND_LAST_ACTIVE, VT, Legal);
setOperationAction(ISD::GET_ACTIVE_LANE_MASK, VT, Legal);
+ setOperationAction(ISD::MASK_BEFOREFIRST, VT, Legal);
}
if (Subtarget->isSVEorStreamingSVEAvailable() &&
diff --git a/llvm/lib/Target/AArch64/AArch64SVEInstrInfo.td b/llvm/lib/Target/AArch64/AArch64SVEInstrInfo.td
index 4712406e37e6b..4ffdc1bcef1fb 100644
--- a/llvm/lib/Target/AArch64/AArch64SVEInstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64SVEInstrInfo.td
@@ -1143,6 +1143,15 @@ let Predicates = [HasSVE_or_SME] in {
defm BRKB_PPmP : sve_int_break_m<0b101, "brkb", int_aarch64_sve_brkb>;
defm BRKBS_PPzP : sve_int_break_z<0b110, "brkbs", null_frag>;
+ def : Pat<(nxv16i1 (mask_beforefirst PPR8:$Pn)),
+ (BRKB_PPzP (PTRUE_B 31), PPR8:$Pn)>;
+ def : Pat<(nxv8i1 (mask_beforefirst PPR8:$Pn)),
+ (BRKB_PPzP (PTRUE_H 31), PPR8:$Pn)>;
+ def : Pat<(nxv4i1 (mask_beforefirst PPR8:$Pn)),
+ (BRKB_PPzP (PTRUE_S 31), PPR8:$Pn)>;
+ def : Pat<(nxv2i1 (mask_beforefirst PPR8:$Pn)),
+ (BRKB_PPzP (PTRUE_D 31), PPR8:$Pn)>;
+
defm PTEST_PP : sve_int_ptest<0b010000, "ptest", AArch64ptest, AArch64ptest_any, AArch64ptest_first>;
defm PFALSE : sve_int_pfalse<0b000000, "pfalse">;
defm PFIRST : sve_int_pfirst<0b00000, "pfirst", int_aarch64_sve_pfirst>;
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 4a937fe2c6462..008a14981821f 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -1043,6 +1043,8 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
setOperationPromotedToType(
{ISD::VECTOR_SPLICE_LEFT, ISD::VECTOR_SPLICE_RIGHT}, VT,
MVT::getVectorVT(MVT::i8, VT.getVectorElementCount()));
+
+ setOperationAction(ISD::MASK_BEFOREFIRST, VT, Legal);
}
for (MVT VT : IntVecVTs) {
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoVSDPatterns.td b/llvm/lib/Target/RISCV/RISCVInstrInfoVSDPatterns.td
index 3c8ab6bad5452..1ac47725adf20 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoVSDPatterns.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoVSDPatterns.td
@@ -1254,6 +1254,10 @@ foreach mti = AllMasks in {
def : Pat<(mti.Mask rvv_vmnot_op:$rs),
(!cast<Instruction>("PseudoVMNAND_MM_"#mti.BX)
VR:$rs, VR:$rs, mti.AVL, mti.Log2SEW)>;
+
+ def : Pat<(mti.Mask (mask_beforefirst VR:$vs2)),
+ (!cast<Instruction>("PseudoVMSBF_M_"#mti.BX)
+ VR:$vs2, mti.AVL, mti.Log2SEW)>;
}
}
diff --git a/llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll b/llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll
new file mode 100644
index 0000000000000..2fe1d44a459bf
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll
@@ -0,0 +1,121 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple aarch64 -mattr=+sve | FileCheck %s
+
+define <vscale x 16 x i1> @nxv16i1(<vscale x 16 x i1> %m) {
+; CHECK-LABEL: nxv16i1:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ptrue p1.b
+; CHECK-NEXT: brkb p0.b, p1/z, p0.b
+; CHECK-NEXT: ret
+ %x = call <vscale x 16 x i1> @llvm.mask.beforefirst(<vscale x 16 x i1> %m)
+ ret <vscale x 16 x i1> %x
+}
+
+define <vscale x 8 x i1> @nxv8i1(<vscale x 8 x i1> %m) {
+; CHECK-LABEL: nxv8i1:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ptrue p1.h
+; CHECK-NEXT: brkb p0.b, p1/z, p0.b
+; CHECK-NEXT: ret
+ %x = call <vscale x 8 x i1> @llvm.mask.beforefirst(<vscale x 8 x i1> %m)
+ ret <vscale x 8 x i1> %x
+}
+
+define <vscale x 4 x i1> @nxv4i1(<vscale x 4 x i1> %m) {
+; CHECK-LABEL: nxv4i1:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ptrue p1.s
+; CHECK-NEXT: brkb p0.b, p1/z, p0.b
+; CHECK-NEXT: ret
+ %x = call <vscale x 4 x i1> @llvm.mask.beforefirst(<vscale x 4 x i1> %m)
+ ret <vscale x 4 x i1> %x
+}
+
+define <vscale x 2 x i1> @nxv2i1(<vscale x 2 x i1> %m) {
+; CHECK-LABEL: nxv2i1:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ptrue p1.d
+; CHECK-NEXT: brkb p0.b, p1/z, p0.b
+; CHECK-NEXT: ret
+ %x = call <vscale x 2 x i1> @llvm.mask.beforefirst(<vscale x 2 x i1> %m)
+ ret <vscale x 2 x i1> %x
+}
+
+define <16 x i1> @v16i1(<16 x i1> %m) {
+; CHECK-LABEL: v16i1:
+; CHECK: // %bb.0:
+; CHECK-NEXT: shl v0.16b, v0.16b, #7
+; CHECK-NEXT: mov w8, #16 // =0x10
+; CHECK-NEXT: index z1.b, w8, #-1
+; CHECK-NEXT: cmlt v0.16b, v0.16b, #0
+; CHECK-NEXT: and v0.16b, v0.16b, v1.16b
+; CHECK-NEXT: umaxv b0, v0.16b
+; CHECK-NEXT: fmov w9, s0
+; CHECK-NEXT: index z0.b, #0, #1
+; CHECK-NEXT: sub w8, w8, w9, uxtb
+; CHECK-NEXT: dup v1.16b, w8
+; CHECK-NEXT: cmhi v0.16b, v1.16b, v0.16b
+; CHECK-NEXT: ret
+ %x = call <16 x i1> @llvm.mask.beforefirst(<16 x i1> %m)
+ ret <16 x i1> %x
+}
+
+define <8 x i1> @v8i1(<8 x i1> %m) {
+; CHECK-LABEL: v8i1:
+; CHECK: // %bb.0:
+; CHECK-NEXT: shl v0.8b, v0.8b, #7
+; CHECK-NEXT: index z1.b, #8, #-1
+; CHECK-NEXT: mov w9, #8 // =0x8
+; CHECK-NEXT: cmlt v0.8b, v0.8b, #0
+; CHECK-NEXT: and v0.8b, v0.8b, v1.8b
+; CHECK-NEXT: index z1.b, #0, #1
+; CHECK-NEXT: umaxv b0, v0.8b
+; CHECK-NEXT: fmov w8, s0
+; CHECK-NEXT: sub w8, w9, w8, uxtb
+; CHECK-NEXT: dup v0.8b, w8
+; CHECK-NEXT: cmhi v0.8b, v0.8b, v1.8b
+; CHECK-NEXT: ret
+ %x = call <8 x i1> @llvm.mask.beforefirst(<8 x i1> %m)
+ ret <8 x i1> %x
+}
+
+define <4 x i1> @v4i1(<4 x i1> %m) {
+; CHECK-LABEL: v4i1:
+; CHECK: // %bb.0:
+; CHECK-NEXT: shl v0.4h, v0.4h, #15
+; CHECK-NEXT: index z1.h, #4, #-1
+; CHECK-NEXT: mov w9, #4 // =0x4
+; CHECK-NEXT: cmlt v0.4h, v0.4h, #0
+; CHECK-NEXT: and v0.8b, v0.8b, v1.8b
+; CHECK-NEXT: index z1.h, #0, #1
+; CHECK-NEXT: umaxv h0, v0.4h
+; CHECK-NEXT: fmov w8, s0
+; CHECK-NEXT: sub w8, w9, w8
+; CHECK-NEXT: dup v0.4h, w8
+; CHECK-NEXT: bic v0.4h, #255, lsl #8
+; CHECK-NEXT: cmhi v0.4h, v0.4h, v1.4h
+; CHECK-NEXT: ret
+ %x = call <4 x i1> @llvm.mask.beforefirst(<4 x i1> %m)
+ ret <4 x i1> %x
+}
+
+define <2 x i1> @v2i1(<2 x i1> %m) {
+; CHECK-LABEL: v2i1:
+; CHECK: // %bb.0:
+; CHECK-NEXT: shl v0.2s, v0.2s, #31
+; CHECK-NEXT: index z1.s, #2, #-1
+; CHECK-NEXT: mov w9, #2 // =0x2
+; CHECK-NEXT: index z2.s, #0, #1
+; CHECK-NEXT: cmlt v0.2s, v0.2s, #0
+; CHECK-NEXT: and v0.8b, v0.8b, v1.8b
+; CHECK-NEXT: movi d1, #0x0000ff000000ff
+; CHECK-NEXT: umaxp v0.2s, v0.2s, v0.2s
+; CHECK-NEXT: fmov w8, s0
+; CHECK-NEXT: sub w8, w9, w8
+; CHECK-NEXT: dup v0.2s, w8
+; CHECK-NEXT: and v0.8b, v0.8b, v1.8b
+; CHECK-NEXT: cmhi v0.2s, v0.2s, v2.2s
+; CHECK-NEXT: ret
+ %x = call <2 x i1> @llvm.mask.beforefirst(<2 x i1> %m)
+ ret <2 x i1> %x
+}
diff --git a/llvm/test/CodeGen/AArch64/mask-beforefirst.ll b/llvm/test/CodeGen/AArch64/mask-beforefirst.ll
new file mode 100644
index 0000000000000..698627a8a36df
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/mask-beforefirst.ll
@@ -0,0 +1,90 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple aarch64 | FileCheck %s
+
+define <16 x i1> @v16i1(<16 x i1> %m) {
+; CHECK-LABEL: v16i1:
+; CHECK: // %bb.0:
+; CHECK-NEXT: shl v0.16b, v0.16b, #7
+; CHECK-NEXT: adrp x8, .LCPI0_0
+; CHECK-NEXT: mov w9, #16 // =0x10
+; CHECK-NEXT: ldr q1, [x8, :lo12:.LCPI0_0]
+; CHECK-NEXT: cmlt v0.16b, v0.16b, #0
+; CHECK-NEXT: and v0.16b, v0.16b, v1.16b
+; CHECK-NEXT: umaxv b0, v0.16b
+; CHECK-NEXT: fmov w8, s0
+; CHECK-NEXT: sub w8, w9, w8, uxtb
+; CHECK-NEXT: dup v0.16b, w8
+; CHECK-NEXT: adrp x8, .LCPI0_1
+; CHECK-NEXT: ldr q1, [x8, :lo12:.LCPI0_1]
+; CHECK-NEXT: cmhi v0.16b, v0.16b, v1.16b
+; CHECK-NEXT: ret
+ %x = call <16 x i1> @llvm.mask.beforefirst(<16 x i1> %m)
+ ret <16 x i1> %x
+}
+
+define <8 x i1> @v8i1(<8 x i1> %m) {
+; CHECK-LABEL: v8i1:
+; CHECK: // %bb.0:
+; CHECK-NEXT: shl v0.8b, v0.8b, #7
+; CHECK-NEXT: adrp x8, .LCPI1_0
+; CHECK-NEXT: mov w9, #8 // =0x8
+; CHECK-NEXT: ldr d1, [x8, :lo12:.LCPI1_0]
+; CHECK-NEXT: cmlt v0.8b, v0.8b, #0
+; CHECK-NEXT: and v0.8b, v0.8b, v1.8b
+; CHECK-NEXT: umaxv b0, v0.8b
+; CHECK-NEXT: fmov w8, s0
+; CHECK-NEXT: sub w8, w9, w8, uxtb
+; CHECK-NEXT: dup v0.8b, w8
+; CHECK-NEXT: adrp x8, .LCPI1_1
+; CHECK-NEXT: ldr d1, [x8, :lo12:.LCPI1_1]
+; CHECK-NEXT: cmhi v0.8b, v0.8b, v1.8b
+; CHECK-NEXT: ret
+ %x = call <8 x i1> @llvm.mask.beforefirst(<8 x i1> %m)
+ ret <8 x i1> %x
+}
+
+define <4 x i1> @v4i1(<4 x i1> %m) {
+; CHECK-LABEL: v4i1:
+; CHECK: // %bb.0:
+; CHECK-NEXT: shl v0.4h, v0.4h, #15
+; CHECK-NEXT: adrp x8, .LCPI2_0
+; CHECK-NEXT: mov w9, #4 // =0x4
+; CHECK-NEXT: ldr d1, [x8, :lo12:.LCPI2_0]
+; CHECK-NEXT: cmlt v0.4h, v0.4h, #0
+; CHECK-NEXT: and v0.8b, v0.8b, v1.8b
+; CHECK-NEXT: umaxv h0, v0.4h
+; CHECK-NEXT: fmov w8, s0
+; CHECK-NEXT: sub w8, w9, w8
+; CHECK-NEXT: dup v0.4h, w8
+; CHECK-NEXT: adrp x8, .LCPI2_1
+; CHECK-NEXT: ldr d1, [x8, :lo12:.LCPI2_1]
+; CHECK-NEXT: bic v0.4h, #255, lsl #8
+; CHECK-NEXT: cmhi v0.4h, v0.4h, v1.4h
+; CHECK-NEXT: ret
+ %x = call <4 x i1> @llvm.mask.beforefirst(<4 x i1> %m)
+ ret <4 x i1> %x
+}
+
+define <2 x i1> @v2i1(<2 x i1> %m) {
+; CHECK-LABEL: v2i1:
+; CHECK: // %bb.0:
+; CHECK-NEXT: shl v0.2s, v0.2s, #31
+; CHECK-NEXT: mov x8, #2 // =0x2
+; CHECK-NEXT: mov w9, #2 // =0x2
+; CHECK-NEXT: movk x8, #1, lsl #32
+; CHECK-NEXT: fmov d1, x8
+; CHECK-NEXT: cmlt v0.2s, v0.2s, #0
+; CHECK-NEXT: and v0.8b, v0.8b, v1.8b
+; CHECK-NEXT: movi d1, #0x0000ff000000ff
+; CHECK-NEXT: umaxp v0.2s, v0.2s, v0.2s
+; CHECK-NEXT: fmov w8, s0
+; CHECK-NEXT: sub w8, w9, w8
+; CHECK-NEXT: dup v0.2s, w8
+; CHECK-NEXT: mov x8, #4294967296 // =0x100000000
+; CHECK-NEXT: and v0.8b, v0.8b, v1.8b
+; CHECK-NEXT: fmov d1, x8
+; CHECK-NEXT: cmhi v0.2s, v0.2s, v1.2s
+; CHECK-NEXT: ret
+ %x = call <2 x i1> @llvm.mask.beforefirst(<2 x i1> %m)
+ ret <2 x i1> %x
+}
diff --git a/llvm/test/CodeGen/RISCV/rvv/fixed-vectors-mask-beforefirst.ll b/llvm/test/CodeGen/RISCV/rvv/fixed-vectors-mask-beforefirst.ll
new file mode 100644
index 0000000000000..ab4cdfc311285
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/rvv/fixed-vectors-mask-beforefirst.ll
@@ -0,0 +1,134 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple riscv32 -mattr=+v | FileCheck %s
+; RUN: llc < %s -mtriple riscv64 -mattr=+v | FileCheck %s
+
+define <1 x i1> @v1i1(<1 x i1> %m) {
+; CHECK-LABEL: v1i1:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetivli zero, 1, e8, mf8, ta, ma
+; CHECK-NEXT: vfirst.m a0, v0
+; CHECK-NEXT: bgez a0, .LBB0_2
+; CHECK-NEXT: # %bb.1:
+; CHECK-NEXT: li a0, 1
+; CHECK-NEXT: .LBB0_2:
+; CHECK-NEXT: vmv.v.x v8, a0
+; CHECK-NEXT: vmsgtu.vi v0, v8, 0
+; CHECK-NEXT: ret
+ %x = call <1 x i1> @llvm.mask.beforefirst(<1 x i1> %m)
+ ret <1 x i1> %x
+}
+
+define <2 x i1> @v2i1(<2 x i1> %m) {
+; CHECK-LABEL: v2i1:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetivli zero, 2, e8, mf8, ta, ma
+; CHECK-NEXT: vfirst.m a0, v0
+; CHECK-NEXT: bgez a0, .LBB1_2
+; CHECK-NEXT: # %bb.1:
+; CHECK-NEXT: li a0, 2
+; CHECK-NEXT: .LBB1_2:
+; CHECK-NEXT: vid.v v8
+; CHECK-NEXT: vmsltu.vx v0, v8, a0
+; CHECK-NEXT: ret
+ %x = call <2 x i1> @llvm.mask.beforefirst(<2 x i1> %m)
+ ret <2 x i1> %x
+}
+
+define <4 x i1> @v4i1(<4 x i1> %m) {
+; CHECK-LABEL: v4i1:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetivli zero, 4, e8, mf4, ta, ma
+; CHECK-NEXT: vfirst.m a0, v0
+; CHECK-NEXT: bgez a0, .LBB2_2
+; CHECK-NEXT: # %bb.1:
+; CHECK-NEXT: li a0, 4
+; CHECK-NEXT: .LBB2_2:
+; CHECK-NEXT: vid.v v8
+; CHECK-NEXT: vmsltu.vx v0, v8, a0
+; CHECK-NEXT: ret
+ %x = call <4 x i1> @llvm.mask.beforefirst(<4 x i1> %m)
+ ret <4 x i1> %x
+}
+
+define <8 x i1> @v8i1(<8 x i1> %m) {
+; CHECK-LABEL: v8i1:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetivli zero, 8, e8, mf2, ta, ma
+; CHECK-NEXT: vfirst.m a0, v0
+; CHECK-NEXT: bgez a0, .LBB3_2
+; CHECK-NEXT: # %bb.1:
+; CHECK-NEXT: li a0, 8
+; CHECK-NEXT: .LBB3_2:
+; CHECK-NEXT: vid.v v8
+; CHECK-NEXT: vmsltu.vx v0, v8, a0
+; CHECK-NEXT: ret
+ %x = call <8 x i1> @llvm.mask.beforefirst(<8 x i1> %m)
+ ret <8 x i1> %x
+}
+
+define <16 x i1> @v16i1(<16 x i1> %m) {
+; CHECK-LABEL: v16i1:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetivli zero, 16, e8, m1, ta, ma
+; CHECK-NEXT: vfirst.m a0, v0
+; CHECK-NEXT: bgez a0, .LBB4_2
+; CHECK-NEXT: # %bb.1:
+; CHECK-NEXT: li a0, 16
+; CHECK-NEXT: .LBB4_2:
+; CHECK-NEXT: vid.v v8
+; CHECK-NEXT: vmsltu.vx v0, v8, a0
+; CHECK-NEXT: ret
+ %x = call <16 x i1> @llvm.mask.beforefirst(<16 x i1> %m)
+ ret <16 x i1> %x
+}
+
+define <32 x i1> @v32i1(<32 x i1> %m) {
+; CHECK-LABEL: v32i1:
+; CHECK: # %bb.0:
+; CHECK-NEXT: li a0, 32
+; CHECK-NEXT: vsetvli zero, a0, e8, m2, ta, ma
+; CHECK-NEXT: vfirst.m a0, v0
+; CHECK-NEXT: bgez a0, .LBB5_2
+; CHECK-NEXT: # %bb.1:
+; CHECK-NEXT: li a0, 32
+; CHECK-NEXT: .LBB5_2:
+; CHECK-NEXT: vid.v v8
+; CHECK-NEXT: vmsltu.vx v0, v8, a0
+; CHECK-NEXT: ret
+ %x = call <32 x i1> @llvm.mask.beforefirst(<32 x i1> %m)
+ ret <32 x i1> %x
+}
+
+define <64 x i1> @v64i1(<64 x i1> %m) {
+; CHECK-LABEL: v64i1:
+; CHECK: # %bb.0:
+; CHECK-NEXT: li a0, 64
+; CHECK-NEXT: vsetvli zero, a0, e8, m4, ta, ma
+; CHECK-NEXT: vfirst.m a0, v0
+; CHECK-NEXT: bgez a0, .LBB6_2
+; CHECK-NEXT: # %bb.1:
+; CHECK-NEXT: li a0, 64
+; CHECK-NEXT: .LBB6_2:
+; CHECK-NEXT: vid.v v8
+; CHECK-NEXT: vmsltu.vx v0, v8, a0
+; CHECK-NEXT: ret
+ %x = call <64 x i1> @llvm.mask.beforefirst(<64 x i1> %m)
+ ret <64 x i1> %x
+}
+
+define <128 x i1> @v128i1(<128 x i1> %m) {
+; CHECK-LABEL: v128i1:
+; CHECK: # %bb.0:
+; CHECK-NEXT: li a0, 128
+; CHECK-NEXT: vsetvli zero, a0, e8, m8, ta, ma
+; CHECK-NEXT: vfirst.m a0, v0
+; CHECK-NEXT: bgez a0, .LBB7_2
+; CHECK-NEXT: # %bb.1:
+; CHECK-NEXT: li a0, 128
+; CHECK-NEXT: .LBB7_2:
+; CHECK-NEXT: vid.v v8
+; CHECK-NEXT: vmsltu.vx v0, v8, a0
+; CHECK-NEXT: ret
+ %x = call <128 x i1> @llvm.mask.beforefirst(<128 x i1> %m)
+ ret <128 x i1> %x
+}
diff --git a/llvm/test/CodeGen/RISCV/rvv/mask-beforefirst.ll b/llvm/test/CodeGen/RISCV/rvv/mask-beforefirst.ll
new file mode 100644
index 0000000000000..195ddbaff0ba0
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/rvv/mask-beforefirst.ll
@@ -0,0 +1,80 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple riscv32 -mattr=+v | FileCheck %s
+; RUN: llc < %s -mtriple riscv64 -mattr=+v | FileCheck %s
+
+define <vscale x 1 x i1> @nxv1i1(<vscale x 1 x i1> %m) {
+; CHECK-LABEL: nxv1i1:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetvli a0, zero, e8, mf8, ta, ma
+; CHECK-NEXT: vmsbf.m v8, v0
+; CHECK-NEXT: vmv1r.v v0, v8
+; CHECK-NEXT: ret
+ %x = call <vscale x 1 x i1> @llvm.mask.beforefirst(<vscale x 1 x i1> %m)
+ ret <vscale x 1 x i1> %x
+}
+
+define <vscale x 2 x i1> @nxv2i1(<vscale x 2 x i1> %m) {
+; CHECK-LABEL: nxv2i1:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetvli a0, zero, e8, mf4, ta, ma
+; CHECK-NEXT: vmsbf.m v8, v0
+; CHECK-NEXT: vmv1r.v v0, v8
+; CHECK-NEXT: ret
+ %x = call <vscale x 2 x i1> @llvm.mask.beforefirst(<vscale x 2 x i1> %m)
+ ret <vscale x 2 x i1> %x
+}
+
+define <vscale x 4 x i1> @nxv4i1(<vscale x 4 x i1> %m) {
+; CHECK-LABEL: nxv4i1:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetvli a0, zero, e8, mf2, ta, ma
+; CHECK-NEXT: vmsbf.m v8, v0
+; CHECK-NEXT: vmv1r.v v0, v8
+; CHECK-NEXT: ret
+ %x = call <vscale x 4 x i1> @llvm.mask.beforefirst(<vscale x 4 x i1> %m)
+ ret <vscale x 4 x i1> %x
+}
+
+define <vscale x 8 x i1> @nxv8i1(<vscale x 8 x i1> %m) {
+; CHECK-LABEL: nxv8i1:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetvli a0, zero, e8, m1, ta, ma
+; CHECK-NEXT: vmsbf.m v8, v0
+; CHECK-NEXT: vmv.v.v v0, v8
+; CHECK-NEXT: ret
+ %x = call <vscale x 8 x i1> @llvm.mask.beforefirst(<vscale x 8 x i1> %m)
+ ret <vscale x 8 x i1> %x
+}
+
+define <vscale x 16 x i1> @nxv16i1(<vscale x 16 x i1> %m) {
+; CHECK-LABEL: nxv16i1:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetvli a0, zero, e8, m2, ta, ma
+; CHECK-NEXT: vmsbf.m v8, v0
+; CHECK-NEXT: vmv1r.v v0, v8
+; CHECK-NEXT: ret
+ %x = call <vscale x 16 x i1> @llvm.mask.beforefirst(<vscale x 16 x i1> %m)
+ ret <vscale x 16 x i1> %x
+}
+
+define <vscale x 32 x i1> @nxv32i1(<vscale x 32 x i1> %m) {
+; CHECK-LABEL: nxv32i1:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetvli a0, zero, e8, m4, ta, ma
+; CHECK-NEXT: vmsbf.m v8, v0
+; CHECK-NEXT: vmv1r.v v0, v8
+; CHECK-NEXT: ret
+ %x = call <vscale x 32 x i1> @llvm.mask.beforefirst(<vscale x 32 x i1> %m)
+ ret <vscale x 32 x i1> %x
+}
+
+define <vscale x 64 x i1> @nxv64i1(<vscale x 64 x i1> %m) {
+; CHECK-LABEL: nxv64i1:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetvli a0, zero, e8, m8, ta, ma
+; CHECK-NEXT: vmsbf.m v8, v0
+; CHECK-NEXT: vmv1r.v v0, v8
+; CHECK-NEXT: ret
+ %x = call <vscale x 64 x i1> @llvm.mask.beforefirst(<vscale x 64 x i1> %m)
+ ret <vscale x 64 x i1> %x
+}
diff --git a/llvm/test/Verifier/mask-beforefirst.ll b/llvm/test/Verifier/mask-beforefirst.ll
new file mode 100644
index 0000000000000..4cdd9ed025782
--- /dev/null
+++ b/llvm/test/Verifier/mask-beforefirst.ll
@@ -0,0 +1,9 @@
+; RUN: not opt -S -passes=verify < %s 2>&1 | FileCheck %s
+
+; Reject llvm.mask.beforefirst with non-mask vector arguments.
+
+define <4 x i8> @v4i8(<4 x i8> %m) {
+; CHECK: mask.beforefirst element type must be i1
+ %res = call <4 x i8> @llvm.mask.beforefirst(<4 x i8> %m)
+ ret <4 x i8> %res
+}
>From f3057cc91bd24d196fc604908adfba5c29a012e8 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Mon, 15 Jun 2026 21:34:45 +0800
Subject: [PATCH 02/10] Fix LangRef.rst build
---
llvm/docs/LangRef.rst | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 519ef6db68c75..7477b26ede094 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -22004,7 +22004,7 @@ Examples:
; because if any more lanes were active the load would be dependent on the
; completion of the store.
-.. _int_mask_beforefirst
+.. _int_mask_beforefirst:
'``llvm.mask.beforefirst.*``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -22038,6 +22038,7 @@ Examples:
"""""""""
.. code-block:: llvm
+
@llvm.mask.beforefirst(<0,0,1,1>); ==> <1,1,0,0>
@llvm.mask.beforefirst(<0,0,0,0>); ==> <1,1,1,1>
@llvm.mask.beforefirst(<0,1,0,1>); ==> <1,0,0,0>
>From 5bfaaba428b1e234e1badda1b0e07a2f099e65d0 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Tue, 16 Jun 2026 16:07:47 +0800
Subject: [PATCH 03/10] Don't legalize op, promote op during result
legalization
---
.../SelectionDAG/LegalizeIntegerTypes.cpp | 27 +++++++++++++++----
llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h | 4 ++-
2 files changed, 25 insertions(+), 6 deletions(-)
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp
index 3472842ed5d58..d2b36ab39e50e 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp
@@ -162,9 +162,14 @@ void DAGTypeLegalizer::PromoteIntegerResult(SDNode *N, unsigned ResNo) {
Res = PromoteIntRes_EXTEND_VECTOR_INREG(N); break;
case ISD::VECTOR_FIND_LAST_ACTIVE:
- case ISD::MASK_BEFOREFIRST:
+ Res = PromoteIntRes_VECTOR_FIND_LAST_ACTIVE(N);
+ break;
+
case ISD::GET_ACTIVE_LANE_MASK:
- Res = PromoteIntRes_BooleanVector(N);
+ Res = PromoteIntRes_GET_ACTIVE_LANE_MASK(N);
+ break;
+ case ISD::MASK_BEFOREFIRST:
+ Res = PromoteIntRes_MASK_BEFOREFIRST(N);
break;
case ISD::PARTIAL_REDUCE_UMLA:
@@ -2193,7 +2198,6 @@ bool DAGTypeLegalizer::PromoteIntegerOperand(SDNode *N, unsigned OpNo) {
case ISD::VECTOR_FIND_LAST_ACTIVE:
case ISD::CTTZ_ELTS:
case ISD::CTTZ_ELTS_ZERO_POISON:
- case ISD::MASK_BEFOREFIRST:
Res = PromoteIntOp_UnaryBooleanVectorOp(N, OpNo);
break;
case ISD::GET_ACTIVE_LANE_MASK:
@@ -6481,10 +6485,23 @@ SDValue DAGTypeLegalizer::PromoteIntRes_EXTEND_VECTOR_INREG(SDNode *N) {
return DAG.getNode(N->getOpcode(), dl, NVT, N->getOperand(0));
}
-SDValue DAGTypeLegalizer::PromoteIntRes_BooleanVector(SDNode *N) {
+SDValue DAGTypeLegalizer::PromoteIntRes_VECTOR_FIND_LAST_ACTIVE(SDNode *N) {
+ EVT VT = N->getValueType(0);
+ EVT NVT = TLI.getTypeToTransformTo(*DAG.getContext(), VT);
+ return DAG.getNode(ISD::VECTOR_FIND_LAST_ACTIVE, SDLoc(N), NVT, N->ops());
+}
+
+SDValue DAGTypeLegalizer::PromoteIntRes_GET_ACTIVE_LANE_MASK(SDNode *N) {
+ EVT VT = N->getValueType(0);
+ EVT NVT = TLI.getTypeToTransformTo(*DAG.getContext(), VT);
+ return DAG.getNode(ISD::GET_ACTIVE_LANE_MASK, SDLoc(N), NVT, N->ops());
+}
+
+SDValue DAGTypeLegalizer::PromoteIntRes_MASK_BEFOREFIRST(SDNode *N) {
EVT VT = N->getValueType(0);
EVT NVT = TLI.getTypeToTransformTo(*DAG.getContext(), VT);
- return DAG.getNode(N->getOpcode(), SDLoc(N), NVT, N->ops());
+ SDValue Op = PromoteTargetBoolean(N->getOperand(0), NVT);
+ return DAG.getNode(ISD::MASK_BEFOREFIRST, SDLoc(N), NVT, Op);
}
SDValue DAGTypeLegalizer::PromoteIntRes_PARTIAL_REDUCE_MLA(SDNode *N) {
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
index 5a81c96320c3c..6916bcaa08f35 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
@@ -362,7 +362,9 @@ class LLVM_LIBRARY_VISIBILITY DAGTypeLegalizer {
SDValue PromoteIntRes_IS_FPCLASS(SDNode *N);
SDValue PromoteIntRes_PATCHPOINT(SDNode *N);
SDValue PromoteIntRes_READ_REGISTER(SDNode *N);
- SDValue PromoteIntRes_BooleanVector(SDNode *N);
+ SDValue PromoteIntRes_VECTOR_FIND_LAST_ACTIVE(SDNode *N);
+ SDValue PromoteIntRes_GET_ACTIVE_LANE_MASK(SDNode *N);
+ SDValue PromoteIntRes_MASK_BEFOREFIRST(SDNode *N);
SDValue PromoteIntRes_PARTIAL_REDUCE_MLA(SDNode *N);
SDValue PromoteIntRes_LOOP_DEPENDENCE_MASK(SDNode *N);
>From f1a75a03ea222cb1f0cf18ad88451b8b48756825 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Tue, 16 Jun 2026 17:36:27 +0800
Subject: [PATCH 04/10] Fix tablegen indentation
---
llvm/include/llvm/IR/Intrinsics.td | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index 323b8894f0517..9de4b0031116a 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -2617,8 +2617,8 @@ def int_get_active_lane_mask:
def int_mask_beforefirst:
DefaultAttrsIntrinsic<[llvm_anyvector_ty],
- [LLVMMatchType<0>],
- [IntrNoMem, IntrSpeculatable]>;
+ [LLVMMatchType<0>],
+ [IntrNoMem, IntrSpeculatable]>;
def int_experimental_get_vector_length:
DefaultAttrsIntrinsic<[llvm_i32_ty],
>From fcced5a33f5a35339b012c1fafe3def82d816f65 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Tue, 16 Jun 2026 17:42:12 +0800
Subject: [PATCH 05/10] Add widening legalization
---
.../CodeGen/SelectionDAG/LegalizeVectorTypes.cpp | 1 +
llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll | 12 ++++++++++++
llvm/test/CodeGen/RISCV/rvv/mask-beforefirst.ll | 13 +++++++++++++
3 files changed, 26 insertions(+)
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
index 5350be412176d..c2038e6d20db9 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
@@ -5478,6 +5478,7 @@ void DAGTypeLegalizer::WidenVectorResult(SDNode *N, unsigned ResNo) {
case ISD::ARITH_FENCE:
case ISD::FCANONICALIZE:
case ISD::AssertNoFPClass:
+ case ISD::MASK_BEFOREFIRST:
Res = WidenVecRes_Unary(N);
break;
case ISD::FMA: case ISD::VP_FMA:
diff --git a/llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll b/llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll
index 2fe1d44a459bf..c939e8b6af409 100644
--- a/llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll
+++ b/llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll
@@ -119,3 +119,15 @@ define <2 x i1> @v2i1(<2 x i1> %m) {
%x = call <2 x i1> @llvm.mask.beforefirst(<2 x i1> %m)
ret <2 x i1> %x
}
+
+; Widening
+
+define <vscale x 3 x i1> @nxv3i1(<vscale x 3 x i1> %m) {
+; CHECK-LABEL: nxv3i1:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ptrue p1.s
+; CHECK-NEXT: brkb p0.b, p1/z, p0.b
+; CHECK-NEXT: ret
+ %x = call <vscale x 3 x i1> @llvm.mask.beforefirst(<vscale x 3 x i1> %m)
+ ret <vscale x 3 x i1> %x
+}
diff --git a/llvm/test/CodeGen/RISCV/rvv/mask-beforefirst.ll b/llvm/test/CodeGen/RISCV/rvv/mask-beforefirst.ll
index 195ddbaff0ba0..e2db80b6a9d1d 100644
--- a/llvm/test/CodeGen/RISCV/rvv/mask-beforefirst.ll
+++ b/llvm/test/CodeGen/RISCV/rvv/mask-beforefirst.ll
@@ -78,3 +78,16 @@ define <vscale x 64 x i1> @nxv64i1(<vscale x 64 x i1> %m) {
%x = call <vscale x 64 x i1> @llvm.mask.beforefirst(<vscale x 64 x i1> %m)
ret <vscale x 64 x i1> %x
}
+
+; Widening
+
+define <vscale x 3 x i1> @nxv3i1(<vscale x 3 x i1> %m) {
+; CHECK-LABEL: nxv3i1:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetvli a0, zero, e8, mf2, ta, ma
+; CHECK-NEXT: vmsbf.m v8, v0
+; CHECK-NEXT: vmv1r.v v0, v8
+; CHECK-NEXT: ret
+ %x = call <vscale x 3 x i1> @llvm.mask.beforefirst(<vscale x 3 x i1> %m)
+ ret <vscale x 3 x i1> %x
+}
>From 646e953e83f8ce42d0ed914c6897776a9051b697 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Tue, 16 Jun 2026 18:06:21 +0800
Subject: [PATCH 06/10] Add splitting legalization
---
llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h | 1 +
.../SelectionDAG/LegalizeVectorTypes.cpp | 19 +++++++++++++++++++
.../CodeGen/AArch64/mask-beforefirst-sve.ll | 18 ++++++++++++++++++
.../CodeGen/RISCV/rvv/mask-beforefirst.ll | 19 +++++++++++++++++++
4 files changed, 57 insertions(+)
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
index 6916bcaa08f35..28f120a80e987 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
@@ -937,6 +937,7 @@ class LLVM_LIBRARY_VISIBILITY DAGTypeLegalizer {
void SplitVecRes_BITCAST(SDNode *N, SDValue &Lo, SDValue &Hi);
void SplitVecRes_LOOP_DEPENDENCE_MASK(SDNode *N, SDValue &Lo, SDValue &Hi);
+ void SplitVecRes_MASK_BEFOREFIRST(SDNode *N, SDValue &Lo, SDValue &Hi);
void SplitVecRes_BUILD_VECTOR(SDNode *N, SDValue &Lo, SDValue &Hi);
void SplitVecRes_CONCAT_VECTORS(SDNode *N, SDValue &Lo, SDValue &Hi);
void SplitVecRes_EXTRACT_SUBVECTOR(SDNode *N, SDValue &Lo, SDValue &Hi);
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
index c2038e6d20db9..024b1858b95b1 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
@@ -1335,6 +1335,9 @@ void DAGTypeLegalizer::SplitVectorResult(SDNode *N, unsigned ResNo) {
case ISD::LOOP_DEPENDENCE_WAR_MASK:
SplitVecRes_LOOP_DEPENDENCE_MASK(N, Lo, Hi);
break;
+ case ISD::MASK_BEFOREFIRST:
+ SplitVecRes_MASK_BEFOREFIRST(N, Lo, Hi);
+ break;
case ISD::MERGE_VALUES: SplitRes_MERGE_VALUES(N, ResNo, Lo, Hi); break;
case ISD::AssertZext: SplitVecRes_AssertZext(N, Lo, Hi); break;
case ISD::AssertSext: SplitVecRes_AssertSext(N, Lo, Hi); break;
@@ -1882,6 +1885,22 @@ void DAGTypeLegalizer::SplitVecRes_LOOP_DEPENDENCE_MASK(SDNode *N, SDValue &Lo,
/*LaneOffset=*/DAG.getConstant(LaneOffset, DL, MVT::i64));
}
+void DAGTypeLegalizer::SplitVecRes_MASK_BEFOREFIRST(SDNode *N, SDValue &Lo,
+ SDValue &Hi) {
+ SDLoc DL(N);
+ SDValue InLo, InHi;
+ GetSplitVector(N->getOperand(0), InLo, InHi);
+ EVT VT = InLo.getValueType();
+ Lo = DAG.getNode(ISD::MASK_BEFOREFIRST, DL, VT, InLo);
+
+ // hi = AnyLoActive ? all-ones : (mask_beforefirst hi)
+ SDValue AnyLoActive = DAG.getNode(ISD::VECREDUCE_OR, DL, MVT::i1, Lo);
+ SDValue Cond = DAG.getBoolExtOrTrunc(AnyLoActive, DL,
+ getSetCCResultType(MVT::i1), MVT::i1);
+ Hi = DAG.getNode(ISD::SELECT, DL, VT, Cond, DAG.getConstant(0, DL, VT),
+ DAG.getNode(ISD::MASK_BEFOREFIRST, DL, VT, InHi));
+}
+
void DAGTypeLegalizer::SplitVecRes_BUILD_VECTOR(SDNode *N, SDValue &Lo,
SDValue &Hi) {
EVT LoVT, HiVT;
diff --git a/llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll b/llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll
index c939e8b6af409..eeddfd24197bf 100644
--- a/llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll
+++ b/llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll
@@ -131,3 +131,21 @@ define <vscale x 3 x i1> @nxv3i1(<vscale x 3 x i1> %m) {
%x = call <vscale x 3 x i1> @llvm.mask.beforefirst(<vscale x 3 x i1> %m)
ret <vscale x 3 x i1> %x
}
+
+; Splitting
+
+define <vscale x 32 x i1> @nxv32i1(<vscale x 32 x i1> %m) {
+; CHECK-LABEL: nxv32i1:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ptrue p2.b
+; CHECK-NEXT: brkb p0.b, p2/z, p0.b
+; CHECK-NEXT: brkb p1.b, p2/z, p1.b
+; CHECK-NEXT: ptest p0, p0.b
+; CHECK-NEXT: cset w8, ne
+; CHECK-NEXT: sbfx x8, x8, #0, #1
+; CHECK-NEXT: whilelo p3.b, xzr, x8
+; CHECK-NEXT: bic p1.b, p1/z, p1.b, p3.b
+; CHECK-NEXT: ret
+ %x = call <vscale x 32 x i1> @llvm.mask.beforefirst(<vscale x 32 x i1> %m)
+ ret <vscale x 32 x i1> %x
+}
diff --git a/llvm/test/CodeGen/RISCV/rvv/mask-beforefirst.ll b/llvm/test/CodeGen/RISCV/rvv/mask-beforefirst.ll
index e2db80b6a9d1d..aafc6b4690109 100644
--- a/llvm/test/CodeGen/RISCV/rvv/mask-beforefirst.ll
+++ b/llvm/test/CodeGen/RISCV/rvv/mask-beforefirst.ll
@@ -91,3 +91,22 @@ define <vscale x 3 x i1> @nxv3i1(<vscale x 3 x i1> %m) {
%x = call <vscale x 3 x i1> @llvm.mask.beforefirst(<vscale x 3 x i1> %m)
ret <vscale x 3 x i1> %x
}
+
+; Splitting
+
+define <vscale x 128 x i1> @nxv128i1(<vscale x 128 x i1> %m) {
+; CHECK-LABEL: nxv128i1:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetvli a0, zero, e8, m8, ta, ma
+; CHECK-NEXT: vmsbf.m v9, v0
+; CHECK-NEXT: vcpop.m a0, v9
+; CHECK-NEXT: snez a0, a0
+; CHECK-NEXT: vmv.v.x v16, a0
+; CHECK-NEXT: vmsne.vi v10, v16, 0
+; CHECK-NEXT: vmsbf.m v11, v8
+; CHECK-NEXT: vmandn.mm v8, v11, v10
+; CHECK-NEXT: vmv1r.v v0, v9
+; CHECK-NEXT: ret
+ %x = call <vscale x 128 x i1> @llvm.mask.beforefirst(<vscale x 128 x i1> %m)
+ ret <vscale x 128 x i1> %x
+}
>From 59fa5b2e108e1966e97286d83ba6534731eb89fd Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Wed, 17 Jun 2026 14:52:27 +0200
Subject: [PATCH 07/10] Fix all-ones typo -> all-zeros
---
llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
index 024b1858b95b1..aca019a9632de 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
@@ -1893,7 +1893,7 @@ void DAGTypeLegalizer::SplitVecRes_MASK_BEFOREFIRST(SDNode *N, SDValue &Lo,
EVT VT = InLo.getValueType();
Lo = DAG.getNode(ISD::MASK_BEFOREFIRST, DL, VT, InLo);
- // hi = AnyLoActive ? all-ones : (mask_beforefirst hi)
+ // hi = AnyLoActive ? all-zeros : (mask_beforefirst hi)
SDValue AnyLoActive = DAG.getNode(ISD::VECREDUCE_OR, DL, MVT::i1, Lo);
SDValue Cond = DAG.getBoolExtOrTrunc(AnyLoActive, DL,
getSetCCResultType(MVT::i1), MVT::i1);
>From 4668e853ac6f0777632485f23d002805ff538f4a Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Sun, 19 Jul 2026 15:40:02 +0800
Subject: [PATCH 08/10] Update sve test
---
llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll b/llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll
index eeddfd24197bf..1bd814f4ef84b 100644
--- a/llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll
+++ b/llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll
@@ -141,8 +141,7 @@ define <vscale x 32 x i1> @nxv32i1(<vscale x 32 x i1> %m) {
; CHECK-NEXT: brkb p0.b, p2/z, p0.b
; CHECK-NEXT: brkb p1.b, p2/z, p1.b
; CHECK-NEXT: ptest p0, p0.b
-; CHECK-NEXT: cset w8, ne
-; CHECK-NEXT: sbfx x8, x8, #0, #1
+; CHECK-NEXT: csetm x8, ne
; CHECK-NEXT: whilelo p3.b, xzr, x8
; CHECK-NEXT: bic p1.b, p1/z, p1.b, p3.b
; CHECK-NEXT: ret
>From 89bc56512f11802729de59024486140fb9398874 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Sun, 19 Jul 2026 23:41:27 +0800
Subject: [PATCH 09/10] Fix split legalization
---
llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp | 2 +-
llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll | 6 +++---
llvm/test/CodeGen/RISCV/rvv/mask-beforefirst.ll | 10 +++++-----
3 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
index ba15e9ac40a7b..84b462afb266a 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
@@ -1936,7 +1936,7 @@ void DAGTypeLegalizer::SplitVecRes_MASK_BEFOREFIRST(SDNode *N, SDValue &Lo,
Lo = DAG.getNode(ISD::MASK_BEFOREFIRST, DL, VT, InLo);
// hi = AnyLoActive ? all-zeros : (mask_beforefirst hi)
- SDValue AnyLoActive = DAG.getNode(ISD::VECREDUCE_OR, DL, MVT::i1, Lo);
+ SDValue AnyLoActive = DAG.getNode(ISD::VECREDUCE_OR, DL, MVT::i1, InLo);
SDValue Cond = DAG.getBoolExtOrTrunc(AnyLoActive, DL,
getSetCCResultType(MVT::i1), MVT::i1);
Hi = DAG.getNode(ISD::SELECT, DL, VT, Cond, DAG.getConstant(0, DL, VT),
diff --git a/llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll b/llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll
index 1bd814f4ef84b..27f2e34a5bc61 100644
--- a/llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll
+++ b/llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll
@@ -137,12 +137,12 @@ define <vscale x 3 x i1> @nxv3i1(<vscale x 3 x i1> %m) {
define <vscale x 32 x i1> @nxv32i1(<vscale x 32 x i1> %m) {
; CHECK-LABEL: nxv32i1:
; CHECK: // %bb.0:
-; CHECK-NEXT: ptrue p2.b
-; CHECK-NEXT: brkb p0.b, p2/z, p0.b
-; CHECK-NEXT: brkb p1.b, p2/z, p1.b
; CHECK-NEXT: ptest p0, p0.b
+; CHECK-NEXT: ptrue p2.b
; CHECK-NEXT: csetm x8, ne
+; CHECK-NEXT: brkb p1.b, p2/z, p1.b
; CHECK-NEXT: whilelo p3.b, xzr, x8
+; CHECK-NEXT: brkb p0.b, p2/z, p0.b
; CHECK-NEXT: bic p1.b, p1/z, p1.b, p3.b
; CHECK-NEXT: ret
%x = call <vscale x 32 x i1> @llvm.mask.beforefirst(<vscale x 32 x i1> %m)
diff --git a/llvm/test/CodeGen/RISCV/rvv/mask-beforefirst.ll b/llvm/test/CodeGen/RISCV/rvv/mask-beforefirst.ll
index aafc6b4690109..f00f40ea1fcca 100644
--- a/llvm/test/CodeGen/RISCV/rvv/mask-beforefirst.ll
+++ b/llvm/test/CodeGen/RISCV/rvv/mask-beforefirst.ll
@@ -98,13 +98,13 @@ define <vscale x 128 x i1> @nxv128i1(<vscale x 128 x i1> %m) {
; CHECK-LABEL: nxv128i1:
; CHECK: # %bb.0:
; CHECK-NEXT: vsetvli a0, zero, e8, m8, ta, ma
-; CHECK-NEXT: vmsbf.m v9, v0
-; CHECK-NEXT: vcpop.m a0, v9
+; CHECK-NEXT: vcpop.m a0, v0
; CHECK-NEXT: snez a0, a0
; CHECK-NEXT: vmv.v.x v16, a0
-; CHECK-NEXT: vmsne.vi v10, v16, 0
-; CHECK-NEXT: vmsbf.m v11, v8
-; CHECK-NEXT: vmandn.mm v8, v11, v10
+; CHECK-NEXT: vmsne.vi v9, v16, 0
+; CHECK-NEXT: vmsbf.m v10, v8
+; CHECK-NEXT: vmandn.mm v8, v10, v9
+; CHECK-NEXT: vmsbf.m v9, v0
; CHECK-NEXT: vmv1r.v v0, v9
; CHECK-NEXT: ret
%x = call <vscale x 128 x i1> @llvm.mask.beforefirst(<vscale x 128 x i1> %m)
>From 4e139c3e8eef073b79c049fce2d5100157fabc3f Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Mon, 20 Jul 2026 20:26:08 +0800
Subject: [PATCH 10/10] Expand to get_active_lane_mask if possible
This gives us the good SVE fixed-length lowering in optimizeBrk
---
.../SelectionDAG/LegalizeVectorOps.cpp | 13 +++-
.../CodeGen/AArch64/mask-beforefirst-sve.ll | 63 ++++++-------------
2 files changed, 32 insertions(+), 44 deletions(-)
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp
index 4be6d073b3fb4..444c3f8ad8a8c 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp
@@ -1941,11 +1941,22 @@ SDValue VectorLegalizer::ExpandMASK_BEFOREFIRST(SDNode *N) {
SDLoc DL(N);
EVT VT = N->getValueType(0);
+ // Try to expand via get_active_lane_mask if supported.
+ EVT BoolVT = VT.changeVectorElementType(*DAG.getContext(), MVT::i1);
+ EVT VecIdxVT = TLI.getVectorIdxTy(DAG.getDataLayout());
+ if (!TLI.shouldExpandGetActiveLaneMask(BoolVT, VecIdxVT)) {
+ // Perform cttz_elts in bool VT to get the custom target lowering.
+ SDValue CttzElts =
+ DAG.getNode(ISD::CTTZ_ELTS, DL, VecIdxVT,
+ DAG.getNode(ISD::TRUNCATE, DL, BoolVT, N->getOperand(0)));
+ return DAG.getNode(ISD::GET_ACTIVE_LANE_MASK, DL, VT,
+ DAG.getConstant(0, DL, VecIdxVT), CttzElts);
+ }
+
// Compute the type for the cttz_elts.
ConstantRange VScaleRange(1, /*isFullSet=*/true); // Fixed length default.
if (VT.isScalableVector())
VScaleRange = getVScaleRange(&DAG.getMachineFunction().getFunction(), 64);
- const TargetLowering &TLI = DAG.getTargetLoweringInfo();
uint64_t EltWidth = TLI.getBitWidthForCttzElements(
EVT(TLI.getVectorIdxTy(DAG.getDataLayout())), VT.getVectorElementCount(),
/*ZeroIsPoison=*/false, &VScaleRange);
diff --git a/llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll b/llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll
index 27f2e34a5bc61..9f9d9029ee8df 100644
--- a/llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll
+++ b/llvm/test/CodeGen/AArch64/mask-beforefirst-sve.ll
@@ -45,16 +45,11 @@ define <16 x i1> @v16i1(<16 x i1> %m) {
; CHECK-LABEL: v16i1:
; CHECK: // %bb.0:
; CHECK-NEXT: shl v0.16b, v0.16b, #7
-; CHECK-NEXT: mov w8, #16 // =0x10
-; CHECK-NEXT: index z1.b, w8, #-1
-; CHECK-NEXT: cmlt v0.16b, v0.16b, #0
-; CHECK-NEXT: and v0.16b, v0.16b, v1.16b
-; CHECK-NEXT: umaxv b0, v0.16b
-; CHECK-NEXT: fmov w9, s0
-; CHECK-NEXT: index z0.b, #0, #1
-; CHECK-NEXT: sub w8, w8, w9, uxtb
-; CHECK-NEXT: dup v1.16b, w8
-; CHECK-NEXT: cmhi v0.16b, v1.16b, v0.16b
+; CHECK-NEXT: ptrue p0.b, vl16
+; CHECK-NEXT: cmpne p1.b, p0/z, z0.b, #0
+; CHECK-NEXT: brkb p1.b, p0/z, p1.b
+; CHECK-NEXT: mov z0.b, p1/z, #-1 // =0xffffffffffffffff
+; CHECK-NEXT: // kill: def $q0 killed $q0 killed $z0
; CHECK-NEXT: ret
%x = call <16 x i1> @llvm.mask.beforefirst(<16 x i1> %m)
ret <16 x i1> %x
@@ -64,16 +59,11 @@ define <8 x i1> @v8i1(<8 x i1> %m) {
; CHECK-LABEL: v8i1:
; CHECK: // %bb.0:
; CHECK-NEXT: shl v0.8b, v0.8b, #7
-; CHECK-NEXT: index z1.b, #8, #-1
-; CHECK-NEXT: mov w9, #8 // =0x8
-; CHECK-NEXT: cmlt v0.8b, v0.8b, #0
-; CHECK-NEXT: and v0.8b, v0.8b, v1.8b
-; CHECK-NEXT: index z1.b, #0, #1
-; CHECK-NEXT: umaxv b0, v0.8b
-; CHECK-NEXT: fmov w8, s0
-; CHECK-NEXT: sub w8, w9, w8, uxtb
-; CHECK-NEXT: dup v0.8b, w8
-; CHECK-NEXT: cmhi v0.8b, v0.8b, v1.8b
+; CHECK-NEXT: ptrue p0.b, vl8
+; CHECK-NEXT: cmpne p1.b, p0/z, z0.b, #0
+; CHECK-NEXT: brkb p1.b, p0/z, p1.b
+; CHECK-NEXT: mov z0.b, p1/z, #-1 // =0xffffffffffffffff
+; CHECK-NEXT: // kill: def $d0 killed $d0 killed $z0
; CHECK-NEXT: ret
%x = call <8 x i1> @llvm.mask.beforefirst(<8 x i1> %m)
ret <8 x i1> %x
@@ -83,17 +73,11 @@ define <4 x i1> @v4i1(<4 x i1> %m) {
; CHECK-LABEL: v4i1:
; CHECK: // %bb.0:
; CHECK-NEXT: shl v0.4h, v0.4h, #15
-; CHECK-NEXT: index z1.h, #4, #-1
-; CHECK-NEXT: mov w9, #4 // =0x4
-; CHECK-NEXT: cmlt v0.4h, v0.4h, #0
-; CHECK-NEXT: and v0.8b, v0.8b, v1.8b
-; CHECK-NEXT: index z1.h, #0, #1
-; CHECK-NEXT: umaxv h0, v0.4h
-; CHECK-NEXT: fmov w8, s0
-; CHECK-NEXT: sub w8, w9, w8
-; CHECK-NEXT: dup v0.4h, w8
-; CHECK-NEXT: bic v0.4h, #255, lsl #8
-; CHECK-NEXT: cmhi v0.4h, v0.4h, v1.4h
+; CHECK-NEXT: ptrue p0.h, vl4
+; CHECK-NEXT: cmpne p1.h, p0/z, z0.h, #0
+; CHECK-NEXT: brkb p1.b, p0/z, p1.b
+; CHECK-NEXT: mov z0.h, p1/z, #-1 // =0xffffffffffffffff
+; CHECK-NEXT: // kill: def $d0 killed $d0 killed $z0
; CHECK-NEXT: ret
%x = call <4 x i1> @llvm.mask.beforefirst(<4 x i1> %m)
ret <4 x i1> %x
@@ -103,18 +87,11 @@ define <2 x i1> @v2i1(<2 x i1> %m) {
; CHECK-LABEL: v2i1:
; CHECK: // %bb.0:
; CHECK-NEXT: shl v0.2s, v0.2s, #31
-; CHECK-NEXT: index z1.s, #2, #-1
-; CHECK-NEXT: mov w9, #2 // =0x2
-; CHECK-NEXT: index z2.s, #0, #1
-; CHECK-NEXT: cmlt v0.2s, v0.2s, #0
-; CHECK-NEXT: and v0.8b, v0.8b, v1.8b
-; CHECK-NEXT: movi d1, #0x0000ff000000ff
-; CHECK-NEXT: umaxp v0.2s, v0.2s, v0.2s
-; CHECK-NEXT: fmov w8, s0
-; CHECK-NEXT: sub w8, w9, w8
-; CHECK-NEXT: dup v0.2s, w8
-; CHECK-NEXT: and v0.8b, v0.8b, v1.8b
-; CHECK-NEXT: cmhi v0.2s, v0.2s, v2.2s
+; CHECK-NEXT: ptrue p0.s, vl2
+; CHECK-NEXT: cmpne p1.s, p0/z, z0.s, #0
+; CHECK-NEXT: brkb p1.b, p0/z, p1.b
+; CHECK-NEXT: mov z0.s, p1/z, #-1 // =0xffffffffffffffff
+; CHECK-NEXT: // kill: def $d0 killed $d0 killed $z0
; CHECK-NEXT: ret
%x = call <2 x i1> @llvm.mask.beforefirst(<2 x i1> %m)
ret <2 x i1> %x
More information about the llvm-commits
mailing list