lenary created this revision.
lenary added reviewers: asb, luismarques.
Herald added subscribers: llvm-commits, cfe-commits, apazos, sameer.abuasal, 
pzheng, s.egerton, benna, psnobl, jocewei, PkmX, rkruppe, the_o, brucehoult, 
MartinMosbeck, rogfer01, edward-jones, zzheng, MaskRay, jrtc27, shiva0217, 
kito-cheng, niosHD, sabuasal, simoncook, johnrusso, rbar, hiraditya.
Herald added projects: clang, LLVM.

As of clang 9.0, the only way to access the RISC-V Control and Status Registers
is to use inline assembly, which is ugly and hinders optimisations.

This patch adds some Clang Builtins and LLVM Intrinsics to allow programmers
to access the CSRs without using inline assembly, which should therefore be
safer.

I hope to build a slightly nicer interface to this, to support the read-only or
write-only CSR operations (akin to the `csrr`, `csrw`, `csrs` and `csrc`
pseudo-instructions) via these builtins, which will eventually be available via
`<rvintrin.h>`.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D71778

Files:
  clang/include/clang/Basic/BuiltinsRISCV.def
  clang/include/clang/Basic/TargetBuiltins.h
  clang/lib/Basic/Targets/RISCV.cpp
  clang/lib/Basic/Targets/RISCV.h
  clang/lib/CodeGen/CGBuiltin.cpp
  clang/lib/CodeGen/CodeGenFunction.h
  clang/test/CodeGen/riscv-csr-builtins.c
  llvm/include/llvm/IR/IntrinsicsRISCV.td
  llvm/lib/Target/RISCV/RISCVInstrInfo.td
  llvm/lib/Target/RISCV/RISCVInstructionSelector.cpp
  llvm/test/CodeGen/RISCV/intrinsics/csr_accesses_rv32.ll
  llvm/test/CodeGen/RISCV/intrinsics/csr_accesses_rv64.ll

Index: llvm/test/CodeGen/RISCV/intrinsics/csr_accesses_rv64.ll
===================================================================
--- /dev/null
+++ llvm/test/CodeGen/RISCV/intrinsics/csr_accesses_rv64.ll
@@ -0,0 +1,34 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s \
+; RUN:   | FileCheck -check-prefix=RV64I %s
+
+declare i64 @llvm.riscv.csr.read.write.i64(i64, i64)
+declare i64 @llvm.riscv.csr.read.set.i64(i64, i64)
+declare i64 @llvm.riscv.csr.read.clear.i64(i64, i64)
+
+define i64 @test_csr_read_ustatus(i64 %a) nounwind {
+; RV64I-LABEL: test_csr_read_ustatus:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    csrrw a0, ustatus, a0
+; RV64I-NEXT:    ret
+  %1 = call i64 @llvm.riscv.csr.read.write.i64(i64 0, i64 %a)
+  ret i64 %1
+}
+
+define i64 @test_csr_set_ustatus(i64 %a) nounwind {
+; RV64I-LABEL: test_csr_set_ustatus:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    csrrs a0, ustatus, a0
+; RV64I-NEXT:    ret
+  %1 = call i64 @llvm.riscv.csr.read.set.i64(i64 0, i64 %a)
+  ret i64 %1
+}
+
+define i64 @test_csr_clear_ustatus(i64 %a) nounwind {
+; RV64I-LABEL: test_csr_clear_ustatus:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    csrrc a0, ustatus, a0
+; RV64I-NEXT:    ret
+  %1 = call i64 @llvm.riscv.csr.read.clear.i64(i64 0, i64 %a)
+  ret i64 %1
+}
Index: llvm/test/CodeGen/RISCV/intrinsics/csr_accesses_rv32.ll
===================================================================
--- /dev/null
+++ llvm/test/CodeGen/RISCV/intrinsics/csr_accesses_rv32.ll
@@ -0,0 +1,34 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
+; RUN:   | FileCheck -check-prefix=RV32I %s
+
+declare i32 @llvm.riscv.csr.read.write.i32(i32, i32)
+declare i32 @llvm.riscv.csr.read.set.i32(i32, i32)
+declare i32 @llvm.riscv.csr.read.clear.i32(i32, i32)
+
+define i32 @test_csr_read_ustatus(i32 %a) nounwind {
+; RV32I-LABEL: test_csr_read_ustatus:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    csrrw a0, ustatus, a0
+; RV32I-NEXT:    ret
+  %1 = call i32 @llvm.riscv.csr.read.write.i32(i32 0, i32 %a)
+  ret i32 %1
+}
+
+define i32 @test_csr_set_ustatus(i32 %a) nounwind {
+; RV32I-LABEL: test_csr_set_ustatus:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    csrrs a0, ustatus, a0
+; RV32I-NEXT:    ret
+  %1 = call i32 @llvm.riscv.csr.read.set.i32(i32 0, i32 %a)
+  ret i32 %1
+}
+
+define i32 @test_csr_clear_ustatus(i32 %a) nounwind {
+; RV32I-LABEL: test_csr_clear_ustatus:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    csrrc a0, ustatus, a0
+; RV32I-NEXT:    ret
+  %1 = call i32 @llvm.riscv.csr.read.clear.i32(i32 0, i32 %a)
+  ret i32 %1
+}
Index: llvm/lib/Target/RISCV/RISCVInstructionSelector.cpp
===================================================================
--- llvm/lib/Target/RISCV/RISCVInstructionSelector.cpp
+++ llvm/lib/Target/RISCV/RISCVInstructionSelector.cpp
@@ -16,6 +16,7 @@
 #include "RISCVTargetMachine.h"
 #include "llvm/CodeGen/GlobalISel/InstructionSelector.h"
 #include "llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h"
+#include "llvm/IR/IntrinsicsRISCV.h"
 #include "llvm/Support/Debug.h"
 
 #define DEBUG_TYPE "riscv-isel"
Index: llvm/lib/Target/RISCV/RISCVInstrInfo.td
===================================================================
--- llvm/lib/Target/RISCV/RISCVInstrInfo.td
+++ llvm/lib/Target/RISCV/RISCVInstrInfo.td
@@ -1087,6 +1087,26 @@
 defm : StPat<store, SD, GPR>;
 } // Predicates = [IsRV64]
 
+// RV32 CSR Access Intrinsics
+let Predicates = [IsRV32] in {
+  def : Pat<(int_riscv_csr_read_write_i32 (i32 timm:$csr), (i32 GPR:$source)),
+            (CSRRW csr_sysreg:$csr, GPR:$source)>;
+  def : Pat<(int_riscv_csr_read_set_i32 (i32 timm:$csr), (i32 GPR:$source)),
+            (CSRRS csr_sysreg:$csr, GPR:$source)>;
+  def : Pat<(int_riscv_csr_read_clear_i32 (i32 timm:$csr), (i32 GPR:$source)),
+            (CSRRC csr_sysreg:$csr, GPR:$source)>;
+}
+
+// RV64 CSR Access Intrinsics
+let Predicates = [IsRV64] in {
+  def : Pat<(int_riscv_csr_read_write_i64 (i64 timm:$csr), (i64 GPR:$source)),
+            (CSRRW csr_sysreg:$csr, GPR:$source)>;
+  def : Pat<(int_riscv_csr_read_set_i64 (i64 timm:$csr), (i64 GPR:$source)),
+            (CSRRS csr_sysreg:$csr, GPR:$source)>;
+  def : Pat<(int_riscv_csr_read_clear_i64 (i64 timm:$csr), (i64 GPR:$source)),
+            (CSRRC csr_sysreg:$csr, GPR:$source)>;
+}
+
 /// readcyclecounter
 // On RV64, we can directly read the 64-bit "cycle" CSR.
 let Predicates = [IsRV64] in
