https://github.com/jacquesguan created 
https://github.com/llvm/llvm-project/pull/205468

Include __builtin_riscv_ntl_load and __builtin_riscv_ntl_store.

>From 91179a8a63f77ea074913d5f96cb5bdc07b8360e Mon Sep 17 00:00:00 2001
From: Jianjian GUAN <[email protected]>
Date: Tue, 23 Jun 2026 15:24:00 +0800
Subject: [PATCH] [CIR][RISCV] Support Zihintntl builtin

Include __builtin_riscv_ntl_load and __builtin_riscv_ntl_store.
---
 clang/lib/CIR/CodeGen/CIRGenBuiltinRISCV.cpp  |  34 ++++-
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp |   4 +
 .../Lowering/DirectToLLVM/LowerToLLVMIR.cpp   |  25 ++++
 .../CodeGenBuiltins/RISCV/riscv-zihintntl.c   | 127 ++++++++++++++++++
 4 files changed, 187 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/CIR/CodeGenBuiltins/RISCV/riscv-zihintntl.c

diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinRISCV.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltinRISCV.cpp
index ec262922be942..2109ef45c392e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinRISCV.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinRISCV.cpp
@@ -38,6 +38,12 @@ CIRGenFunction::emitRISCVBuiltinExpr(unsigned builtinID, 
const CallExpr *e) {
   ASTContext::GetBuiltinTypeError error;
   getContext().GetBuiltinType(builtinID, error, &iceArguments);
   assert(error == ASTContext::GE_None && "Should not codegen an error");
+
+  if (builtinID == RISCV::BI__builtin_riscv_ntl_load)
+    iceArguments |= (1 << 1);
+  if (builtinID == RISCV::BI__builtin_riscv_ntl_store)
+    iceArguments |= (1 << 2);
+
   for (auto [idx, arg] : llvm::enumerate(e->arguments()))
     ops.push_back(emitScalarOrConstFoldImmArg(iceArguments, idx, arg));
 
@@ -154,9 +160,31 @@ CIRGenFunction::emitRISCVBuiltinExpr(unsigned builtinID, 
const CallExpr *e) {
   // Zihintntl
   case RISCV::BI__builtin_riscv_ntl_load:
   case RISCV::BI__builtin_riscv_ntl_store: {
-    cgm.errorNYI(e->getSourceRange(),
-                 std::string("unimplemented RISC-V builtin call: ") +
-                     getContext().BuiltinInfo.getName(builtinID));
+    unsigned domainVal = 5; // Default __RISCV_NTLH_ALL
+    unsigned domainArgNo =
+        builtinID == RISCV::BI__builtin_riscv_ntl_load ? 1 : 2;
+    if (e->getNumArgs() > domainArgNo) {
+      const std::optional<llvm::APSInt> result =
+          e->getArg(domainArgNo)->getIntegerConstantExpr(getContext());
+      assert(result && "Expected NTLH domain argument to be a constant");
+      domainVal = result->getZExtValue();
+    }
+
+    mlir::Location loc = getLoc(e->getSourceRange());
+    mlir::Attribute domainAttr = builder.getI32IntegerAttr(domainVal);
+    Address addr(ops[0],
+                 cgm.getNaturalPointeeTypeAlignment(e->getArg(0)->getType()));
+    if (builtinID == RISCV::BI__builtin_riscv_ntl_load) {
+      auto load = builder.createLoad(loc, addr, /*isVolatile=*/false,
+                                     /*isNontemporal=*/true);
+      load->setAttr("cir.riscv_nontemporal_domain", domainAttr);
+      return load.getResult();
+    }
+
+    mlir::Value val = emitToMemory(ops[1], e->getArg(1)->getType());
+    auto store = builder.createStore(loc, val, addr, /*isVolatile=*/false,
+                                     /*isNontemporal=*/true);
+    store->setAttr("cir.riscv_nontemporal_domain", domainAttr);
     return mlir::Value{};
   }
 
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 7cb15f8c5e8a3..cb703a038f721 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -1931,6 +1931,8 @@ mlir::LogicalResult 
CIRToLLVMLoadOpLowering::matchAndRewrite(
       op.getIsVolatile(), /*isNonTemporal=*/op.getIsNontemporal(),
       /*isInvariant=*/false, /*isInvariantGroup=*/false, ordering,
       llvmSyncScope.value_or(std::string()));
+  if (mlir::Attribute domain = op->getAttr("cir.riscv_nontemporal_domain"))
+    newLoad->setAttr("cir.riscv_nontemporal_domain", domain);
 
   // Convert adapted result to its original type if needed.
   mlir::Value result =
@@ -1986,6 +1988,8 @@ mlir::LogicalResult 
CIRToLLVMStoreOpLowering::matchAndRewrite(
       op.getIsVolatile(),
       /*isNonTemporal=*/op.getIsNontemporal(), /*isInvariantGroup=*/false,
       memorder, llvmSyncScope.value_or(std::string()));
+  if (mlir::Attribute domain = op->getAttr("cir.riscv_nontemporal_domain"))
+    storeOp->setAttr("cir.riscv_nontemporal_domain", domain);
   rewriter.replaceOp(op, storeOp);
   assert(!cir::MissingFeatures::opLoadStoreTbaa());
   return mlir::LogicalResult::success();
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp
index dbcd0aed88056..36b46d7a9c666 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp
@@ -18,7 +18,9 @@
 #include "clang/CIR/Dialect/IR/CIRDialect.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/IR/Constant.h"
+#include "llvm/IR/Constants.h"
 #include "llvm/IR/GlobalVariable.h"
+#include "llvm/IR/Metadata.h"
 
 using namespace llvm;
 
@@ -59,11 +61,34 @@ class CIRDialectLLVMIRTranslationInterface
     } else if (auto mod = dyn_cast<mlir::ModuleOp>(op)) {
       if (mlir::failed(amendModule(mod, attribute, moduleTranslation)))
         return mlir::failure();
+    } else if (attribute.getName() == "cir.riscv_nontemporal_domain") {
+      if (mlir::failed(amendRISCVNontemporalDomain(op, instructions, attribute,
+                                                   moduleTranslation)))
+        return mlir::failure();
     }
     return mlir::success();
   }
 
 private:
