Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RISCV] Add riscv_atomic.h and Zawrs/Zalrsc builtins #94578

Conversation

wangpc-pp
Copy link
Contributor

@wangpc-pp wangpc-pp commented Jun 6, 2024

riscv_atomic.h contains all builtins for atomics.

Doc: riscv-non-isa/riscv-c-api-doc#79

Created using spr 1.3.6-beta.1
@llvmbot llvmbot added clang Clang issues not falling into any other category backend:RISC-V backend:X86 clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:headers Headers provided by Clang, e.g. for intrinsics clang:codegen llvm:ir labels Jun 6, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Jun 6, 2024

@llvm/pr-subscribers-clang
@llvm/pr-subscribers-backend-x86

@llvm/pr-subscribers-backend-risc-v

Author: Pengcheng Wang (wangpc-pp)

Changes

riscv_atomics.h contains all builtins for atomics.


Patch is 26.60 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/94578.diff

14 Files Affected:

  • (modified) clang/include/clang/Basic/BuiltinsRISCV.td (+18)
  • (modified) clang/lib/CodeGen/CGBuiltin.cpp (+22)
  • (modified) clang/lib/Headers/CMakeLists.txt (+1)
  • (added) clang/lib/Headers/riscv_atomics.h (+36)
  • (modified) clang/lib/Sema/SemaRISCV.cpp (+9-1)
  • (added) clang/test/CodeGen/RISCV/atomics-intrinsics/zalrsc-error.c (+13)
  • (added) clang/test/CodeGen/RISCV/atomics-intrinsics/zalrsc.c (+222)
  • (added) clang/test/CodeGen/RISCV/atomics-intrinsics/zawrs.c (+42)
  • (modified) llvm/include/llvm/IR/IntrinsicsRISCV.td (+32)
  • (modified) llvm/lib/Target/RISCV/RISCVInstrInfoA.td (+25)
  • (modified) llvm/lib/Target/RISCV/RISCVInstrInfoZa.td (+4-1)
  • (added) llvm/test/CodeGen/RISCV/zalrsc-rv32.ll (+74)
  • (added) llvm/test/CodeGen/RISCV/zalrsc-rv64.ll (+74)
  • (added) llvm/test/CodeGen/RISCV/zawrs.ll (+33)
diff --git a/clang/include/clang/Basic/BuiltinsRISCV.td b/clang/include/clang/Basic/BuiltinsRISCV.td
index 4cc89a8a9d8af..458c755179417 100644
--- a/clang/include/clang/Basic/BuiltinsRISCV.td
+++ b/clang/include/clang/Basic/BuiltinsRISCV.td
@@ -146,3 +146,21 @@ let Features = "zihintntl", Attributes = [CustomTypeChecking] in {
 def ntl_load : RISCVBuiltin<"void(...)">;
 def ntl_store : RISCVBuiltin<"void(...)">;
 } // Features = "zihintntl", Attributes = [CustomTypeChecking]
+
+//===----------------------------------------------------------------------===//
+// Zawrs extension.
+//===----------------------------------------------------------------------===//
+let Features = "zawrs" in {
+def wrs_nto : RISCVBuiltin<"void()">;
+def wrs_sto : RISCVBuiltin<"void()">;
+} // Features = "zawrs"
+
+//===----------------------------------------------------------------------===//
+// Zalrsc extension.
+//===----------------------------------------------------------------------===//
+let Features = "zalrsc" in {
+def lr_w : RISCVBuiltin<"int(int *, _Constant unsigned int)">;
+def lr_d : RISCVBuiltin<"int64_t(int64_t *, _Constant unsigned int)">;
+def sc_w : RISCVBuiltin<"int(int, int *, _Constant unsigned int)">;
+def sc_d : RISCVBuiltin<"int64_t(int64_t, int64_t *, _Constant unsigned int)">;
+} // Features = "zalrsc"
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 37d0c478e0330..db48c69e10c86 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -21769,6 +21769,28 @@ Value *CodeGenFunction::EmitRISCVBuiltinExpr(unsigned BuiltinID,
     ID = Intrinsic::riscv_sm3p1;
     break;
 
+  // Zawrs
+  case RISCV::BI__builtin_riscv_wrs_nto:
+    ID = Intrinsic::riscv_wrs_nto;
+    break;
+  case RISCV::BI__builtin_riscv_wrs_sto:
+    ID = Intrinsic::riscv_wrs_sto;
+    break;
+
+  // Zalrsc
+  case RISCV::BI__builtin_riscv_lr_w:
+    ID = Intrinsic::riscv_lr_w;
+    break;
+  case RISCV::BI__builtin_riscv_lr_d:
+    ID = Intrinsic::riscv_lr_d;
+    break;
+  case RISCV::BI__builtin_riscv_sc_w:
+    ID = Intrinsic::riscv_sc_w;
+    break;
+  case RISCV::BI__builtin_riscv_sc_d:
+    ID = Intrinsic::riscv_sc_d;
+    break;
+
   // Zihintntl
   case RISCV::BI__builtin_riscv_ntl_load: {
     llvm::Type *ResTy = ConvertType(E->getType());
diff --git a/clang/lib/Headers/CMakeLists.txt b/clang/lib/Headers/CMakeLists.txt
index d3090e488306f..cf2fbf1893772 100644
--- a/clang/lib/Headers/CMakeLists.txt
+++ b/clang/lib/Headers/CMakeLists.txt
@@ -118,6 +118,7 @@ set(ppc_htm_files
   )
 
 set(riscv_files
+  riscv_atomics.h
   riscv_bitmanip.h
   riscv_crypto.h
   riscv_ntlh.h
diff --git a/clang/lib/Headers/riscv_atomics.h b/clang/lib/Headers/riscv_atomics.h
new file mode 100644
index 0000000000000..35db57fe36131
--- /dev/null
+++ b/clang/lib/Headers/riscv_atomics.h
@@ -0,0 +1,36 @@
+/*===---- riscv_atomics.h - RISC-V atomics intrinsics ----------------------===
+ *
+ * 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
+ *
+ *===-----------------------------------------------------------------------===
+ */
+
+#ifndef __RISCV_ATOMICS_H
+#define __RISCV_ATOMICS_H
+
+#ifdef __riscv_zalrsc
+enum {
+  __RISCV_ORDER_NONE = 0,
+  __RISCV_ORDER_AQ = 1,
+  __RISCV_ORDER_RL = 2,
+  __RISCV_ORDER_AQ_RL = 3
+};
+
+#define __riscv_lr_w __builtin_riscv_lr_w
+#define __riscv_sc_w __builtin_riscv_sc_w
+
+#if __riscv_xlen == 64
+#define __riscv_lr_d __builtin_riscv_lr_d
+#define __riscv_sc_d __builtin_riscv_sc_d
+#endif
+
+#endif
+
+#ifdef __riscv_zawrs
+#define __riscv_wrs_nto __builtin_riscv_wrs_nto
+#define __riscv_wrs_sto __builtin_riscv_wrs_sto
+#endif
+
+#endif
diff --git a/clang/lib/Sema/SemaRISCV.cpp b/clang/lib/Sema/SemaRISCV.cpp
index fd4fc15c1fd79..0b69b72cea8bb 100644
--- a/clang/lib/Sema/SemaRISCV.cpp
+++ b/clang/lib/Sema/SemaRISCV.cpp
@@ -1303,7 +1303,7 @@ bool SemaRISCV::CheckBuiltinFunctionCall(const TargetInfo &TI,
   case RISCVVector::BI__builtin_rvv_vfwnmsac_vf_rm_mu:
     return SemaRef.BuiltinConstantArgRange(TheCall, 4, 0, 4);
   case RISCV::BI__builtin_riscv_ntl_load:
-  case RISCV::BI__builtin_riscv_ntl_store:
+  case RISCV::BI__builtin_riscv_ntl_store: {
     DeclRefExpr *DRE =
         cast<DeclRefExpr>(TheCall->getCallee()->IgnoreParenCasts());
     assert((BuiltinID == RISCV::BI__builtin_riscv_ntl_store ||
@@ -1368,6 +1368,14 @@ bool SemaRISCV::CheckBuiltinFunctionCall(const TargetInfo &TI,
     return false;
   }
 
+  case RISCV::BI__builtin_riscv_lr_w:
+  case RISCV::BI__builtin_riscv_lr_d:
+    return SemaRef.BuiltinConstantArgRange(TheCall, 1, 0, 3);
+  case RISCV::BI__builtin_riscv_sc_w:
+  case RISCV::BI__builtin_riscv_sc_d:
+    return SemaRef.BuiltinConstantArgRange(TheCall, 2, 0, 3);
+  }
+
   return false;
 }
 
diff --git a/clang/test/CodeGen/RISCV/atomics-intrinsics/zalrsc-error.c b/clang/test/CodeGen/RISCV/atomics-intrinsics/zalrsc-error.c
new file mode 100644
index 0000000000000..d274077c9acfd
--- /dev/null
+++ b/clang/test/CodeGen/RISCV/atomics-intrinsics/zalrsc-error.c
@@ -0,0 +1,13 @@
+// REQUIRES: riscv-registered-target
+// RUN: %clang_cc1 -triple riscv64 -target-feature +zalrsc -S -verify %s -o -
+// RUN: %clang_cc1 -triple riscv64 -target-feature +zalrsc -S -verify %s -o -
+
+#include <riscv_atomics.h>
+
+int zalrsc_lr_w(int* ptr) {
+  return __riscv_lr_w(ptr, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
+}
+
+int zalrsc_sc_w(int v, int* ptr) {
+  return __riscv_sc_w(v, ptr, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
+}
diff --git a/clang/test/CodeGen/RISCV/atomics-intrinsics/zalrsc.c b/clang/test/CodeGen/RISCV/atomics-intrinsics/zalrsc.c
new file mode 100644
index 0000000000000..662ac53ee1e57
--- /dev/null
+++ b/clang/test/CodeGen/RISCV/atomics-intrinsics/zalrsc.c
@@ -0,0 +1,222 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 2
+// REQUIRES: riscv-registered-target
+// RUN: %clang_cc1 -triple riscv32 -target-feature +zalrsc -disable-O0-optnone \
+// RUN:   -emit-llvm %s -o - | opt -S -passes=mem2reg | \
+// RUN:   FileCheck --check-prefixes=CHECK-RV32 %s
+// RUN: %clang_cc1 -triple riscv64 -target-feature +zalrsc -disable-O0-optnone \
+// RUN:   -emit-llvm %s -o - | opt -S -passes=mem2reg | \
+// RUN:   FileCheck --check-prefix=CHECK-RV64 %s
+
+#include <stdint.h>
+#include <riscv_atomics.h>
+
+// CHECK-RV32-LABEL: define dso_local i32 @zalrsc_lr_w_none
+// CHECK-RV32-SAME: (ptr noundef [[PTR:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-RV32-NEXT:  entry:
+// CHECK-RV32-NEXT:    [[TMP0:%.*]] = call i32 @llvm.riscv.lr.w(ptr [[PTR]], i32 0)
+// CHECK-RV32-NEXT:    ret i32 [[TMP0]]
+//
+// CHECK-RV64-LABEL: define dso_local signext i32 @zalrsc_lr_w_none
+// CHECK-RV64-SAME: (ptr noundef [[PTR:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-RV64-NEXT:  entry:
+// CHECK-RV64-NEXT:    [[TMP0:%.*]] = call i32 @llvm.riscv.lr.w(ptr [[PTR]], i32 0)
+// CHECK-RV64-NEXT:    ret i32 [[TMP0]]
+//
+int zalrsc_lr_w_none(int* ptr) {
+  return __riscv_lr_w(ptr, __RISCV_ORDER_NONE);
+}
+
+// CHECK-RV32-LABEL: define dso_local i32 @zalrsc_lr_w_aq
+// CHECK-RV32-SAME: (ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-RV32-NEXT:  entry:
+// CHECK-RV32-NEXT:    [[TMP0:%.*]] = call i32 @llvm.riscv.lr.w(ptr [[PTR]], i32 1)
+// CHECK-RV32-NEXT:    ret i32 [[TMP0]]
+//
+// CHECK-RV64-LABEL: define dso_local signext i32 @zalrsc_lr_w_aq
+// CHECK-RV64-SAME: (ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-RV64-NEXT:  entry:
+// CHECK-RV64-NEXT:    [[TMP0:%.*]] = call i32 @llvm.riscv.lr.w(ptr [[PTR]], i32 1)
+// CHECK-RV64-NEXT:    ret i32 [[TMP0]]
+//
+int zalrsc_lr_w_aq(int* ptr) {
+  return __riscv_lr_w(ptr, __RISCV_ORDER_AQ);
+}
+
+// CHECK-RV32-LABEL: define dso_local i32 @zalrsc_lr_w_rl
+// CHECK-RV32-SAME: (ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-RV32-NEXT:  entry:
+// CHECK-RV32-NEXT:    [[TMP0:%.*]] = call i32 @llvm.riscv.lr.w(ptr [[PTR]], i32 2)
+// CHECK-RV32-NEXT:    ret i32 [[TMP0]]
+//
+// CHECK-RV64-LABEL: define dso_local signext i32 @zalrsc_lr_w_rl
+// CHECK-RV64-SAME: (ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-RV64-NEXT:  entry:
+// CHECK-RV64-NEXT:    [[TMP0:%.*]] = call i32 @llvm.riscv.lr.w(ptr [[PTR]], i32 2)
+// CHECK-RV64-NEXT:    ret i32 [[TMP0]]
+//
+int zalrsc_lr_w_rl(int* ptr) {
+  return __riscv_lr_w(ptr, __RISCV_ORDER_RL);
+}
+
+// CHECK-RV32-LABEL: define dso_local i32 @zalrsc_lr_w_aqrl
+// CHECK-RV32-SAME: (ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-RV32-NEXT:  entry:
+// CHECK-RV32-NEXT:    [[TMP0:%.*]] = call i32 @llvm.riscv.lr.w(ptr [[PTR]], i32 3)
+// CHECK-RV32-NEXT:    ret i32 [[TMP0]]
+//
+// CHECK-RV64-LABEL: define dso_local signext i32 @zalrsc_lr_w_aqrl
+// CHECK-RV64-SAME: (ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-RV64-NEXT:  entry:
+// CHECK-RV64-NEXT:    [[TMP0:%.*]] = call i32 @llvm.riscv.lr.w(ptr [[PTR]], i32 3)
+// CHECK-RV64-NEXT:    ret i32 [[TMP0]]
+//
+int zalrsc_lr_w_aqrl(int* ptr) {
+  return __riscv_lr_w(ptr, __RISCV_ORDER_AQ_RL);
+}
+
+// CHECK-RV32-LABEL: define dso_local i32 @zalrsc_sc_w_none
+// CHECK-RV32-SAME: (i32 noundef [[V:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-RV32-NEXT:  entry:
+// CHECK-RV32-NEXT:    [[TMP0:%.*]] = call i32 @llvm.riscv.sc.w(i32 [[V]], ptr [[PTR]], i32 0)
+// CHECK-RV32-NEXT:    ret i32 [[TMP0]]
+//
+// CHECK-RV64-LABEL: define dso_local signext i32 @zalrsc_sc_w_none
+// CHECK-RV64-SAME: (i32 noundef signext [[V:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-RV64-NEXT:  entry:
+// CHECK-RV64-NEXT:    [[TMP0:%.*]] = call i32 @llvm.riscv.sc.w(i32 [[V]], ptr [[PTR]], i32 0)
+// CHECK-RV64-NEXT:    ret i32 [[TMP0]]
+//
+int zalrsc_sc_w_none(int v, int* ptr) {
+  return __riscv_sc_w(v, ptr, __RISCV_ORDER_NONE);
+}
+
+// CHECK-RV32-LABEL: define dso_local i32 @zalrsc_sc_w_aq
+// CHECK-RV32-SAME: (i32 noundef [[V:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-RV32-NEXT:  entry:
+// CHECK-RV32-NEXT:    [[TMP0:%.*]] = call i32 @llvm.riscv.sc.w(i32 [[V]], ptr [[PTR]], i32 1)
+// CHECK-RV32-NEXT:    ret i32 [[TMP0]]
+//
+// CHECK-RV64-LABEL: define dso_local signext i32 @zalrsc_sc_w_aq
+// CHECK-RV64-SAME: (i32 noundef signext [[V:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-RV64-NEXT:  entry:
+// CHECK-RV64-NEXT:    [[TMP0:%.*]] = call i32 @llvm.riscv.sc.w(i32 [[V]], ptr [[PTR]], i32 1)
+// CHECK-RV64-NEXT:    ret i32 [[TMP0]]
+//
+int zalrsc_sc_w_aq(int v, int* ptr) {
+  return __riscv_sc_w(v, ptr, __RISCV_ORDER_AQ);
+}
+
+// CHECK-RV32-LABEL: define dso_local i32 @zalrsc_sc_w_rl
+// CHECK-RV32-SAME: (i32 noundef [[V:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-RV32-NEXT:  entry:
+// CHECK-RV32-NEXT:    [[TMP0:%.*]] = call i32 @llvm.riscv.sc.w(i32 [[V]], ptr [[PTR]], i32 2)
+// CHECK-RV32-NEXT:    ret i32 [[TMP0]]
+//
+// CHECK-RV64-LABEL: define dso_local signext i32 @zalrsc_sc_w_rl
+// CHECK-RV64-SAME: (i32 noundef signext [[V:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-RV64-NEXT:  entry:
+// CHECK-RV64-NEXT:    [[TMP0:%.*]] = call i32 @llvm.riscv.sc.w(i32 [[V]], ptr [[PTR]], i32 2)
+// CHECK-RV64-NEXT:    ret i32 [[TMP0]]
+//
+int zalrsc_sc_w_rl(int v, int* ptr) {
+  return __riscv_sc_w(v, ptr, __RISCV_ORDER_RL);
+}
+
+// CHECK-RV32-LABEL: define dso_local i32 @zalrsc_sc_w_aqrl
+// CHECK-RV32-SAME: (i32 noundef [[V:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-RV32-NEXT:  entry:
+// CHECK-RV32-NEXT:    [[TMP0:%.*]] = call i32 @llvm.riscv.sc.w(i32 [[V]], ptr [[PTR]], i32 3)
+// CHECK-RV32-NEXT:    ret i32 [[TMP0]]
+//
+// CHECK-RV64-LABEL: define dso_local signext i32 @zalrsc_sc_w_aqrl
+// CHECK-RV64-SAME: (i32 noundef signext [[V:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-RV64-NEXT:  entry:
+// CHECK-RV64-NEXT:    [[TMP0:%.*]] = call i32 @llvm.riscv.sc.w(i32 [[V]], ptr [[PTR]], i32 3)
+// CHECK-RV64-NEXT:    ret i32 [[TMP0]]
+//
+int zalrsc_sc_w_aqrl(int v, int* ptr) {
+  return __riscv_sc_w(v, ptr, __RISCV_ORDER_AQ_RL);
+}
+
+#if __riscv_xlen == 64
+// CHECK-RV64-LABEL: define dso_local i64 @zalrsc_lr_d_none
+// CHECK-RV64-SAME: (ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-RV64-NEXT:  entry:
+// CHECK-RV64-NEXT:    [[TMP0:%.*]] = call i64 @llvm.riscv.lr.d(ptr [[PTR]], i32 0)
+// CHECK-RV64-NEXT:    ret i64 [[TMP0]]
+//
+int64_t zalrsc_lr_d_none(int64_t* ptr) {
+  return __riscv_lr_d(ptr, __RISCV_ORDER_NONE);
+}
+
+// CHECK-RV64-LABEL: define dso_local i64 @zalrsc_lr_d_aq
+// CHECK-RV64-SAME: (ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-RV64-NEXT:  entry:
+// CHECK-RV64-NEXT:    [[TMP0:%.*]] = call i64 @llvm.riscv.lr.d(ptr [[PTR]], i32 1)
+// CHECK-RV64-NEXT:    ret i64 [[TMP0]]
+//
+int64_t zalrsc_lr_d_aq(int64_t* ptr) {
+  return __riscv_lr_d(ptr, __RISCV_ORDER_AQ);
+}
+
+// CHECK-RV64-LABEL: define dso_local i64 @zalrsc_lr_d_rl
+// CHECK-RV64-SAME: (ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-RV64-NEXT:  entry:
+// CHECK-RV64-NEXT:    [[TMP0:%.*]] = call i64 @llvm.riscv.lr.d(ptr [[PTR]], i32 2)
+// CHECK-RV64-NEXT:    ret i64 [[TMP0]]
+//
+int64_t zalrsc_lr_d_rl(int64_t* ptr) {
+  return __riscv_lr_d(ptr, __RISCV_ORDER_RL);
+}
+
+// CHECK-RV64-LABEL: define dso_local i64 @zalrsc_lr_d_aqrl
+// CHECK-RV64-SAME: (ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-RV64-NEXT:  entry:
+// CHECK-RV64-NEXT:    [[TMP0:%.*]] = call i64 @llvm.riscv.lr.d(ptr [[PTR]], i32 3)
+// CHECK-RV64-NEXT:    ret i64 [[TMP0]]
+//
+int64_t zalrsc_lr_d_aqrl(int64_t* ptr) {
+  return __riscv_lr_d(ptr, __RISCV_ORDER_AQ_RL);
+}
+
+// CHECK-RV64-LABEL: define dso_local i64 @zalrsc_sc_d_none
+// CHECK-RV64-SAME: (i64 noundef [[V:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-RV64-NEXT:  entry:
+// CHECK-RV64-NEXT:    [[TMP0:%.*]] = call i64 @llvm.riscv.sc.d(i64 [[V]], ptr [[PTR]], i32 0)
+// CHECK-RV64-NEXT:    ret i64 [[TMP0]]
+//
+int64_t zalrsc_sc_d_none(int64_t v, int64_t* ptr) {
+  return __riscv_sc_d(v, ptr, __RISCV_ORDER_NONE);
+}
+
+// CHECK-RV64-LABEL: define dso_local i64 @zalrsc_sc_d_aq
+// CHECK-RV64-SAME: (i64 noundef [[V:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-RV64-NEXT:  entry:
+// CHECK-RV64-NEXT:    [[TMP0:%.*]] = call i64 @llvm.riscv.sc.d(i64 [[V]], ptr [[PTR]], i32 1)
+// CHECK-RV64-NEXT:    ret i64 [[TMP0]]
+//
+int64_t zalrsc_sc_d_aq(int64_t v, int64_t* ptr) {
+  return __riscv_sc_d(v, ptr, __RISCV_ORDER_AQ);
+}
+
+// CHECK-RV64-LABEL: define dso_local i64 @zalrsc_sc_d_rl
+// CHECK-RV64-SAME: (i64 noundef [[V:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-RV64-NEXT:  entry:
+// CHECK-RV64-NEXT:    [[TMP0:%.*]] = call i64 @llvm.riscv.sc.d(i64 [[V]], ptr [[PTR]], i32 2)
+// CHECK-RV64-NEXT:    ret i64 [[TMP0]]
+//
+int64_t zalrsc_sc_d_rl(int64_t v, int64_t* ptr) {
+  return __riscv_sc_d(v, ptr, __RISCV_ORDER_RL);
+}
+
+// CHECK-RV64-LABEL: define dso_local i64 @zalrsc_sc_d_aqrl
+// CHECK-RV64-SAME: (i64 noundef [[V:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-RV64-NEXT:  entry:
+// CHECK-RV64-NEXT:    [[TMP0:%.*]] = call i64 @llvm.riscv.sc.d(i64 [[V]], ptr [[PTR]], i32 3)
+// CHECK-RV64-NEXT:    ret i64 [[TMP0]]
+//
+int64_t zalrsc_sc_d_aqrl(int64_t v, int64_t* ptr) {
+  return __riscv_sc_d(v, ptr, __RISCV_ORDER_AQ_RL);
+}
+
+#endif
diff --git a/clang/test/CodeGen/RISCV/atomics-intrinsics/zawrs.c b/clang/test/CodeGen/RISCV/atomics-intrinsics/zawrs.c
new file mode 100644
index 0000000000000..998655e67a160
--- /dev/null
+++ b/clang/test/CodeGen/RISCV/atomics-intrinsics/zawrs.c
@@ -0,0 +1,42 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 2
+// REQUIRES: riscv-registered-target
+// RUN: %clang_cc1 -triple riscv32 -target-feature +zawrs -disable-O0-optnone \
+// RUN:   -emit-llvm %s -o - | opt -S -passes=mem2reg | \
+// RUN:   FileCheck --check-prefixes=CHECK-RV32 %s
+// RUN: %clang_cc1 -triple riscv64 -target-feature +zawrs -disable-O0-optnone \
+// RUN:   -emit-llvm %s -o - | opt -S -passes=mem2reg | \
+// RUN:   FileCheck --check-prefix=CHECK-RV64 %s
+
+#include <riscv_atomics.h>
+
+// CHECK-RV32-LABEL: define dso_local void @zawrs_nto
+// CHECK-RV32-SAME: () #[[ATTR0:[0-9]+]] {
+// CHECK-RV32-NEXT:  entry:
+// CHECK-RV32-NEXT:    call void @llvm.riscv.wrs.nto()
+// CHECK-RV32-NEXT:    ret void
+//
+// CHECK-RV64-LABEL: define dso_local void @zawrs_nto
+// CHECK-RV64-SAME: () #[[ATTR0:[0-9]+]] {
+// CHECK-RV64-NEXT:  entry:
+// CHECK-RV64-NEXT:    call void @llvm.riscv.wrs.nto()
+// CHECK-RV64-NEXT:    ret void
+//
+void zawrs_nto(){
+  __riscv_wrs_nto();
+}
+
+// CHECK-RV32-LABEL: define dso_local void @zawrs_sto
+// CHECK-RV32-SAME: () #[[ATTR0]] {
+// CHECK-RV32-NEXT:  entry:
+// CHECK-RV32-NEXT:    call void @llvm.riscv.wrs.sto()
+// CHECK-RV32-NEXT:    ret void
+//
+// CHECK-RV64-LABEL: define dso_local void @zawrs_sto
+// CHECK-RV64-SAME: () #[[ATTR0]] {
+// CHECK-RV64-NEXT:  entry:
+// CHECK-RV64-NEXT:    call void @llvm.riscv.wrs.sto()
+// CHECK-RV64-NEXT:    ret void
+//
+void zawrs_sto(){
+  __riscv_wrs_sto();
+}
diff --git a/llvm/include/llvm/IR/IntrinsicsRISCV.td b/llvm/include/llvm/IR/IntrinsicsRISCV.td
index 4c4e7351212f8..c9a0e75645694 100644
--- a/llvm/include/llvm/IR/IntrinsicsRISCV.td
+++ b/llvm/include/llvm/IR/IntrinsicsRISCV.td
@@ -124,6 +124,38 @@ let TargetPrefix = "riscv" in {
                               [IntrNoMem, IntrSpeculatable, ImmArg<ArgIndex<2>>]>;
 } // TargetPrefix = "riscv"
 
+//===----------------------------------------------------------------------===//
+// 'Zawrs' (Wait on Reservation Set)
+
+let TargetPrefix = "riscv" in {
+  def int_riscv_wrs_nto
+      : DefaultAttrsIntrinsic<[], [], [IntrHasSideEffects]>;
+  def int_riscv_wrs_sto
+      : DefaultAttrsIntrinsic<[], [], [IntrHasSideEffects]>;
+} // TargetPrefix = "riscv"
+
+//===----------------------------------------------------------------------===//
+// 'Zalrsc' (Load-Reserved/Store-Conditional)
+
+let TargetPrefix = "riscv" in {
+  def int_riscv_lr_w
+      : DefaultAttrsIntrinsic<[llvm_i32_ty],
+                              [llvm_ptr_ty, llvm_i32_ty],
+                              [IntrReadMem, ImmArg<ArgIndex<1>>]>;
+  def int_riscv_lr_d
+      : DefaultAttrsIntrinsic<[llvm_i64_ty],
+                              [llvm_ptr_ty, llvm_i32_ty],
+                              [IntrReadMem, ImmArg<ArgIndex<1>>]>;
+  def int_riscv_sc_w
+      : DefaultAttrsIntrinsic<[llvm_i32_ty],
+                              [llvm_i32_ty, llvm_ptr_ty, llvm_i32_ty],
+                              [IntrWriteMem, ImmArg<ArgIndex<2>>]>;
+  def int_riscv_sc_d
+      : DefaultAttrsIntrinsic<[llvm_i64_ty],
+                              [llvm_i64_ty, llvm_ptr_ty, llvm_i32_ty],
+                              [IntrWriteMem, ImmArg<ArgIndex<2>>]>;
+} // TargetPrefix = "riscv"
+
 //===----------------------------------------------------------------------===//
 // Vectors
 
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoA.td b/llvm/lib/Target/RISCV/RISCVInstrInfoA.td
index 814e0ddf111e6..5dbf954995bed 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoA.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoA.td
@@ -136,6 +136,31 @@ let Predicates = [HasAtomicLdSt, IsRV64] in {
   def : StPat<atomic_store_64, SD, GPR, i64>;
 }
 
+/// LR/SC intrinsics patterns
+multiclass LRPat<Intrinsic intrinsic, string inst> {
+  def : Pat<(intrinsic GPR:$src, 0), (!cast<Instruction>(inst) GPR:$src)>;
+  def : Pat<(intrinsic GPR:$src, 1), (!cast<Instruction>(inst # "_AQ") GPR:$src)>;
+  def : Pat<(intrinsic GPR:$src, 2), (!cast<Instruction>(inst # "_RL") GPR:$src)>;
+  def : Pat<(intrinsic GPR:$src, 3), (!cast<Instruction>(inst # "_AQ_RL") GPR:$src)>;
+}
+
+multiclass SCPat<Intrinsic intrinsic, string inst> {
+  def : Pat<(intrinsic GPR:$src, GPR:$dst, 0), (!cast<Instruction>(inst) GPR:$src, GPR:$dst)>;
+  def : Pat<(intrinsic GPR:$src, GPR:$dst, 1), (!cast<Instruction>(inst # "_AQ") GPR:$src, GPR:$dst)>;
+  def : Pat<(intrinsic GPR:$src, GPR:$dst, 2), (!cast<Instruction>(inst # "_RL") GPR:$src, GPR:$dst)>;
+  def : Pat<(intrinsic GPR:$src, GPR:$dst, 3), (!cast<Instruction>(inst # "_AQ_RL") GPR:$src, GPR:$dst)>;
+}
+
+let Predicates = [HasStdExtAOrZalrsc] in {
+  defm : LRPat<int_riscv_lr_w, "LR_W">;
+  defm : SCPat<int_riscv_sc_w, "SC_W">;
+} // Predicates = [HasStdExtAOrZalrsc]
+
+let Predicates = [HasStdExtAOrZalrsc, IsRV64] in {
+  defm : LRPat<int_riscv_lr_d, "LR_D">;
+  defm :...
[truncated]

@wangpc-pp wangpc-pp marked this pull request as draft June 6, 2024 05:49
Created using spr 1.3.6-beta.1
@wangpc-pp wangpc-pp marked this pull request as ready for review June 6, 2024 06:39
@wangpc-pp wangpc-pp requested a review from MaskRay June 6, 2024 06:39
@wangpc-pp wangpc-pp assigned asb and kito-cheng and unassigned asb and kito-cheng Jun 6, 2024
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1
@wangpc-pp wangpc-pp changed the title [RISCV] Add riscv_atomics.h and Zawrs/Zalrsc builtins [RISCV] Add riscv_atomic.h and Zawrs/Zalrsc builtins Jun 7, 2024
@efriedma-quic
Copy link
Collaborator

lr/sc builtins are extremely fragile: there's no reasonable way for the compiler to guarantee that the sc is placed in such a way that it will eventually succeed. (The equivalent intrinsics do exist on ARM, but ARM has significantly stronger guarantees here. Even then, it's not completely reliable.)

@wangpc-pp
Copy link
Contributor Author

lr/sc builtins are extremely fragile: there's no reasonable way for the compiler to guarantee that the sc is placed in such a way that it will eventually succeed.

I think the user should have enough knowledges about lr/sc to make the logic reasonable. If we don't provide these intrinsics, the user who wants to implement custom locks will use inline assemly instead.

(The equivalent intrinsics do exist on ARM, but ARM has significantly stronger guarantees here. Even then, it's not completely reliable.)

I don't know much about there intrinsics on ARM, what are the stronger guarantees?

@jrtc27
Copy link
Collaborator

jrtc27 commented Jun 11, 2024

lr/sc builtins are extremely fragile: there's no reasonable way for the compiler to guarantee that the sc is placed in such a way that it will eventually succeed.

I think the user should have enough knowledges about lr/sc to make the logic reasonable.

It's not about knowledge, it's that they are basically impossible for the compiler to actually guarantee they'll work at all.

If we don't provide these intrinsics, the user who wants to implement custom locks will use inline assemly instead.

Good, because other than using C11-style atomics (or Itanium-style __sync builtins), that's the right thing to do.

@wangpc-pp
Copy link
Contributor Author

wangpc-pp commented Jun 11, 2024

If we are talking about the necessariness of adding these intrinsics, please refer to the ARM implementations in DPDK (https://github.com/DPDK/dpdk/blob/76cef1af8bdaeaf67a5c4ca5df3f221df994dc46/lib/eal/arm/include/rte_pause_64.h).

We want to use Zawrs&Zalrsc instructions to implement these on RISCV.

@jrtc27
Copy link
Collaborator

jrtc27 commented Jun 11, 2024

If we are talking about the necessariness of adding these intrinsics, please refer to the ARM implementations in DPDK (https://github.com/DPDK/dpdk/blob/76cef1af8bdaeaf67a5c4ca5df3f221df994dc46/lib/eal/arm/include/rte_pause_64.h).

We want to use Zawrs&Zalrsc instructions to implement these on RISCV.

Then write the loops in assembly. Really, it's not hard to do, and it's the only real way to guarantee it'll actually work the way you think it does. Compilers are free to insert whatever stack spills and reloads they want in between your inline assembly blocks or intrinsic calls, which can wreak havoc with load reservations if you're expecting them to be untouched in between. C is just not the right language to be modelling that kind of thing in, assembly is.

@topperc
Copy link
Collaborator

topperc commented Jun 11, 2024

Compilers are free to insert whatever stack spills and reloads they want in between your inline assembly blocks or intrinsic calls

Especially with -O0.

@efriedma-quic
Copy link
Collaborator

efriedma-quic commented Jun 12, 2024

I don't know much about there intrinsics on ARM, what are the stronger guarantees?

Arm specifies that there's a memory reservation, and you can write whatever operations you want as long as you don't break that reservation. And the reservation is usually only a few bytes. RISC-V specifically only guarantees behavior for sequences of integer instructions.

@asb
Copy link
Contributor

asb commented Jun 20, 2024

My understanding from the sync-up call just now is you're planning to drop lr/sc intrinsics due to the concerns listed in this thread (which I share), but will keep pushing the zawrs intrinsics.

@wangpc-pp wangpc-pp closed this Jun 21, 2024
@wangpc-pp wangpc-pp deleted the users/wangpc-pp/spr/riscv-add-riscv_atomicsh-and-zawrszalrsc-builtins branch June 21, 2024 07:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:RISC-V backend:X86 clang:codegen clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:headers Headers provided by Clang, e.g. for intrinsics clang Clang issues not falling into any other category llvm:ir
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants