https://github.com/adams381 created https://github.com/llvm/llvm-project/pull/197262
`__builtin_assume_dereferenceable` was reported NYI by ClangIR. It's heavily exercised by libcxx via `__memory/valid_range.h`, so most of `<algorithm>` currently fails to compile under `-fclangir`. Adds a `cir.assume_dereferenceable` op (pointer + pointer-sized integer), emits it from CIRGenBuiltin, and lowers it to ``` call void @llvm.assume(i1 true) [ "dereferenceable"(ptr, i64) ] ``` which is what classic Clang's `Builder.CreateDereferenceableAssumption` produces. The size operand is widened/narrowed to `intptr_t` at the CIRGen layer to match classic CodeGen's `IntPtrTy` conversion. Tests cover the dynamic-size, constant-size, and narrow-integer-size cases against CIR, CIR->LLVM, and OGCG. >From c0a6baf7745fafc4372c5634595a7825a8a6b436 Mon Sep 17 00:00:00 2001 From: Adam Smith <[email protected]> Date: Tue, 12 May 2026 10:55:09 -0700 Subject: [PATCH] [CIR] Lower __builtin_assume_dereferenceable Adds a new cir.assume_dereferenceable op modeled on cir.assume_aligned and cir.assume_separate_storage, wires up __builtin_assume_dereferenceable in CIRGenBuiltin, and lowers the op to llvm.assume with a "dereferenceable"(ptr, size) operand bundle so it matches what classic Clang emits. The size operand is widened/narrowed to intptr_t at the CIRGen layer to match classic CodeGen's IntPtrTy conversion. This was the #4 libcxx-with-CIR blocker in our post-#197068+#197085 baseline (42 hits in libcxx std/, exposed via __memory/valid_range.h:64 from most of <algorithm>). --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 29 +++++++++++ clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 12 +++++ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 12 +++++ .../test/CIR/CodeGenBuiltins/builtin-call.cpp | 48 +++++++++++++++++++ 4 files changed, 101 insertions(+) diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 9d9aaec1b275a..42d4cb74f81d8 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -6230,6 +6230,35 @@ def CIR_AssumeSepStorageOp : CIR_Op<"assume_separate_storage", [ }]; } +def CIR_AssumeDereferenceableOp : CIR_Op<"assume_dereferenceable"> { + let summary = "Tell the optimizer that a pointer is dereferenceable"; + let description = [{ + The `cir.assume_dereferenceable` operation takes a pointer and a size in + bytes. It tells the optimizer that loading from `pointer` is well-defined + for any number of bytes up to `size`, i.e. the pointer is known to refer + to a region of at least `size` allocated bytes. + + The size argument must be a pointer-sized integer (the lowering converts + smaller integer types to the target pointer width before emitting the + operand bundle). + + This operation corresponds to the `__builtin_assume_dereferenceable` + builtin function. + + Example: + + ``` + cir.assume_dereferenceable %p, %n : !cir.ptr<!void>, !u64i + ``` + }]; + + let arguments = (ins CIR_PointerType:$pointer, CIR_IntType:$size); + + let assemblyFormat = [{ + $pointer `,` $size `:` qualified(type($pointer)) `,` type($size) attr-dict + }]; +} + //===----------------------------------------------------------------------===// // Branch Probability Operations //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp index 868bca404949b..c0c135b202293 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -1098,6 +1098,18 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID, return RValue::get(nullptr); } + case Builtin::BI__builtin_assume_dereferenceable: { + mlir::Value ptrValue = emitScalarExpr(e->getArg(0)); + mlir::Value sizeValue = emitScalarExpr(e->getArg(1)); + // The operand bundle expects a pointer-sized integer; widen/narrow to + // intptr_t to match classic CodeGen. + mlir::Type intPtrTy = convertType(getContext().getIntPtrType()); + if (sizeValue.getType() != intPtrTy) + sizeValue = builder.createIntCast(sizeValue, intPtrTy); + cir::AssumeDereferenceableOp::create(builder, loc, ptrValue, sizeValue); + return RValue::get(nullptr); + } + case Builtin::BI__builtin_assume_aligned: { const Expr *ptrExpr = e->getArg(0); mlir::Value ptrValue = emitScalarExpr(ptrExpr); diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index dca079eff1752..e1a01485445b2 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -859,6 +859,18 @@ mlir::LogicalResult CIRToLLVMAssumeSepStorageOpLowering::matchAndRewrite( return mlir::success(); } +mlir::LogicalResult CIRToLLVMAssumeDereferenceableOpLowering::matchAndRewrite( + cir::AssumeDereferenceableOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + auto cond = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(), + rewriter.getI1Type(), 1); + mlir::LLVM::AssumeOp::create( + rewriter, op.getLoc(), cond, "dereferenceable", + mlir::ValueRange{adaptor.getPointer(), adaptor.getSize()}); + rewriter.eraseOp(op); + return mlir::success(); +} + static mlir::LLVM::AtomicOrdering getLLVMMemOrder(std::optional<cir::MemOrder> memorder) { if (!memorder) diff --git a/clang/test/CIR/CodeGenBuiltins/builtin-call.cpp b/clang/test/CIR/CodeGenBuiltins/builtin-call.cpp index 86a96be8b21c3..8b83531e1384e 100644 --- a/clang/test/CIR/CodeGenBuiltins/builtin-call.cpp +++ b/clang/test/CIR/CodeGenBuiltins/builtin-call.cpp @@ -160,6 +160,54 @@ void assume_separate_storage(void *p1, void *p2) { // OGCG: call void @llvm.assume(i1 true) [ "separate_storage"(ptr %{{.+}}, ptr %{{.+}}) ] // OGCG: } +void assume_dereferenceable(void *p, unsigned long n) { + __builtin_assume_dereferenceable(p, n); +} + +// CIR: cir.func{{.*}} @_Z22assume_dereferenceablePvm +// CIR: cir.assume_dereferenceable %{{.+}}, %{{.+}} : !cir.ptr<!void>, !s64i +// CIR: } + +// LLVM: define {{.*}}void @_Z22assume_dereferenceablePvm +// LLVM: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %{{.+}}, i64 %{{.+}}) ] +// LLVM: } + +// OGCG: define {{.*}}void @_Z22assume_dereferenceablePvm +// OGCG: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %{{.+}}, i64 %{{.+}}) ] +// OGCG: } + +void assume_dereferenceable_const(void *p) { + __builtin_assume_dereferenceable(p, 16); +} + +// CIR: cir.func{{.*}} @_Z28assume_dereferenceable_constPv +// CIR: cir.assume_dereferenceable %{{.+}}, %{{.+}} : !cir.ptr<!void>, !s64i +// CIR: } + +// LLVM: define {{.*}}void @_Z28assume_dereferenceable_constPv +// LLVM: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %{{.+}}, i64 16) ] +// LLVM: } + +// OGCG: define {{.*}}void @_Z28assume_dereferenceable_constPv +// OGCG: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %{{.+}}, i64 16) ] +// OGCG: } + +void assume_dereferenceable_narrow_size(void *p, unsigned n) { + __builtin_assume_dereferenceable(p, n); +} + +// CIR: cir.func{{.*}} @_Z34assume_dereferenceable_narrow_sizePvj +// CIR: cir.assume_dereferenceable %{{.+}}, %{{.+}} : !cir.ptr<!void>, !s64i +// CIR: } + +// LLVM: define {{.*}}void @_Z34assume_dereferenceable_narrow_sizePvj +// LLVM: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %{{.+}}, i64 %{{.+}}) ] +// LLVM: } + +// OGCG: define {{.*}}void @_Z34assume_dereferenceable_narrow_sizePvj +// OGCG: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %{{.+}}, i64 %{{.+}}) ] +// OGCG: } + void expect(int x, int y) { __builtin_expect(x, y); } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
