Skip to content

Commit

Permalink
[RISCV] Inline Assembly: RVC constraint and N modifier (#112561)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
lenary authored Oct 18, 2024
1 parent 7b65971 commit 228f88f
Show file tree
Hide file tree
Showing 17 changed files with 1,040 additions and 5 deletions.
10 changes: 10 additions & 0 deletions clang/lib/Basic/Targets/RISCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@ bool RISCVTargetInfo::validateAsmConstraint(
case 'S': // A symbol or label reference with a constant offset
Info.setAllowsRegister();
return true;
case 'c':
// A RVC register - GPR or FPR
if (Name[1] == 'r' || Name[1] == 'f') {
Info.setAllowsRegister();
Name += 1;
return true;
}
return false;
case 'v':
// A vector register.
if (Name[1] == 'r' || Name[1] == 'd' || Name[1] == 'm') {
Expand All @@ -114,6 +122,8 @@ bool RISCVTargetInfo::validateAsmConstraint(
std::string RISCVTargetInfo::convertConstraint(const char *&Constraint) const {
std::string R;
switch (*Constraint) {
// c* and v* are two-letter constraints on RISC-V.
case 'c':
case 'v':
R = std::string("^") + std::string(Constraint, 2);
Constraint += 1;
Expand Down
40 changes: 39 additions & 1 deletion clang/test/CodeGen/RISCV/riscv-inline-asm.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,35 @@
// RUN: %clang_cc1 -triple riscv64 -O2 -emit-llvm %s -o - \
// RUN: | FileCheck %s

// Test RISC-V specific inline assembly constraints.
// Test RISC-V specific inline assembly constraints and modifiers.

long test_r(long x) {
// CHECK-LABEL: define{{.*}} {{i64|i32}} @test_r(
// CHECK: call {{i64|i32}} asm sideeffect "", "=r,r"({{i64|i32}} %{{.*}})
long ret;
asm volatile ("" : "=r"(ret) : "r"(x));
// CHECK: call {{i64|i32}} asm sideeffect "", "=r,r"({{i64|i32}} %{{.*}})
asm volatile ("" : "=r"(ret) : "r"(x));
return ret;
}

long test_cr(long x) {
// CHECK-LABEL: define{{.*}} {{i64|i32}} @test_cr(
// CHECK: call {{i64|i32}} asm sideeffect "", "=^cr,^cr"({{i64|i32}} %{{.*}})
long ret;
asm volatile ("" : "=cr"(ret) : "cr"(x));
return ret;
}

float cf;
double cd;
void test_cf(float f, double d) {
// CHECK-LABEL: define{{.*}} void @test_cf(
// CHECK: call float asm sideeffect "", "=^cf,^cf"(float %{{.*}})
asm volatile("" : "=cf"(cf) : "cf"(f));
// CHECK: call double asm sideeffect "", "=^cf,^cf"(double %{{.*}})
asm volatile("" : "=cf"(cd) : "cf"(d));
}

void test_I(void) {
// CHECK-LABEL: define{{.*}} void @test_I()
Expand Down Expand Up @@ -58,3 +86,13 @@ void test_s(void) {

asm("// %0 %1 %2" :: "S"(&var), "S"(&arr[1][1]), "S"(test_s));
}

// CHECK-LABEL: test_modifiers(
// CHECK: call void asm sideeffect "// ${0:i} ${1:i}", "r,r"({{i32|i64}} %val, i32 37)
// CHECK: call void asm sideeffect "// ${0:z} ${1:z}", "i,i"(i32 0, i32 1)
// CHECK: call void asm sideeffect "// ${0:N}", "r"({{i32|i64}} %val)
void test_modifiers(long val) {
asm volatile("// %i0 %i1" :: "r"(val), "r"(37));
asm volatile("// %z0 %z1" :: "i"(0), "i"(1));
asm volatile("// %N0" :: "r"(val));
}
8 changes: 8 additions & 0 deletions llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "RISCV.h"
#include "RISCVConstantPoolValue.h"
#include "RISCVMachineFunctionInfo.h"
#include "RISCVRegisterInfo.h"
#include "RISCVTargetMachine.h"
#include "TargetInfo/RISCVTargetInfo.h"
#include "llvm/ADT/APInt.h"
Expand Down Expand Up @@ -348,6 +349,13 @@ bool RISCVAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
if (!MO.isReg())
OS << 'i';
return false;
case 'N': // Print the register encoding as an integer (0-31)
if (!MO.isReg())
return true;

const RISCVRegisterInfo *TRI = STI->getRegisterInfo();
OS << TRI->getEncodingValue(MO.getReg());
return false;
}
}

Expand Down
18 changes: 18 additions & 0 deletions llvm/lib/Target/RISCV/RISCVISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20366,6 +20366,8 @@ RISCVTargetLowering::getConstraintType(StringRef Constraint) const {
} else {
if (Constraint == "vr" || Constraint == "vd" || Constraint == "vm")
return C_RegisterClass;
if (Constraint == "cr" || Constraint == "cf")
return C_RegisterClass;
}
return TargetLowering::getConstraintType(Constraint);
}
Expand Down Expand Up @@ -20428,6 +20430,22 @@ RISCVTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
} else if (Constraint == "vm") {
if (TRI->isTypeLegalForClass(RISCV::VMV0RegClass, VT.SimpleTy))
return std::make_pair(0U, &RISCV::VMV0RegClass);
} else if (Constraint == "cr") {
if (VT == MVT::f16 && Subtarget.hasStdExtZhinxmin())
return std::make_pair(0U, &RISCV::GPRF16CRegClass);
if (VT == MVT::f32 && Subtarget.hasStdExtZfinx())
return std::make_pair(0U, &RISCV::GPRF32CRegClass);
if (VT == MVT::f64 && Subtarget.hasStdExtZdinx() && !Subtarget.is64Bit())
return std::make_pair(0U, &RISCV::GPRPairCRegClass);
if (!VT.isVector())
return std::make_pair(0U, &RISCV::GPRCRegClass);
} else if (Constraint == "cf") {
if (Subtarget.hasStdExtZfhmin() && VT == MVT::f16)
return std::make_pair(0U, &RISCV::FPR16CRegClass);
if (Subtarget.hasStdExtF() && VT == MVT::f32)
return std::make_pair(0U, &RISCV::FPR32CRegClass);
if (Subtarget.hasStdExtD() && VT == MVT::f64)
return std::make_pair(0U, &RISCV::FPR64CRegClass);
}

// Clang will correctly decode the usage of register name aliases into their
Expand Down
19 changes: 16 additions & 3 deletions llvm/lib/Target/RISCV/RISCVRegisterInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,11 @@ def FPR16 : RISCVRegisterClass<[f16, bf16], 16, (add
(sequence "F%u_H", 18, 27) // fs2-fs11
)>;

def FPR16C : RISCVRegisterClass<[f16, bf16], 16, (add
(sequence "F%u_H", 15, 10),
(sequence "F%u_H", 8, 9)
)>;

def FPR32 : RISCVRegisterClass<[f32], 32, (add
(sequence "F%u_F", 15, 10),
(sequence "F%u_F", 0, 7),
Expand Down Expand Up @@ -667,6 +672,10 @@ def GPRF32C : RISCVRegisterClass<[f32], 32, (add (sequence "X%u_W", 10, 15),
(sequence "X%u_W", 8, 9))>;
def GPRF32NoX0 : RISCVRegisterClass<[f32], 32, (sub GPRF32, X0_W)>;

def XLenPairRI : RegInfoByHwMode<
[RV32, RV64],
[RegInfo<64, 64, 32>, RegInfo<128, 128, 64>]>;

// Dummy zero register for use in the register pair containing X0 (as X1 is
// not read to or written when the X0 register pair is used).
def DUMMY_REG_PAIR_WITH_X0 : RISCVReg<0, "0">;
Expand Down Expand Up @@ -698,9 +707,8 @@ let RegAltNameIndices = [ABIRegAltName] in {
}
}

let RegInfos = RegInfoByHwMode<[RV32, RV64],
[RegInfo<64, 64, 32>, RegInfo<128, 128, 64>]>,
DecoderMethod = "DecodeGPRPairRegisterClass" in
let RegInfos = XLenPairRI,
DecoderMethod = "DecodeGPRPairRegisterClass" in {
def GPRPair : RISCVRegisterClass<[XLenPairFVT], 64, (add
X10_X11, X12_X13, X14_X15, X16_X17,
X6_X7,
Expand All @@ -710,6 +718,11 @@ def GPRPair : RISCVRegisterClass<[XLenPairFVT], 64, (add
X0_Pair, X2_X3, X4_X5
)>;

def GPRPairC : RISCVRegisterClass<[XLenPairFVT], 64, (add
X10_X11, X12_X13, X14_X15, X8_X9
)>;
} // let RegInfos = XLenPairRI, DecoderMethod = "DecodeGPRPairRegisterClass"

// The register class is added for inline assembly for vector mask types.
def VM : VReg<VMaskVTs, (add VR), 1>;

Expand Down
33 changes: 33 additions & 0 deletions llvm/test/CodeGen/RISCV/inline-asm-d-constraint-f.ll
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,39 @@ define double @constraint_f_double(double %a) nounwind {
ret double %2
}

define double @constraint_cf_double(double %a) nounwind {
; RV32F-LABEL: constraint_cf_double:
; RV32F: # %bb.0:
; RV32F-NEXT: addi sp, sp, -16
; RV32F-NEXT: sw a0, 8(sp)
; RV32F-NEXT: sw a1, 12(sp)
; RV32F-NEXT: fld fa5, 8(sp)
; RV32F-NEXT: lui a0, %hi(gd)
; RV32F-NEXT: fld fa4, %lo(gd)(a0)
; RV32F-NEXT: #APP
; RV32F-NEXT: fadd.d fa5, fa5, fa4
; RV32F-NEXT: #NO_APP
; RV32F-NEXT: fsd fa5, 8(sp)
; RV32F-NEXT: lw a0, 8(sp)
; RV32F-NEXT: lw a1, 12(sp)
; RV32F-NEXT: addi sp, sp, 16
; RV32F-NEXT: ret
;
; RV64F-LABEL: constraint_cf_double:
; RV64F: # %bb.0:
; RV64F-NEXT: lui a1, %hi(gd)
; RV64F-NEXT: fld fa5, %lo(gd)(a1)
; RV64F-NEXT: fmv.d.x fa4, a0
; RV64F-NEXT: #APP
; RV64F-NEXT: fadd.d fa5, fa4, fa5
; RV64F-NEXT: #NO_APP
; RV64F-NEXT: fmv.x.d a0, fa5
; RV64F-NEXT: ret
%1 = load double, ptr @gd
%2 = tail call double asm "fadd.d $0, $1, $2", "=^cf,^cf,^cf"(double %a, double %1)
ret double %2
}

define double @constraint_f_double_abi_name(double %a) nounwind {
; RV32F-LABEL: constraint_f_double_abi_name:
; RV32F: # %bb.0:
Expand Down
109 changes: 109 additions & 0 deletions llvm/test/CodeGen/RISCV/inline-asm-d-modifier-N.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc -mtriple=riscv32 -mattr=+d -target-abi=ilp32 -verify-machineinstrs -no-integrated-as < %s \
; RUN: | FileCheck -check-prefix=RV32F %s
; RUN: llc -mtriple=riscv64 -mattr=+d -target-abi=lp64 -verify-machineinstrs -no-integrated-as < %s \
; RUN: | FileCheck -check-prefix=RV64F %s

;; `.insn 0x4, 0x02000053 | (${0:N} << 7) | (${1:N} << 15) | (${2:N} << 20)` is
;; the raw encoding for `fadd.d`

@gd = external global double

define double @constraint_f_double(double %a) nounwind {
; RV32F-LABEL: constraint_f_double:
; RV32F: # %bb.0:
; RV32F-NEXT: addi sp, sp, -16
; RV32F-NEXT: sw a0, 8(sp)
; RV32F-NEXT: sw a1, 12(sp)
; RV32F-NEXT: fld fa5, 8(sp)
; RV32F-NEXT: lui a0, %hi(gd)
; RV32F-NEXT: fld fa4, %lo(gd)(a0)
; RV32F-NEXT: #APP
; RV32F-NEXT: .insn 0x4, 0x02000053 | (15 << 7) | (15 << 15) | (14 << 20)
; RV32F-NEXT: #NO_APP
; RV32F-NEXT: fsd fa5, 8(sp)
; RV32F-NEXT: lw a0, 8(sp)
; RV32F-NEXT: lw a1, 12(sp)
; RV32F-NEXT: addi sp, sp, 16
; RV32F-NEXT: ret
;
; RV64F-LABEL: constraint_f_double:
; RV64F: # %bb.0:
; RV64F-NEXT: lui a1, %hi(gd)
; RV64F-NEXT: fld fa5, %lo(gd)(a1)
; RV64F-NEXT: fmv.d.x fa4, a0
; RV64F-NEXT: #APP
; RV64F-NEXT: .insn 0x4, 0x02000053 | (15 << 7) | (14 << 15) | (15 << 20)
; RV64F-NEXT: #NO_APP
; RV64F-NEXT: fmv.x.d a0, fa5
; RV64F-NEXT: ret
%1 = load double, ptr @gd
%2 = tail call double asm ".insn 0x4, 0x02000053 | (${0:N} << 7) | (${1:N} << 15) | (${2:N} << 20)", "=f,f,f"(double %a, double %1)
ret double %2
}

define double @constraint_cf_double(double %a) nounwind {
; RV32F-LABEL: constraint_cf_double:
; RV32F: # %bb.0:
; RV32F-NEXT: addi sp, sp, -16
; RV32F-NEXT: sw a0, 8(sp)
; RV32F-NEXT: sw a1, 12(sp)
; RV32F-NEXT: fld fa5, 8(sp)
; RV32F-NEXT: lui a0, %hi(gd)
; RV32F-NEXT: fld fa4, %lo(gd)(a0)
; RV32F-NEXT: #APP
; RV32F-NEXT: .insn 0x4, 0x02000053 | (15 << 7) | (15 << 15) | (14 << 20)
; RV32F-NEXT: #NO_APP
; RV32F-NEXT: fsd fa5, 8(sp)
; RV32F-NEXT: lw a0, 8(sp)
; RV32F-NEXT: lw a1, 12(sp)
; RV32F-NEXT: addi sp, sp, 16
; RV32F-NEXT: ret
;
; RV64F-LABEL: constraint_cf_double:
; RV64F: # %bb.0:
; RV64F-NEXT: lui a1, %hi(gd)
; RV64F-NEXT: fld fa5, %lo(gd)(a1)
; RV64F-NEXT: fmv.d.x fa4, a0
; RV64F-NEXT: #APP
; RV64F-NEXT: .insn 0x4, 0x02000053 | (15 << 7) | (14 << 15) | (15 << 20)
; RV64F-NEXT: #NO_APP
; RV64F-NEXT: fmv.x.d a0, fa5
; RV64F-NEXT: ret
%1 = load double, ptr @gd
%2 = tail call double asm ".insn 0x4, 0x02000053 | (${0:N} << 7) | (${1:N} << 15) | (${2:N} << 20)", "=^cf,^cf,^cf"(double %a, double %1)
ret double %2
}

define double @constraint_f_double_abi_name(double %a) nounwind {
; RV32F-LABEL: constraint_f_double_abi_name:
; RV32F: # %bb.0:
; RV32F-NEXT: addi sp, sp, -16
; RV32F-NEXT: sw a0, 8(sp)
; RV32F-NEXT: sw a1, 12(sp)
; RV32F-NEXT: fld fa1, 8(sp)
; RV32F-NEXT: lui a0, %hi(gd)
; RV32F-NEXT: fld fs0, %lo(gd)(a0)
; RV32F-NEXT: #APP
; RV32F-NEXT: .insn 0x4, 0x02000053 | (0 << 7) | (11 << 15) | (8 << 20)
; RV32F-NEXT: #NO_APP
; RV32F-NEXT: fsd ft0, 8(sp)
; RV32F-NEXT: lw a0, 8(sp)
; RV32F-NEXT: lw a1, 12(sp)
; RV32F-NEXT: addi sp, sp, 16
; RV32F-NEXT: ret
;
; RV64F-LABEL: constraint_f_double_abi_name:
; RV64F: # %bb.0:
; RV64F-NEXT: lui a1, %hi(gd)
; RV64F-NEXT: fld fs0, %lo(gd)(a1)
; RV64F-NEXT: fmv.d.x fa1, a0
; RV64F-NEXT: #APP
; RV64F-NEXT: .insn 0x4, 0x02000053 | (0 << 7) | (11 << 15) | (8 << 20)
; RV64F-NEXT: #NO_APP
; RV64F-NEXT: fmv.x.d a0, ft0
; RV64F-NEXT: ret
%1 = load double, ptr @gd
%2 = tail call double asm ".insn 0x4, 0x02000053 | (${0:N} << 7) | (${1:N} << 15) | (${2:N} << 20)", "={ft0},{fa1},{fs0}"(double %a, double %1)
ret double %2
}
28 changes: 27 additions & 1 deletion llvm/test/CodeGen/RISCV/inline-asm-f-constraint-f.ll
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; NOTE: Assertions gave been autogenerated by utils/update_llc_test_checks.py
; RUN: llc -mtriple=riscv32 -mattr=+f -target-abi=ilp32 -verify-machineinstrs < %s \
; RUN: | FileCheck -check-prefix=RV32F %s
; RUN: llc -mtriple=riscv64 -mattr=+f -target-abi=lp64 -verify-machineinstrs < %s \
Expand Down Expand Up @@ -38,6 +37,33 @@ define float @constraint_f_float(float %a) nounwind {
ret float %2
}

define float @constraint_cf_float(float %a) nounwind {
; RV32F-LABEL: constraint_cf_float:
; RV32F: # %bb.0:
; RV32F-NEXT: lui a1, %hi(gf)
; RV32F-NEXT: flw fa5, %lo(gf)(a1)
; RV32F-NEXT: fmv.w.x fa4, a0
; RV32F-NEXT: #APP
; RV32F-NEXT: fadd.s fa5, fa4, fa5
; RV32F-NEXT: #NO_APP
; RV32F-NEXT: fmv.x.w a0, fa5
; RV32F-NEXT: ret
;
; RV64F-LABEL: constraint_cf_float:
; RV64F: # %bb.0:
; RV64F-NEXT: lui a1, %hi(gf)
; RV64F-NEXT: flw fa5, %lo(gf)(a1)
; RV64F-NEXT: fmv.w.x fa4, a0
; RV64F-NEXT: #APP
; RV64F-NEXT: fadd.s fa5, fa4, fa5
; RV64F-NEXT: #NO_APP
; RV64F-NEXT: fmv.x.w a0, fa5
; RV64F-NEXT: ret
%1 = load float, ptr @gf
%2 = tail call float asm "fadd.s $0, $1, $2", "=^cf,cf,cf"(float %a, float %1)
ret float %2
}

define float @constraint_f_float_abi_name(float %a) nounwind {
; RV32F-LABEL: constraint_f_float_abi_name:
; RV32F: # %bb.0:
Expand Down
Loading

0 comments on commit 228f88f

Please sign in to comment.