https://github.com/bruteforceboy updated https://github.com/llvm/llvm-project/pull/178260
>From de1e6d1d08d5dd6d3346e7399abd728594e73f0a Mon Sep 17 00:00:00 2001 From: bruteforceboy <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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" <[email protected]> Date: Wed, 28 Jan 2026 18:12:08 +0100 Subject: [PATCH 6/6] Apply suggestion from @andykaylor Co-authored-by: Andy Kaylor <[email protected]> --- 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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
