[clang] [CIR] Upstream proper function alias lowering (PR #150520)
Andy Kaylor via cfe-commits
cfe-commits at lists.llvm.org
Tue Jul 29 09:20:49 PDT 2025
https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/150520
>From 88039a468792435666e3026b5bf42ad31405d332 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Thu, 24 Jul 2025 13:14:35 -0700
Subject: [PATCH 1/3] [CIR] Upstream proper function alias lowering
This change implements correct lowering of function aliases to the
LLVM dialect.
---
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 76 +++++++++++++++++--
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 5 ++
clang/test/CIR/CodeGen/ctor-alias.cpp | 25 +++++-
clang/test/CIR/CodeGen/dtor-alias.cpp | 6 +-
4 files changed, 98 insertions(+), 14 deletions(-)
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 3cd7de0a56bc3..baa0a5e326c4a 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -919,13 +919,46 @@ rewriteCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands,
memoryEffects, noUnwind, willReturn);
mlir::LLVM::LLVMFunctionType llvmFnTy;
+
+ // Temporary to handle the case where we need to prepend an operand if the
+ // callee is an alias.
+ SmallVector<mlir::Value> adjustedCallOperands;
+
if (calleeAttr) { // direct call
- mlir::FunctionOpInterface fn =
- mlir::SymbolTable::lookupNearestSymbolFrom<mlir::FunctionOpInterface>(
- op, calleeAttr);
- assert(fn && "Did not find function for call");
- llvmFnTy = cast<mlir::LLVM::LLVMFunctionType>(
- converter->convertType(fn.getFunctionType()));
+ mlir::Operation *callee =
+ mlir::SymbolTable::lookupNearestSymbolFrom(op, calleeAttr);
+ if (auto fn = dyn_cast<mlir::FunctionOpInterface>(callee)) {
+ llvmFnTy = cast<mlir::LLVM::LLVMFunctionType>(
+ converter->convertType(fn.getFunctionType()));
+ } else if (auto alias = cast<mlir::LLVM::AliasOp>(callee)) {
+ // If the callee wasan alias. In that case,
+ // we need to prepend the address of the alias to the operands. The
+ // way aliases work in the LLVM dialect is a little counter-intuitive.
+ // The AliasOp itself is a pseudo-function that returns the address of
+ // the global value being aliased, but when we generate the call we
+ // need to insert an operation that gets the address of the AliasOp.
+ // This all gets sorted out when the LLVM dialect is lowered to LLVM IR.
+ auto symAttr = cast<mlir::FlatSymbolRefAttr>(calleeAttr);
+ auto addrOfAlias =
+ rewriter
+ .create<mlir::LLVM::AddressOfOp>(
+ op->getLoc(),
+ mlir::LLVM::LLVMPointerType::get(rewriter.getContext()),
+ symAttr)
+ .getResult();
+ adjustedCallOperands.push_back(addrOfAlias);
+
+ // Now add the regular operands and assign this to the range value.
+ llvm::append_range(adjustedCallOperands, callOperands);
+ callOperands = adjustedCallOperands;
+
+ // Clear the callee attribute because we're calling an alias.
+ calleeAttr = {};
+ llvmFnTy = cast<mlir::LLVM::LLVMFunctionType>(alias.getType());
+ } else {
+ // Was this an ifunc?
+ return op->emitError("Unexpected callee type!");
+ }
} else { // indirect call
assert(!op->getOperands().empty() &&
"operands list must no be empty for the indirect call");
@@ -1166,6 +1199,31 @@ void CIRToLLVMFuncOpLowering::lowerFuncAttributes(
}
}
+mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewriteAlias(
+ cir::FuncOp op, mlir::FlatSymbolRefAttr aliasee, mlir::Type ty,
+ OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const {
+ SmallVector<mlir::NamedAttribute, 4> attributes;
+ lowerFuncAttributes(op, /*filterArgAndResAttrs=*/false, attributes);
+
+ mlir::Location loc = op.getLoc();
+ auto aliasOp = rewriter.replaceOpWithNewOp<mlir::LLVM::AliasOp>(
+ op, ty, convertLinkage(op.getLinkage()), op.getName(), op.getDsoLocal(),
+ /*threadLocal=*/false, attributes);
+
+ // Create the alias body
+ mlir::OpBuilder builder(op.getContext());
+ mlir::Block *block = builder.createBlock(&aliasOp.getInitializerRegion());
+ builder.setInsertionPointToStart(block);
+ // The type of AddressOfOp is always a pointer.
+ assert(!cir::MissingFeatures::addressSpace());
+ mlir::Type ptrTy = mlir::LLVM::LLVMPointerType::get(ty.getContext());
+ auto addrOp =
+ builder.create<mlir::LLVM::AddressOfOp>(loc, ptrTy, aliasee.getValue());
+ builder.create<mlir::LLVM::ReturnOp>(loc, addrOp);
+
+ return mlir::success();
+}
+
mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite(
cir::FuncOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
@@ -1190,6 +1248,12 @@ mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite(
resultType ? resultType : mlir::LLVM::LLVMVoidType::get(getContext()),
signatureConversion.getConvertedTypes(),
/*isVarArg=*/fnType.isVarArg());
+
+ // If this is an alias, it needs to be lowered to llvm::AliasOp.
+ std::optional<mlir::FlatSymbolRefAttr> aliasee = op.getAliaseeAttr();
+ if (aliasee && *aliasee)
+ return matchAndRewriteAlias(op, *aliasee, llvmFnTy, adaptor, rewriter);
+
// LLVMFuncOp expects a single FileLine Location instead of a fused
// location.
mlir::Location loc = op.getLoc();
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
index 2911ced66e58e..0f003f644d2fa 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -257,6 +257,11 @@ class CIRToLLVMFuncOpLowering : public mlir::OpConversionPattern<cir::FuncOp> {
cir::FuncOp func, bool filterArgAndResAttrs,
mlir::SmallVectorImpl<mlir::NamedAttribute> &result) const;
+ mlir::LogicalResult
+ matchAndRewriteAlias(cir::FuncOp op, mlir::FlatSymbolRefAttr aliasee,
+ mlir::Type ty, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const;
+
public:
using mlir::OpConversionPattern<cir::FuncOp>::OpConversionPattern;
diff --git a/clang/test/CIR/CodeGen/ctor-alias.cpp b/clang/test/CIR/CodeGen/ctor-alias.cpp
index a20e206c6d714..c4bf455e3a09b 100644
--- a/clang/test/CIR/CodeGen/ctor-alias.cpp
+++ b/clang/test/CIR/CodeGen/ctor-alias.cpp
@@ -11,6 +11,8 @@ struct B {
B::B() {
}
+// LLVM: @_ZN1BC1Ev = alias void (ptr), ptr @_ZN1BC2Ev
+
// OGCG: @_ZN1BC1Ev = unnamed_addr alias void (ptr), ptr @_ZN1BC2Ev
// CHECK: cir.func{{.*}} @_ZN1BC2Ev(%arg0: !cir.ptr<!rec_B>
@@ -25,15 +27,30 @@ B::B() {
// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
-// This should be an alias, like the similar OGCG alias above, but that's not
-// implemented yet.
-// LLVM: declare dso_local void @_ZN1BC1Ev(ptr)
-
// OGCG: define{{.*}} @_ZN1BC2Ev(ptr{{.*}} %[[THIS_ARG:.*]])
// OGCG: %[[THIS_ADDR:.*]] = alloca ptr
// OGCG: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
// OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
+void bar() {
+ B b;
+}
+
+// CHECK: cir.func{{.*}} @_Z3barv()
+// CHECK: %[[B:.*]] = cir.alloca !rec_B, !cir.ptr<!rec_B>, ["b", init]
+// CHECK: cir.call @_ZN1BC1Ev(%[[B]]) : (!cir.ptr<!rec_B>) -> ()
+// CHECK: cir.return
+
+// LLVM: define{{.*}} void @_Z3barv()
+// LLVM: %[[B:.*]] = alloca %struct.B, i64 1, align 1
+// LLVM: call void @_ZN1BC1Ev(ptr %[[B]])
+// LLVM: ret void
+
+// OGCG: define{{.*}} void @_Z3barv()
+// OGCG: %[[B:.*]] = alloca %struct.B, align 1
+// OGCG: call void @_ZN1BC1Ev(ptr{{.*}} %[[B]])
+// OGCG: ret void
+
// The constructor in this cases is handled by RAUW rather than aliasing.
struct Struk {
Struk() {}
diff --git a/clang/test/CIR/CodeGen/dtor-alias.cpp b/clang/test/CIR/CodeGen/dtor-alias.cpp
index e37ddabb5b4fd..f4d54dfd7da26 100644
--- a/clang/test/CIR/CodeGen/dtor-alias.cpp
+++ b/clang/test/CIR/CodeGen/dtor-alias.cpp
@@ -11,6 +11,8 @@ struct B {
B::~B() {
}
+// LLVM: @_ZN1BD1Ev = alias void (ptr), ptr @_ZN1BD2Ev
+
// OGCG: @_ZN1BD1Ev = unnamed_addr alias void (ptr), ptr @_ZN1BD2Ev
// CHECK: cir.func{{.*}} @_ZN1BD2Ev(%arg0: !cir.ptr<!rec_B>
@@ -25,10 +27,6 @@ B::~B() {
// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
-// This should be an alias, like the similar OGCG alias above, but that's not
-// implemented yet.
-// LLVM: declare dso_local void @_ZN1BD1Ev(ptr)
-
// OGCG: define{{.*}} @_ZN1BD2Ev(ptr{{.*}} %[[THIS_ARG:.*]])
// OGCG: %[[THIS_ADDR:.*]] = alloca ptr
// OGCG: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
>From 2e95edbd4db6065cef1665dc4039410590fa1bc3 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Fri, 25 Jul 2025 13:37:45 -0700
Subject: [PATCH 2/3] Address review feedback
---
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 33 +++++++++----------
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 4 +--
2 files changed, 17 insertions(+), 20 deletions(-)
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index baa0a5e326c4a..3ad84413e717d 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -927,10 +927,11 @@ rewriteCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands,
if (calleeAttr) { // direct call
mlir::Operation *callee =
mlir::SymbolTable::lookupNearestSymbolFrom(op, calleeAttr);
- if (auto fn = dyn_cast<mlir::FunctionOpInterface>(callee)) {
- llvmFnTy = cast<mlir::LLVM::LLVMFunctionType>(
- converter->convertType(fn.getFunctionType()));
- } else if (auto alias = cast<mlir::LLVM::AliasOp>(callee)) {
+ if (auto fn = mlir::dyn_cast<mlir::FunctionOpInterface>(callee)) {
+ llvmFnTy = converter->convertType<mlir::LLVM::LLVMFunctionType>(
+ fn.getFunctionType());
+ assert(llvmFnTy && "Failed to convert function type");
+ } else if (auto alias = mlir::cast<mlir::LLVM::AliasOp>(callee)) {
// If the callee wasan alias. In that case,
// we need to prepend the address of the alias to the operands. The
// way aliases work in the LLVM dialect is a little counter-intuitive.
@@ -938,13 +939,11 @@ rewriteCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands,
// the global value being aliased, but when we generate the call we
// need to insert an operation that gets the address of the AliasOp.
// This all gets sorted out when the LLVM dialect is lowered to LLVM IR.
- auto symAttr = cast<mlir::FlatSymbolRefAttr>(calleeAttr);
+ auto symAttr = mlir::cast<mlir::FlatSymbolRefAttr>(calleeAttr);
auto addrOfAlias =
- rewriter
- .create<mlir::LLVM::AddressOfOp>(
- op->getLoc(),
- mlir::LLVM::LLVMPointerType::get(rewriter.getContext()),
- symAttr)
+ mlir::LLVM::AddressOfOp::create(
+ rewriter, op->getLoc(),
+ mlir::LLVM::LLVMPointerType::get(rewriter.getContext()), symAttr)
.getResult();
adjustedCallOperands.push_back(addrOfAlias);
@@ -954,7 +953,7 @@ rewriteCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands,
// Clear the callee attribute because we're calling an alias.
calleeAttr = {};
- llvmFnTy = cast<mlir::LLVM::LLVMFunctionType>(alias.getType());
+ llvmFnTy = mlir::cast<mlir::LLVM::LLVMFunctionType>(alias.getType());
} else {
// Was this an ifunc?
return op->emitError("Unexpected callee type!");
@@ -1200,8 +1199,8 @@ void CIRToLLVMFuncOpLowering::lowerFuncAttributes(
}
mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewriteAlias(
- cir::FuncOp op, mlir::FlatSymbolRefAttr aliasee, mlir::Type ty,
- OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const {
+ cir::FuncOp op, llvm::StringRef aliasee, mlir::Type ty, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
SmallVector<mlir::NamedAttribute, 4> attributes;
lowerFuncAttributes(op, /*filterArgAndResAttrs=*/false, attributes);
@@ -1217,9 +1216,8 @@ mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewriteAlias(
// The type of AddressOfOp is always a pointer.
assert(!cir::MissingFeatures::addressSpace());
mlir::Type ptrTy = mlir::LLVM::LLVMPointerType::get(ty.getContext());
- auto addrOp =
- builder.create<mlir::LLVM::AddressOfOp>(loc, ptrTy, aliasee.getValue());
- builder.create<mlir::LLVM::ReturnOp>(loc, addrOp);
+ auto addrOp = mlir::LLVM::AddressOfOp::create(builder, loc, ptrTy, aliasee);
+ mlir::LLVM::ReturnOp::create(builder, loc, addrOp);
return mlir::success();
}
@@ -1250,8 +1248,7 @@ mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite(
/*isVarArg=*/fnType.isVarArg());
// If this is an alias, it needs to be lowered to llvm::AliasOp.
- std::optional<mlir::FlatSymbolRefAttr> aliasee = op.getAliaseeAttr();
- if (aliasee && *aliasee)
+ if (std::optional<llvm::StringRef> aliasee = op.getAliasee())
return matchAndRewriteAlias(op, *aliasee, llvmFnTy, adaptor, rewriter);
// LLVMFuncOp expects a single FileLine Location instead of a fused
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
index 0f003f644d2fa..46c8ebc3cf7e2 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -258,8 +258,8 @@ class CIRToLLVMFuncOpLowering : public mlir::OpConversionPattern<cir::FuncOp> {
mlir::SmallVectorImpl<mlir::NamedAttribute> &result) const;
mlir::LogicalResult
- matchAndRewriteAlias(cir::FuncOp op, mlir::FlatSymbolRefAttr aliasee,
- mlir::Type ty, OpAdaptor adaptor,
+ matchAndRewriteAlias(cir::FuncOp op, llvm::StringRef aliasee, mlir::Type ty,
+ OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const;
public:
>From 4c4aeaf02c48f05ab6f8b34ddba99d6ea9977ae1 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Tue, 29 Jul 2025 09:20:16 -0700
Subject: [PATCH 3/3] Fix comment typo
---
clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 3ad84413e717d..dfe07e821d767 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -932,7 +932,7 @@ rewriteCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands,
fn.getFunctionType());
assert(llvmFnTy && "Failed to convert function type");
} else if (auto alias = mlir::cast<mlir::LLVM::AliasOp>(callee)) {
- // If the callee wasan alias. In that case,
+ // If the callee was an alias. In that case,
// we need to prepend the address of the alias to the operands. The
// way aliases work in the LLVM dialect is a little counter-intuitive.
// The AliasOp itself is a pseudo-function that returns the address of
More information about the cfe-commits
mailing list