Index: llvm/include/llvm/IR/IntrinsicsRISCV.td
===================================================================
--- llvm/include/llvm/IR/IntrinsicsRISCV.td
+++ llvm/include/llvm/IR/IntrinsicsRISCV.td
@@ -60,4 +60,26 @@
   // @llvm.riscv.masked.cmpxchg.{i32,i64}
   defm int_riscv_masked_cmpxchg : MaskedAtomicRMWFiveArgIntrinsics;
 
+  //===--------------------------------------------------------------------===//
+  // High-level CSR Accesses
+
+  // The first `imm` here is the CSR Id, the second argument is the new value
+  // for writing/setting/clearing the CSR.
+  //
+  // T @llvm.<name>.T(T imm, T);
+  class CSRAccess<LLVMType itype>
+      : Intrinsic<[itype], [itype, itype],
+                  [IntrReadMem, IntrWriteMem, IntrHasSideEffects, ImmArg<0>]>;
+
+  multiclass CSRAccess<string builtin_name> {
+    // i32 @llvm.<name>.i32(i32 imm, i32);
+    def _i32 : CSRAccess<llvm_i32_ty>;
+    // i64 @llvm.<name>.i64(i64 imm, i64);
+    def _i64 : CSRAccess<llvm_i64_ty>;
+  }
+
+  defm int_riscv_csr_read_write : CSRAccess<"write">;
+  defm int_riscv_csr_read_set : CSRAccess<"set">;
+  defm int_riscv_csr_read_clear : CSRAccess<"clear">;
+
 } // TargetPrefix = "riscv"
Index: clang/test/CodeGen/riscv-csr-builtins.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/riscv-csr-builtins.c
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -ffreestanding %s -triple=riscv32 -emit-llvm -o - -Wall -Werror | FileCheck %s -check-prefixes=RV,RV32
+// RUN: %clang_cc1 -ffreestanding %s -triple=riscv64 -emit-llvm -o - -Wall -Werror | FileCheck %s -check-prefixes=RV,RV64
+
+long test_read_write(long i) {
+  // RV-LABEL: test_read_write
+  // RV32: @llvm.riscv.csr.read.write.i32
+  // RV64: @llvm.riscv.csr.read.write.i64
+  return __builtin_riscv_csr_read_write(0x0, i);
+}
+
+long test_read_set(long i) {
+  // RV-LABEL: test_read_set
+  // RV32: @llvm.riscv.csr.read.set.i32
+  // RV64: @llvm.riscv.csr.read.set.i64
+  return __builtin_riscv_csr_read_set(0x0, i);
+}
+
+long test_read_clear(long i) {
+  // RV-LABEL: test_read_clear
+  // RV32: @llvm.riscv.csr.read.clear.i32
+  // RV64: @llvm.riscv.csr.read.clear.i64
+  return __builtin_riscv_csr_read_clear(0x0, i);
+}
\ No newline at end of file
Index: clang/lib/CodeGen/CodeGenFunction.h
===================================================================
--- clang/lib/CodeGen/CodeGenFunction.h
+++ clang/lib/CodeGen/CodeGenFunction.h
@@ -3783,6 +3783,8 @@
   llvm::Value *BuildVector(ArrayRef<llvm::Value*> Ops);
   llvm::Value *EmitX86BuiltinExpr(unsigned BuiltinID, const CallExpr *E);
   llvm::Value *EmitPPCBuiltinExpr(unsigned BuiltinID, const CallExpr *E);
+  llvm::Value *EmitRISCVBuiltinExpr(unsigned BuiltinID, const CallExpr *E,
+                                    llvm::Triple::ArchType Arch);
   llvm::Value *EmitAMDGPUBuiltinExpr(unsigned BuiltinID, const CallExpr *E);
   llvm::Value *EmitSystemZBuiltinExpr(unsigned BuiltinID, const CallExpr *E);
   llvm::Value *EmitNVPTXBuiltinExpr(unsigned BuiltinID, const CallExpr *E);
