[clang] [llvm] [RISC-V] Add CSR read/write builtins (PR #85091)
Nemanja Ivanovic via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 13 07:47:00 PDT 2024
https://github.com/nemanjai created https://github.com/llvm/llvm-project/pull/85091
To facilitate proper range checking and better error messages if an attempt is made to call these with non-litaral arguments, we provide builtins to emit the read/write CSR instructions.
>From 543086dd6a20852721bd54667196c68011d0e46e Mon Sep 17 00:00:00 2001
From: Nemanja Ivanovic <nemanja at synopsys.com>
Date: Wed, 13 Mar 2024 15:44:15 +0100
Subject: [PATCH] [RISC-V] Add CSR read/write builtins
To facilitate proper range checking and better error messages
if an attempt is made to call these with non-litaral arguments,
we provide builtins to emit the read/write CSR instructions.
---
clang/include/clang/Basic/BuiltinsRISCV.td | 6 ++
clang/lib/CodeGen/CGBuiltin.cpp | 10 +++
clang/lib/Sema/SemaChecking.cpp | 3 +
.../csr-intrinsics/riscv-zicsr-invalid.c | 25 ++++++++
.../RISCV/csr-intrinsics/riscv-zicsr.c | 63 +++++++++++++++++++
llvm/include/llvm/IR/IntrinsicsRISCV.td | 15 +++++
llvm/lib/Target/RISCV/RISCVInstrInfo.td | 17 +++++
.../test/CodeGen/RISCV/rv32zicsr-intrinsic.ll | 34 ++++++++++
.../test/CodeGen/RISCV/rv64zicsr-intrinsic.ll | 34 ++++++++++
9 files changed, 207 insertions(+)
create mode 100644 clang/test/CodeGen/RISCV/csr-intrinsics/riscv-zicsr-invalid.c
create mode 100644 clang/test/CodeGen/RISCV/csr-intrinsics/riscv-zicsr.c
create mode 100644 llvm/test/CodeGen/RISCV/rv32zicsr-intrinsic.ll
create mode 100644 llvm/test/CodeGen/RISCV/rv64zicsr-intrinsic.ll
diff --git a/clang/include/clang/Basic/BuiltinsRISCV.td b/clang/include/clang/Basic/BuiltinsRISCV.td
index 4cc89a8a9d8af2..14ef6f9d313a41 100644
--- a/clang/include/clang/Basic/BuiltinsRISCV.td
+++ b/clang/include/clang/Basic/BuiltinsRISCV.td
@@ -20,6 +20,12 @@ class RISCVBuiltin<string prototype, string features = ""> : TargetBuiltin {
let Attributes = [NoThrow, Const] in {
//===----------------------------------------------------------------------===//
+// Zicsr extension.
+//===----------------------------------------------------------------------===//
+def csrr : RISCVBuiltin<"unsigned long int(unsigned long int)", "zicsr">;
+def csrw :
+ RISCVBuiltin<"void(unsigned long int, unsigned long int)", "zicsr">;
+//===----------------------------------------------------------------------===//
// Zbb extension.
//===----------------------------------------------------------------------===//
def orc_b_32 : RISCVBuiltin<"unsigned int(unsigned int)", "zbb">;
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 93ab465079777b..99486d8aee6f9e 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -21379,6 +21379,16 @@ Value *CodeGenFunction::EmitRISCVBuiltinExpr(unsigned BuiltinID,
llvm::SmallVector<llvm::Type *, 2> IntrinsicTypes;
switch (BuiltinID) {
default: llvm_unreachable("unexpected builtin ID");
+ // Zicsr
+ case RISCV::BI__builtin_riscv_csrr:
+ case RISCV::BI__builtin_riscv_csrw:
+ if (IntPtrTy->getScalarSizeInBits() == 32)
+ ID = BuiltinID == RISCV::BI__builtin_riscv_csrr ? Intrinsic::riscv_csrr
+ : Intrinsic::riscv_csrw;
+ else
+ ID = BuiltinID == RISCV::BI__builtin_riscv_csrr ? Intrinsic::riscv_csrr64
+ : Intrinsic::riscv_csrw64;
+ break;
case RISCV::BI__builtin_riscv_orc_b_32:
case RISCV::BI__builtin_riscv_orc_b_64:
case RISCV::BI__builtin_riscv_clz_32:
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index a5f42b630c3fa2..637da165c73589 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -6223,6 +6223,9 @@ bool Sema::CheckRISCVBuiltinFunctionCall(const TargetInfo &TI,
case RISCVVector::BI__builtin_rvv_vfwnmsac_vv_rm_mu:
case RISCVVector::BI__builtin_rvv_vfwnmsac_vf_rm_mu:
return SemaBuiltinConstantArgRange(TheCall, 4, 0, 4);
+ case RISCV::BI__builtin_riscv_csrr:
+ case RISCV::BI__builtin_riscv_csrw:
+ return SemaBuiltinConstantArgRange(TheCall, 0, 0, 4095);
case RISCV::BI__builtin_riscv_ntl_load:
case RISCV::BI__builtin_riscv_ntl_store:
DeclRefExpr *DRE =
diff --git a/clang/test/CodeGen/RISCV/csr-intrinsics/riscv-zicsr-invalid.c b/clang/test/CodeGen/RISCV/csr-intrinsics/riscv-zicsr-invalid.c
new file mode 100644
index 00000000000000..d0420731977d05
--- /dev/null
+++ b/clang/test/CodeGen/RISCV/csr-intrinsics/riscv-zicsr-invalid.c
@@ -0,0 +1,25 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
+// RUN: %clang_cc1 -triple riscv32 -target-feature +zicsr %s -fsyntax-only -verify
+// RUN: %clang_cc1 -triple riscv64 -target-feature +zicsr %s -fsyntax-only -verify
+// RUN: %clang_cc1 -triple riscv32 %s -fsyntax-only -verify
+
+#ifdef __riscv_zicsr
+unsigned long non_const(unsigned long a) {
+ return __builtin_riscv_csrr(a); // expected-error {{argument to '__builtin_riscv_csrr' must be a constant integer}}
+}
+
+unsigned long too_large() {
+ return __builtin_riscv_csrr(33312); // expected-error {{argument value 33312 is outside the valid range [0, 4095]}}
+}
+
+void non_const_write(unsigned long d) {
+ return __builtin_riscv_csrw(d, d); // expected-error {{argument to '__builtin_riscv_csrw' must be a constant integer}}
+}
+#else
+unsigned long read(unsigned long a) {
+ return __builtin_riscv_csrr(3); // expected-error {{builtin requires at least one of the following extensions: 'Zicsr'}}
+}
+void write(unsigned long d) {
+ return __builtin_riscv_csrw(3, d); // expected-error {{builtin requires at least one of the following extensions: 'Zicsr'}}
+}
+#endif
diff --git a/clang/test/CodeGen/RISCV/csr-intrinsics/riscv-zicsr.c b/clang/test/CodeGen/RISCV/csr-intrinsics/riscv-zicsr.c
new file mode 100644
index 00000000000000..c86c796303eba6
--- /dev/null
+++ b/clang/test/CodeGen/RISCV/csr-intrinsics/riscv-zicsr.c
@@ -0,0 +1,63 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
+// RUN: %clang_cc1 -triple riscv32 -target-feature +zicsr -emit-llvm %s -o - \
+// RUN: -disable-O0-optnone | opt -S -passes=mem2reg \
+// RUN: | FileCheck %s -check-prefix=RV32ZICSR
+// RUN: %clang_cc1 -triple riscv64 -target-feature +zicsr -emit-llvm %s -o - \
+// RUN: -disable-O0-optnone | opt -S -passes=mem2reg \
+// RUN: | FileCheck %s -check-prefix=RV64ZICSR
+
+// RV32ZICSR-LABEL: @readcsr(
+// RV32ZICSR-NEXT: entry:
+// RV32ZICSR-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.csrr(i32 3)
+// RV32ZICSR-NEXT: ret i32 [[TMP0]]
+//
+// RV64ZICSR-LABEL: @readcsr(
+// RV64ZICSR-NEXT: entry:
+// RV64ZICSR-NEXT: [[TMP0:%.*]] = call i64 @llvm.riscv.csrr64(i64 3)
+// RV64ZICSR-NEXT: ret i64 [[TMP0]]
+//
+unsigned long readcsr() {
+ return __builtin_riscv_csrr(3);
+}
+
+// RV32ZICSR-LABEL: @readcsr_arbitrary(
+// RV32ZICSR-NEXT: entry:
+// RV32ZICSR-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.csrr(i32 333)
+// RV32ZICSR-NEXT: ret i32 [[TMP0]]
+//
+// RV64ZICSR-LABEL: @readcsr_arbitrary(
+// RV64ZICSR-NEXT: entry:
+// RV64ZICSR-NEXT: [[TMP0:%.*]] = call i64 @llvm.riscv.csrr64(i64 333)
+// RV64ZICSR-NEXT: ret i64 [[TMP0]]
+//
+unsigned long readcsr_arbitrary() {
+ return __builtin_riscv_csrr(333);
+}
+
+// RV32ZICSR-LABEL: @writecsr(
+// RV32ZICSR-NEXT: entry:
+// RV32ZICSR-NEXT: call void @llvm.riscv.csrw(i32 3, i32 [[D:%.*]])
+// RV32ZICSR-NEXT: ret void
+//
+// RV64ZICSR-LABEL: @writecsr(
+// RV64ZICSR-NEXT: entry:
+// RV64ZICSR-NEXT: call void @llvm.riscv.csrw64(i64 3, i64 [[D:%.*]])
+// RV64ZICSR-NEXT: ret void
+//
+void writecsr(unsigned long d) {
+ return __builtin_riscv_csrw(3, d);
+}
+
+// RV32ZICSR-LABEL: @writecsr_arbitrary(
+// RV32ZICSR-NEXT: entry:
+// RV32ZICSR-NEXT: call void @llvm.riscv.csrw(i32 333, i32 [[D:%.*]])
+// RV32ZICSR-NEXT: ret void
+//
+// RV64ZICSR-LABEL: @writecsr_arbitrary(
+// RV64ZICSR-NEXT: entry:
+// RV64ZICSR-NEXT: call void @llvm.riscv.csrw64(i64 333, i64 [[D:%.*]])
+// RV64ZICSR-NEXT: ret void
+//
+void writecsr_arbitrary(unsigned long d) {
+ return __builtin_riscv_csrw(333, d);
+}
diff --git a/llvm/include/llvm/IR/IntrinsicsRISCV.td b/llvm/include/llvm/IR/IntrinsicsRISCV.td
index 1d58860a0afc8a..71d20f5be0bfa2 100644
--- a/llvm/include/llvm/IR/IntrinsicsRISCV.td
+++ b/llvm/include/llvm/IR/IntrinsicsRISCV.td
@@ -74,6 +74,21 @@ let TargetPrefix = "riscv" in {
} // TargetPrefix = "riscv"
+let TargetPrefix = "riscv" in {
+ // Zicsr
+ def int_riscv_csrr :
+ DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_i32_ty],
+ [IntrNoMem, IntrHasSideEffects, ImmArg<ArgIndex<0>>]>;
+ def int_riscv_csrr64 :
+ DefaultAttrsIntrinsic<[llvm_i64_ty], [llvm_i64_ty],
+ [IntrNoMem, IntrHasSideEffects, ImmArg<ArgIndex<0>>]>;
+ def int_riscv_csrw :
+ DefaultAttrsIntrinsic<[], [llvm_i32_ty, llvm_i32_ty],
+ [IntrNoMem, IntrHasSideEffects, ImmArg<ArgIndex<0>>]>;
+ def int_riscv_csrw64 :
+ DefaultAttrsIntrinsic<[], [llvm_i64_ty, llvm_i64_ty],
+ [IntrNoMem, IntrHasSideEffects, ImmArg<ArgIndex<0>>]>;
+} // TargetPrefix = "riscv"
//===----------------------------------------------------------------------===//
// Bitmanip (Bit Manipulation) Extension
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
index e753c1f1add0c6..fa01ae9fdeb915 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
@@ -565,6 +565,13 @@ class CSR_ir<bits<3> funct3, string opcodestr>
: RVInstI<funct3, OPC_SYSTEM, (outs GPR:$rd), (ins csr_sysreg:$imm12, GPR:$rs1),
opcodestr, "$rd, $imm12, $rs1">, Sched<[WriteCSR, ReadCSR]>;
+let hasNoSchedulingInfo = 1,
+ hasSideEffects = 1, mayLoad = 0, mayStore = 0 in
+class CSR_ir_x0<bits<3> funct3, string opcodestr>
+ : RVInstI<funct3, OPC_SYSTEM, (outs), (ins csr_sysreg:$imm12, GPR:$rs1),
+ opcodestr, "$imm12, $rs1">, Sched<[WriteCSR]> {
+ let rd = 0;
+}
let hasNoSchedulingInfo = 1,
hasSideEffects = 1, mayLoad = 0, mayStore = 0 in
class CSR_ii<bits<3> funct3, string opcodestr>
@@ -733,6 +740,8 @@ def UNIMP : RVInstI<0b001, OPC_SYSTEM, (outs), (ins), "unimp", "">,
} // hasSideEffects = 1, mayLoad = 0, mayStore = 0
+let isCodeGenOnly = 1 in
+def CSRW : CSR_ir_x0<0b001, "csrw">;
def CSRRW : CSR_ir<0b001, "csrrw">;
def CSRRS : CSR_ir<0b010, "csrrs">;
def CSRRC : CSR_ir<0b011, "csrrc">;
@@ -1845,6 +1854,14 @@ def ReadCounterWide : Pseudo<(outs GPR:$lo, GPR:$hi), (ins i32imm:$csr_lo, i32im
(riscv_read_counter_wide csr_sysreg:$csr_lo, csr_sysreg:$csr_hi))],
"", "">;
+// Zicsr
+let Predicates = [IsRV64] in {
+ def : Pat<(i64 (int_riscv_csrr64 timm:$I)), (CSRRS csr_sysreg:$I, (XLenVT X0))>;
+ def : Pat<(int_riscv_csrw64 timm:$I, i64:$D), (CSRW csr_sysreg:$I, i64:$D)>;
+}
+def : Pat<(i32 (int_riscv_csrr timm:$I)), (CSRRS csr_sysreg:$I, (XLenVT X0))>;
+def : Pat<(int_riscv_csrw timm:$I, i32:$D), (CSRW csr_sysreg:$I, i32:$D)>;
+
/// traps
// We lower `trap` to `unimp`, as this causes a hard exception on nearly all
diff --git a/llvm/test/CodeGen/RISCV/rv32zicsr-intrinsic.ll b/llvm/test/CodeGen/RISCV/rv32zicsr-intrinsic.ll
new file mode 100644
index 00000000000000..b866bdb415cd30
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/rv32zicsr-intrinsic.ll
@@ -0,0 +1,34 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -mattr=+zicsr -verify-machineinstrs < %s \
+; RUN: | FileCheck %s -check-prefix=RV32ZICSR
+
+declare i32 @llvm.riscv.csrr(i32 immarg)
+declare void @llvm.riscv.csrw(i32 immarg, i32)
+
+define i32 @read() nounwind {
+; RV32ZICSR-LABEL: read:
+; RV32ZICSR: # %bb.0:
+; RV32ZICSR-NEXT: csrr a0, fcsr
+; RV32ZICSR-NEXT: csrr a1, fcsr
+; RV32ZICSR-NEXT: csrr a2, stval
+; RV32ZICSR-NEXT: add a1, a1, a2
+; RV32ZICSR-NEXT: add a0, a0, a1
+; RV32ZICSR-NEXT: ret
+ %val = call i32 @llvm.riscv.csrr(i32 3)
+ %val2 = call i32 @llvm.riscv.csrr(i32 3)
+ %val3 = call i32 @llvm.riscv.csrr(i32 323)
+ %add = add i32 %val2, %val3
+ %ret = add i32 %val, %add
+ ret i32 %ret
+}
+
+define void @testwrite(i32 %d) nounwind {
+; RV32ZICSR-LABEL: testwrite:
+; RV32ZICSR: # %bb.0:
+; RV32ZICSR-NEXT: csrw fcsr, a0
+; RV32ZICSR-NEXT: csrw 3435, a0
+; RV32ZICSR-NEXT: ret
+ call void @llvm.riscv.csrw(i32 3, i32 %d)
+ call void @llvm.riscv.csrw(i32 3435, i32 %d)
+ ret void
+}
diff --git a/llvm/test/CodeGen/RISCV/rv64zicsr-intrinsic.ll b/llvm/test/CodeGen/RISCV/rv64zicsr-intrinsic.ll
new file mode 100644
index 00000000000000..39163100673b0f
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/rv64zicsr-intrinsic.ll
@@ -0,0 +1,34 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv64 -mattr=+zicsr -verify-machineinstrs < %s \
+; RUN: | FileCheck %s
+
+declare i64 @llvm.riscv.csrr64(i64 immarg)
+declare void @llvm.riscv.csrw64(i64 immarg, i64)
+
+define i64 @read() nounwind {
+; CHECK-LABEL: read:
+; CHECK: # %bb.0:
+; CHECK-NEXT: csrr a0, fcsr
+; CHECK-NEXT: csrr a1, fcsr
+; CHECK-NEXT: csrr a2, 111
+; CHECK-NEXT: add a1, a1, a2
+; CHECK-NEXT: add a0, a0, a1
+; CHECK-NEXT: ret
+ %val = call i64 @llvm.riscv.csrr64(i64 3)
+ %val2 = call i64 @llvm.riscv.csrr64(i64 3)
+ %val3 = call i64 @llvm.riscv.csrr64(i64 111)
+ %add = add i64 %val2, %val3
+ %ret = add i64 %val, %add
+ ret i64 %ret
+}
+
+define void @testwrite(i64 %d) nounwind {
+; CHECK-LABEL: testwrite:
+; CHECK: # %bb.0:
+; CHECK-NEXT: csrw fcsr, a0
+; CHECK-NEXT: csrw 3231, a0
+; CHECK-NEXT: ret
+ call void @llvm.riscv.csrw64(i64 3, i64 %d)
+ call void @llvm.riscv.csrw64(i64 3231, i64 %d)
+ ret void
+}
More information about the cfe-commits
mailing list