Skip to content

[CIR] Upstream proper function alias lowering #150520

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 29, 2025

Conversation

andykaylor
Copy link
Contributor

This change implements correct lowering of function aliases to the LLVM dialect.

This change implements correct lowering of function aliases to the
LLVM dialect.
@llvmbot llvmbot added clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project labels Jul 24, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 24, 2025

@llvm/pr-subscribers-clangir

@llvm/pr-subscribers-clang

Author: Andy Kaylor (andykaylor)

Changes

This change implements correct lowering of function aliases to the LLVM dialect.


Full diff: https://github.com/llvm/llvm-project/pull/150520.diff

4 Files Affected:

  • (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+70-6)
  • (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h (+5)
  • (modified) clang/test/CIR/CodeGen/ctor-alias.cpp (+21-4)
  • (modified) clang/test/CIR/CodeGen/dtor-alias.cpp (+2-4)
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]]

Comment on lines 931 to 932
llvmFnTy = cast<mlir::LLVM::LLVMFunctionType>(
converter->convertType(fn.getFunctionType()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
llvmFnTy = cast<mlir::LLVM::LLVMFunctionType>(
converter->convertType(fn.getFunctionType()));
llvmFnTy =
converter->convertType<mlir::LLVM::LLVMFunctionType>(fn.getFunctionType());

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)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
} else if (auto alias = cast<mlir::LLVM::AliasOp>(callee)) {
} else if (auto alias = mlir::cast<mlir::LLVM::AliasOp>(callee)) {

// 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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
auto symAttr = cast<mlir::FlatSymbolRefAttr>(calleeAttr);
auto symAttr = mlir::cast<mlir::FlatSymbolRefAttr>(calleeAttr);

Comment on lines 943 to 944
rewriter
.create<mlir::LLVM::AddressOfOp>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
rewriter
.create<mlir::LLVM::AddressOfOp>(
mlir::LLVM::AddressOfOp::create(rewriter,
  • some formatting


// Clear the callee attribute because we're calling an alias.
calleeAttr = {};
llvmFnTy = cast<mlir::LLVM::LLVMFunctionType>(alias.getType());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
llvmFnTy = cast<mlir::LLVM::LLVMFunctionType>(alias.getType());
llvmFnTy = mlir::cast<mlir::LLVM::LLVMFunctionType>(alias.getType());

Comment on lines 1220 to 1222
auto addrOp =
builder.create<mlir::LLVM::AddressOfOp>(loc, ptrTy, aliasee.getValue());
builder.create<mlir::LLVM::ReturnOp>(loc, addrOp);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.getValue());
mlir::LLVM::ReturnOp::(builder, loc, addrOp);

Comment on lines 1253 to 1255
std::optional<mlir::FlatSymbolRefAttr> aliasee = op.getAliaseeAttr();
if (aliasee && *aliasee)
return matchAndRewriteAlias(op, *aliasee, llvmFnTy, adaptor, rewriter);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
std::optional<mlir::FlatSymbolRefAttr> aliasee = op.getAliaseeAttr();
if (aliasee && *aliasee)
return matchAndRewriteAlias(op, *aliasee, llvmFnTy, adaptor, rewriter);
if (std::optional<llvm::StringRef> aliasee = op.getAliasee())
return matchAndRewriteAlias(op, *aliasee, llvmFnTy, adaptor, rewriter);

@@ -1166,6 +1199,31 @@ void CIRToLLVMFuncOpLowering::lowerFuncAttributes(
}
}

mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewriteAlias(
cir::FuncOp op, mlir::FlatSymbolRefAttr aliasee, mlir::Type ty,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cir::FuncOp op, mlir::FlatSymbolRefAttr aliasee, mlir::Type ty,
cir::FuncOp op, llvm::StringRef aliasee, mlir::Type ty,

assert(!cir::MissingFeatures::addressSpace());
mlir::Type ptrTy = mlir::LLVM::LLVMPointerType::get(ty.getContext());
auto addrOp =
builder.create<mlir::LLVM::AddressOfOp>(loc, ptrTy, aliasee.getValue());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
builder.create<mlir::LLVM::AddressOfOp>(loc, ptrTy, aliasee.getValue());
builder.create<mlir::LLVM::AddressOfOp>(loc, ptrTy, aliasee);

@@ -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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
matchAndRewriteAlias(cir::FuncOp op, mlir::FlatSymbolRefAttr aliasee,
matchAndRewriteAlias(cir::FuncOp op, llvm::StringRef aliasee,

Copy link
Member

@bcardosolopes bcardosolopes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, minor nit

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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wasan -> was an

Copy link
Contributor

@xlauko xlauko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@andykaylor andykaylor merged commit 8a1b252 into llvm:main Jul 29, 2025
9 checks passed
@andykaylor andykaylor deleted the cir-lower-fn-alias branch July 29, 2025 16:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants