https://github.com/banach-space updated 
https://github.com/llvm/llvm-project/pull/176782

From 1a9b901f56fe4dd417d17a1de8b0496ff663bc78 Mon Sep 17 00:00:00 2001
From: Andrzej Warzynski <[email protected]>
Date: Mon, 19 Jan 2026 17:11:58 +0000
Subject: [PATCH 1/2] [Clang][CIR] Implement CIRGen logic for
 __builtin_bit_cast

NOTE: This patch merely upstreams code from
  * https://github.com/llvm/clangir.

This Op was originally implemented by Sirui Mu in #762 Further
modification were made by other ClangIR contributors.

co-authored-by: Sirui Mu <[email protected]>
---
 clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp |  24 ++++
 clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp    |  17 +++
 clang/test/CIR/CodeGen/builtin-bit-cast.cpp   | 135 ++++++++++++++++++
 3 files changed, 176 insertions(+)
 create mode 100644 clang/test/CIR/CodeGen/builtin-bit-cast.cpp

diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
index 11da95c996ea5..d65cc26b56f6e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
@@ -233,6 +233,30 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
   // Stubs -- These should be moved up when they are implemented.
   void VisitCastExpr(CastExpr *e) {
     switch (e->getCastKind()) {
+    case CK_LValueToRValueBitCast: {
+      if (dest.isIgnored()) {
+        cgf.emitAnyExpr(e->getSubExpr(), AggValueSlot::ignored(),
+                        /*ignoreResult=*/true);
+        break;
+      }
+
+      LValue sourceLV = cgf.emitLValue(e->getSubExpr());
+      Address sourceAddress = sourceLV.getAddress();
+      Address destAddress = dest.getAddress();
+
+      auto loc = cgf.getLoc(e->getExprLoc());
+      mlir::Value srcPtr = cgf.getBuilder().createBitcast(
+          loc, sourceAddress.getPointer(), cgf.voidPtrTy);
+      mlir::Value dstPtr = cgf.getBuilder().createBitcast(
+          loc, destAddress.getPointer(), cgf.voidPtrTy);
+
+      mlir::Value sizeVal = cgf.getBuilder().getConstInt(
+          loc, cgf.sizeTy,
+          cgf.getContext().getTypeSizeInChars(e->getType()).getQuantity());
+      cgf.getBuilder().createMemCpy(loc, dstPtr, srcPtr, sizeVal);
+
+      break;
+    }
     case CK_LValueToRValue:
       // If we're loading from a volatile type, force the destination
       // into existence.
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 72429591a11bb..17b08148614d5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -2072,6 +2072,23 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr 
*ce) {
     llvm_unreachable("dependent cast kind in CIR gen!");
   case clang::CK_BuiltinFnToFnPtr:
     llvm_unreachable("builtin functions are handled elsewhere");
+  case CK_LValueBitCast:
+  case CK_LValueToRValueBitCast: {
+    LValue sourceLVal = cgf.emitLValue(subExpr);
+    Address sourceAddr = sourceLVal.getAddress();
+
+    mlir::Type destElemTy = cgf.convertTypeForMem(destTy);
+    mlir::Type destPtrTy = cgf.getBuilder().getPointerTo(destElemTy);
+    mlir::Value destPtr = cgf.getBuilder().createBitcast(
+        cgf.getLoc(subExpr->getExprLoc()), sourceAddr.getPointer(), destPtrTy);
+
+    Address destAddr = Address(destPtr, destElemTy, sourceAddr.getAlignment(),
+                               sourceAddr.isKnownNonNull());
+    LValue destLVal = cgf.makeAddrLValue(destAddr, destTy);
+    // TOOD: Uncomment once TBAA is upstreamed
+    // destLVal.setTBAAInfo(TBAAAccessInfo::getMayAliasInfo());
+    return emitLoadOfLValue(destLVal, ce->getExprLoc());
+  }
 
   case CK_CPointerToObjCPointerCast:
   case CK_BlockPointerToObjCPointerCast:
diff --git a/clang/test/CIR/CodeGen/builtin-bit-cast.cpp 
b/clang/test/CIR/CodeGen/builtin-bit-cast.cpp
new file mode 100644
index 0000000000000..63e3db51c52cf
--- /dev/null
+++ b/clang/test/CIR/CodeGen/builtin-bit-cast.cpp
@@ -0,0 +1,135 @@
+// RUN: %clang_cc1 -std=c++20 -fclangir -triple aarch64 -emit-cir %s  -o - | 
FileCheck --check-prefix=CIR %s
+// RUN: %clang_cc1 -std=c++20 -fclangir -triple aarch64 -emit-llvm %s -o - | 
FileCheck --check-prefixes=LLVM,LLVM-VIA-CIR %s
+// RUN: %clang_cc1 -std=c++20           -triple aarch64 -emit-llvm %s -o - | 
FileCheck --check-prefixes=LLVM,LLVM-DIRECT %s
+
+//=============================================================================
+// NOTES
+//
+// Major differences between code lowered via ClangIR and directly to LLVM
+// (e.g. different return types) are captured by using LLVM-VIA-CIR and 
LLVM-DIRECT labels.
+//
+// Minor differences (e.g. presence of `noundef` attached to argumens, `align`
+// attribute attached to pointers), look for catch-alls like {{.*}}.
+//
+//=============================================================================
+
+float test_scalar(int &oper) {
+  return __builtin_bit_cast(float, oper);
+}
+
+// CIR-LABEL: cir.func {{.*}} @_Z11test_scalarRi
+//       CIR:   %[[#SRC_PTR:]] = cir.load{{.*}} %{{.+}} : 
!cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i>
+//  CIR-NEXT:   %[[#DST_PTR:]] = cir.cast bitcast %[[#SRC_PTR]] : 
!cir.ptr<!s32i> -> !cir.ptr<!cir.float>
+//  CIR-NEXT:   %{{.+}} = cir.load{{.*}} %[[#DST_PTR]] : !cir.ptr<!cir.float>, 
!cir.float
+
+// LLVM-LABEL: define dso_local{{.*}} float @_Z11test_scalarRi
+//       LLVM:   %[[#PTR:]] = load ptr, ptr %{{.+}}, align 8
+//  LLVM-NEXT:   %{{.+}} = load float, ptr %[[#PTR]], align 4
+
+struct two_ints {
+  int x;
+  int y;
+};
+
+unsigned long test_aggregate_to_scalar(two_ints &ti) {
+  return __builtin_bit_cast(unsigned long, ti);
+}
+
+// CIR-LABEL: cir.func {{.*}} @_Z24test_aggregate_to_scalarR8two_ints
+//       CIR:   %[[#SRC_PTR:]] = cir.load{{.*}} %{{.+}} : 
!cir.ptr<!cir.ptr<!rec_two_ints>>, !cir.ptr<!rec_two_ints>
+//  CIR-NEXT:   %[[#DST_PTR:]] = cir.cast bitcast %[[#SRC_PTR]] : 
!cir.ptr<!rec_two_ints> -> !cir.ptr<!u64i>
+//  CIR-NEXT:   %{{.+}} = cir.load{{.*}} %[[#DST_PTR]] : !cir.ptr<!u64i>, !u64i
+
+// LLVM-LABEL: define dso_local{{.*}} i64 
@_Z24test_aggregate_to_scalarR8two_ints
+//       LLVM:   %[[#PTR:]] = load ptr, ptr %{{.+}}, align 8
+//  LLVM-NEXT:   %{{.+}} = load i64, ptr %[[#PTR]], align 4
+
+struct two_floats {
+  float x;
+  float y;
+};
+
+two_floats test_aggregate_record(two_ints& ti) {
+   return __builtin_bit_cast(two_floats, ti);
+}
+
+// CIR-LABEL: cir.func {{.*}} @_Z21test_aggregate_recordR8two_ints
+//       CIR:   %[[#SRC_PTR:]] = cir.load{{.*}} %{{.+}} : 
!cir.ptr<!cir.ptr<!rec_two_ints>>, !cir.ptr<!rec_two_ints>
+//  CIR-NEXT:   %[[#SRC_VOID_PTR:]] = cir.cast bitcast %[[#SRC_PTR]] : 
!cir.ptr<!rec_two_ints> -> !cir.ptr<!void>
+//  CIR-NEXT:   %[[#DST_VOID_PTR:]] = cir.cast bitcast %{{.+}} : 
!cir.ptr<!rec_two_floats> -> !cir.ptr<!void>
+//  CIR-NEXT:   %[[#SIZE:]] = cir.const #cir.int<8> : !u64i
+//  CIR-NEXT:   cir.libc.memcpy %[[#SIZE]] bytes from %[[#SRC_VOID_PTR]] to 
%[[#DST_VOID_PTR]] : !u64i, !cir.ptr<!void> -> !cir.ptr<!void>
+
+// LLVM-LABEL: define dso_local{{.*}} %struct.two_floats 
@_Z21test_aggregate_recordR8two_ints
+//       LLVM:   %[[DST_SLOT:.*]] = alloca %struct.two_floats{{.*}}, align 4
+//       LLVM:   %[[SRC_PTR:.*]] = load ptr, ptr {{.*}}, align 8
+//  LLVM-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr{{.*}} %[[DST_SLOT]], 
ptr{{.*}} %[[SRC_PTR]], i64 8, i1 false)
+//  LLVM-NEXT:   %{{.+}} = load %struct.two_floats, ptr %[[DST_SLOT]], align 4
+
+two_floats test_aggregate_array(int (&ary)[2]) {
+  return __builtin_bit_cast(two_floats, ary);
+}
+
+// CIR-LABEL: cir.func {{.*}} @_Z20test_aggregate_arrayRA2_i
+//       CIR:   %[[#SRC_PTR:]] = cir.load{{.*}} %{{.+}} : 
!cir.ptr<!cir.ptr<!cir.array<!s32i x 2>>>, !cir.ptr<!cir.array<!s32i x 2>>
+//  CIR-NEXT:   %[[#SRC_VOID_PTR:]] = cir.cast bitcast %[[#SRC_PTR]] : 
!cir.ptr<!cir.array<!s32i x 2>> -> !cir.ptr<!void>
+//  CIR-NEXT:   %[[#DST_VOID_PTR:]] = cir.cast bitcast %{{.+}} : 
!cir.ptr<!rec_two_floats> -> !cir.ptr<!void>
+//  CIR-NEXT:   %[[#SIZE:]] = cir.const #cir.int<8> : !u64i
+//  CIR-NEXT:   cir.libc.memcpy %[[#SIZE]] bytes from %[[#SRC_VOID_PTR]] to 
%[[#DST_VOID_PTR]] : !u64i, !cir.ptr<!void> -> !cir.ptr<!void>
+
+// LLVM-LABEL: define dso_local{{.*}} %struct.two_floats 
@_Z20test_aggregate_arrayRA2_i
+//       LLVM:   %[[DST_SLOT:.*]] = alloca %struct.two_floats{{.*}}, align 4
+//       LLVM:   %[[SRC_PTR:.*]] = load ptr, ptr {{.*}}, align 8
+//  LLVM-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr{{.*}} %[[DST_SLOT]], 
ptr{{.*}} %[[SRC_PTR]], i64 8, i1 false)
+//  LLVM-NEXT:   %{{.+}} = load %struct.two_floats, ptr %[[DST_SLOT]], align 4
+
+two_ints test_scalar_to_aggregate(unsigned long ul) {
+  return __builtin_bit_cast(two_ints, ul);
+}
+
+// CIR-LABEL: cir.func {{.*}} @_Z24test_scalar_to_aggregatem
+//       CIR:   %[[#SRC_VOID_PTR:]] = cir.cast bitcast %{{.+}} : 
!cir.ptr<!u64i> -> !cir.ptr<!void>
+//  CIR-NEXT:   %[[#DST_VOID_PTR:]] = cir.cast bitcast %{{.+}} : 
!cir.ptr<!rec_two_ints> -> !cir.ptr<!void>
+//  CIR-NEXT:   %[[#SIZE:]] = cir.const #cir.int<8> : !u64i
+//  CIR-NEXT:   cir.libc.memcpy %[[#SIZE]] bytes from %[[#SRC_VOID_PTR]] to 
%[[#DST_VOID_PTR]] : !u64i, !cir.ptr<!void> -> !cir.ptr<!void>
+
+// LLVM-DIRECT-LABEL: define dso_local i64 @_Z24test_scalar_to_aggregatem
+// LLVM-VIA-CIR-LABEL: define dso_local %struct.two_ints 
@_Z24test_scalar_to_aggregatem
+//       LLVM:   %[[DST_SLOT:.*]] = alloca %struct.two_ints{{.*}}, align 4
+//       LLVM:   call void @llvm.memcpy.p0.p0.i64(ptr{{.*}} %[[DST_SLOT]], 
ptr{{.*}} %{{.+}}, i64 8, i1 false)
+//  LLVM-DIRECT-NEXT:   %{{.+}} = load i64, ptr %[[DST_SLOT]], align 4
+//  LLVM-VIA-CIR-NEXT:   %{{.+}} = load %struct.two_ints, ptr %[[DST_SLOT]], 
align 4
+
+unsigned long test_array(int (&ary)[2]) {
+  return __builtin_bit_cast(unsigned long, ary);
+}
+
+// CIR-LABEL: cir.func {{.*}} @_Z10test_arrayRA2_i
+//      CIR:   %[[#SRC_PTR:]] = cir.load{{.*}} %{{.+}} : 
!cir.ptr<!cir.ptr<!cir.array<!s32i x 2>>>, !cir.ptr<!cir.array<!s32i x 2>>
+// CIR-NEXT:   %[[#DST_PTR:]] = cir.cast bitcast %[[#SRC_PTR]] : 
!cir.ptr<!cir.array<!s32i x 2>> -> !cir.ptr<!u64i>
+// CIR-NEXT:   %{{.+}} = cir.load{{.*}} %[[#DST_PTR]] : !cir.ptr<!u64i>, !u64i
+
+// LLVM-LABEL: define dso_local{{.*}} i64 @_Z10test_arrayRA2_i
+//       LLVM:   %[[SRC_PTR:.*]] = load ptr, ptr %{{.+}}, align 8
+//  LLVM-NEXT:   %{{.+}} = load i64, ptr %[[SRC_PTR]], align 4
+
+two_ints test_rvalue_aggregate() {
+  return __builtin_bit_cast(two_ints, 42ul);
+}
+
+// CIR-LABEL: cir.func {{.*}} @_Z21test_rvalue_aggregatev()
+//       CIR:   cir.scope {
+//  CIR-NEXT:     %[[#TMP_SLOT:]] = cir.alloca !u64i, !cir.ptr<!u64i>
+//  CIR-NEXT:     %[[#A:]] = cir.const #cir.int<42> : !u64i
+//  CIR-NEXT:     cir.store{{.*}} %[[#A]], %[[#TMP_SLOT]] : !u64i, 
!cir.ptr<!u64i>
+//  CIR-NEXT:     %[[#SRC_VOID_PTR:]] = cir.cast bitcast %[[#TMP_SLOT]] : 
!cir.ptr<!u64i> -> !cir.ptr<!void>
+//  CIR-NEXT:     %[[#DST_VOID_PTR:]] = cir.cast bitcast %0 : 
!cir.ptr<!rec_two_ints> -> !cir.ptr<!void>
+//  CIR-NEXT:     %[[#SIZE:]] = cir.const #cir.int<8> : !u64i
+//  CIR-NEXT:     cir.libc.memcpy %[[#SIZE]] bytes from %[[#SRC_VOID_PTR]] to 
%[[#DST_VOID_PTR]] : !u64i, !cir.ptr<!void> -> !cir.ptr<!void>
+//  CIR-NEXT:   }
+
+// LLVM-DIRECT-LABEL: define dso_local{{.*}} i64 @_Z21test_rvalue_aggregatev
+// LLVM-VIA-CIR-LABEL: define dso_local{{.*}} %struct.two_ints 
@_Z21test_rvalue_aggregatev
+//  LLVM:   %[[SRC_SLOT:.*]] = alloca i64{{.*}}, align 8
+//  LLVM:   store i64 42, ptr %[[SRC_SLOT]], align 8
+//  LLVM-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr{{.*}} %{{.+}}, ptr{{.*}} 
%[[SRC_SLOT]], i64 8, i1 false)

From dcbe8f162c3d9b8f5eaf5f9c6e46df983469d8c0 Mon Sep 17 00:00:00 2001
From: Andrzej Warzynski <[email protected]>
Date: Wed, 21 Jan 2026 16:28:01 +0000
Subject: [PATCH 2/2] Address comments from Andy

---
 clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp | 15 +++++++--------
 clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp    | 10 ++--------
 clang/test/CIR/CodeGen/builtin-bit-cast.cpp   |  4 ++++
 3 files changed, 13 insertions(+), 16 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
index d65cc26b56f6e..f5f3655802915 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
@@ -241,19 +241,18 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> 
{
       }
 
       LValue sourceLV = cgf.emitLValue(e->getSubExpr());
-      Address sourceAddress = sourceLV.getAddress();
-      Address destAddress = dest.getAddress();
+      Address sourceAddress =
+          sourceLV.getAddress().withElementType(cgf.getBuilder(), cgf.voidTy);
+      Address destAddress =
+          dest.getAddress().withElementType(cgf.getBuilder(), cgf.voidTy);
 
-      auto loc = cgf.getLoc(e->getExprLoc());
-      mlir::Value srcPtr = cgf.getBuilder().createBitcast(
-          loc, sourceAddress.getPointer(), cgf.voidPtrTy);
-      mlir::Value dstPtr = cgf.getBuilder().createBitcast(
-          loc, destAddress.getPointer(), cgf.voidPtrTy);
+      mlir::Location loc = cgf.getLoc(e->getExprLoc());
 
       mlir::Value sizeVal = cgf.getBuilder().getConstInt(
           loc, cgf.sizeTy,
           cgf.getContext().getTypeSizeInChars(e->getType()).getQuantity());
-      cgf.getBuilder().createMemCpy(loc, dstPtr, srcPtr, sizeVal);
+      cgf.getBuilder().createMemCpy(loc, destAddress.getPointer(),
+                                    sourceAddress.getPointer(), sizeVal);
 
       break;
     }
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 17b08148614d5..2af210bed98ff 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -2078,15 +2078,9 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr 
*ce) {
     Address sourceAddr = sourceLVal.getAddress();
 
     mlir::Type destElemTy = cgf.convertTypeForMem(destTy);
-    mlir::Type destPtrTy = cgf.getBuilder().getPointerTo(destElemTy);
-    mlir::Value destPtr = cgf.getBuilder().createBitcast(
-        cgf.getLoc(subExpr->getExprLoc()), sourceAddr.getPointer(), destPtrTy);
-
-    Address destAddr = Address(destPtr, destElemTy, sourceAddr.getAlignment(),
-                               sourceAddr.isKnownNonNull());
+    Address destAddr = sourceAddr.withElementType(cgf.getBuilder(), 
destElemTy);
     LValue destLVal = cgf.makeAddrLValue(destAddr, destTy);
-    // TOOD: Uncomment once TBAA is upstreamed
-    // destLVal.setTBAAInfo(TBAAAccessInfo::getMayAliasInfo());
+    assert(!cir::MissingFeatures::opTBAA());
     return emitLoadOfLValue(destLVal, ce->getExprLoc());
   }
 
diff --git a/clang/test/CIR/CodeGen/builtin-bit-cast.cpp 
b/clang/test/CIR/CodeGen/builtin-bit-cast.cpp
index 63e3db51c52cf..aea2b1db17b84 100644
--- a/clang/test/CIR/CodeGen/builtin-bit-cast.cpp
+++ b/clang/test/CIR/CodeGen/builtin-bit-cast.cpp
@@ -128,6 +128,10 @@ two_ints test_rvalue_aggregate() {
 //  CIR-NEXT:     cir.libc.memcpy %[[#SIZE]] bytes from %[[#SRC_VOID_PTR]] to 
%[[#DST_VOID_PTR]] : !u64i, !cir.ptr<!void> -> !cir.ptr<!void>
 //  CIR-NEXT:   }
 
+/// FIXME: The function signature below should be identical for both lowering
+/// paths, but CIR is still missing calling convention lowering. Update this
+/// once calling convention is unstreamed.
+
 // LLVM-DIRECT-LABEL: define dso_local{{.*}} i64 @_Z21test_rvalue_aggregatev
 // LLVM-VIA-CIR-LABEL: define dso_local{{.*}} %struct.two_ints 
@_Z21test_rvalue_aggregatev
 //  LLVM:   %[[SRC_SLOT:.*]] = alloca i64{{.*}}, align 8

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to