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

Reply via email to