Skip to content

[CIR] Fix incorrect CIR_GlobalOp.global_visibility assembly format#189673

Merged
andykaylor merged 2 commits intollvm:mainfrom
NotLebedev:cir-189666
Apr 3, 2026
Merged

[CIR] Fix incorrect CIR_GlobalOp.global_visibility assembly format#189673
andykaylor merged 2 commits intollvm:mainfrom
NotLebedev:cir-189666

Conversation

@NotLebedev
Copy link
Copy Markdown
Contributor

Closes #189666 .

Fix incorrect printing and parsing of cir.global if global_visibility attribute is present. Incorrect assembly format

(`` $global_visibility^)?

Resulted in keyword sticking to previous word and producing incorrect cir like this:

cir.globalhidden external dso_local @hidden_var = #cir.int<10> : !s32i {alignment = 4 : i64} loc(#loc22)
cir.global "private"hidden internal dso_local @hidden_static_var = #cir.int<10> : !s32i {alignment = 4 : i64} loc(#loc24)

Using custom parser/printer that is used in cir.func parser fixes this issue and makes printed/parsed attribute for functions and global values consistent.

Also added tests for both global values and functions.

Use custom parser for CIR_VisibilityAttr in cir.global like cir.func
does
to avoid issues with attribute being incorrectly printed and not parsed
@github-actions
Copy link
Copy Markdown

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot llvmbot added clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project labels Mar 31, 2026
@llvmbot
Copy link
Copy Markdown
Member

llvmbot commented Mar 31, 2026

@llvm/pr-subscribers-clangir

@llvm/pr-subscribers-clang

Author: Artemiy (NotLebedev)

Changes

Closes #189666 .

Fix incorrect printing and parsing of cir.global if global_visibility attribute is present. Incorrect assembly format

(`` $global_visibility^)?

Resulted in keyword sticking to previous word and producing incorrect cir like this:

cir.globalhidden external dso_local @<!-- -->hidden_var = #cir.int&lt;10&gt; : !s32i {alignment = 4 : i64} loc(#loc22)
cir.global "private"hidden internal dso_local @<!-- -->hidden_static_var = #cir.int&lt;10&gt; : !s32i {alignment = 4 : i64} loc(#loc24)

Using custom parser/printer that is used in cir.func parser fixes this issue and makes printed/parsed attribute for functions and global values consistent.

Also added tests for both global values and functions.


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

