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
