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 <arghn...@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) _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits