Skip to content

Commit 98f4b77

Browse files
authored
[RISCV][llvm] Support fixed-length vector inline assembly constraints (#150724)
1 parent 9ddc043 commit 98f4b77

File tree

3 files changed

+170
-3
lines changed

3 files changed

+170
-3
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// REQUIRES: riscv-registered-target
2+
3+
// RUN: %clang_cc1 -triple riscv32 -target-feature +v \
4+
// RUN: -mvscale-min=2 -mvscale-max=2 -O2 -emit-llvm %s -o - \
5+
// RUN: | FileCheck %s
6+
// RUN: %clang_cc1 -triple riscv64 -target-feature +v \
7+
// RUN: -mvscale-min=2 -mvscale-max=2 -O2 -emit-llvm %s -o - \
8+
// RUN: | FileCheck %s
9+
10+
// Test RISC-V V-extension fixed-length vector inline assembly constraints.
11+
#include <riscv_vector.h>
12+
#include <stdbool.h>
13+
14+
typedef vbool1_t fixed_bool1_t __attribute__((riscv_rvv_vector_bits(__riscv_v_fixed_vlen)));
15+
typedef vint32m1_t fixed_i32m1_t __attribute__((riscv_rvv_vector_bits(__riscv_v_fixed_vlen)));
16+
typedef vint8mf2_t fixed_i8mf2_t __attribute__((riscv_rvv_vector_bits(__riscv_v_fixed_vlen / 2)));
17+
18+
typedef bool bx2 __attribute__((ext_vector_type(16)));
19+
typedef int i32x2 __attribute__((ext_vector_type(2)));
20+
typedef char i8x4 __attribute__((ext_vector_type(4)));
21+
22+
fixed_i32m1_t test_vr(fixed_i32m1_t a) {
23+
// CHECK-LABEL: define{{.*}} @test_vr
24+
// 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)
25+
fixed_i32m1_t ret;
26+
asm volatile ("vadd.vv %0, %1, %2" : "=vr"(ret) : "vr"(a), "vr"(a));
27+
return ret;
28+
}
29+
30+
i32x2 test_vr2(i32x2 a) {
31+
// CHECK-LABEL: define{{.*}} @test_vr2
32+
// CHECK: %1 = tail call <2 x i32> asm sideeffect "vadd.vv $0, $1, $2", "=^vr,^vr,^vr"(<2 x i32> %0, <2 x i32> %0)
33+
i32x2 ret;
34+
asm volatile ("vadd.vv %0, %1, %2" : "=vr"(ret) : "vr"(a), "vr"(a));
35+
return ret;
36+
}
37+
38+
fixed_i8mf2_t test_vd(fixed_i8mf2_t a) {
39+
// CHECK-LABEL: define{{.*}} @test_vd
40+
// 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)
41+
fixed_i8mf2_t ret;
42+
asm volatile ("vadd.vv %0, %1, %2" : "=vd"(ret) : "vr"(a), "vr"(a));
43+
return ret;
44+
}
45+
46+
i8x4 test_vd2(i8x4 a) {
47+
// CHECK-LABEL: define{{.*}} @test_vd2
48+
// CHECK: %1 = tail call <4 x i8> asm sideeffect "vadd.vv $0, $1, $2", "=^vd,^vr,^vr"(<4 x i8> %0, <4 x i8> %0)
49+
i8x4 ret;
50+
asm volatile ("vadd.vv %0, %1, %2" : "=vd"(ret) : "vr"(a), "vr"(a));
51+
return ret;
52+
}
53+
54+
fixed_bool1_t test_vm(fixed_bool1_t a) {
55+
// CHECK-LABEL: define{{.*}} @test_vm
56+
// 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)
57+
fixed_bool1_t ret;
58+
asm volatile ("vmand.mm %0, %1, %2" : "=vm"(ret) : "vm"(a), "vm"(a));
59+
return ret;
60+
}
61+
62+
void test_vm2(bx2 a) {
63+
// CHECK-LABEL: define{{.*}} @test_vm2
64+
// CHECK: tail call void asm sideeffect "dummy $0", "^vm"(<16 x i1> %a1)
65+
asm volatile ("dummy %0" :: "vm"(a));
66+
}

llvm/lib/Target/RISCV/RISCVISelLowering.cpp

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23408,6 +23408,12 @@ RISCVTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
2340823408
&RISCV::VRN2M4RegClass}) {
2340923409
if (TRI->isTypeLegalForClass(*RC, VT.SimpleTy))
2341023410
return std::make_pair(0U, RC);
23411+
23412+
if (VT.isFixedLengthVector() && useRVVForFixedLengthVectorVT(VT)) {
23413+
MVT ContainerVT = getContainerForFixedLengthVector(VT);
23414+
if (TRI->isTypeLegalForClass(*RC, ContainerVT))
23415+
return std::make_pair(0U, RC);
23416+
}
2341123417
}
2341223418
} else if (Constraint == "vd") {
2341323419
for (const auto *RC :
@@ -23421,10 +23427,24 @@ RISCVTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
2342123427
&RISCV::VRN2M4NoV0RegClass}) {
2342223428
if (TRI->isTypeLegalForClass(*RC, VT.SimpleTy))
2342323429
return std::make_pair(0U, RC);
23430+
23431+
if (VT.isFixedLengthVector() && useRVVForFixedLengthVectorVT(VT)) {
23432+
MVT ContainerVT = getContainerForFixedLengthVector(VT);
23433+
if (TRI->isTypeLegalForClass(*RC, ContainerVT))
23434+
return std::make_pair(0U, RC);
23435+
}
2342423436
}
2342523437
} else if (Constraint == "vm") {
2342623438
if (TRI->isTypeLegalForClass(RISCV::VMV0RegClass, VT.SimpleTy))
2342723439
return std::make_pair(0U, &RISCV::VMV0RegClass);
23440+
23441+
if (VT.isFixedLengthVector() && useRVVForFixedLengthVectorVT(VT)) {
23442+
MVT ContainerVT = getContainerForFixedLengthVector(VT);
23443+
// VT here might be coerced to vector with i8 elements, so we need to
23444+
// check if this is a M1 register here instead of checking VMV0RegClass.
23445+
if (TRI->isTypeLegalForClass(RISCV::VRRegClass, ContainerVT))
23446+
return std::make_pair(0U, &RISCV::VMV0RegClass);
23447+
}
2342823448
} else if (Constraint == "cr") {
2342923449
if (VT == MVT::f16 && Subtarget.hasStdExtZhinxmin())
2343023450
return std::make_pair(0U, &RISCV::GPRF16CRegClass);
@@ -24302,7 +24322,12 @@ bool RISCVTargetLowering::splitValueIntoRegisterParts(
2430224322
return true;
2430324323
}
2430424324

24305-
if (ValueVT.isScalableVector() && PartVT.isScalableVector()) {
24325+
if ((ValueVT.isScalableVector() || ValueVT.isFixedLengthVector()) &&
24326+
PartVT.isScalableVector()) {
24327+
if (ValueVT.isFixedLengthVector()) {
24328+
ValueVT = getContainerForFixedLengthVector(ValueVT.getSimpleVT());
24329+
Val = convertToScalableVector(ValueVT, Val, DAG, Subtarget);
24330+
}
2430624331
LLVMContext &Context = *DAG.getContext();
2430724332
EVT ValueEltVT = ValueVT.getVectorElementType();
2430824333
EVT PartEltVT = PartVT.getVectorElementType();
@@ -24372,12 +24397,17 @@ SDValue RISCVTargetLowering::joinRegisterPartsIntoValue(
2437224397
return Val;
2437324398
}
2437424399

24375-
if (ValueVT.isScalableVector() && PartVT.isScalableVector()) {
24400+
if ((ValueVT.isScalableVector() || ValueVT.isFixedLengthVector()) &&
24401+
PartVT.isScalableVector()) {
2437624402
LLVMContext &Context = *DAG.getContext();
2437724403
SDValue Val = Parts[0];
2437824404
EVT ValueEltVT = ValueVT.getVectorElementType();
2437924405
EVT PartEltVT = PartVT.getVectorElementType();
2438024406
unsigned ValueVTBitSize = ValueVT.getSizeInBits().getKnownMinValue();
24407+
if (ValueVT.isFixedLengthVector())
24408+
ValueVTBitSize = getContainerForFixedLengthVector(ValueVT.getSimpleVT())
24409+
.getSizeInBits()
24410+
.getKnownMinValue();
2438124411
unsigned PartVTBitSize = PartVT.getSizeInBits().getKnownMinValue();
2438224412
if (PartVTBitSize % ValueVTBitSize == 0) {
2438324413
assert(PartVTBitSize >= ValueVTBitSize);
@@ -24395,7 +24425,10 @@ SDValue RISCVTargetLowering::joinRegisterPartsIntoValue(
2439524425
EVT::getVectorVT(Context, ValueEltVT, Count, /*IsScalable=*/true);
2439624426
Val = DAG.getNode(ISD::BITCAST, DL, SameEltTypeVT, Val);
2439724427
}
24398-
Val = DAG.getExtractSubvector(DL, ValueVT, Val, 0);
24428+
if (ValueVT.isFixedLengthVector())
24429+
Val = convertFromScalableVector(ValueVT, Val, DAG, Subtarget);
24430+
else
24431+
Val = DAG.getExtractSubvector(DL, ValueVT, Val, 0);
2439924432
return Val;
2440024433
}
2440124434
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2+
; RUN: llc -mtriple=riscv32 -mattr=+v -verify-machineinstrs < %s \
3+
; RUN: | FileCheck -check-prefix=RV32I %s
4+
; RUN: llc -mtriple=riscv64 -mattr=+v -verify-machineinstrs < %s \
5+
; RUN: | FileCheck -check-prefix=RV64I %s
6+
7+
define <1 x i8> @constraint_vr_fixed(<1 x i8> %0, <1 x i8> %1) nounwind {
8+
; RV32I-LABEL: constraint_vr_fixed:
9+
; RV32I: # %bb.0:
10+
; RV32I-NEXT: #APP
11+
; RV32I-NEXT: vadd.vv v8, v8, v9
12+
; RV32I-NEXT: #NO_APP
13+
; RV32I-NEXT: ret
14+
;
15+
; RV64I-LABEL: constraint_vr_fixed:
16+
; RV64I: # %bb.0:
17+
; RV64I-NEXT: #APP
18+
; RV64I-NEXT: vadd.vv v8, v8, v9
19+
; RV64I-NEXT: #NO_APP
20+
; RV64I-NEXT: ret
21+
%a = tail call <1 x i8> asm "vadd.vv $0, $1, $2", "=^vr,^vr,^vr"(
22+
<1 x i8> %0, <1 x i8> %1)
23+
ret <1 x i8> %a
24+
}
25+
26+
define <4 x i32> @constraint_vd_fixed(<4 x i32> %0, <4 x i32> %1) nounwind {
27+
; RV32I-LABEL: constraint_vd_fixed:
28+
; RV32I: # %bb.0:
29+
; RV32I-NEXT: #APP
30+
; RV32I-NEXT: vadd.vv v8, v8, v9
31+
; RV32I-NEXT: #NO_APP
32+
; RV32I-NEXT: ret
33+
;
34+
; RV64I-LABEL: constraint_vd_fixed:
35+
; RV64I: # %bb.0:
36+
; RV64I-NEXT: #APP
37+
; RV64I-NEXT: vadd.vv v8, v8, v9
38+
; RV64I-NEXT: #NO_APP
39+
; RV64I-NEXT: ret
40+
%a = tail call <4 x i32> asm "vadd.vv $0, $1, $2", "=^vd,^vr,^vr"(
41+
<4 x i32> %0, <4 x i32> %1)
42+
ret <4 x i32> %a
43+
}
44+
45+
define <16 x i1> @constraint_vm_fixed(<16 x i1> %0, <16 x i1> %1) nounwind {
46+
; RV32I-LABEL: constraint_vm_fixed:
47+
; RV32I: # %bb.0:
48+
; RV32I-NEXT: vsetivli zero, 1, e8, m1, ta, ma
49+
; RV32I-NEXT: vmv1r.v v9, v0
50+
; RV32I-NEXT: vmv1r.v v0, v8
51+
; RV32I-NEXT: #APP
52+
; RV32I-NEXT: vadd.vv v0, v9, v0
53+
; RV32I-NEXT: #NO_APP
54+
; RV32I-NEXT: ret
55+
;
56+
; RV64I-LABEL: constraint_vm_fixed:
57+
; RV64I: # %bb.0:
58+
; RV64I-NEXT: vsetivli zero, 1, e8, m1, ta, ma
59+
; RV64I-NEXT: vmv1r.v v9, v0
60+
; RV64I-NEXT: vmv1r.v v0, v8
61+
; RV64I-NEXT: #APP
62+
; RV64I-NEXT: vadd.vv v0, v9, v0
63+
; RV64I-NEXT: #NO_APP
64+
; RV64I-NEXT: ret
65+
%a = tail call <16 x i1> asm "vadd.vv $0, $1, $2", "=^vr,^vr,^vm"(
66+
<16 x i1> %0, <16 x i1> %1)
67+
ret <16 x i1> %a
68+
}

0 commit comments

Comments
 (0)