[clang] [CIR] Upstream X86 builtin _mm_prefetch and _mm_clflush (PR #167401)
Hendrik Hübner via cfe-commits
cfe-commits at lists.llvm.org
Tue Nov 11 02:56:58 PST 2025
https://github.com/HendrikHuebner updated https://github.com/llvm/llvm-project/pull/167401
>From 539eebe34fb2184e20525078df8f6413b66625ce Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Mon, 10 Nov 2025 23:04:51 +0100
Subject: [PATCH 1/3] [CIR] Upstream X86 builtin _mm_prefetch and _mm_clflush
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 45 ++++++++++++++
clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp | 60 +++++++++++++++++++
clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 22 +++++++
clang/lib/CIR/CodeGen/CIRGenFunction.h | 4 ++
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 14 +++++
clang/test/CIR/CodeGen/X86/sse-builtins.c | 23 +++++++
clang/test/CIR/CodeGen/X86/sse2-builtins.c | 23 +++++++
7 files changed, 191 insertions(+)
create mode 100644 clang/test/CIR/CodeGen/X86/sse-builtins.c
create mode 100644 clang/test/CIR/CodeGen/X86/sse2-builtins.c
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 16258513239d9..902b1fa64fb5b 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -413,6 +413,18 @@ def CIR_ConstantOp : CIR_Op<"const", [
template <typename T>
T getValueAttr() { return mlir::dyn_cast<T>(getValue()); }
+
+ llvm::APInt getIntValue() {
+ if (const auto intAttr = getValueAttr<cir::IntAttr>())
+ return intAttr.getValue();
+ llvm_unreachable("Expected an IntAttr in ConstantOp");
+ }
+
+ bool getBoolValue() {
+ if (const auto boolAttr = getValueAttr<cir::BoolAttr>())
+ return boolAttr.getValue();
+ llvm_unreachable("Expected a BoolAttr in ConstantOp");
+ }
}];
let hasFolder = 1;
@@ -2579,6 +2591,39 @@ def CIR_FuncOp : CIR_Op<"func", [
}];
}
+//===----------------------------------------------------------------------===//
+// LLVMIntrinsicCallOp
+//===----------------------------------------------------------------------===//
+
+def CIR_LLVMIntrinsicCallOp : CIR_Op<"llvm.intrinsic"> {
+ let summary = "Call to llvm intrinsic functions that is not defined in CIR";
+ let description = [{
+ `cir.llvm.intrinsic` operation represents a call-like expression which has
+ return type and arguments that maps directly to a llvm intrinsic.
+ It only records intrinsic `intrinsic_name`.
+ }];
+
+ let results = (outs Optional<CIR_AnyType>:$result);
+ let arguments = (ins
+ StrAttr:$intrinsic_name, Variadic<CIR_AnyType>:$arg_ops);
+
+ let skipDefaultBuilders = 1;
+
+ let assemblyFormat = [{
+ $intrinsic_name $arg_ops `:` functional-type($arg_ops, $result) attr-dict
+ }];
+
+ let builders = [
+ OpBuilder<(ins "mlir::StringAttr":$intrinsic_name, "mlir::Type":$resType,
+ CArg<"mlir::ValueRange", "{}">:$operands), [{
+ $_state.addAttribute("intrinsic_name", intrinsic_name);
+ $_state.addOperands(operands);
+ if (resType)
+ $_state.addTypes(resType);
+ }]>,
+ ];
+}
+
//===----------------------------------------------------------------------===//
// CallOp
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp
index 0198a9d4eb192..2f02dd0319cd0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp
@@ -21,6 +21,49 @@
using namespace clang;
using namespace clang::CIRGen;
+/// Get integer from a mlir::Value that is an int constant or a constant op.
+static int64_t getIntValueFromConstOp(mlir::Value val) {
+ return val.getDefiningOp<cir::ConstantOp>().getIntValue().getSExtValue();
+}
+
+static mlir::Value emitClFlush(CIRGenFunction& cgf,
+ const CallExpr* e,
+ mlir::Value& op) {
+ mlir::Type voidTy = cir::VoidType::get(&cgf.getMLIRContext());
+ mlir::Location location = cgf.getLoc(e->getExprLoc());
+ return cgf.getBuilder()
+ .create<cir::LLVMIntrinsicCallOp>(
+ location, cgf.getBuilder().getStringAttr("x86.sse2.clflush"),
+ voidTy, op)
+ .getResult();
+}
+
+static mlir::Value emitPrefetch(CIRGenFunction& cgf,
+ const CallExpr* e,
+ mlir::Value& addr,
+ int64_t hint) {
+ CIRGenBuilderTy& builder = cgf.getBuilder();
+ mlir::Type voidTy = cir::VoidType::get(&cgf.getMLIRContext());
+ mlir::Type sInt32Ty = cir::IntType::get(&cgf.getMLIRContext(), 32, true);
+ mlir::Value address = builder.createPtrBitcast(addr, voidTy);
+ mlir::Location location = cgf.getLoc(e->getExprLoc());
+ mlir::Value rw =
+ cir::ConstantOp::create(builder, location,
+ cir::IntAttr::get(sInt32Ty, (hint >> 2) & 0x1));
+ mlir::Value locality =
+ cir::ConstantOp::create(builder, location,
+ cir::IntAttr::get(sInt32Ty, hint & 0x3));
+ mlir::Value data = cir::ConstantOp::create(builder, location,
+ cir::IntAttr::get(sInt32Ty, 1));
+
+ return cir::LLVMIntrinsicCallOp::create(
+ builder, location,
+ builder.getStringAttr("prefetch"), voidTy,
+ mlir::ValueRange{address, rw, locality, data})
+ .getResult();
+}
+
+
mlir::Value CIRGenFunction::emitX86BuiltinExpr(unsigned builtinID,
const CallExpr *e) {
if (builtinID == Builtin::BI__builtin_cpu_is) {
@@ -43,11 +86,28 @@ mlir::Value CIRGenFunction::emitX86BuiltinExpr(unsigned builtinID,
// Find out if any arguments are required to be integer constant expressions.
assert(!cir::MissingFeatures::handleBuiltinICEArguments());
+ // The operands of the builtin call
+ llvm::SmallVector<mlir::Value, 4> ops;
+
+ // `ICEArguments` is a bitmap indicating whether the argument at the i-th bit
+ // is required to be a constant integer expression.
+ unsigned ICEArguments = 0;
+ ASTContext::GetBuiltinTypeError error;
+ getContext().GetBuiltinType(builtinID, error, &ICEArguments);
+ assert(error == ASTContext::GE_None && "Error while getting builtin type.");
+
+ const unsigned numArgs = e->getNumArgs();
+ for (unsigned i = 0; i != numArgs; i++) {
+ ops.push_back(emitScalarOrConstFoldImmArg(ICEArguments, i, e));
+ }
+
switch (builtinID) {
default:
return {};
case X86::BI_mm_prefetch:
+ return emitPrefetch(*this, e, ops[0], getIntValueFromConstOp(ops[1]));
case X86::BI_mm_clflush:
+ return emitClFlush(*this, e, ops[0]);
case X86::BI_mm_lfence:
case X86::BI_mm_pause:
case X86::BI_mm_mfence:
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 5eba5ba6c3df1..236f487afd9ba 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -1430,6 +1430,28 @@ mlir::Value CIRGenFunction::emitPromotedScalarExpr(const Expr *e,
return ScalarExprEmitter(*this, builder).Visit(const_cast<Expr *>(e));
}
+mlir::Value CIRGenFunction::emitScalarOrConstFoldImmArg(unsigned ICEArguments,
+ unsigned index,
+ const CallExpr *e) {
+ mlir::Value arg{};
+
+ // The bit at the specified index indicates whether the argument is required
+ // to be a constant integer expression.
+ bool isArgRequiredToBeConstant = (ICEArguments & (1 << index));
+
+ if (!isArgRequiredToBeConstant) {
+ arg = emitScalarExpr(e->getArg(index));
+ } else {
+ // If this is required to be a constant, constant fold it so that we
+ // know that the generated intrinsic gets a ConstantInt.
+ std::optional<llvm::APSInt> result =
+ e->getArg(index)->getIntegerConstantExpr(getContext());
+ assert(result && "Expected argument to be a constant");
+ arg = builder.getConstInt(getLoc(e->getSourceRange()), *result);
+ }
+ return arg;
+}
+
[[maybe_unused]] static bool mustVisitNullValue(const Expr *e) {
// If a null pointer expression's type is the C++0x nullptr_t and
// the expression is not a simple literal, it must be evaluated
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index f879e580989f7..0ce8714b23e82 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1529,6 +1529,10 @@ class CIRGenFunction : public CIRGenTypeCache {
mlir::Value emitScalarExpr(const clang::Expr *e,
bool ignoreResultAssign = false);
+ mlir::Value emitScalarOrConstFoldImmArg(unsigned ICEArguments,
+ unsigned index,
+ const CallExpr *e);
+
mlir::Value emitScalarPrePostIncDec(const UnaryOperator *e, LValue lv,
cir::UnaryOpKind kind, bool isPre);
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index b4afed7019417..f4379b402fe13 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -320,6 +320,20 @@ static mlir::LLVM::CallIntrinsicOp replaceOpWithCallLLVMIntrinsicOp(
return callIntrinOp;
}
+mlir::LogicalResult CIRToLLVMLLVMIntrinsicCallOpLowering::matchAndRewrite(
+ cir::LLVMIntrinsicCallOp op,
+ OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ mlir::Type llvmResTy =
+ getTypeConverter()->convertType(op->getResultTypes()[0]);
+ if (!llvmResTy)
+ return op.emitError("expected LLVM result type");
+ StringRef name = op.getIntrinsicName();
+ replaceOpWithCallLLVMIntrinsicOp(rewriter, op, "llvm." + name, llvmResTy,
+ adaptor.getOperands());
+ return mlir::success();
+}
+
/// IntAttr visitor.
mlir::Value CIRAttrToValue::visitCirAttr(cir::IntAttr intAttr) {
mlir::Location loc = parentOp->getLoc();
diff --git a/clang/test/CIR/CodeGen/X86/sse-builtins.c b/clang/test/CIR/CodeGen/X86/sse-builtins.c
new file mode 100644
index 0000000000000..4dd141168ba66
--- /dev/null
+++ b/clang/test/CIR/CodeGen/X86/sse-builtins.c
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -x c -flax-vector-conversions=none -ffreestanding %s -triple=x86_64-unknown-linux -target-feature +sse -fclangir -emit-cir -o %t.cir -Wall -Werror
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -x c -flax-vector-conversions=none -ffreestanding %s -triple=x86_64-unknown-linux -target-feature +sse -fclangir -emit-llvm -o %t.ll -Wall -Werror
+// RUN: FileCheck --check-prefixes=LLVM --input-file=%t.ll %s
+
+// RUN: %clang_cc1 -x c++ -flax-vector-conversions=none -ffreestanding %s -triple=x86_64-unknown-linux -target-feature +sse -fno-signed-char -fclangir -emit-cir -o %t.cir -Wall -Werror
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -x c++ -flax-vector-conversions=none -ffreestanding %s -triple=x86_64-unknown-linux -target-feature +sse -fclangir -emit-llvm -o %t.ll -Wall -Werror
+// RUN: FileCheck --check-prefixes=LLVM --input-file=%t.ll %s
+
+// This test mimics clang/test/CodeGen/X86/sse-builtins.c, which eventually
+// CIR shall be able to support fully.
+
+#include <immintrin.h>
+
+
+void test_mm_prefetch(char const* p) {
+ // CIR-LABEL: test_mm_prefetch
+ // LLVM-LABEL: test_mm_prefetch
+ _mm_prefetch(p, 0);
+ // CIR: cir.prefetch read locality(0) %{{.*}} : !cir.ptr<!void>
+ // LLVM: call void @llvm.prefetch.p0(ptr {{.*}}, i32 0, i32 0, i32 1)
+}
diff --git a/clang/test/CIR/CodeGen/X86/sse2-builtins.c b/clang/test/CIR/CodeGen/X86/sse2-builtins.c
new file mode 100644
index 0000000000000..0c275fa089262
--- /dev/null
+++ b/clang/test/CIR/CodeGen/X86/sse2-builtins.c
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -x c -flax-vector-conversions=none -ffreestanding %s -triple=x86_64-unknown-linux -target-feature +sse2 -fclangir -emit-cir -o %t.cir -Wall -Werror
+// RUN: FileCheck --check-prefixes=CIR-CHECK --input-file=%t.cir %s
+// RUN: %clang_cc1 -x c -flax-vector-conversions=none -ffreestanding %s -triple=x86_64-unknown-linux -target-feature +sse2 -fno-signed-char -fclangir -emit-cir -o %t.cir -Wall -Werror
+// RUN: FileCheck --check-prefixes=CIR-CHECK --input-file=%t.cir %s
+
+// RUN: %clang_cc1 -x c++ -flax-vector-conversions=none -ffreestanding %s -triple=x86_64-unknown-linux -target-feature +sse2 -fclangir -emit-llvm -o %t.ll -Wall -Werror
+// RUN: FileCheck --check-prefixes=LLVM-CHECK --input-file=%t.ll %s
+// RUN: %clang_cc1 -x c++ -flax-vector-conversions=none -ffreestanding %s -triple=x86_64-unknown-linux -target-feature +sse2 -fno-signed-char -fclangir -emit-llvm -o %t.ll -Wall -Werror
+// RUN: FileCheck --check-prefixes=LLVM-CHECK --input-file=%t.ll %s
+
+// This test mimics clang/test/CodeGen/X86/sse2-builtins.c, which eventually
+// CIR shall be able to support fully.
+
+#include <immintrin.h>
+
+
+void test_mm_clflush(void* A) {
+ // CIR-LABEL: test_mm_clflush
+ // LLVM-LABEL: teh
+ _mm_clflush(A);
+ // CIR-CHECK: {{%.*}} = cir.llvm.intrinsic "x86.sse2.clflush" {{%.*}} : (!cir.ptr<!void>) -> !void
+ // LLVM-CHECK: call void @llvm.x86.sse2.clflush(ptr {{%.*}})
+}
>From de4743272c246c35ddcc3123a2ecfdd5a5d4a938 Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Mon, 10 Nov 2025 23:18:23 +0100
Subject: [PATCH 2/3] fix warning
---
clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp
index 2f02dd0319cd0..0c168d86ccde5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp
@@ -31,11 +31,10 @@ static mlir::Value emitClFlush(CIRGenFunction& cgf,
mlir::Value& op) {
mlir::Type voidTy = cir::VoidType::get(&cgf.getMLIRContext());
mlir::Location location = cgf.getLoc(e->getExprLoc());
- return cgf.getBuilder()
- .create<cir::LLVMIntrinsicCallOp>(
- location, cgf.getBuilder().getStringAttr("x86.sse2.clflush"),
- voidTy, op)
- .getResult();
+ return cir::LLVMIntrinsicCallOp::create(
+ cgf.getBuilder(), location,
+ cgf.getBuilder().getStringAttr("x86.sse2.clflush"), voidTy, op)
+ .getResult();
}
static mlir::Value emitPrefetch(CIRGenFunction& cgf,
>From 26d8914b8c9ee4e01ea8fa55464e8ab0cb17225f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hendrik=20H=C3=BCbner?=
<117831077+HendrikHuebner at users.noreply.github.com>
Date: Tue, 11 Nov 2025 11:56:49 +0100
Subject: [PATCH 3/3] Update clang/include/clang/CIR/Dialect/IR/CIROps.td
Co-authored-by: Andy Kaylor <akaylor at nvidia.com>
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 902b1fa64fb5b..d40466610fc13 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2595,7 +2595,7 @@ def CIR_FuncOp : CIR_Op<"func", [
// LLVMIntrinsicCallOp
//===----------------------------------------------------------------------===//
-def CIR_LLVMIntrinsicCallOp : CIR_Op<"llvm.intrinsic"> {
+def CIR_LLVMIntrinsicCallOp : CIR_Op<"call_llvm_intrinsic"> {
let summary = "Call to llvm intrinsic functions that is not defined in CIR";
let description = [{
`cir.llvm.intrinsic` operation represents a call-like expression which has
More information about the cfe-commits
mailing list