Index: clang/lib/CodeGen/CGBuiltin.cpp
===================================================================
--- clang/lib/CodeGen/CGBuiltin.cpp
+++ clang/lib/CodeGen/CGBuiltin.cpp
@@ -39,6 +39,7 @@
 #include "llvm/IR/IntrinsicsNVPTX.h"
 #include "llvm/IR/IntrinsicsPowerPC.h"
 #include "llvm/IR/IntrinsicsR600.h"
+#include "llvm/IR/IntrinsicsRISCV.h"
 #include "llvm/IR/IntrinsicsS390.h"
 #include "llvm/IR/IntrinsicsWebAssembly.h"
 #include "llvm/IR/IntrinsicsX86.h"
@@ -4377,6 +4378,9 @@
   case llvm::Triple::ppc64:
   case llvm::Triple::ppc64le:
     return CGF->EmitPPCBuiltinExpr(BuiltinID, E);
+  case llvm::Triple::riscv32:
+  case llvm::Triple::riscv64:
+    return CGF->EmitRISCVBuiltinExpr(BuiltinID, E, Arch);
   case llvm::Triple::r600:
   case llvm::Triple::amdgcn:
     return CGF->EmitAMDGPUBuiltinExpr(BuiltinID, E);
@@ -13006,6 +13010,49 @@
   }
 }
 
