[clang] [CIR] Handle FunctionToPointerDecay casts (#153657) (PR #154060)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Aug 17 21:44:38 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Justin Riddell (Arghnews)
<details>
<summary>Changes</summary>
Add upstream support for handling implicit FunctionToPointerDecay casts in ClangIR, for task #<!-- -->153657, migrate code from clangir repo
Passes `check-clang` test target, have run `git-clang-format` from repo as instructed
Appreciate any feedback
Notes:
- Omitted `clang::CIRGen::TBAAAccessInfo` from `emitPointerWithAlignment` as it looks like a large task to include that and not necessary
- Omitted `KnownNonNull_t` param too, although this is much smaller but would have to alter all sites where it's currently used in clangir, in llvm
- Copied `emitFunctionDeclLValue` from clangir
- Have just used the simple example test given in the task + the existing `function-to-pointer-decay.c` test, maybe the CHECKs can be improved or more test cases are needed (if so please specify what, am a CIR noob)
- Left `llvm_unreachable("NYI");` as those rather than a longer string, other instances in file have the same
---
Full diff: https://github.com/llvm/llvm-project/pull/154060.diff
3 Files Affected:
- (modified) clang/lib/CIR/CodeGen/CIRGenExpr.cpp (+86-14)
- (modified) clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp (+2)
- (added) clang/test/CIR/CodeGen/function-to-pointer-decay.c (+25)
``````````diff
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 8bcca6f5d1803..e8a25b6f9ecf4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -73,21 +73,59 @@ 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;
+ 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);
+ }
+ }
+
+ if (sanOpts.has(SanitizerKind::CFIUnrelatedCast) &&
+ ce->getCastKind() == CK_BitCast) {
+ if (expr->getType()->getAs<PointerType>())
+ llvm_unreachable("NYI");
+ }
+
+ const auto eltTy = convertTypeForMem(expr->getType()->getPointeeType());
+ addr = getBuilder().createElementBitCast(getLoc(expr->getSourceRange()),
+ addr, eltTy);
+ if (ce->getCastKind() == CK_AddressSpaceConversion) {
+ assert(!cir::MissingFeatures::addressSpace());
+ llvm_unreachable("NYI");
+ }
+
+ return addr;
+ }
+ break;
+ }
// Array-to-pointer decay. TODO(cir): BaseInfo and TBAAInfo.
case CK_ArrayToPointerDecay: {
@@ -553,6 +591,35 @@ 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());
+ auto funcOp = emitFunctionDeclPointer(CGF.cgm, GD);
+ auto loc = CGF.getLoc(E->getSourceRange());
+ CharUnits align = CGF.getContext().getDeclAlign(FD);
+
+ mlir::Type fnTy = funcOp.getFunctionType();
+ auto 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 = CGF.getBuilder().create<cir::CastOp>(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();
@@ -609,6 +676,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();
}
@@ -1386,11 +1463,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 8649bab91ce8e..bdda362818784 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -1879,6 +1879,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..96c4294ef2466
--- /dev/null
+++ b/clang/test/CIR/CodeGen/function-to-pointer-decay.c
@@ -0,0 +1,25 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - | FileCheck %s
+
+int f1();
+void f2() {
+ int (*t)() = f1;
+}
+
+// CHECK: cir.func {{.*}}@f2()
+// CHECK: %[[SLOT:.*]] = cir.alloca
+// CHECK: %[[F1:.+]] = cir.get_global @f1
+// CHECK: cir.store {{.*}} %[[F1]], %[[SLOT]] : {{.*}}
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - | FileCheck %s
+
+void f(void);
+
+void test_call_lvalue_cast() {
+ (*(void (*)(int))f)(42);
+}
+
+// CHECK: cir.func {{.*}}@test_call_lvalue_cast()
+// CHECK: [[F:%.+]] = cir.get_global @f
+// CHECK: [[CASTED:%.+]] = cir.cast(bitcast, [[F]]
+// CHECK: [[CONST:%.+]] = cir.const #cir.int<42>
+// CHECK: cir.call [[CASTED]]([[CONST]])
``````````
</details>
https://github.com/llvm/llvm-project/pull/154060
More information about the cfe-commits
mailing list