Skip to content

Commit 6d60f09

Browse files
committed
[RISCV] Inline Assembly: RVC constraint and N modifier
This change implements support for the `cr` and `cf` register constraints (which allocate a RVC GPR or RVC FPR respectively), and the `N` modifier (which prints the raw encoding of a register rather than the name). The intention behind these additions is to make it easier to use inline assembly when assembling raw instructions that are not supported by the compiler, for instance when experimenting with new instructions or when supporting proprietary extensions outside the toolchain. These implement part of my proposal in riscv-non-isa/riscv-c-api-doc#92 As part of the implementation, I felt there was not enough coverage of inline assembly and the "in X" floating-point extensions, so I have added more regression tests around these configurations.
1 parent a24e8a7 commit 6d60f09

17 files changed

+1041
-5
lines changed

clang/lib/Basic/Targets/RISCV.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,14 @@ bool RISCVTargetInfo::validateAsmConstraint(
100100
case 'S': // A symbol or label reference with a constant offset
101101
Info.setAllowsRegister();
102102
return true;
103+
case 'c':
104+
// A RVC register - GPR or FPR
105+
if (Name[1] == 'r' || Name[1] == 'f') {
106+
Info.setAllowsRegister();
107+
Name += 1;
108+
return true;
109+
}
110+
return false;
103111
case 'v':
104112
// A vector register.
105113
if (Name[1] == 'r' || Name[1] == 'd' || Name[1] == 'm') {
@@ -114,6 +122,8 @@ bool RISCVTargetInfo::validateAsmConstraint(
114122
std::string RISCVTargetInfo::convertConstraint(const char *&Constraint) const {
115123
std::string R;
116124
switch (*Constraint) {
125+
// c* and v* are two-letter constraints on RISC-V.
126+
case 'c':
117127
case 'v':
118128
R = std::string("^") + std::string(Constraint, 2);
119129
Constraint += 1;

clang/test/CodeGen/RISCV/riscv-inline-asm.c

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,35 @@
33
// RUN: %clang_cc1 -triple riscv64 -O2 -emit-llvm %s -o - \
44
// RUN: | FileCheck %s
55

6-
// Test RISC-V specific inline assembly constraints.
6+
// Test RISC-V specific inline assembly constraints and modifiers.
7+
8+
long test_r(long x) {
9+
// CHECK-LABEL: define{{.*}} {{i64|i32}} @test_r(
10+
// CHECK: call {{i64|i32}} asm sideeffect "", "=r,r"({{i64|i32}} %{{.*}})
11+
long ret;
12+
asm volatile ("" : "=r"(ret) : "r"(x));
13+
// CHECK: call {{i64|i32}} asm sideeffect "", "=r,r"({{i64|i32}} %{{.*}})
14+
asm volatile ("" : "=r"(ret) : "r"(x));
15+
return ret;
16+
}
17+
18+
long test_cr(long x) {
19+
// CHECK-LABEL: define{{.*}} {{i64|i32}} @test_cr(
20+
// CHECK: call {{i64|i32}} asm sideeffect "", "=^cr,^cr"({{i64|i32}} %{{.*}})
21+
long ret;
22+
asm volatile ("" : "=cr"(ret) : "cr"(x));
23+
return ret;
24+
}
25+
26+
float cf;
27+
double cd;
28+
void test_cf(float f, double d) {
29+
// CHECK-LABEL: define{{.*}} void @test_cf(
30+
// CHECK: call float asm sideeffect "", "=^cf,^cf"(float %{{.*}})
31+
asm volatile("" : "=cf"(cf) : "cf"(f));
32+
// CHECK: call double asm sideeffect "", "=^cf,^cf"(double %{{.*}})
33+
asm volatile("" : "=cf"(cd) : "cf"(d));
34+
}
735

836
void test_I(void) {
937
// CHECK-LABEL: define{{.*}} void @test_I()
@@ -58,3 +86,13 @@ void test_s(void) {
5886

5987
asm("// %0 %1 %2" :: "S"(&var), "S"(&arr[1][1]), "S"(test_s));
6088
}
89+
90+
// CHECK-LABEL: test_modifiers(
91+
// CHECK: call void asm sideeffect "// ${0:i} ${1:i}", "r,r"({{i32|i64}} %val, i32 37)
92+
// CHECK: call void asm sideeffect "// ${0:z} ${1:z}", "i,i"(i32 0, i32 1)
93+
// CHECK: call void asm sideeffect "// ${0:N}", "r"({{i32|i64}} %val)
94+
void test_modifiers(long val) {
95+
asm volatile("// %i0 %i1" :: "r"(val), "r"(37));
96+
asm volatile("// %z0 %z1" :: "i"(0), "i"(1));
97+
asm volatile("// %N0" :: "r"(val));
98+
}

llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "RISCV.h"
2020
#include "RISCVConstantPoolValue.h"
2121
#include "RISCVMachineFunctionInfo.h"
22+
#include "RISCVRegisterInfo.h"
2223
#include "RISCVTargetMachine.h"
2324
#include "TargetInfo/RISCVTargetInfo.h"
2425
#include "llvm/ADT/APInt.h"
@@ -348,6 +349,14 @@ bool RISCVAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
348349
if (!MO.isReg())
349350
OS << 'i';
350351
return false;
352+
case 'N': // Print the register encoding as an integer (0-31, or 0-7 when
353+
// the constraint was 'c*')
354+
if (!MO.isReg())
355+
return true;
356+
357+
const RISCVRegisterInfo *TRI = STI->getRegisterInfo();
358+
OS << TRI->getEncodingValue(MO.getReg());
359+
return false;
351360
}
352361
}
353362

llvm/lib/Target/RISCV/RISCVISelLowering.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20229,6 +20229,8 @@ RISCVTargetLowering::getConstraintType(StringRef Constraint) const {
2022920229
} else {
2023020230
if (Constraint == "vr" || Constraint == "vd" || Constraint == "vm")
2023120231
return C_RegisterClass;
20232+
if (Constraint == "cr" || Constraint == "cf")
20233+
return C_RegisterClass;
2023220234
}
2023320235
return TargetLowering::getConstraintType(Constraint);
2023420236
}
@@ -20291,6 +20293,22 @@ RISCVTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
2029120293
} else if (Constraint == "vm") {
2029220294
if (TRI->isTypeLegalForClass(RISCV::VMV0RegClass, VT.SimpleTy))
2029320295
return std::make_pair(0U, &RISCV::VMV0RegClass);
20296+
} else if (Constraint == "cr") {
20297+
if (VT == MVT::f16 && Subtarget.hasStdExtZhinxmin())
20298+
return std::make_pair(0U, &RISCV::GPRF16CRegClass);
20299+
if (VT == MVT::f32 && Subtarget.hasStdExtZfinx())
20300+
return std::make_pair(0U, &RISCV::GPRF32CRegClass);
20301+
if (VT == MVT::f64 && Subtarget.hasStdExtZdinx() && !Subtarget.is64Bit())
20302+
return std::make_pair(0U, &RISCV::GPRPairCRegClass);
20303+
if (!VT.isVector())
20304+
return std::make_pair(0U, &RISCV::GPRCRegClass);
20305+
} else if (Constraint == "cf") {
20306+
if (Subtarget.hasStdExtZfhmin() && VT == MVT::f16)
20307+
return std::make_pair(0U, &RISCV::FPR16CRegClass);
20308+
if (Subtarget.hasStdExtF() && VT == MVT::f32)
20309+
return std::make_pair(0U, &RISCV::FPR32CRegClass);
20310+
if (Subtarget.hasStdExtD() && VT == MVT::f64)
20311+
return std::make_pair(0U, &RISCV::FPR64CRegClass);
2029420312
}
2029520313

2029620314
// Clang will correctly decode the usage of register name aliases into their

llvm/lib/Target/RISCV/RISCVRegisterInfo.td

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,11 @@ def FPR16 : RISCVRegisterClass<[f16, bf16], 16, (add
338338
(sequence "F%u_H", 18, 27) // fs2-fs11
339339
)>;
340340

341+
def FPR16C : RISCVRegisterClass<[f16, bf16], 16, (add
342+
(sequence "F%u_H", 15, 10),
343+
(sequence "F%u_H", 8, 9)
344+
)>;
345+
341346
def FPR32 : RISCVRegisterClass<[f32], 32, (add
342347
(sequence "F%u_F", 15, 10),
343348
(sequence "F%u_F", 0, 7),
@@ -667,6 +672,10 @@ def GPRF32C : RISCVRegisterClass<[f32], 32, (add (sequence "X%u_W", 10, 15),
667672
(sequence "X%u_W", 8, 9))>;
668673
def GPRF32NoX0 : RISCVRegisterClass<[f32], 32, (sub GPRF32, X0_W)>;
669674

675+
def XLenPairRI : RegInfoByHwMode<
676+
[RV32, RV64],
677+
[RegInfo<64, 64, 32>, RegInfo<128, 128, 64>]>;
678+
670679
// Dummy zero register for use in the register pair containing X0 (as X1 is
671680
// not read to or written when the X0 register pair is used).
672681
def DUMMY_REG_PAIR_WITH_X0 : RISCVReg<0, "0">;
@@ -698,9 +707,8 @@ let RegAltNameIndices = [ABIRegAltName] in {
698707
}
699708
}
700709

701-
let RegInfos = RegInfoByHwMode<[RV32, RV64],
702-
[RegInfo<64, 64, 32>, RegInfo<128, 128, 64>]>,
703-
DecoderMethod = "DecodeGPRPairRegisterClass" in
710+
let RegInfos = XLenPairRI,
711+
DecoderMethod = "DecodeGPRPairRegisterClass" in {
704712
def GPRPair : RISCVRegisterClass<[XLenPairFVT], 64, (add
705713
X10_X11, X12_X13, X14_X15, X16_X17,
706714
X6_X7,
@@ -710,6 +718,11 @@ def GPRPair : RISCVRegisterClass<[XLenPairFVT], 64, (add
710718
X0_Pair, X2_X3, X4_X5
711719
)>;
712720

721+
def GPRPairC : RISCVRegisterClass<[XLenPairFVT], 64, (add
722+
X10_X11, X12_X13, X14_X15, X8_X9
723+
)>;
724+
} // let RegInfos = XLenPairRI, DecoderMethod = "DecodeGPRPairRegisterClass"
725+
713726
// The register class is added for inline assembly for vector mask types.
714727
def VM : VReg<VMaskVTs, (add VR), 1>;
715728

llvm/test/CodeGen/RISCV/inline-asm-d-constraint-f.ll

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,39 @@ define double @constraint_f_double(double %a) nounwind {
3939
ret double %2
4040
}
4141

42+
define double @constraint_cf_double(double %a) nounwind {
43+
; RV32F-LABEL: constraint_cf_double:
44+
; RV32F: # %bb.0:
45+
; RV32F-NEXT: addi sp, sp, -16
46+
; RV32F-NEXT: sw a0, 8(sp)
47+
; RV32F-NEXT: sw a1, 12(sp)
48+
; RV32F-NEXT: fld fa5, 8(sp)
49+
; RV32F-NEXT: lui a0, %hi(gd)
50+
; RV32F-NEXT: fld fa4, %lo(gd)(a0)
51+
; RV32F-NEXT: #APP
52+
; RV32F-NEXT: fadd.d fa5, fa5, fa4
53+
; RV32F-NEXT: #NO_APP
54+
; RV32F-NEXT: fsd fa5, 8(sp)
55+
; RV32F-NEXT: lw a0, 8(sp)
56+
; RV32F-NEXT: lw a1, 12(sp)
57+
; RV32F-NEXT: addi sp, sp, 16
58+
; RV32F-NEXT: ret
59+
;
60+
; RV64F-LABEL: constraint_cf_double:
61+
; RV64F: # %bb.0:
62+
; RV64F-NEXT: lui a1, %hi(gd)
63+
; RV64F-NEXT: fld fa5, %lo(gd)(a1)
64+
; RV64F-NEXT: fmv.d.x fa4, a0
65+
; RV64F-NEXT: #APP
66+
; RV64F-NEXT: fadd.d fa5, fa4, fa5
67+
; RV64F-NEXT: #NO_APP
68+
; RV64F-NEXT: fmv.x.d a0, fa5
69+
; RV64F-NEXT: ret
70+
%1 = load double, ptr @gd
71+
%2 = tail call double asm "fadd.d $0, $1, $2", "=^cf,^cf,^cf"(double %a, double %1)
72+
ret double %2
73+
}
74+
4275
define double @constraint_f_double_abi_name(double %a) nounwind {
4376
; RV32F-LABEL: constraint_f_double_abi_name:
4477
; RV32F: # %bb.0:
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2+
; RUN: llc -mtriple=riscv32 -mattr=+d -target-abi=ilp32 -verify-machineinstrs -no-integrated-as < %s \
3+
; RUN: | FileCheck -check-prefix=RV32F %s
4+
; RUN: llc -mtriple=riscv64 -mattr=+d -target-abi=lp64 -verify-machineinstrs -no-integrated-as < %s \
5+
; RUN: | FileCheck -check-prefix=RV64F %s
6+
7+
;; `.insn 0x4, 0x02000053 | (${0:N} << 7) | (${1:N} << 15) | (${2:N} << 20)` is
8+
;; the raw encoding for `fadd.d`
9+
10+
@gd = external global double
11+
12+
define double @constraint_f_double(double %a) nounwind {
13+
; RV32F-LABEL: constraint_f_double:
14+
; RV32F: # %bb.0:
15+
; RV32F-NEXT: addi sp, sp, -16
16+
; RV32F-NEXT: sw a0, 8(sp)
17+
; RV32F-NEXT: sw a1, 12(sp)
18+
; RV32F-NEXT: fld fa5, 8(sp)
19+
; RV32F-NEXT: lui a0, %hi(gd)
20+
; RV32F-NEXT: fld fa4, %lo(gd)(a0)
21+
; RV32F-NEXT: #APP
22+
; RV32F-NEXT: .insn 0x4, 0x02000053 | (15 << 7) | (15 << 15) | (14 << 20)
23+
; RV32F-NEXT: #NO_APP
24+
; RV32F-NEXT: fsd fa5, 8(sp)
25+
; RV32F-NEXT: lw a0, 8(sp)
26+
; RV32F-NEXT: lw a1, 12(sp)
27+
; RV32F-NEXT: addi sp, sp, 16
28+
; RV32F-NEXT: ret
29+
;
30+
; RV64F-LABEL: constraint_f_double:
31+
; RV64F: # %bb.0:
32+
; RV64F-NEXT: lui a1, %hi(gd)
33+
; RV64F-NEXT: fld fa5, %lo(gd)(a1)
34+
; RV64F-NEXT: fmv.d.x fa4, a0
35+
; RV64F-NEXT: #APP
36+
; RV64F-NEXT: .insn 0x4, 0x02000053 | (15 << 7) | (14 << 15) | (15 << 20)
37+
; RV64F-NEXT: #NO_APP
38+
; RV64F-NEXT: fmv.x.d a0, fa5
39+
; RV64F-NEXT: ret
40+
%1 = load double, ptr @gd
41+
%2 = tail call double asm ".insn 0x4, 0x02000053 | (${0:N} << 7) | (${1:N} << 15) | (${2:N} << 20)", "=f,f,f"(double %a, double %1)
42+
ret double %2
43+
}
44+
45+
define double @constraint_cf_double(double %a) nounwind {
46+
; RV32F-LABEL: constraint_cf_double:
47+
; RV32F: # %bb.0:
48+
; RV32F-NEXT: addi sp, sp, -16
49+
; RV32F-NEXT: sw a0, 8(sp)
50+
; RV32F-NEXT: sw a1, 12(sp)
51+
; RV32F-NEXT: fld fa5, 8(sp)
52+
; RV32F-NEXT: lui a0, %hi(gd)
53+
; RV32F-NEXT: fld fa4, %lo(gd)(a0)
54+
; RV32F-NEXT: #APP
55+
; RV32F-NEXT: .insn 0x4, 0x02000053 | (15 << 7) | (15 << 15) | (14 << 20)
56+
; RV32F-NEXT: #NO_APP
57+
; RV32F-NEXT: fsd fa5, 8(sp)
58+
; RV32F-NEXT: lw a0, 8(sp)
59+
; RV32F-NEXT: lw a1, 12(sp)
60+
; RV32F-NEXT: addi sp, sp, 16
61+
; RV32F-NEXT: ret
62+
;
63+
; RV64F-LABEL: constraint_cf_double:
64+
; RV64F: # %bb.0:
65+
; RV64F-NEXT: lui a1, %hi(gd)
66+
; RV64F-NEXT: fld fa5, %lo(gd)(a1)
67+
; RV64F-NEXT: fmv.d.x fa4, a0
68+
; RV64F-NEXT: #APP
69+
; RV64F-NEXT: .insn 0x4, 0x02000053 | (15 << 7) | (14 << 15) | (15 << 20)
70+
; RV64F-NEXT: #NO_APP
71+
; RV64F-NEXT: fmv.x.d a0, fa5
72+
; RV64F-NEXT: ret
73+
%1 = load double, ptr @gd
74+
%2 = tail call double asm ".insn 0x4, 0x02000053 | (${0:N} << 7) | (${1:N} << 15) | (${2:N} << 20)", "=^cf,^cf,^cf"(double %a, double %1)
75+
ret double %2
76+
}
77+
78+
define double @constraint_f_double_abi_name(double %a) nounwind {
79+
; RV32F-LABEL: constraint_f_double_abi_name:
80+
; RV32F: # %bb.0:
81+
; RV32F-NEXT: addi sp, sp, -16
82+
; RV32F-NEXT: sw a0, 8(sp)
83+
; RV32F-NEXT: sw a1, 12(sp)
84+
; RV32F-NEXT: fld fa1, 8(sp)
85+
; RV32F-NEXT: lui a0, %hi(gd)
86+
; RV32F-NEXT: fld fs0, %lo(gd)(a0)
87+
; RV32F-NEXT: #APP
88+
; RV32F-NEXT: .insn 0x4, 0x02000053 | (0 << 7) | (11 << 15) | (8 << 20)
89+
; RV32F-NEXT: #NO_APP
90+
; RV32F-NEXT: fsd ft0, 8(sp)
91+
; RV32F-NEXT: lw a0, 8(sp)
92+
; RV32F-NEXT: lw a1, 12(sp)
93+
; RV32F-NEXT: addi sp, sp, 16
94+
; RV32F-NEXT: ret
95+
;
96+
; RV64F-LABEL: constraint_f_double_abi_name:
97+
; RV64F: # %bb.0:
98+
; RV64F-NEXT: lui a1, %hi(gd)
99+
; RV64F-NEXT: fld fs0, %lo(gd)(a1)
100+
; RV64F-NEXT: fmv.d.x fa1, a0
101+
; RV64F-NEXT: #APP
102+
; RV64F-NEXT: .insn 0x4, 0x02000053 | (0 << 7) | (11 << 15) | (8 << 20)
103+
; RV64F-NEXT: #NO_APP
104+
; RV64F-NEXT: fmv.x.d a0, ft0
105+
; RV64F-NEXT: ret
106+
%1 = load double, ptr @gd
107+
%2 = tail call double asm ".insn 0x4, 0x02000053 | (${0:N} << 7) | (${1:N} << 15) | (${2:N} << 20)", "={ft0},{fa1},{fs0}"(double %a, double %1)
108+
ret double %2
109+
}

llvm/test/CodeGen/RISCV/inline-asm-f-constraint-f.ll

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2-
; NOTE: Assertions gave been autogenerated by utils/update_llc_test_checks.py
32
; RUN: llc -mtriple=riscv32 -mattr=+f -target-abi=ilp32 -verify-machineinstrs < %s \
43
; RUN: | FileCheck -check-prefix=RV32F %s
54
; RUN: llc -mtriple=riscv64 -mattr=+f -target-abi=lp64 -verify-machineinstrs < %s \
@@ -38,6 +37,33 @@ define float @constraint_f_float(float %a) nounwind {
3837
ret float %2
3938
}
4039

40+
define float @constraint_cf_float(float %a) nounwind {
41+
; RV32F-LABEL: constraint_cf_float:
42+
; RV32F: # %bb.0:
43+
; RV32F-NEXT: lui a1, %hi(gf)
44+
; RV32F-NEXT: flw fa5, %lo(gf)(a1)
45+
; RV32F-NEXT: fmv.w.x fa4, a0
46+
; RV32F-NEXT: #APP
47+
; RV32F-NEXT: fadd.s fa5, fa4, fa5
48+
; RV32F-NEXT: #NO_APP
49+
; RV32F-NEXT: fmv.x.w a0, fa5
50+
; RV32F-NEXT: ret
51+
;
52+
; RV64F-LABEL: constraint_cf_float:
53+
; RV64F: # %bb.0:
54+
; RV64F-NEXT: lui a1, %hi(gf)
55+
; RV64F-NEXT: flw fa5, %lo(gf)(a1)
56+
; RV64F-NEXT: fmv.w.x fa4, a0
57+
; RV64F-NEXT: #APP
58+
; RV64F-NEXT: fadd.s fa5, fa4, fa5
59+
; RV64F-NEXT: #NO_APP
60+
; RV64F-NEXT: fmv.x.w a0, fa5
61+
; RV64F-NEXT: ret
62+
%1 = load float, ptr @gf
63+
%2 = tail call float asm "fadd.s $0, $1, $2", "=^cf,cf,cf"(float %a, float %1)
64+
ret float %2
65+
}
66+
4167
define float @constraint_f_float_abi_name(float %a) nounwind {
4268
; RV32F-LABEL: constraint_f_float_abi_name:
4369
; RV32F: # %bb.0:

0 commit comments

Comments
 (0)