Skip to content

Conversation

@4vtomat
Copy link
Member

@4vtomat 4vtomat commented Jul 25, 2025

No description provided.

@llvmbot llvmbot added clang Clang issues not falling into any other category backend:RISC-V labels Jul 25, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 25, 2025

@llvm/pr-subscribers-backend-risc-v

@llvm/pr-subscribers-clang

Author: Brandon Wu (4vtomat)

Changes

Full diff: https://github.com/llvm/llvm-project/pull/150724.diff

2 Files Affected:

  • (added) clang/test/CodeGen/RISCV/riscv-inline-asm-fixed-length-vector.c (+39)
  • (modified) llvm/lib/Target/RISCV/RISCVISelLowering.cpp (+32-2)
diff --git a/clang/test/CodeGen/RISCV/riscv-inline-asm-fixed-length-vector.c b/clang/test/CodeGen/RISCV/riscv-inline-asm-fixed-length-vector.c
new file mode 100644
index 0000000000000..0bfd9d6f158c6
--- /dev/null
+++ b/clang/test/CodeGen/RISCV/riscv-inline-asm-fixed-length-vector.c
@@ -0,0 +1,39 @@
+// REQUIRES: riscv-registered-target
+
+// RUN: %clang_cc1 -triple riscv32 -target-feature +v \
+// RUN:     -mvscale-min=2 -mvscale-max=2 -O2 -emit-llvm %s -o - \
+// RUN:     | FileCheck %s
+// RUN: %clang_cc1 -triple riscv64 -target-feature +v \
+// RUN:     -mvscale-min=2 -mvscale-max=2 -O2 -emit-llvm %s -o - \
+// RUN:     | FileCheck %s
+
+// Test RISC-V V-extension fixed-length vector inline assembly constraints.
+#include <riscv_vector.h>
+
+typedef vbool1_t fixed_bool1_t __attribute__((riscv_rvv_vector_bits(__riscv_v_fixed_vlen)));
+typedef vint32m1_t fixed_i32m1_t __attribute__((riscv_rvv_vector_bits(__riscv_v_fixed_vlen)));
+typedef vint8mf2_t fixed_i8mf2_t __attribute__((riscv_rvv_vector_bits(__riscv_v_fixed_vlen / 2)));
+
+fixed_i32m1_t test_vr(fixed_i32m1_t a) {
+// CHECK-LABEL: define{{.*}} @test_vr
+// CHECK: %0 = tail call <4 x i32> asm sideeffect "vadd.vv $0, $1, $2", "=^vr,^vr,^vr"(<4 x i32> %a, <4 x i32> %a)
+  fixed_i32m1_t ret;
+  asm volatile ("vadd.vv %0, %1, %2" : "=vr"(ret) : "vr"(a), "vr"(a));
+  return ret;
+}
+
+fixed_i8mf2_t test_vd(fixed_i8mf2_t a) {
+// CHECK-LABEL: define{{.*}} @test_vd
+// CHECK: %0 = tail call <8 x i8> asm sideeffect "vadd.vv $0, $1, $2", "=^vd,^vr,^vr"(<8 x i8> %a, <8 x i8> %a)
+  fixed_i8mf2_t ret;
+  asm volatile ("vadd.vv %0, %1, %2" : "=vd"(ret) : "vr"(a), "vr"(a));
+  return ret;
+}
+
+fixed_bool1_t test_vm(fixed_bool1_t a) {
+// CHECK-LABEL: define{{.*}} @test_vm
+// CHECK: %1 = tail call <16 x i8> asm sideeffect "vmand.mm $0, $1, $2", "=^vm,^vm,^vm"(<16 x i8> %a, <16 x i8> %a)
+  fixed_bool1_t ret;
+  asm volatile ("vmand.mm %0, %1, %2" : "=vm"(ret) : "vm"(a), "vm"(a));
+  return ret;
+}
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 809fbc8926e35..53550e69e4c6d 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -23133,6 +23133,12 @@ RISCVTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
           &RISCV::VRN2M4RegClass}) {
       if (TRI->isTypeLegalForClass(*RC, VT.SimpleTy))
         return std::make_pair(0U, RC);
+
+      if (VT.isFixedLengthVector() && Subtarget.useRVVForFixedLengthVectors()) {
+        MVT ContainerVT = getContainerForFixedLengthVector(VT);
+        if (TRI->isTypeLegalForClass(*RC, ContainerVT))
+          return std::make_pair(0U, RC);
+      }
     }
   } else if (Constraint == "vd") {
     for (const auto *RC :
@@ -23146,10 +23152,24 @@ RISCVTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
           &RISCV::VRN2M4NoV0RegClass}) {
       if (TRI->isTypeLegalForClass(*RC, VT.SimpleTy))
         return std::make_pair(0U, RC);
+
+      if (VT.isFixedLengthVector() && Subtarget.useRVVForFixedLengthVectors()) {
+        MVT ContainerVT = getContainerForFixedLengthVector(VT);
+        if (TRI->isTypeLegalForClass(*RC, ContainerVT))
+          return std::make_pair(0U, RC);
+      }
     }
   } else if (Constraint == "vm") {
     if (TRI->isTypeLegalForClass(RISCV::VMV0RegClass, VT.SimpleTy))
       return std::make_pair(0U, &RISCV::VMV0RegClass);
+
+    if (VT.isFixedLengthVector() && Subtarget.useRVVForFixedLengthVectors()) {
+      MVT ContainerVT = getContainerForFixedLengthVector(VT);
+      // VT here is coerced to vector with i8 elements, so we need to check if
+      // this is a M1 register here instead of checking VMV0RegClass.
+      if (TRI->isTypeLegalForClass(RISCV::VRRegClass, ContainerVT))
+        return std::make_pair(0U, &RISCV::VMV0RegClass);
+    }
   } else if (Constraint == "cr") {
     if (VT == MVT::f16 && Subtarget.hasStdExtZhinxmin())
       return std::make_pair(0U, &RISCV::GPRF16CRegClass);
@@ -24027,7 +24047,12 @@ bool RISCVTargetLowering::splitValueIntoRegisterParts(
     return true;
   }
 
-  if (ValueVT.isScalableVector() && PartVT.isScalableVector()) {
+  if ((ValueVT.isScalableVector() || ValueVT.isFixedLengthVector()) &&
+      PartVT.isScalableVector()) {
+    if (ValueVT.isFixedLengthVector()) {
+      ValueVT = getContainerForFixedLengthVector(ValueVT.getSimpleVT());
+      Val = convertToScalableVector(ValueVT, Val, DAG, Subtarget);
+    }
     LLVMContext &Context = *DAG.getContext();
     EVT ValueEltVT = ValueVT.getVectorElementType();
     EVT PartEltVT = PartVT.getVectorElementType();
@@ -24097,12 +24122,17 @@ SDValue RISCVTargetLowering::joinRegisterPartsIntoValue(
     return Val;
   }
 
-  if (ValueVT.isScalableVector() && PartVT.isScalableVector()) {
+  if ((ValueVT.isScalableVector() || ValueVT.isFixedLengthVector()) &&
+      PartVT.isScalableVector()) {
     LLVMContext &Context = *DAG.getContext();
     SDValue Val = Parts[0];
     EVT ValueEltVT = ValueVT.getVectorElementType();
     EVT PartEltVT = PartVT.getVectorElementType();
     unsigned ValueVTBitSize = ValueVT.getSizeInBits().getKnownMinValue();
+    if (ValueVT.isFixedLengthVector())
+      ValueVTBitSize = getContainerForFixedLengthVector(ValueVT.getSimpleVT())
+                           .getSizeInBits()
+                           .getKnownMinValue();
     unsigned PartVTBitSize = PartVT.getSizeInBits().getKnownMinValue();
     if (PartVTBitSize % ValueVTBitSize == 0) {
       assert(PartVTBitSize >= ValueVTBitSize);

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this comment. Where is it coerced?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see. You tested riscv_rvv_vector_bits but this should also work without riscv_rvv_vector_bits.

Copy link
Member

@lenary lenary left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please can you add a LLVM IR test for llc?

I don't think I know enough about the "fixed vectors in scalable vectors" lowering to know if splitValueIntoRegisterParts and joinRegisterPartsIntoValue are correct but they don't seem so wrong at a glance. I might have expected to see a call to convertFromScalableVector (as join and split tend to be symmetric) but I see there's a getExtractSubvector already.

@4vtomat
Copy link
Member Author

4vtomat commented Jul 26, 2025

Please can you add a LLVM IR test for llc?

Yeah sure!

I don't think I know enough about the "fixed vectors in scalable vectors" lowering to know if splitValueIntoRegisterParts and joinRegisterPartsIntoValue are correct but they don't seem so wrong at a glance. I might have expected to see a call to convertFromScalableVector (as join and split tend to be symmetric) but I see there's a getExtractSubvector already.

The reason I didn't use convertFromScalableVector in joinRegisterPartsIntoValue is because it's just a wrapper of getExtractSubvector with additional assertion. It's guaranteed that those assertion always doesn't fail in this case, so we can just use getExtractSubvector.
But yeah I agree with you that it's better to use convertFromScalableVector to make the code more readable lol

@4vtomat
Copy link
Member Author

4vtomat commented Aug 11, 2025

Gentle ping~

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to use useRVVForFixedLengthVectorVT(VT) instead of Subtarget.useRVVForFixedLengthVectors()? getContainerForFixedLengthVector expects only legal VTs.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah right, we should use useRVVForFixedLengthVectorVT

@4vtomat 4vtomat force-pushed the fixed_vec_asm_constraint branch from 9ab4f5f to 1fcc25d Compare August 14, 2025 02:04
@4vtomat
Copy link
Member Author

4vtomat commented Aug 14, 2025

rebase

Copy link
Collaborator

@topperc topperc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@4vtomat 4vtomat force-pushed the fixed_vec_asm_constraint branch from 1fcc25d to 6d39385 Compare August 18, 2025 03:03
@4vtomat 4vtomat enabled auto-merge (squash) August 18, 2025 03:05
@4vtomat 4vtomat merged commit 98f4b77 into llvm:main Aug 18, 2025
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend:RISC-V clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants