llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clangir

Author: Andy Kaylor (andykaylor)

<details>
<summary>Changes</summary>

This adds handling for globals with the WeakRefAttr (not emitted) or AliasAttr 
attributes set. CIR already had support for function aliases, but we weren't 
handling the explicit alias attribute, and we didn't have any support for 
global variable aliases. This change adds the global variable alias support and 
adds the code to handle the explicit attribute for variables and functions.

Assisted-by: Cursor / claude-opus-4.7-thinking-xhigh

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


9 Files Affected:

- (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+6-1) 
- (modified) clang/include/clang/CIR/MissingFeatures.h (+2) 
- (modified) clang/lib/CIR/CodeGen/CIRGenModule.cpp (+94-3) 
- (modified) clang/lib/CIR/CodeGen/CIRGenModule.h (+3) 
- (modified) clang/lib/CIR/Dialect/IR/CIRDialect.cpp (+31-21) 
- (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+17) 
- (added) clang/test/CIR/CodeGen/attr-alias.c (+71) 
- (added) clang/test/CIR/CodeGen/attr-weakref.c (+26) 
- (added) clang/test/CIR/IR/invalid-global.cir (+42) 


``````````diff
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index db3ac6340eccb..b7437ce1731b0 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2871,7 +2871,8 @@ def CIR_GlobalOp : CIR_Op<"global", [
                        OptionalAttr<I64Attr>:$alignment,
                        OptionalAttr<ASTVarDeclInterface>:$ast,
                        OptionalAttr<StrAttr>:$section,
-                       OptionalAttr<CIR_AnnotationArrayAttr>:$annotations
+                       OptionalAttr<CIR_AnnotationArrayAttr>:$annotations,
+                       OptionalAttr<FlatSymbolRefAttr>:$aliasee
                        );
 
   let regions = (region AnyRegion:$ctorRegion,
@@ -2888,6 +2889,7 @@ def CIR_GlobalOp : CIR_Op<"global", [
     (`static_local_guard` `` $static_local_guard^)?
     (` ` custom<GlobalAddressSpaceValue>($addr_space)^ )?
     $sym_name
+    (`alias` `(` $aliasee^ `)`)?
     custom<GlobalOpTypeAndInitialValue>($sym_type, $initial_value,
                                         $ctorRegion, $dtorRegion)
     ($annotations^)?
@@ -2896,6 +2898,9 @@ def CIR_GlobalOp : CIR_Op<"global", [
 
   let extraClassDeclaration = [{
     bool isDeclaration() {
+      // Aliases are always definitions.
+      if (getAliasee())
+        return false;
       return !getInitialValue() && getCtorRegion().empty() && 
getDtorRegion().empty();
     }
     bool hasInitializer() { return !isDeclaration(); }
diff --git a/clang/include/clang/CIR/MissingFeatures.h 
b/clang/include/clang/CIR/MissingFeatures.h
index c85fcf6556f7b..d068874bfd567 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -144,6 +144,8 @@ struct MissingFeatures {
 
   // Various handling of deferred processing in CIRGenModule.
   static bool cgmRelease() { return false; }
+  static bool checkAliases() { return false; }
+  static bool shouldSkipAliasEmission() { return false; }
   static bool deferredFuncDecls() { return false; }
 
   // CXXABI
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index af8fd52bef017..bf6b5a22e6c4e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -464,11 +464,19 @@ void CIRGenModule::emitGlobal(clang::GlobalDecl gd) {
 
   const auto *global = cast<ValueDecl>(gd.getDecl());
 
+  // Weak references don't produce any output by themselves.
   if (global->hasAttr<WeakRefAttr>())
-    errorNYI(global->getSourceRange(), "emitGlobal: WeakRefAttr");
+    return;
 
-  if (global->hasAttr<AliasAttr>())
-    errorNYI(global->getSourceRange(), "emitGlobal: AliasAttr");
+  // If this is an alias definition (which otherwise looks like a declaration)
+  // emit it now.
+  if (global->hasAttr<AliasAttr>()) {
+    // Classic codegen calls shouldSkipAliasEmission here to skip alias
+    // emission for OpenMP target device and CUDA configurations.
+    assert(!cir::MissingFeatures::shouldSkipAliasEmission());
+    emitAliasDefinition(gd);
+    return;
+  }
 
   // If this is CUDA, be selective about which declarations we emit.
   // Non-constexpr non-lambda implicit host device functions are not emitted
@@ -3371,10 +3379,93 @@ void CIRGenModule::release() {
 
   emitLLVMUsed();
 
+  // Classic codegen calls `checkAliases` here to validate any alias
+  // definitions emitted during codegen.
+  assert(!cir::MissingFeatures::checkAliases());
+
   // There's a lot of code that is not implemented yet.
   assert(!cir::MissingFeatures::cgmRelease());
 }
 
+void CIRGenModule::emitAliasDefinition(GlobalDecl gd) {
+  const auto *d = cast<ValueDecl>(gd.getDecl());
+  const AliasAttr *aa = d->getAttr<AliasAttr>();
+  assert(aa && "Not an alias?");
+
+  StringRef mangledName = getMangledName(gd);
+
+  if (aa->getAliasee() == mangledName) {
+    diags.Report(aa->getLocation(), diag::err_cyclic_alias) << 0;
+    return;
+  }
+
+  // If there is a definition in the module, then it wins over the alias.
+  // This is dubious, but allow it to be safe. Just ignore the alias.
+  mlir::Operation *entry = getGlobalValue(mangledName);
+  if (entry) {
+    auto entryGV = mlir::dyn_cast<cir::CIRGlobalValueInterface>(entry);
+    if (entryGV && !entryGV.isDeclaration())
+      return;
+  }
+
+  // Classic codegen pushes the alias onto an `Aliases` list at this point so
+  // that `checkAliases` can later validate the alias and recover on error.
+  assert(!cir::MissingFeatures::checkAliases());
+
+  mlir::Location loc = getLoc(d->getSourceRange());
+  const bool isFunction = isa<FunctionDecl>(d);
+
+  // Get the linkage and the type of the alias.
+  cir::GlobalLinkageKind linkage;
+  mlir::Type declTy;
+  if (isFunction) {
+    declTy = getTypes().getFunctionType(gd);
+    linkage = getFunctionLinkage(gd);
+  } else {
+    declTy = getTypes().convertTypeForMem(d->getType());
+    if (const auto *vd = dyn_cast<VarDecl>(d))
+      linkage = getCIRLinkageVarDefinition(vd);
+    else
+      linkage = getFunctionLinkage(gd);
+  }
+
+  // Aliases that target weak symbols must themselves be marked weak.
+  if (d->hasAttr<WeakAttr>() || d->hasAttr<WeakRefAttr>() ||
+      d->isWeakImported())
+    linkage = cir::GlobalLinkageKind::WeakAnyLinkage;
+
+  // Create the alias op. If there is an existing declaration with the same
+  // name, erase it: any references to it via flat symbol reference will
+  // automatically resolve to the new alias.
+  if (entry) {
+    eraseGlobalSymbol(entry);
+    entry->erase();
+  }
+
+  // Aliases are always definitions, so the MLIR visibility should match the
+  // linkage rather than defaulting to private.
+  mlir::SymbolTable::Visibility visibility =
+      getMLIRVisibilityFromCIRLinkage(linkage);
+
+  if (isFunction) {
+    cir::FuncType fnType = mlir::cast<cir::FuncType>(declTy);
+    cir::FuncOp alias =
+        createCIRFunction(loc, mangledName, fnType, cast<FunctionDecl>(d));
+    alias.setAliasee(aa->getAliasee());
+    alias.setLinkage(linkage);
+    mlir::SymbolTable::setSymbolVisibility(alias, visibility);
+    setCommonAttributes(gd, alias);
+  } else {
+    cir::GlobalOp alias = createGlobalOp(*this, loc, mangledName, declTy);
+    alias.setAliasee(aa->getAliasee());
+    alias.setLinkage(linkage);
+    mlir::SymbolTable::setSymbolVisibility(alias, visibility);
+    assert(!cir::MissingFeatures::opGlobalThreadLocal());
+    setCommonAttributes(gd, alias);
+  }
+  assert(!cir::MissingFeatures::generateDebugInfo());
+}
+
 void CIRGenModule::emitAliasForGlobal(StringRef mangledName,
                                       mlir::Operation *op, GlobalDecl aliasGD,
                                       cir::FuncOp aliasee,
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h 
b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 2869411015bc5..1146e4561db0b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -596,6 +596,9 @@ class CIRGenModule : public CIRGenTypeCache {
                           GlobalDecl aliasGD, cir::FuncOp aliasee,
                           cir::GlobalLinkageKind linkage);
 
+  /// Emit a definition for an `__attribute__((alias))` declaration.
+  void emitAliasDefinition(GlobalDecl gd);
+
   mlir::Type convertType(clang::QualType type);
 
   /// Set the visibility for the given global.
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp 
b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 47ccb291ea745..d9ed9e40e2547 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -1899,6 +1899,13 @@ mlir::LogicalResult cir::GlobalOp::verify() {
         "with a constructor or destructor, they require in-function "
         "initialization via LocalInitOp");
 
+  if (getAliasee().has_value()) {
+    if (getInitialValue().has_value() || !getCtorRegion().empty() ||
+        !getDtorRegion().empty())
+      return emitOpError("global alias shall not have an initializer or "
+                         "constructor/destructor regions");
+  }
+
   // TODO(CIR): Many other checks for properties that haven't been upstreamed
   // yet.
 
@@ -1980,29 +1987,32 @@ static void 
printGlobalOpTypeAndInitialValue(OpAsmPrinter &p, cir::GlobalOp op,
                                              mlir::Region &ctorRegion,
                                              mlir::Region &dtorRegion) {
   auto printType = [&]() { p << ": " << type; };
-  if (!op.isDeclaration()) {
-    p << "= ";
-    if (!ctorRegion.empty()) {
-      p << "ctor ";
-      printType();
-      p << " ";
-      p.printRegion(ctorRegion,
-                    /*printEntryBlockArgs=*/false,
-                    /*printBlockTerminators=*/false);
-    } else {
-      // This also prints the type...
-      if (initAttr)
-        printConstant(p, initAttr);
-    }
+  // Aliases are definitions but they have no initial value or ctor/dtor; the
+  // assembly prints them like declarations (`: type`).
+  if (op.isDeclaration() || op.getAliasee()) {
+    printType();
+    return;
+  }
 
-    if (!dtorRegion.empty()) {
-      p << " dtor ";
-      p.printRegion(dtorRegion,
-                    /*printEntryBlockArgs=*/false,
-                    /*printBlockTerminators=*/false);
-    }
-  } else {
+  p << "= ";
+  if (!ctorRegion.empty()) {
+    p << "ctor ";
     printType();
+    p << " ";
+    p.printRegion(ctorRegion,
+                  /*printEntryBlockArgs=*/false,
+                  /*printBlockTerminators=*/false);
+  } else {
+    // This also prints the type...
+    if (initAttr)
+      printConstant(p, initAttr);
+  }
+
+  if (!dtorRegion.empty()) {
+    p << " dtor ";
+    p.printRegion(dtorRegion,
+                  /*printEntryBlockArgs=*/false,
+                  /*printBlockTerminators=*/false);
   }
 }
 
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index e17c7a209db6b..2430f07b1462d 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2473,6 +2473,23 @@ mlir::LogicalResult 
CIRToLLVMGlobalOpLowering::matchAndRewrite(
   SmallVector<mlir::NamedAttribute> attributes =
       lowerGlobalAttributes(op, rewriter);
 
+  // If this is a variable alias, lower it to llvm.mlir.alias.
+  if (std::optional<llvm::StringRef> aliasee = op.getAliasee()) {
+    mlir::Location loc = op.getLoc();
+    auto aliasOp = rewriter.replaceOpWithNewOp<mlir::LLVM::AliasOp>(
+        op, llvmType, linkage, symbol, isDsoLocal, isThreadLocal, attributes);
+
+    mlir::OpBuilder builder(op.getContext());
+    mlir::Block *block = builder.createBlock(&aliasOp.getInitializerRegion());
+    builder.setInsertionPointToStart(block);
+    mlir::Type ptrTy =
+        mlir::LLVM::LLVMPointerType::get(getContext(), addrSpace);
+    auto addrOp =
+        mlir::LLVM::AddressOfOp::create(builder, loc, ptrTy, *aliasee);
+    mlir::LLVM::ReturnOp::create(builder, loc, addrOp);
+    return mlir::success();
+  }
+
   if (init.has_value()) {
     if (mlir::isa<cir::FPAttr, cir::IntAttr, cir::BoolAttr>(init.value())) {
       GlobalInitAttrRewriter initRewriter(llvmType, rewriter);
diff --git a/clang/test/CIR/CodeGen/attr-alias.c 
b/clang/test/CIR/CodeGen/attr-alias.c
new file mode 100644
index 0000000000000..84091cc693753
--- /dev/null
+++ b/clang/test/CIR/CodeGen/attr-alias.c
@@ -0,0 +1,71 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o 
%t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o 
%t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+// Variable alias to a defined target.
+int alias_target = 42;
+extern int alias_var __attribute__((alias("alias_target")));
+
+// Function alias to a defined target.
+void alias_func_target(void) {}
+extern void alias_func(void) __attribute__((alias("alias_func_target")));
+
+// Variable alias preceded by an extern declaration. The previous declaration
+// must be replaced by the alias, otherwise we'd see two symbols with the same
+// name.
+int prior_decl_var_target = 7;
+extern int prior_decl_var;
+extern int prior_decl_var __attribute__((alias("prior_decl_var_target")));
+
+// Function alias preceded by an extern declaration. Same as above but for the
+// FuncOp entry-erasure path.
+extern void prior_decl_func(void);
+void prior_decl_func_target(void) {}
+extern void prior_decl_func(void) 
__attribute__((alias("prior_decl_func_target")));
+
+// Weak variable alias - exercises the WeakAttr linkage override branch.
+int weak_var_target = 9;
+extern int weak_var_alias __attribute__((weak, alias("weak_var_target")));
+
+// Weak function alias - exercises the WeakAttr linkage override branch for
+// functions.
+void weak_func_target(void) {}
+extern void weak_func_alias(void) __attribute__((weak, 
alias("weak_func_target")));
+
+// CIR-DAG: cir.global external @alias_target = #cir.int<42> : !s32i
+// CIR-DAG: cir.global external @alias_var alias(@alias_target) : !s32i
+// CIR-DAG: cir.global external @prior_decl_var_target = #cir.int<7>
+// CIR-DAG: cir.global external @prior_decl_var alias(@prior_decl_var_target) 
: !s32i
+// CIR-DAG: cir.global external @weak_var_target = #cir.int<9>
+// CIR-DAG: cir.global weak @weak_var_alias alias(@weak_var_target) : !s32i
+// CIR-DAG: cir.func{{.*}} @alias_func_target()
+// CIR-DAG: cir.func dso_local @alias_func() alias(@alias_func_target)
+// CIR-DAG: cir.func{{.*}} @prior_decl_func_target()
+// CIR-DAG: cir.func dso_local @prior_decl_func() 
alias(@prior_decl_func_target)
+// CIR-DAG: cir.func{{.*}} @weak_func_target()
+// CIR-DAG: cir.func weak @weak_func_alias() alias(@weak_func_target)
+
+// LLVM-DAG: @alias_target = global i32 42
+// LLVM-DAG: @prior_decl_var_target = global i32 7
+// LLVM-DAG: @weak_var_target = global i32 9
+// LLVM-DAG: @alias_var = alias i32, ptr @alias_target
+// LLVM-DAG: @prior_decl_var = alias i32, ptr @prior_decl_var_target
+// LLVM-DAG: @weak_var_alias = weak alias i32, ptr @weak_var_target
+// LLVM-DAG: @alias_func = alias void (), ptr @alias_func_target
+// LLVM-DAG: @prior_decl_func = alias void (), ptr @prior_decl_func_target
+// LLVM-DAG: @weak_func_alias = weak alias void (), ptr @weak_func_target
+// LLVM: define {{.*}}void @alias_func_target()
+
+// OGCG-DAG: @alias_target = {{.*}}global i32 42
+// OGCG-DAG: @prior_decl_var_target = {{.*}}global i32 7
+// OGCG-DAG: @weak_var_target = {{.*}}global i32 9
+// OGCG-DAG: @alias_var = {{.*}}alias i32, ptr @alias_target
+// OGCG-DAG: @prior_decl_var = {{.*}}alias i32, ptr @prior_decl_var_target
+// OGCG-DAG: @weak_var_alias = {{.*}}weak alias i32, ptr @weak_var_target
+// OGCG-DAG: @alias_func = {{.*}}alias void (), ptr @alias_func_target
+// OGCG-DAG: @prior_decl_func = {{.*}}alias void (), ptr 
@prior_decl_func_target
+// OGCG-DAG: @weak_func_alias = {{.*}}weak alias void (), ptr @weak_func_target
+// OGCG: define {{.*}}void @alias_func_target()
diff --git a/clang/test/CIR/CodeGen/attr-weakref.c 
b/clang/test/CIR/CodeGen/attr-weakref.c
new file mode 100644
index 0000000000000..621a277d79e79
--- /dev/null
+++ b/clang/test/CIR/CodeGen/attr-weakref.c
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o 
%t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o 
%t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+// A weakref declaration that is never referenced should produce no output for
+// either the alias or its target. Only the unrelated definition below should
+// be emitted.
+void weakref_target(void);
+static void weakref_alias(void) __attribute__((weakref("weakref_target")));
+
+void unrelated(void) {}
+
+// The weakref alias produces no output by itself.
+// CIR-NOT: @weakref_alias
+// CIR-NOT: @weakref_target
+// LLVM-NOT: @weakref_alias
+// LLVM-NOT: @weakref_target
+// OGCG-NOT: @weakref_alias
+// OGCG-NOT: @weakref_target
+
+// CIR: cir.func{{.*}}@unrelated
+// LLVM: define{{.*}}@unrelated
+// OGCG: define{{.*}}@unrelated
diff --git a/clang/test/CIR/IR/invalid-global.cir 
b/clang/test/CIR/IR/invalid-global.cir
new file mode 100644
index 0000000000000..d0a2f0d116c7f
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-global.cir
@@ -0,0 +1,42 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+!s32i = !cir.int<s, 32>
+
+module {
+
+cir.global external @target = #cir.int<42> : !s32i
+
+// expected-error @below {{global alias shall not have an initializer or 
constructor/destructor regions}}
+cir.global external @bad_alias_with_init alias(@target) = #cir.int<7> : !s32i
+
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+
+module {
+
+cir.global external @target = #cir.int<42> : !s32i
+
+// expected-error @below {{global alias shall not have an initializer or 
constructor/destructor regions}}
+cir.global external @bad_alias_with_ctor alias(@target) = ctor : !s32i {
+  cir.yield
+}
+
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+
+module {
+
+cir.global external @target = #cir.int<42> : !s32i
+
+// expected-error @below {{global alias shall not have an initializer or 
constructor/destructor regions}}
+cir.global external @bad_alias_with_dtor alias(@target) = #cir.int<7> : !s32i 
dtor {
+  cir.yield
+}
+
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/195972
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to