diff --git a/clang/include/clang/Basic/BuiltinsRISCV.def b/clang/include/clang/Basic/BuiltinsRISCV.def new file mode 100644 --- /dev/null +++ b/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 diff --git a/clang/include/clang/Basic/TargetBuiltins.h b/clang/include/clang/Basic/TargetBuiltins.h --- a/clang/include/clang/Basic/TargetBuiltins.h +++ b/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 { diff --git a/clang/lib/Basic/Targets/RISCV.h b/clang/lib/Basic/Targets/RISCV.h --- a/clang/lib/Basic/Targets/RISCV.h +++ b/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 getTargetBuiltins() const override { return None; } + ArrayRef getTargetBuiltins() const override; BuiltinVaListKind getBuiltinVaListKind() const override { return TargetInfo::VoidPtrBuiltinVaList; diff --git a/clang/lib/Basic/Targets/RISCV.cpp b/clang/lib/Basic/Targets/RISCV.cpp --- a/clang/lib/Basic/Targets/RISCV.cpp +++ b/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 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" +}; diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/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) { diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3783,6 +3783,8 @@ llvm::Value *BuildVector(ArrayRef 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); diff --git a/clang/test/CodeGen/riscv-csr-builtins.c b/clang/test/CodeGen/riscv-csr-builtins.c new file mode 100644 --- /dev/null +++ b/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 diff --git a/llvm/include/llvm/IR/IntrinsicsRISCV.td b/llvm/include/llvm/IR/IntrinsicsRISCV.td --- a/llvm/include/llvm/IR/IntrinsicsRISCV.td +++ b/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..T(T imm, T); + class CSRAccess + : Intrinsic<[itype], [itype, itype], + [IntrReadMem, IntrWriteMem, IntrHasSideEffects, ImmArg<0>]>; + + multiclass CSRAccess { + // i32 @llvm..i32(i32 imm, i32); + def _i32 : CSRAccess; + // i64 @llvm..i64(i64 imm, i64); + def _i64 : CSRAccess; + } + + 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" diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td @@ -1087,6 +1087,26 @@ defm : StPat; } // 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 diff --git a/llvm/lib/Target/RISCV/RISCVInstructionSelector.cpp b/llvm/lib/Target/RISCV/RISCVInstructionSelector.cpp --- a/llvm/lib/Target/RISCV/RISCVInstructionSelector.cpp +++ b/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" diff --git a/llvm/test/CodeGen/RISCV/intrinsics/csr_accesses_rv32.ll b/llvm/test/CodeGen/RISCV/intrinsics/csr_accesses_rv32.ll new file mode 100644 --- /dev/null +++ b/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 +} diff --git a/llvm/test/CodeGen/RISCV/intrinsics/csr_accesses_rv64.ll b/llvm/test/CodeGen/RISCV/intrinsics/csr_accesses_rv64.ll new file mode 100644 --- /dev/null +++ b/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 +}