[clang] [CIR] Handle FunctionToPointerDecay casts (#153657) (PR #154060)
Justin Riddell via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 20 19:00:06 PDT 2025
https://github.com/Arghnews updated https://github.com/llvm/llvm-project/pull/154060
>From c3ef9b7dbad67be0fa24b228dd445a4b8a28d280 Mon Sep 17 00:00:00 2001
From: Justin Riddell <arghnews at hotmail.co.uk>
Date: Thu, 21 Aug 2025 02:59:44 +0100
Subject: [PATCH] [CIR] Handle FunctionToPointerDecay casts (#153657)
Add upstream support for handling implicit FunctionToPointerDecay casts
in ClangIR
---
clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 97 ++++++++++++++++---
clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 2 +
.../CIR/CodeGen/function-to-pointer-decay.c | 47 +++++++++
3 files changed, 132 insertions(+), 14 deletions(-)
create mode 100644 clang/test/CIR/CodeGen/function-to-pointer-decay.c
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 057b518cb39c6..2b74b27100402 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -73,21 +73,54 @@ Address CIRGenFunction::emitPointerWithAlignment(const Expr *expr,
// Casts:
if (auto const *ce = dyn_cast<CastExpr>(expr)) {
- if (isa<ExplicitCastExpr>(ce)) {
- cgm.errorNYI(expr->getSourceRange(),
- "emitPointerWithAlignment: explicit cast");
- return Address::invalid();
- }
+ if (const auto *ece = dyn_cast<ExplicitCastExpr>(ce))
+ cgm.emitExplicitCastExprType(ece);
switch (ce->getCastKind()) {
// Non-converting casts (but not C's implicit conversion from void*).
case CK_BitCast:
case CK_NoOp:
case CK_AddressSpaceConversion: {
- cgm.errorNYI(expr->getSourceRange(),
- "emitPointerWithAlignment: noop cast");
- return Address::invalid();
- } break;
+ if (const auto *ptrTy =
+ ce->getSubExpr()->getType()->getAs<PointerType>()) {
+ if (ptrTy->getPointeeType()->isVoidType())
+ break;
+
+ LValueBaseInfo innerBaseInfo;
+ assert(!cir::MissingFeatures::opTBAA());
+ Address addr =
+ emitPointerWithAlignment(ce->getSubExpr(), &innerBaseInfo);
+ if (baseInfo)
+ *baseInfo = innerBaseInfo;
+
+ if (isa<ExplicitCastExpr>(ce)) {
+ LValueBaseInfo targetTypeBaseInfo;
+
+ const QualType pointeeType = expr->getType()->getPointeeType();
+ const CharUnits align =
+ cgm.getNaturalTypeAlignment(pointeeType, &targetTypeBaseInfo);
+
+ // If the source l-value is opaque, honor the alignment of the
+ // casted-to type.
+ if (innerBaseInfo.getAlignmentSource() != AlignmentSource::Decl) {
+ if (baseInfo)
+ baseInfo->mergeForCast(targetTypeBaseInfo);
+ addr = Address(addr.getPointer(), addr.getElementType(), align);
+ }
+ }
+
+ assert(!cir::MissingFeatures::sanitizers());
+
+ const mlir::Type eltTy =
+ convertTypeForMem(expr->getType()->getPointeeType());
+ addr = getBuilder().createElementBitCast(getLoc(expr->getSourceRange()),
+ addr, eltTy);
+ assert(!cir::MissingFeatures::addressSpace());
+
+ return addr;
+ }
+ break;
+ }
// Array-to-pointer decay. TODO(cir): BaseInfo and TBAAInfo.
case CK_ArrayToPointerDecay:
@@ -551,6 +584,37 @@ RValue CIRGenFunction::emitLoadOfLValue(LValue lv, SourceLocation loc) {
return RValue::get(nullptr);
}
+static cir::FuncOp emitFunctionDeclPointer(CIRGenModule &cgm, GlobalDecl gd) {
+ assert(!cir::MissingFeatures::weakRefReference());
+ return cgm.getAddrOfFunction(gd);
+}
+
+static LValue emitFunctionDeclLValue(CIRGenFunction &cgf, const Expr *e,
+ GlobalDecl gd) {
+ const FunctionDecl *fd = cast<FunctionDecl>(gd.getDecl());
+ cir::FuncOp funcOp = emitFunctionDeclPointer(cgf.cgm, gd);
+ mlir::Location loc = cgf.getLoc(e->getSourceRange());
+ CharUnits align = cgf.getContext().getDeclAlign(fd);
+
+ assert(!cir::MissingFeatures::sanitizers());
+
+ mlir::Type fnTy = funcOp.getFunctionType();
+ mlir::Type ptrTy = cir::PointerType::get(fnTy);
+ mlir::Value addr = cgf.getBuilder().create<cir::GetGlobalOp>(
+ loc, ptrTy, funcOp.getSymName());
+
+ if (funcOp.getFunctionType() != cgf.convertType(fd->getType())) {
+ fnTy = cgf.convertType(fd->getType());
+ ptrTy = cir::PointerType::get(fnTy);
+
+ addr = cir::CastOp::create(cgf.getBuilder(), addr.getLoc(), ptrTy,
+ cir::CastKind::bitcast, addr);
+ }
+
+ return cgf.makeAddrLValue(Address(addr, fnTy, align), e->getType(),
+ AlignmentSource::Decl);
+}
+
LValue CIRGenFunction::emitDeclRefLValue(const DeclRefExpr *e) {
const NamedDecl *nd = e->getDecl();
QualType ty = e->getType();
@@ -607,6 +671,16 @@ LValue CIRGenFunction::emitDeclRefLValue(const DeclRefExpr *e) {
return emitLValue(bd->getBinding());
}
+ if (const auto *fd = dyn_cast<FunctionDecl>(nd)) {
+ LValue lv = emitFunctionDeclLValue(*this, e, fd);
+
+ // Emit debuginfo for the function declaration if the target wants to.
+ if (getContext().getTargetInfo().allowDebugInfoForExternalRef())
+ assert(!cir::MissingFeatures::generateDebugInfo());
+
+ return lv;
+ }
+
cgm.errorNYI(e->getSourceRange(), "emitDeclRefLValue: unhandled decl type");
return LValue();
}
@@ -1401,11 +1475,6 @@ RValue CIRGenFunction::emitAnyExpr(const Expr *e, AggValueSlot aggSlot) {
llvm_unreachable("bad evaluation kind");
}
-static cir::FuncOp emitFunctionDeclPointer(CIRGenModule &cgm, GlobalDecl gd) {
- assert(!cir::MissingFeatures::weakRefReference());
- return cgm.getAddrOfFunction(gd);
-}
-
// Detect the unusual situation where an inline version is shadowed by a
// non-inline version. In that case we should pick the external one
// everywhere. That's GCC behavior too.
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index f6b2c88f2cfb4..46934e7155adf 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -1905,6 +1905,8 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) {
cgf.getLoc(subExpr->getSourceRange()), cgf.convertType(destTy),
Visit(subExpr));
}
+ case CK_FunctionToPointerDecay:
+ return cgf.emitLValue(subExpr).getPointer();
default:
cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(),
diff --git a/clang/test/CIR/CodeGen/function-to-pointer-decay.c b/clang/test/CIR/CodeGen/function-to-pointer-decay.c
new file mode 100644
index 0000000000000..507957a5a1a91
--- /dev/null
+++ b/clang/test/CIR/CodeGen/function-to-pointer-decay.c
@@ -0,0 +1,47 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+
+void f(void);
+
+void f1() {
+ (void (*)())f;
+}
+
+void f2() {
+ (*(void (*)(void))f)();
+}
+
+void test_lvalue_cast() {
+ (*(void (*)(int))f)(42);
+}
+
+// CIR-LABEL: cir.func{{.*}} @f()
+// CIR: cir.func{{.*}} @f1()
+// CIR: cir.return{{.*}}
+
+// CIR-LABEL: cir.func{{.*}} @f2()
+// CIR: cir.call @f() : () -> ()
+
+// CIR-LABEL: cir.func{{.*}} @test_lvalue_cast()
+// CIR: %[[S0:.+]] = {{.*}}@f : !cir.ptr<!cir.func<()>>{{.*}}
+// CIR: %[[S1:.+]] = cir.cast{{.*}}%[[S0]] : !cir.ptr<!cir.func<()>>{{.*}}
+// CIR: %[[S2:.+]] = cir.const #cir.int<42> : !s32i
+// CIR: cir.call %[[S1]](%[[S2]]) : (!cir.ptr<!cir.func<(!s32i)>>, !s32i) -> ()
+
+// LLVM-LABEL: define{{.*}} void @f1()
+// LLVM: ret void
+// LLVM: define{{.*}} void @f2()
+// LLVM: call void @f()
+// LLVM: define{{.*}} void @test_lvalue_cast()
+// LLVM: call void @f(i32 42)
+
+// OGCG-LABEL: define{{.*}} void @f1()
+// OGCG: ret void
+// OGCG: define{{.*}} void @f2()
+// OGCG: call void @f()
+// OGCG: define{{.*}} void @test_lvalue_cast()
+// OGCG: call void @f(i32 noundef 42)
More information about the cfe-commits
mailing list