[clang] cc79c8c - [CIR][RISCV] Support Zihintntl builtins (#205468)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Jul 15 19:12:22 PDT 2026
Author: Jianjian Guan
Date: 2026-07-16T10:12:17+08:00
New Revision: cc79c8cdc4e7f0a8a968e1d8c5b91b14ce1e183a
URL: https://github.com/llvm/llvm-project/commit/cc79c8cdc4e7f0a8a968e1d8c5b91b14ce1e183a
DIFF: https://github.com/llvm/llvm-project/commit/cc79c8cdc4e7f0a8a968e1d8c5b91b14ce1e183a.diff
LOG: [CIR][RISCV] Support Zihintntl builtins (#205468)
Include __builtin_riscv_ntl_load and __builtin_riscv_ntl_store.
Added:
clang/test/CIR/CodeGenBuiltins/RISCV/riscv-zihintntl.c
Modified:
clang/lib/CIR/CodeGen/CIRGenBuiltinRISCV.cpp
clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp
Removed:
################################################################################
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinRISCV.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltinRISCV.cpp
index 48f19e0070f0b..25e714a612147 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 0e412090a16da..7748c81664a40 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2026,6 +2026,8 @@ mlir::LogicalResult CIRToLLVMLoadOpLowering::matchAndRewrite(
op.getIsVolatile(), /*isNonTemporal=*/op.getIsNontemporal(),
/*isInvariant=*/op.getInvariant(), /*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 =
@@ -2081,6 +2083,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}
More information about the cfe-commits
mailing list