+Value *CodeGenFunction::EmitRISCVBuiltinExpr(unsigned BuiltinID,
+                                             const CallExpr *E,
+                                             llvm::Triple::ArchType Arch) {
+  bool IsRV64 = Arch == llvm::Triple::riscv64;
+  switch (BuiltinID) {
+  case RISCV::BI__builtin_riscv_csr_read_write:
+  case RISCV::BI__builtin_riscv_csr_read_set:
+  case RISCV::BI__builtin_riscv_csr_read_clear: {
+    llvm::APSInt CSRAddrConst;
+    if (!E->getArg(0)->isIntegerConstantExpr(CSRAddrConst, getContext())) {
+      CGM.Error(E->getExprLoc(), "CSR Address isn't a constant.");
+      return nullptr;
+    }
+    Value *CSRAddrVal = llvm::ConstantInt::get(getLLVMContext(), CSRAddrConst);
+    Value *CSRWriteVal = EmitScalarExpr(E->getArg(1));
+
+    unsigned IntrinsicID32, IntrinsicID64;
+    switch (BuiltinID) {
+    case RISCV::BI__builtin_riscv_csr_read_write:
+      IntrinsicID32 = Intrinsic::riscv_csr_read_write_i32;
+      IntrinsicID64 = Intrinsic::riscv_csr_read_write_i64;
+      break;
+    case RISCV::BI__builtin_riscv_csr_read_set:
+      IntrinsicID32 = Intrinsic::riscv_csr_read_set_i32;
+      IntrinsicID64 = Intrinsic::riscv_csr_read_set_i64;
+      break;
+    case RISCV::BI__builtin_riscv_csr_read_clear:
+      IntrinsicID32 = Intrinsic::riscv_csr_read_clear_i32;
+      IntrinsicID64 = Intrinsic::riscv_csr_read_clear_i64;
+      break;
+    default:
+      llvm_unreachable("Unknown RISC-V CSR Intrinsic");
+    }
+
+    llvm::Function *Callee =
+        CGM.getIntrinsic(IsRV64 ? IntrinsicID64 : IntrinsicID32);
+    return Builder.CreateCall(Callee, {CSRAddrVal, CSRWriteVal});
+  }
+  default:
+    return nullptr;
+  }
+}
+
 Value *CodeGenFunction::EmitAMDGPUBuiltinExpr(unsigned BuiltinID,
                                               const CallExpr *E) {
   switch (BuiltinID) {
Index: clang/lib/Basic/Targets/RISCV.h
===================================================================
--- clang/lib/Basic/Targets/RISCV.h
+++ clang/lib/Basic/Targets/RISCV.h
@@ -30,6 +30,7 @@
   bool HasF;
   bool HasD;
   bool HasC;
+  static const Builtin::Info BuiltinInfo[];
 
 public:
   RISCVTargetInfo(const llvm::Triple &Triple, const TargetOptions &)
@@ -47,7 +48,7 @@
   void getTargetDefines(const LangOptions &Opts,
                         MacroBuilder &Builder) const override;
 
-  ArrayRef<Builtin::Info> getTargetBuiltins() const override { return None; }
+  ArrayRef<Builtin::Info> getTargetBuiltins() const override;
 
   BuiltinVaListKind getBuiltinVaListKind() const override {
     return TargetInfo::VoidPtrBuiltinVaList;
Index: clang/lib/Basic/Targets/RISCV.cpp
===================================================================
--- clang/lib/Basic/Targets/RISCV.cpp
+++ clang/lib/Basic/Targets/RISCV.cpp
@@ -11,7 +11,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "RISCV.h"
+#include "clang/Basic/Builtins.h"
 #include "clang/Basic/MacroBuilder.h"
+#include "clang/Basic/TargetBuiltins.h"
 #include "llvm/ADT/StringSwitch.h"
 
 using namespace clang;
@@ -160,3 +162,16 @@
 
   return true;
 }
+
+ArrayRef<Builtin::Info> RISCVTargetInfo::getTargetBuiltins() const {
+  return llvm::makeArrayRef(BuiltinInfo, clang::RISCV::LastTSBuiltin -
+                                             Builtin::FirstTSBuiltin);
+}
+
+const Builtin::Info RISCVTargetInfo::BuiltinInfo[] = {
+#define BUILTIN(ID, TYPE, ATTRS)                                               \
+  {#ID, TYPE, ATTRS, nullptr, ALL_LANGUAGES, nullptr},
+#define TARGET_BUILTIN(ID, TYPE, ATTRS, FEATURE)                               \
+  {#ID, TYPE, ATTRS, nullptr, ALL_LANGUAGES, FEATURE},
+#include "clang/Basic/BuiltinsRISCV.def"
+};
Index: clang/include/clang/Basic/TargetBuiltins.h
===================================================================
--- clang/include/clang/Basic/TargetBuiltins.h
+++ clang/include/clang/Basic/TargetBuiltins.h
@@ -189,6 +189,15 @@
   };
   }
 
+  namespace RISCV {
+  enum {
+    LastTIBuiltin = clang::Builtin::FirstTSBuiltin - 1,
+#define BUILTIN(ID, TYPE, ATTRS) BI##ID,
+#include "clang/Basic/BuiltinsRISCV.def"
+    LastTSBuiltin
+  };
+  } // namespace RISCV
+
   /// SystemZ builtins
   namespace SystemZ {
     enum {
Index: clang/include/clang/Basic/BuiltinsRISCV.def
===================================================================
--- /dev/null
+++ clang/include/clang/Basic/BuiltinsRISCV.def
@@ -0,0 +1,21 @@
+//===-- BuiltinsRISCV.def - RISC-V Builtin function database -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the RISC-V-specific builtin function database.  Users of
+// this file must define the BUILTIN macro to make use of this information.
+//
+//===----------------------------------------------------------------------===//
+
+// The format of this database matches clang/Basic/Builtins.def.
+
+// CSR Accesses are always XLEN-bits wide, which matches the width of `long int`
+BUILTIN(__builtin_riscv_csr_read_write, "LiLiLi", "")
+BUILTIN(__builtin_riscv_csr_read_set, "LiLiLi", "")
+BUILTIN(__builtin_riscv_csr_read_clear, "LiLiLi", "")
+
+#undef BUILTIN
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to