4 Files Affected:

  • (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+1-1)
  • (modified) clang/lib/CIR/Dialect/IR/CIRDialect.cpp (+7-1)
  • (added) clang/test/CIR/CodeGen/attribute-visibility.c (+45)
  • (added) clang/test/CIR/IR/attribute-visibility.cir (+28)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index b15dc30ffb87d..89652cb392bfb 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2849,7 +2849,7 @@ def CIR_GlobalOp : CIR_Op<"global", [
 
   let assemblyFormat = [{
     ($sym_visibility^)?
-    (`` $global_visibility^)?
+    (custom<VisibilityAttr>($global_visibility)^)?
     (`constant` $constant^)?
     $linkage
     (`comdat` $comdat^)?
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index eb322d135a804..1187fd5ddea5d 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -221,10 +221,16 @@ void printVisibilityAttr(OpAsmPrinter &printer,
   }
 }
 
-void parseVisibilityAttr(OpAsmParser &parser, cir::VisibilityAttr &visibility) {
+void printVisibilityAttr(OpAsmPrinter &printer, cir::GlobalOp,
+                         cir::VisibilityAttr visibility) {
+    printVisibilityAttr(printer, visibility);
+}
+
+mlir::OptionalParseResult parseVisibilityAttr(OpAsmParser &parser, cir::VisibilityAttr &visibility) {
   cir::VisibilityKind visibilityKind =
       parseOptionalCIRKeyword(parser, cir::VisibilityKind::Default);
   visibility = cir::VisibilityAttr::get(parser.getContext(), visibilityKind);
+  return mlir::success();
 }
 
 //===----------------------------------------------------------------------===//
diff --git a/clang/test/CIR/CodeGen/attribute-visibility.c b/clang/test/CIR/CodeGen/attribute-visibility.c
new file mode 100644
index 0000000000000..45098770d84ea
--- /dev/null
+++ b/clang/test/CIR/CodeGen/attribute-visibility.c
@@ -0,0 +1,45 @@
+// 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
+
+int normal_var = 10;
+// CIR: cir.global external @normal_var {{.*}}
+// LLVM: @normal_var = global {{.*}}
+// OGCG: @normal_var = global {{.*}}
+
+__attribute__((visibility("hidden")))
+int hidden_var = 10;
+// CIR: cir.global hidden external @hidden_var {{.*}}
+// LLVM: @hidden_var = hidden global {{.*}}
+// OGCG: @hidden_var = hidden global {{.*}}
+
+static int normal_static_var = 10;
+// CIR: cir.global "private" internal dso_local @normal_static_var {{.*}}
+// LLVM: @normal_static_var = internal global {{.*}}
+// OGCG: @normal_static_var = internal global {{.*}}
+
+__attribute__((visibility("hidden")))
+static int hidden_static_var = 10;
+// CIR: cir.global "private" hidden internal dso_local @hidden_static_var {{.*}}
+// LLVM: @hidden_static_var = internal hidden global {{.*}}
+// OGCG: @hidden_static_var = internal global {{.*}}
+
+void normal_func() {
+    normal_var = 0;
+    normal_static_var = 0;
+}
+// CIR: cir.func no_inline no_proto dso_local @normal_func() {{.*}} {
+// LLVM: define dso_local void @normal_func() {{.*}}
+// OGCG: define dso_local void @normal_func() {{.*}}
+
+__attribute__((visibility("hidden")))
+void hidden_func() {
+    hidden_var = 0;
+    hidden_static_var = 0;
+}
+// CIR: cir.func no_inline no_proto hidden dso_local @hidden_func() {{.*}} {
+// LLVM: define hidden void @hidden_func() {{.*}}
+// OGCG: define hidden void @hidden_func() {{.*}}
diff --git a/clang/test/CIR/IR/attribute-visibility.cir b/clang/test/CIR/IR/attribute-visibility.cir
new file mode 100644
index 0000000000000..e4f09a9d657cf
--- /dev/null
+++ b/clang/test/CIR/IR/attribute-visibility.cir
@@ -0,0 +1,28 @@
+// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
+
+!s32i = !cir.int<s, 32>
+module {
+  cir.global external dso_local @normal_var = #cir.int<10> : !s32i {alignment = 4 : i64}
+  // CHECK: cir.global external dso_local @normal_var = #cir.int<10> : !s32i {alignment = 4 : i64}
+
+  cir.global hidden external dso_local @hidden_var = #cir.int<10> : !s32i {alignment = 4 : i64}
+  // CHECK: cir.global hidden external dso_local @hidden_var = #cir.int<10> : !s32i {alignment = 4 : i64}
+
+  cir.global "private" internal dso_local @normal_static_var = #cir.int<10> : !s32i {alignment = 4 : i64}
+  // CHECK: cir.global "private" internal dso_local @normal_static_var = #cir.int<10> : !s32i {alignment = 4 : i64}
+
+  cir.global "private" hidden internal dso_local @hidden_static_var = #cir.int<10> : !s32i {alignment = 4 : i64}
+  // CHECK: cir.global "private" hidden internal dso_local @hidden_static_var = #cir.int<10> : !s32i {alignment = 4 : i64}
+
+  cir.func no_inline no_proto dso_local @normal_func() {
+    cir.return
+  }
+  // CHECK: cir.func no_inline no_proto dso_local @normal_func()
+  // CHECK:   cir.return
+
+  cir.func no_inline no_proto hidden dso_local @hidden_func() {
+    cir.return
+  }
+  // CHECK: cir.func no_inline no_proto hidden dso_local @hidden_func()
+  // CHECK:   cir.return
+}

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 1, 2026

✅ With the latest revision this PR passed the C/C++ code formatter.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 1, 2026

🐧 Linux x64 Test Results

  • 116481 tests passed
  • 3934 tests skipped

✅ The build succeeded and all tests passed.

let assemblyFormat = [{
($sym_visibility^)?
(`` $global_visibility^)?
(custom<VisibilityAttr>($global_visibility)^)?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
(custom<VisibilityAttr>($global_visibility)^)?
($global_visibility^)?

Wouldn't this also fix the problem?

Copy link
Copy Markdown
Contributor Author

@NotLebedev NotLebedev Apr 2, 2026

Choose a reason for hiding this comment

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

Unfortunately it would not, because default parser breaks when VisibilityAttr default/optional attributes needs to be parsed.

image

It seems like it skips all optional arguments and goes straight to parsing linkage attribute and fails. But using cusotm parser works fine.

I looked into generated code and it seems that problem is the following:

  1. By default Parser::parseOptionalAttribute is used when parsing optional attributes
  2. It has four specializations here. For arrays, strings, symbol refs and default for generic Attribute
  3. Generic attribute accepts all kinds of Token, but if it is bare_identifier (which I assume literal hidden is) it tries to parse it as type with parseOptionalType (which it is not)

So straightforward fix here is to use custom parser in which case this code is generated:

if (auto optResult = [&]() -> ::mlir::OptionalParseResult {
  {
    auto odsResult = parseVisibilityAttr(parser, global_visibilityAttr);
    if (!odsResult.has_value()) return {};
    if (::mlir::failed(*odsResult)) return ::mlir::failure();
    if (global_visibilityAttr)
      result.getOrAddProperties<GlobalOp::Properties>().global_visibility = global_visibilityAttr;
  }
    return ::mlir::success();
  }(); optResult.has_value() && ::mlir::failed(*optResult)) {
    return ::mlir::failure();
  } else if (optResult.has_value()) {
  }

It does not use parseOptionalAttribute and uses parseVisibilityAttr directly. This is ultimately because CIR_VisibilityAttr uses custom assembly format

let assemblyFormat = [{
    $value
  }];

which does not work well with default implementation of parseOptionalType.

// OGCG: @normal_static_var = internal global {{.*}}

__attribute__((visibility("hidden")))
static int hidden_static_var = 10;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is causing the test to crash when lowering to LLVM IR because we shouldn't be setting the hidden visibility for variables with local linkage. The CIR code to set visibility needs a general update to match classic codegen more closely. I'll do that in a separate PR. For now, you can just remove this variable from the test.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed variable from test.

@NotLebedev NotLebedev requested a review from andykaylor April 2, 2026 10:33
Copy link
Copy Markdown
Contributor

@andykaylor andykaylor left a comment

Choose a reason for hiding this comment

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

lgtm

cir.global "private" internal dso_local @normal_static_var = #cir.int<10> : !s32i {alignment = 4 : i64}
// CHECK: cir.global "private" internal dso_local @normal_static_var = #cir.int<10> : !s32i {alignment = 4 : i64}

cir.global "private" hidden internal dso_local @hidden_static_var = #cir.int<10> : !s32i {alignment = 4 : i64}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The verifier should reject this because it won't lower to LLVM IR correctly. That can happen in a follow-up change though.

@andykaylor andykaylor merged commit dc83ad2 into llvm:main Apr 3, 2026
10 checks passed
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 3, 2026

@NotLebedev Congratulations on having your first Pull Request (PR) merged into the LLVM Project!

Your changes will be combined with recent changes from other authors, then tested by our build bots. If there is a problem with a build, you may receive a report in an email or a comment on this PR.

Please check whether problems have been caused by your change specifically, as the builds can include changes from many authors. It is not uncommon for your change to be included in a build that fails due to someone else's changes, or infrastructure issues.

How to do this, and the rest of the post-merge process, is covered in detail here.

If your change does cause a problem, it may be reverted, or you can revert it yourself. This is a normal part of LLVM development. You can fix your changes and open a new PR to merge them again.

If you don't get any reports, no action is required from you. Your changes are working as expected, well done!

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.

[CIR] Incorrect CIR_GlobalOp assembly format for global_visibility attribute

3 participants