+  mlir::LogicalResult amendRISCVNontemporalDomain(
+      mlir::Operation *op, llvm::ArrayRef<llvm::Instruction *> instructions,
+      mlir::NamedAttribute attribute,
+      mlir::LLVM::ModuleTranslation &moduleTranslation) const {
+    auto domain = mlir::dyn_cast<mlir::IntegerAttr>(attribute.getValue());
+    if (!domain)
+      return op->emitError()
+             << "expected cir.riscv_nontemporal_domain to be an integer";
+
+    llvm::LLVMContext &llvmContext = moduleTranslation.getLLVMContext();
+    llvm::MDNode *node = llvm::MDNode::get(
+        llvmContext, llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(
+                         llvm::Type::getInt32Ty(llvmContext),
+                         domain.getValue().getZExtValue())));
+    for (llvm::Instruction *inst : instructions)
+      inst->setMetadata("riscv-nontemporal-domain", node);
+    return mlir::success();
+  }
+
   // Translate CIR function attributes to LLVM function attributes.
   mlir::LogicalResult
   amendFunction(mlir::LLVM::LLVMFuncOp func,
diff --git a/clang/test/CIR/CodeGenBuiltins/RISCV/riscv-zihintntl.c 
b/clang/test/CIR/CodeGenBuiltins/RISCV/riscv-zihintntl.c
new file mode 100644
index 0000000000000..fb0f27f334ed6
--- /dev/null
+++ b/clang/test/CIR/CodeGenBuiltins/RISCV/riscv-zihintntl.c
@@ -0,0 +1,127 @@
+// REQUIRES: riscv-registered-target
+// RUN: %clang_cc1 -triple riscv32 -target-feature +zihintntl -fclangir 
-emit-cir %s -o - | FileCheck %s --check-prefix=CIR
+// RUN: %clang_cc1 -triple riscv64 -target-feature +zihintntl -fclangir 
-emit-cir %s -o - | FileCheck %s --check-prefix=CIR
+// RUN: %clang_cc1 -triple riscv32 -target-feature +zihintntl -fclangir 
-emit-llvm %s -o - | FileCheck %s --check-prefix=LLVM
+// RUN: %clang_cc1 -triple riscv64 -target-feature +zihintntl -fclangir 
-emit-llvm %s -o - | FileCheck %s --check-prefix=LLVM
+// RUN: %clang_cc1 -triple riscv32 -target-feature +zihintntl -emit-llvm %s -o 
- | FileCheck %s --check-prefix=LLVM
+// RUN: %clang_cc1 -triple riscv64 -target-feature +zihintntl -emit-llvm %s -o 
- | FileCheck %s --check-prefix=LLVM
+
+#include <riscv_ntlh.h>
+
+signed char sc;
+unsigned char uc;
+signed short ss;
+unsigned short us;
+signed int si;
+unsigned int ui;
+signed long long sll;
+unsigned long long ull;
+_Float16 h1, h2;
+float f1, f2;
+double d1, d2;
+typedef int v4si __attribute__((vector_size(16)));
+typedef signed short v8ss __attribute__((vector_size(16)));
+typedef signed char v16sc __attribute__((vector_size(16)));
+v4si v4si1, v4si2;
+v8ss v8ss1, v8ss2;
+v16sc v16sc1, v16sc2;
+
+// CIR-LABEL: cir.func{{.*}} @ntl_load_all_sizes(
+// LLVM-LABEL: @ntl_load_all_sizes(
+void ntl_load_all_sizes(void) {
+  // CIR: cir.load nontemporal align(1) {{.*}} {cir.riscv_nontemporal_domain = 
2 : i32}
+  // LLVM: load i8, ptr {{.*}}, align 1, !nontemporal [[NT:![0-9]+]], 
!riscv-nontemporal-domain [[D2:![0-9]+]]
+  uc = __riscv_ntl_load(&sc, __RISCV_NTLH_INNERMOST_PRIVATE);
+  // CIR: cir.load nontemporal align(1) {{.*}} {cir.riscv_nontemporal_domain = 
3 : i32}
+  // LLVM: load i8, ptr {{.*}}, align 1, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D3:![0-9]+]]
+  sc = __riscv_ntl_load(&uc, __RISCV_NTLH_ALL_PRIVATE);
+  // CIR: cir.load nontemporal align(2) {{.*}} {cir.riscv_nontemporal_domain = 
4 : i32}
+  // LLVM: load i16, ptr {{.*}}, align 2, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D4:![0-9]+]]
+  us = __riscv_ntl_load(&ss, __RISCV_NTLH_INNERMOST_SHARED);
+  // CIR: cir.load nontemporal align(2) {{.*}} {cir.riscv_nontemporal_domain = 
5 : i32}
+  // LLVM: load i16, ptr {{.*}}, align 2, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D5:![0-9]+]]
+  ss = __riscv_ntl_load(&us, __RISCV_NTLH_ALL);
+  // CIR: cir.load nontemporal align(4) {{.*}} {cir.riscv_nontemporal_domain = 
5 : i32}
+  // LLVM: load i32, ptr {{.*}}, align 4, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D5]]
+  ui = __riscv_ntl_load(&si);
+  // CIR: cir.load nontemporal align(4) {{.*}} {cir.riscv_nontemporal_domain = 
2 : i32}
+  // LLVM: load i32, ptr {{.*}}, align 4, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D2]]
+  si = __riscv_ntl_load(&ui, __RISCV_NTLH_INNERMOST_PRIVATE);
+  // CIR: cir.load nontemporal align(8) {{.*}} {cir.riscv_nontemporal_domain = 
3 : i32}
+  // LLVM: load i64, ptr {{.*}}, align 8, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D3]]
+  ull = __riscv_ntl_load(&sll, __RISCV_NTLH_ALL_PRIVATE);
+  // CIR: cir.load nontemporal align(8) {{.*}} {cir.riscv_nontemporal_domain = 
4 : i32}
+  // LLVM: load i64, ptr {{.*}}, align 8, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D4]]
+  sll = __riscv_ntl_load(&ull, __RISCV_NTLH_INNERMOST_SHARED);
+  // CIR: cir.load nontemporal align(2) {{.*}} {cir.riscv_nontemporal_domain = 
5 : i32}
+  // LLVM: load half, ptr {{.*}}, align 2, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D5]]
+  h1 = __riscv_ntl_load(&h2, __RISCV_NTLH_ALL);
+  // CIR: cir.load nontemporal align(4) {{.*}} {cir.riscv_nontemporal_domain = 
5 : i32}
+  // LLVM: load float, ptr {{.*}}, align 4, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D5]]
+  f1 = __riscv_ntl_load(&f2);
+  // CIR: cir.load nontemporal align(8) {{.*}} {cir.riscv_nontemporal_domain = 
2 : i32}
+  // LLVM: load double, ptr {{.*}}, align 8, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D2]]
+  d1 = __riscv_ntl_load(&d2, __RISCV_NTLH_INNERMOST_PRIVATE);
+  // CIR: cir.load nontemporal align(16) {{.*}} {cir.riscv_nontemporal_domain 
= 3 : i32}
+  // LLVM: load <4 x i32>, ptr {{.*}}, align 16, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D3]]
+  v4si1 = __riscv_ntl_load(&v4si2, __RISCV_NTLH_ALL_PRIVATE);
+  // CIR: cir.load nontemporal align(16) {{.*}} {cir.riscv_nontemporal_domain 
= 4 : i32}
+  // LLVM: load <8 x i16>, ptr {{.*}}, align 16, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D4]]
+  v8ss1 = __riscv_ntl_load(&v8ss2, __RISCV_NTLH_INNERMOST_SHARED);
+  // CIR: cir.load nontemporal align(16) {{.*}} {cir.riscv_nontemporal_domain 
= 5 : i32}
+  // LLVM: load <16 x i8>, ptr {{.*}}, align 16, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D5]]
+  v16sc1 = __riscv_ntl_load(&v16sc2, __RISCV_NTLH_ALL);
+}
+
+// CIR-LABEL: cir.func{{.*}} @ntl_store_all_sizes(
+// LLVM-LABEL: @ntl_store_all_sizes(
+void ntl_store_all_sizes(void) {
+  // CIR: cir.store nontemporal align(1) {{.*}} {cir.riscv_nontemporal_domain 
= 2 : i32}
+  // LLVM: store i8 {{.*}}, ptr {{.*}}, align 1, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D2]]
+  __riscv_ntl_store(&uc, 1, __RISCV_NTLH_INNERMOST_PRIVATE);
+  // CIR: cir.store nontemporal align(1) {{.*}} {cir.riscv_nontemporal_domain 
= 3 : i32}
+  // LLVM: store i8 {{.*}}, ptr {{.*}}, align 1, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D3]]
+  __riscv_ntl_store(&sc, 1, __RISCV_NTLH_ALL_PRIVATE);
+  // CIR: cir.store nontemporal align(2) {{.*}} {cir.riscv_nontemporal_domain 
= 4 : i32}
+  // LLVM: store i16 {{.*}}, ptr {{.*}}, align 2, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D4]]
+  __riscv_ntl_store(&us, 1, __RISCV_NTLH_INNERMOST_SHARED);
+  // CIR: cir.store nontemporal align(2) {{.*}} {cir.riscv_nontemporal_domain 
= 5 : i32}
+  // LLVM: store i16 {{.*}}, ptr {{.*}}, align 2, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D5]]
+  __riscv_ntl_store(&ss, 1, __RISCV_NTLH_ALL);
+  // CIR: cir.store nontemporal align(4) {{.*}} {cir.riscv_nontemporal_domain 
= 5 : i32}
+  // LLVM: store i32 {{.*}}, ptr {{.*}}, align 4, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D5]]
+  __riscv_ntl_store(&ui, 1);
+  // CIR: cir.store nontemporal align(4) {{.*}} {cir.riscv_nontemporal_domain 
= 2 : i32}
+  // LLVM: store i32 {{.*}}, ptr {{.*}}, align 4, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D2]]
+  __riscv_ntl_store(&si, 1, __RISCV_NTLH_INNERMOST_PRIVATE);
+  // CIR: cir.store nontemporal align(8) {{.*}} {cir.riscv_nontemporal_domain 
= 3 : i32}
+  // LLVM: store i64 {{.*}}, ptr {{.*}}, align 8, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D3]]
+  __riscv_ntl_store(&ull, 1, __RISCV_NTLH_ALL_PRIVATE);
+  // CIR: cir.store nontemporal align(8) {{.*}} {cir.riscv_nontemporal_domain 
= 4 : i32}
+  // LLVM: store i64 {{.*}}, ptr {{.*}}, align 8, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D4]]
+  __riscv_ntl_store(&sll, 1, __RISCV_NTLH_INNERMOST_SHARED);
+  // CIR: cir.store nontemporal align(2) {{.*}} {cir.riscv_nontemporal_domain 
= 5 : i32}
+  // LLVM: store half {{.*}}, ptr {{.*}}, align 2, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D5]]
+  __riscv_ntl_store(&h1, 1.0, __RISCV_NTLH_ALL);
+  // CIR: cir.store nontemporal align(4) {{.*}} {cir.riscv_nontemporal_domain 
= 5 : i32}
+  // LLVM: store float {{.*}}, ptr {{.*}}, align 4, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D5]]
+  __riscv_ntl_store(&f1, 1.0);
+  // CIR: cir.store nontemporal align(8) {{.*}} {cir.riscv_nontemporal_domain 
= 2 : i32}
+  // LLVM: store double {{.*}}, ptr {{.*}}, align 8, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D2]]
+  __riscv_ntl_store(&d1, 1.0, __RISCV_NTLH_INNERMOST_PRIVATE);
+  // CIR: cir.store nontemporal align(16) {{.*}} {cir.riscv_nontemporal_domain 
= 3 : i32}
+  // LLVM: store <4 x i32> {{.*}}, ptr {{.*}}, align 16, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D3]]
+  __riscv_ntl_store(&v4si1, v4si2, __RISCV_NTLH_ALL_PRIVATE);
+  // CIR: cir.store nontemporal align(16) {{.*}} {cir.riscv_nontemporal_domain 
= 4 : i32}
+  // LLVM: store <8 x i16> {{.*}}, ptr {{.*}}, align 16, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D4]]
+  __riscv_ntl_store(&v8ss1, v8ss2, __RISCV_NTLH_INNERMOST_SHARED);
+  // CIR: cir.store nontemporal align(16) {{.*}} {cir.riscv_nontemporal_domain 
= 5 : i32}
+  // LLVM: store <16 x i8> {{.*}}, ptr {{.*}}, align 16, !nontemporal [[NT]], 
!riscv-nontemporal-domain [[D5]]
+  __riscv_ntl_store(&v16sc1, v16sc2, __RISCV_NTLH_ALL);
+}
+
+// LLVM-DAG: [[NT]] = !{i32 1}
+// LLVM-DAG: [[D2]] = !{i32 2}
+// LLVM-DAG: [[D3]] = !{i32 3}
+// LLVM-DAG: [[D4]] = !{i32 4}
+// LLVM-DAG: [[D5]] = !{i32 5}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to