Skip to content

Commit 79acb05

Browse files
authored
[CIR] CIRGen for bit cast builtin (#762)
This PR adds CIRGen support for the `__builtin_bit_cast` builtin. No new operations are added so the LLVM IR lowering is also added automatically.
1 parent 1674254 commit 79acb05

File tree

3 files changed

+179
-4
lines changed

3 files changed

+179
-4
lines changed

clang/lib/CIR/CodeGen/CIRGenExprAgg.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,30 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) {
907907
if (const auto *ECE = dyn_cast<ExplicitCastExpr>(E))
908908
CGF.CGM.buildExplicitCastExprType(ECE, &CGF);
909909
switch (E->getCastKind()) {
910+
case CK_LValueToRValueBitCast: {
911+
if (Dest.isIgnored()) {
912+
CGF.buildAnyExpr(E->getSubExpr(), AggValueSlot::ignored(),
913+
/*ignoreResult=*/true);
914+
break;
915+
}
916+
917+
LValue SourceLV = CGF.buildLValue(E->getSubExpr());
918+
Address SourceAddress = SourceLV.getAddress();
919+
Address DestAddress = Dest.getAddress();
920+
921+
auto Loc = CGF.getLoc(E->getExprLoc());
922+
mlir::Value SrcPtr = CGF.getBuilder().createBitcast(
923+
Loc, SourceAddress.getPointer(), CGF.VoidPtrTy);
924+
mlir::Value DstPtr = CGF.getBuilder().createBitcast(
925+
Loc, DestAddress.getPointer(), CGF.VoidPtrTy);
926+
927+
mlir::Value SizeVal = CGF.getBuilder().getConstInt(
928+
Loc, CGF.SizeTy,
929+
CGF.getContext().getTypeSizeInChars(E->getType()).getQuantity());
930+
CGF.getBuilder().createMemCpy(Loc, DstPtr, SrcPtr, SizeVal);
931+
932+
break;
933+
}
910934

911935
case CK_LValueToRValue:
912936
// If we're loading from a volatile type, force the destination

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1548,11 +1548,26 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
15481548
llvm_unreachable("builtin functions are handled elsewhere");
15491549

15501550
case CK_LValueBitCast:
1551-
llvm_unreachable("NYI");
15521551
case CK_ObjCObjectLValueCast:
1553-
llvm_unreachable("NYI");
1554-
case CK_LValueToRValueBitCast:
1555-
llvm_unreachable("NYI");
1552+
case CK_LValueToRValueBitCast: {
1553+
LValue SourceLVal = CGF.buildLValue(E);
1554+
Address SourceAddr = SourceLVal.getAddress();
1555+
1556+
mlir::Type DestElemTy = CGF.convertTypeForMem(DestTy);
1557+
mlir::Type DestPtrTy = CGF.getBuilder().getPointerTo(DestElemTy);
1558+
mlir::Value DestPtr = CGF.getBuilder().createBitcast(
1559+
CGF.getLoc(E->getExprLoc()), SourceAddr.getPointer(), DestPtrTy);
1560+
1561+
Address DestAddr =
1562+
SourceAddr.withPointer(DestPtr).withElementType(DestElemTy);
1563+
LValue DestLVal = CGF.makeAddrLValue(DestAddr, DestTy);
1564+
1565+
if (Kind == CK_LValueToRValueBitCast)
1566+
assert(!MissingFeatures::tbaa());
1567+
1568+
return buildLoadOfLValue(DestLVal, CE->getExprLoc());
1569+
}
1570+
15561571
case CK_CPointerToObjCPointerCast:
15571572
case CK_BlockPointerToObjCPointerCast:
15581573
case CK_AnyPointerToBlockPointerCast:
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir --check-prefix=CIR %s
3+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir -emit-llvm %s -o %t.ll
4+
// RUN: FileCheck --input-file=%t.ll --check-prefix=LLVM %s
5+
6+
float test_scalar(int &oper) {
7+
return __builtin_bit_cast(float, oper);
8+
}
9+
10+
// CIR-LABEL: cir.func @_Z11test_scalarRi
11+
// CIR: %[[#SRC_PTR:]] = cir.load %{{.+}} : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i>
12+
// CIR-NEXT: %[[#DST_PTR:]] = cir.cast(bitcast, %[[#SRC_PTR]] : !cir.ptr<!s32i>), !cir.ptr<!cir.float>
13+
// CIR-NEXT: %{{.+}} = cir.load %[[#DST_PTR]] : !cir.ptr<!cir.float>, !cir.float
14+
// CIR: }
15+
16+
// LLVM-LABEL: define dso_local float @_Z11test_scalarRi
17+
// LLVM: %[[#PTR:]] = load ptr, ptr %{{.+}}, align 8
18+
// LLVM-NEXT: %{{.+}} = load float, ptr %[[#PTR]], align 4
19+
// LLVM: }
20+
21+
struct two_ints {
22+
int x;
23+
int y;
24+
};
25+
26+
unsigned long test_aggregate_to_scalar(two_ints &ti) {
27+
return __builtin_bit_cast(unsigned long, ti);
28+
}
29+
30+
// CIR-LABEL: cir.func @_Z24test_aggregate_to_scalarR8two_ints
31+
// CIR: %[[#SRC_PTR:]] = cir.load %{{.+}} : !cir.ptr<!cir.ptr<!ty_22two_ints22>>, !cir.ptr<!ty_22two_ints22>
32+
// CIR-NEXT: %[[#DST_PTR:]] = cir.cast(bitcast, %[[#SRC_PTR]] : !cir.ptr<!ty_22two_ints22>), !cir.ptr<!u64i>
33+
// CIR-NEXT: %{{.+}} = cir.load %[[#DST_PTR]] : !cir.ptr<!u64i>, !u64i
34+
// CIR: }
35+
36+
// LLVM-LABEL: define dso_local i64 @_Z24test_aggregate_to_scalarR8two_ints
37+
// LLVM: %[[#PTR:]] = load ptr, ptr %{{.+}}, align 8
38+
// LLVM-NEXT: %{{.+}} = load i64, ptr %[[#PTR]], align 8
39+
// LLVM: }
40+
41+
struct two_floats {
42+
float x;
43+
float y;
44+
};
45+
46+
two_floats test_aggregate_record(two_ints& ti) {
47+
return __builtin_bit_cast(two_floats, ti);
48+
}
49+
50+
// CIR-LABEL: cir.func @_Z21test_aggregate_recordR8two_ints
51+
// CIR: %[[#SRC_PTR:]] = cir.load %{{.+}} : !cir.ptr<!cir.ptr<!ty_22two_ints22>>, !cir.ptr<!ty_22two_ints22>
52+
// CIR-NEXT: %[[#SRC_VOID_PTR:]] = cir.cast(bitcast, %[[#SRC_PTR]] : !cir.ptr<!ty_22two_ints22>), !cir.ptr<!void>
53+
// CIR-NEXT: %[[#DST_VOID_PTR:]] = cir.cast(bitcast, %{{.+}} : !cir.ptr<!ty_22two_floats22>), !cir.ptr<!void>
54+
// CIR-NEXT: %[[#SIZE:]] = cir.const #cir.int<8> : !u64i
55+
// CIR-NEXT: cir.libc.memcpy %[[#SIZE]] bytes from %[[#SRC_VOID_PTR]] to %[[#DST_VOID_PTR]] : !u64i, !cir.ptr<!void> -> !cir.ptr<!void>
56+
// CIR: }
57+
58+
// LLVM-LABEL: define dso_local %struct.two_floats @_Z21test_aggregate_recordR8two_ints
59+
// LLVM: %[[#DST_SLOT:]] = alloca %struct.two_floats, i64 1, align 4
60+
// LLVM: %[[#SRC_PTR:]] = load ptr, ptr %2, align 8
61+
// LLVM-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr %[[#DST_SLOT]], ptr %[[#SRC_PTR]], i64 8, i1 false)
62+
// LLVM-NEXT: %{{.+}} = load %struct.two_floats, ptr %[[#DST_SLOT]], align 4
63+
// LLVM: }
64+
65+
two_floats test_aggregate_array(int (&ary)[2]) {
66+
return __builtin_bit_cast(two_floats, ary);
67+
}
68+
69+
// CIR-LABEL: cir.func @_Z20test_aggregate_arrayRA2_i
70+
// CIR: %[[#SRC_PTR:]] = cir.load %{{.+}} : !cir.ptr<!cir.ptr<!cir.array<!s32i x 2>>>, !cir.ptr<!cir.array<!s32i x 2>>
71+
// CIR-NEXT: %[[#SRC_VOID_PTR:]] = cir.cast(bitcast, %[[#SRC_PTR]] : !cir.ptr<!cir.array<!s32i x 2>>), !cir.ptr<!void>
72+
// CIR-NEXT: %[[#DST_VOID_PTR:]] = cir.cast(bitcast, %{{.+}} : !cir.ptr<!ty_22two_floats22>), !cir.ptr<!void>
73+
// CIR-NEXT: %[[#SIZE:]] = cir.const #cir.int<8> : !u64i
74+
// CIR-NEXT: cir.libc.memcpy %[[#SIZE]] bytes from %[[#SRC_VOID_PTR]] to %[[#DST_VOID_PTR]] : !u64i, !cir.ptr<!void> -> !cir.ptr<!void>
75+
// CIR: }
76+
77+
// LLVM-LABEL: define dso_local %struct.two_floats @_Z20test_aggregate_arrayRA2_i
78+
// LLVM: %[[#DST_SLOT:]] = alloca %struct.two_floats, i64 1, align 4
79+
// LLVM: %[[#SRC_PTR:]] = load ptr, ptr %2, align 8
80+
// LLVM-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr %[[#DST_SLOT]], ptr %[[#SRC_PTR]], i64 8, i1 false)
81+
// LLVM-NEXT: %{{.+}} = load %struct.two_floats, ptr %[[#DST_SLOT]], align 4
82+
// LLVM: }
83+
84+
two_ints test_scalar_to_aggregate(unsigned long ul) {
85+
return __builtin_bit_cast(two_ints, ul);
86+
}
87+
88+
// CIR-LABEL: cir.func @_Z24test_scalar_to_aggregatem
89+
// CIR: %[[#SRC_VOID_PTR:]] = cir.cast(bitcast, %{{.+}} : !cir.ptr<!u64i>), !cir.ptr<!void>
90+
// CIR-NEXT: %[[#DST_VOID_PTR:]] = cir.cast(bitcast, %{{.+}} : !cir.ptr<!ty_22two_ints22>), !cir.ptr<!void>
91+
// CIR-NEXT: %[[#SIZE:]] = cir.const #cir.int<8> : !u64i
92+
// CIR-NEXT: cir.libc.memcpy %[[#SIZE]] bytes from %[[#SRC_VOID_PTR]] to %[[#DST_VOID_PTR]] : !u64i, !cir.ptr<!void> -> !cir.ptr<!void>
93+
// CIR: }
94+
95+
// LLVM-LABEL: define dso_local %struct.two_ints @_Z24test_scalar_to_aggregatem
96+
// LLVM: %[[#DST_SLOT:]] = alloca %struct.two_ints, i64 1, align 4
97+
// LLVM: call void @llvm.memcpy.p0.p0.i64(ptr %[[#DST_SLOT]], ptr %{{.+}}, i64 8, i1 false)
98+
// LLVM-NEXT: %{{.+}} = load %struct.two_ints, ptr %[[#DST_SLOT]], align 4
99+
// LLVM: }
100+
101+
unsigned long test_array(int (&ary)[2]) {
102+
return __builtin_bit_cast(unsigned long, ary);
103+
}
104+
105+
// CIR-LABEL: cir.func @_Z10test_arrayRA2_i
106+
// CIR: %[[#SRC_PTR:]] = cir.load %{{.+}} : !cir.ptr<!cir.ptr<!cir.array<!s32i x 2>>>, !cir.ptr<!cir.array<!s32i x 2>>
107+
// CIR-NEXT: %[[#DST_PTR:]] = cir.cast(bitcast, %[[#SRC_PTR]] : !cir.ptr<!cir.array<!s32i x 2>>), !cir.ptr<!u64i>
108+
// CIR-NEXT: %{{.+}} = cir.load %[[#DST_PTR]] : !cir.ptr<!u64i>, !u64i
109+
// CIR: }
110+
111+
// LLVM-LABEL: define dso_local i64 @_Z10test_arrayRA2_i
112+
// LLVM: %[[#SRC_PTR:]] = load ptr, ptr %{{.+}}, align 8
113+
// LLVM-NEXT: %{{.+}} = load i64, ptr %[[#SRC_PTR]], align 8
114+
// LLVM: }
115+
116+
two_ints test_rvalue_aggregate() {
117+
return __builtin_bit_cast(two_ints, 42ul);
118+
}
119+
120+
// CIR-LABEL: cir.func @_Z21test_rvalue_aggregatev()
121+
// CIR: cir.scope {
122+
// CIR-NEXT: %[[#TMP_SLOT:]] = cir.alloca !u64i, !cir.ptr<!u64i>
123+
// CIR-NEXT: %[[#A:]] = cir.const #cir.int<42> : !u64i
124+
// CIR-NEXT: cir.store %[[#A]], %[[#TMP_SLOT]] : !u64i, !cir.ptr<!u64i>
125+
// CIR-NEXT: %[[#SRC_VOID_PTR:]] = cir.cast(bitcast, %[[#TMP_SLOT]] : !cir.ptr<!u64i>), !cir.ptr<!void>
126+
// CIR-NEXT: %[[#DST_VOID_PTR:]] = cir.cast(bitcast, %0 : !cir.ptr<!ty_22two_ints22>), !cir.ptr<!void>
127+
// CIR-NEXT: %[[#SIZE:]] = cir.const #cir.int<8> : !u64i
128+
// CIR-NEXT: cir.libc.memcpy %[[#SIZE]] bytes from %[[#SRC_VOID_PTR]] to %[[#DST_VOID_PTR]] : !u64i, !cir.ptr<!void> -> !cir.ptr<!void>
129+
// CIR-NEXT: }
130+
// CIR: }
131+
132+
// LLVM-LABEL: define dso_local %struct.two_ints @_Z21test_rvalue_aggregatev
133+
// LLVM: %[[#SRC_SLOT:]] = alloca i64, i64 1, align 8
134+
// LLVM-NEXT: store i64 42, ptr %[[#SRC_SLOT]], align 8
135+
// LLVM-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr %{{.+}}, ptr %[[#SRC_SLOT]], i64 8, i1 false)
136+
// LLVM: }

0 commit comments

Comments
 (0)