[clang] [CIR] Upstream ClearCacheOp support for __builtin___clear_cache (PR #178260)
Chibuoyim Ogbonna via cfe-commits
cfe-commits at lists.llvm.org
Wed Jan 28 09:12:21 PST 2026
https://github.com/bruteforceboy updated https://github.com/llvm/llvm-project/pull/178260
>From de1e6d1d08d5dd6d3346e7399abd728594e73f0a Mon Sep 17 00:00:00 2001
From: bruteforceboy <ogbonnachibuoyim12 at gmail.com>
Date: Tue, 27 Jan 2026 18:26:08 +0100
Subject: [PATCH 1/6] [CIR] Upstream ClearCacheOp support for
__builtin___clear_cache
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 20 +++++++++++
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 10 +++++-
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 13 ++++++++
clang/test/CIR/CodeGen/clear-cache.c | 33 +++++++++++++++++++
4 files changed, 75 insertions(+), 1 deletion(-)
create mode 100644 clang/test/CIR/CodeGen/clear-cache.c
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index a6de91b2eda01..c310372844af7 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -5111,6 +5111,26 @@ def CIR_PrefetchOp : CIR_Op<"prefetch"> {
}];
}
+//===----------------------------------------------------------------------===//
+// ClearCacheOp
+//===----------------------------------------------------------------------===//
+
+def CIR_ClearCacheOp : CIR_Op<"clear_cache", [
+ AllTypesMatch<["begin", "end"]>
+]> {
+ let summary = "clear cache operation";
+ let description = [{
+ CIR representation for `__builtin___clear_cache`.
+ }];
+
+ let arguments = (ins CIR_VoidPtrType:$begin, CIR_VoidPtrType:$end);
+ let assemblyFormat = [{
+ $begin `:` qualified(type($begin)) `,`
+ $end `,`
+ attr-dict
+ }];
+}
+
//===----------------------------------------------------------------------===//
// ObjSizeOp
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 88d37d56fcd78..c505359271c49 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -1080,8 +1080,16 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
}
case Builtin::BI__builtin_readcyclecounter:
case Builtin::BI__builtin_readsteadycounter:
- case Builtin::BI__builtin___clear_cache:
return errorBuiltinNYI(*this, e, builtinID);
+ case Builtin::BI__builtin___clear_cache: {
+ mlir::Type voidTy = cir::VoidType::get(&getMLIRContext());
+ mlir::Value begin =
+ builder.createPtrBitcast(emitScalarExpr(e->getArg(0)), voidTy);
+ mlir::Value end =
+ builder.createPtrBitcast(emitScalarExpr(e->getArg(1)), voidTy);
+ cir::ClearCacheOp::create(builder, getLoc(e->getSourceRange()), begin, end);
+ return RValue::get(nullptr);
+ }
case Builtin::BI__builtin_trap:
emitTrap(loc, /*createNewBlock=*/true);
return RValue::getIgnored();
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 4877508b1c3da..d1b6f2a0cd7f7 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -1713,6 +1713,19 @@ mlir::LogicalResult CIRToLLVMFrameAddrOpLowering::matchAndRewrite(
return mlir::success();
}
+mlir::LogicalResult CIRToLLVMClearCacheOpLowering::matchAndRewrite(
+ cir::ClearCacheOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ auto begin = adaptor.getBegin();
+ auto end = adaptor.getEnd();
+ auto intrinNameAttr =
+ mlir::StringAttr::get(op.getContext(), "llvm.clear_cache");
+ rewriter.replaceOpWithNewOp<mlir::LLVM::CallIntrinsicOp>(
+ op, mlir::Type{}, intrinNameAttr, mlir::ValueRange{begin, end});
+
+ return mlir::success();
+}
+
mlir::LogicalResult CIRToLLVMAddrOfReturnAddrOpLowering::matchAndRewrite(
cir::AddrOfReturnAddrOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
diff --git a/clang/test/CIR/CodeGen/clear-cache.c b/clang/test/CIR/CodeGen/clear-cache.c
new file mode 100644
index 0000000000000..2dd84a98a4583
--- /dev/null
+++ b/clang/test/CIR/CodeGen/clear-cache.c
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
+
+char buffer[32] = "This is a largely unused buffer";
+
+// __builtin___clear_cache always maps to @llvm.clear_cache, but what
+// each back-end produces is different, and this is tested in LLVM
+
+// CIR-LABEL: main
+// CIR: %[[VAL_1:.*]] = cir.get_global @buffer : !cir.ptr<!cir.array<!s8i x 32>>
+// CIR: %[[VAL_2:.*]] = cir.cast array_to_ptrdecay %[[VAL_1]] : !cir.ptr<!cir.array<!s8i x 32>> -> !cir.ptr<!s8i>
+// CIR: %[[VAL_3:.*]] = cir.cast bitcast %[[VAL_2]] : !cir.ptr<!s8i> -> !cir.ptr<!void>
+// CIR: %[[VAL_4:.*]] = cir.get_global @buffer : !cir.ptr<!cir.array<!s8i x 32>>
+// CIR: %[[VAL_5:.*]] = cir.cast array_to_ptrdecay %[[VAL_4]] : !cir.ptr<!cir.array<!s8i x 32>>
+// CIR: %[[VAL_6:.*]] = cir.const #cir.int<32> : !s32i
+// CIR: %[[VAL_7:.*]] = cir.ptr_stride %[[VAL_5]], %[[VAL_6]] : (!cir.ptr<!s8i>, !s32i) -> !cir.ptr<!s8i>
+// CIR: %[[VAL_8:.*]] = cir.cast bitcast %[[VAL_7]] : !cir.ptr<!s8i> -> !cir.ptr<!void>
+// CIR: cir.clear_cache %[[VAL_3]] : !cir.ptr<!void>, %[[VAL_8]]
+
+// LLVM-LABEL: main
+// LLVM: call void @llvm.clear_cache(ptr @buffer, ptr getelementptr inbounds nuw (i8, ptr @buffer, i64 32))
+
+// OGCG-LABEL: main
+// OGCG: call void @llvm.clear_cache(ptr @buffer, ptr getelementptr inbounds (i8, ptr @buffer, i64 32))
+
+int main(void) {
+ __builtin___clear_cache(buffer, buffer+32);
+ return 0;
+}
>From 3f5447f5e867c48cc195054c23b072854853921d Mon Sep 17 00:00:00 2001
From: bruteforceboy <ogbonnachibuoyim12 at gmail.com>
Date: Tue, 27 Jan 2026 18:45:42 +0100
Subject: [PATCH 2/6] format nit
---
clang/test/CIR/CodeGen/clear-cache.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/test/CIR/CodeGen/clear-cache.c b/clang/test/CIR/CodeGen/clear-cache.c
index 2dd84a98a4583..2443fdb40f774 100644
--- a/clang/test/CIR/CodeGen/clear-cache.c
+++ b/clang/test/CIR/CodeGen/clear-cache.c
@@ -28,6 +28,6 @@ char buffer[32] = "This is a largely unused buffer";
// OGCG: call void @llvm.clear_cache(ptr @buffer, ptr getelementptr inbounds (i8, ptr @buffer, i64 32))
int main(void) {
- __builtin___clear_cache(buffer, buffer+32);
+ __builtin___clear_cache(buffer, buffer + 32);
return 0;
}
>From c17faed23209c02f9200a1c9881f197a3af13b25 Mon Sep 17 00:00:00 2001
From: bruteforceboy <ogbonnachibuoyim12 at gmail.com>
Date: Tue, 27 Jan 2026 20:06:15 +0100
Subject: [PATCH 3/6] apply review suggestions
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 15 +++++++++++----
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 4 ++--
.../lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 4 ++--
clang/test/CIR/CodeGen/clear-cache.c | 2 +-
4 files changed, 16 insertions(+), 9 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index c310372844af7..7917008190ebc 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -5118,15 +5118,22 @@ def CIR_PrefetchOp : CIR_Op<"prefetch"> {
def CIR_ClearCacheOp : CIR_Op<"clear_cache", [
AllTypesMatch<["begin", "end"]>
]> {
- let summary = "clear cache operation";
+ let summary = "Clear the processor's instruction cache if required.";
let description = [{
- CIR representation for `__builtin___clear_cache`.
+ The `cir.clear_cache` operation provides a representation for the `__builtin__clear_cache` builtin and corresponds to the `llvm.clear_cache` intrinsic in LLVM IR.
+
+ This operation ensures visibility of modifications in the specified range to the execution unit of the processor. On targets with non-unified instruction and data cache, the implementation flushes the instruction cache.
+
+ On platforms with coherent instruction and data caches (e.g., x86), this intrinsic is a nop. On platforms with non-coherent instruction and data cache (e.g., ARM, MIPS), the operation will be lowered either to appropriate instructions or a system call, if cache flushing requires special privileges.
+
+ The default behavior is to emit a call to `__clear_cache` from the runtime library.
+
+ This operation does not empty the instruction pipeline. Modifications of the current function are outside the scope of the operation.
}];
let arguments = (ins CIR_VoidPtrType:$begin, CIR_VoidPtrType:$end);
let assemblyFormat = [{
- $begin `:` qualified(type($begin)) `,`
- $end `,`
+ $begin `,` $end `:` qualified(type($begin))
attr-dict
}];
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index c505359271c49..37c6742471438 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -1084,9 +1084,9 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
case Builtin::BI__builtin___clear_cache: {
mlir::Type voidTy = cir::VoidType::get(&getMLIRContext());
mlir::Value begin =
- builder.createPtrBitcast(emitScalarExpr(e->getArg(0)), voidTy);
+ builder.createPtrBitcast(emitScalarExpr(e->getArg(0)), cgm.voidTy);
mlir::Value end =
- builder.createPtrBitcast(emitScalarExpr(e->getArg(1)), voidTy);
+ builder.createPtrBitcast(emitScalarExpr(e->getArg(1)), cgm.voidTy);
cir::ClearCacheOp::create(builder, getLoc(e->getSourceRange()), begin, end);
return RValue::get(nullptr);
}
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index d1b6f2a0cd7f7..f5377d0fec92b 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -1716,8 +1716,8 @@ mlir::LogicalResult CIRToLLVMFrameAddrOpLowering::matchAndRewrite(
mlir::LogicalResult CIRToLLVMClearCacheOpLowering::matchAndRewrite(
cir::ClearCacheOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
- auto begin = adaptor.getBegin();
- auto end = adaptor.getEnd();
+ mlir::Value begin = adaptor.getBegin();
+ mlir::Value end = adaptor.getEnd();
auto intrinNameAttr =
mlir::StringAttr::get(op.getContext(), "llvm.clear_cache");
rewriter.replaceOpWithNewOp<mlir::LLVM::CallIntrinsicOp>(
diff --git a/clang/test/CIR/CodeGen/clear-cache.c b/clang/test/CIR/CodeGen/clear-cache.c
index 2443fdb40f774..96254be9ea7f2 100644
--- a/clang/test/CIR/CodeGen/clear-cache.c
+++ b/clang/test/CIR/CodeGen/clear-cache.c
@@ -19,7 +19,7 @@ char buffer[32] = "This is a largely unused buffer";
// CIR: %[[VAL_6:.*]] = cir.const #cir.int<32> : !s32i
// CIR: %[[VAL_7:.*]] = cir.ptr_stride %[[VAL_5]], %[[VAL_6]] : (!cir.ptr<!s8i>, !s32i) -> !cir.ptr<!s8i>
// CIR: %[[VAL_8:.*]] = cir.cast bitcast %[[VAL_7]] : !cir.ptr<!s8i> -> !cir.ptr<!void>
-// CIR: cir.clear_cache %[[VAL_3]] : !cir.ptr<!void>, %[[VAL_8]]
+// CIR: cir.clear_cache %[[VAL_3]], %[[VAL_8]] : !cir.ptr<!void>
// LLVM-LABEL: main
// LLVM: call void @llvm.clear_cache(ptr @buffer, ptr getelementptr inbounds nuw (i8, ptr @buffer, i64 32))
>From c22d8d799f3d6596baf564a3410cd6fbe7cef8f9 Mon Sep 17 00:00:00 2001
From: bruteforceboy <ogbonnachibuoyim12 at gmail.com>
Date: Wed, 28 Jan 2026 00:06:15 +0100
Subject: [PATCH 4/6] remove unused variable
---
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 37c6742471438..19286d125f2eb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -1082,7 +1082,6 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
case Builtin::BI__builtin_readsteadycounter:
return errorBuiltinNYI(*this, e, builtinID);
case Builtin::BI__builtin___clear_cache: {
- mlir::Type voidTy = cir::VoidType::get(&getMLIRContext());
mlir::Value begin =
builder.createPtrBitcast(emitScalarExpr(e->getArg(0)), cgm.voidTy);
mlir::Value end =
>From b3e704b70f5860fdcab45111e4eb3d4633949b37 Mon Sep 17 00:00:00 2001
From: bruteforceboy <ogbonnachibuoyim12 at gmail.com>
Date: Wed, 28 Jan 2026 16:59:17 +0100
Subject: [PATCH 5/6] wrap description
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 29 ++++++++++++++------
1 file changed, 20 insertions(+), 9 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 7917008190ebc..4b8c1b0c8207f 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -5120,15 +5120,26 @@ def CIR_ClearCacheOp : CIR_Op<"clear_cache", [
]> {
let summary = "Clear the processor's instruction cache if required.";
let description = [{
- The `cir.clear_cache` operation provides a representation for the `__builtin__clear_cache` builtin and corresponds to the `llvm.clear_cache` intrinsic in LLVM IR.
-
- This operation ensures visibility of modifications in the specified range to the execution unit of the processor. On targets with non-unified instruction and data cache, the implementation flushes the instruction cache.
-
- On platforms with coherent instruction and data caches (e.g., x86), this intrinsic is a nop. On platforms with non-coherent instruction and data cache (e.g., ARM, MIPS), the operation will be lowered either to appropriate instructions or a system call, if cache flushing requires special privileges.
-
- The default behavior is to emit a call to `__clear_cache` from the runtime library.
-
- This operation does not empty the instruction pipeline. Modifications of the current function are outside the scope of the operation.
+ The `cir.clear_cache` operation provides a representation for the
+ `__builtin__clear_cache` builtin and corresponds to the
+ `llvm.clear_cache` intrinsic in LLVM IR.
+
+ This operation ensures visibility of modifications in the specified
+ range to the execution unit of the processor. On targets with
+ non-unified instruction and data cache, the implementation flushes
+ the instruction cache.
+
+ On platforms with coherent instruction and data caches (e.g., x86),
+ this intrinsic is a nop. On platforms with non-coherent instruction
+ and data cache (e.g., ARM, MIPS), the operation will be lowered
+ either to appropriate instructions or a system call, if cache
+ flushing requires special privileges.
+
+ The default behavior is to emit a call to `__clear_cache` from the
+ runtime library.
+
+ This operation does not empty the instruction pipeline. Modifications
+ of the current function are outside the scope of the operation.
}];
let arguments = (ins CIR_VoidPtrType:$begin, CIR_VoidPtrType:$end);
>From 1ae561708b457a4cf3e15691e78d26c9843758c0 Mon Sep 17 00:00:00 2001
From: "Chibuoyim (Wilson) Ogbonna" <ogbonnachibuoyim12 at gmail.com>
Date: Wed, 28 Jan 2026 18:12:08 +0100
Subject: [PATCH 6/6] Apply suggestion from @andykaylor
Co-authored-by: Andy Kaylor <akaylor at nvidia.com>
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 1 -
1 file changed, 1 deletion(-)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 4b8c1b0c8207f..e4c0f479eb4f9 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -5142,7 +5142,6 @@ def CIR_ClearCacheOp : CIR_Op<"clear_cache", [
of the current function are outside the scope of the operation.
}];
- let arguments = (ins CIR_VoidPtrType:$begin, CIR_VoidPtrType:$end);
let assemblyFormat = [{
$begin `,` $end `:` qualified(type($begin))
attr-dict
More information about the cfe-commits
mailing list