Skip to content

[C++20] [Modules] Don't generate the defintition for non-const available external variables #93530

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 1 commit into from
May 29, 2024

Conversation

ChuanqiXu9
Copy link
Member

@ChuanqiXu9 ChuanqiXu9 commented May 28, 2024

Close #93497

The root cause of the problem is, we mark the variable from other
modules as constnant in LLVM incorrectly. This patch fixes this problem
by not emitting the defintition for non-const available external
variables. Since the non const available externally variable is not
helpful to the optimization.

@ChuanqiXu9 ChuanqiXu9 added clang:modules C++20 modules and Clang Header Modules skip-precommit-approval PR for CI feedback, not intended for review labels May 28, 2024
@ChuanqiXu9 ChuanqiXu9 self-assigned this May 28, 2024
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:codegen IR generation bugs: mangling, exceptions, etc. labels May 28, 2024
@llvmbot
Copy link
Member

llvmbot commented May 28, 2024

@llvm/pr-subscribers-clang
@llvm/pr-subscribers-clang-codegen

@llvm/pr-subscribers-clang-modules

Author: Chuanqi Xu (ChuanqiXu9)

Changes

Close #93497

The root cause of the problem is, we mark the variable from other modules as constnant in LLVM incorrectly. This patch fixes this problem and only mark the variable from other modules as constant if its initializer is const.


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

2 Files Affected:

  • (modified) clang/lib/CodeGen/CodeGenModule.cpp (+2)
  • (added) clang/test/Modules/pr93497.cppm (+81)
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index e4774a587707a..5596a6708e4a1 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -5492,6 +5492,8 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
 
   // If it is safe to mark the global 'constant', do so now.
   GV->setConstant(!NeedsGlobalCtor && !NeedsGlobalDtor &&
+                  (!IsDefinitionAvailableExternally ||
+                   D->hasConstantInitialization()) &&
                   D->getType().isConstantStorage(getContext(), true, true));
 
   // If it is in a read-only section, mark it 'constant'.
diff --git a/clang/test/Modules/pr93497.cppm b/clang/test/Modules/pr93497.cppm
new file mode 100644
index 0000000000000..dc0d8eb462e27
--- /dev/null
+++ b/clang/test/Modules/pr93497.cppm
@@ -0,0 +1,81 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %t/mod.cppm \
+// RUN:     -emit-module-interface -o %t/mod.pcm
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %t/use.cpp \
+// RUN:     -fmodule-file=mod=%t/mod.pcm -emit-llvm \
+// RUN:     -o - | FileCheck %t/use.cpp
+
+//--- mod.cppm
+export module mod;
+
+export struct Thing {
+    static const Thing One;
+    explicit Thing(int raw) :raw(raw) { }
+    int raw;
+};
+
+const Thing Thing::One = Thing(1);
+
+export struct C {
+    int value;
+};
+export const C ConstantValue = {1};
+
+export const C *ConstantPtr = &ConstantValue;
+
+C NonConstantValue = {1};
+export const C &ConstantRef = NonConstantValue;
+
+//--- use.cpp
+import mod;
+
+int consume(int);
+int consumeC(C);
+
+extern "C" __attribute__((noinline)) inline int unneeded() {
+    return consume(43);
+}
+
+extern "C" __attribute__((noinline)) inline int needed() {
+    return consume(43);
+}
+
+int use() {
+    Thing t1 = Thing::One;
+    return consume(t1.raw);
+}
+
+int use2() {
+    if (ConstantValue.value)
+        return consumeC(ConstantValue);
+    return unneeded();
+}
+
+int use3() {
+    if (ConstantPtr->value)
+        return consumeC(*ConstantPtr);
+    return needed();
+}
+
+int use4() {
+    if (ConstantRef.value)
+        return consumeC(ConstantRef);
+    return needed();
+}
+
+// CHECK-NOT: @_ZNW3mod5Thing3OneE = {{.*}}constant
+// CHECK: @_ZW3mod13ConstantValue ={{.*}}available_externally{{.*}} constant 
+
+// Check that the middle end can optimize the program by the constant information.
+// CHECK-NOT: @unneeded(
+
+// Check that the use of ConstantPtr won't get optimized incorrectly.
+// CHECK-LABEL: @_Z4use3v(
+// CHECK: @needed(
+
+// Check that the use of ConstantRef won't get optimized incorrectly.
+// CHECK-LABEL: @_Z4use4v(
+// CHECK: @needed(

Copy link

github-actions bot commented May 28, 2024

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

Copy link
Collaborator

@efriedma-quic efriedma-quic left a comment

Choose a reason for hiding this comment

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

I think if a variable is GVA_AvailableExternally, and we can't emit a constant, we should just completely skip emitting the definition: there isn't any point to emitting an available_externally definition that doesn't actually contain any information the optimizer can use.

Not sure off the top of my head where that check belongs; might be okay to just stick it into EmitGlobalVarDefinition itself.

@ChuanqiXu9 ChuanqiXu9 removed the skip-precommit-approval PR for CI feedback, not intended for review label May 29, 2024
@ChuanqiXu9 ChuanqiXu9 force-pushed the ModulesConstantInit branch 2 times, most recently from eaf720c to 05542b6 Compare May 29, 2024 02:51
@ChuanqiXu9 ChuanqiXu9 changed the title [C++20] [Modules] Don't mark variables from other modules as constant if its initializer is not constant [C++20] [Modules] Don't generate the defintition for non-const available external variables May 29, 2024
@ChuanqiXu9
Copy link
Member Author

I think if a variable is GVA_AvailableExternally, and we can't emit a constant, we should just completely skip emitting the definition: there isn't any point to emitting an available_externally definition that doesn't actually contain any information the optimizer can use.

Not sure off the top of my head where that check belongs; might be okay to just stick it into EmitGlobalVarDefinition itself.

Neat suggestion! I've applied it and the generated code looks better.

// comment for ideas.
if (IsDefinitionAvailableExternally &&
(!D->hasConstantInitialization() ||
!D->getType().isConstantStorage(getContext(), true, true)))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we check for needsDestruction()? (For example, consider a struct with a constexpr constructor, but a non-trivial destructor.)

Copy link
Member Author

@ChuanqiXu9 ChuanqiXu9 May 29, 2024

Choose a reason for hiding this comment

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

(we think) the available externally variables don't need ctor or dtor in the current TU. I mentioned this in the comment.

Copy link
Collaborator

Choose a reason for hiding this comment

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

No, I mean, please make sure we don't emit an available_externally definition in that case.

Copy link
Member Author

Choose a reason for hiding this comment

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

Got it. Makes sense. Done.

…ble external variables

Close llvm#93497

The root cause of the problem is, we mark the variable from other
modules as constnant in LLVM incorrectly. This patch fixes this problem
by not emitting the defintition for non-const available external
variables. Since the non const available externally variable is not
helpful to the optimization.
@ChuanqiXu9 ChuanqiXu9 force-pushed the ModulesConstantInit branch from 05542b6 to ebe47b2 Compare May 29, 2024 05:17
Copy link
Collaborator

@efriedma-quic efriedma-quic left a comment

Choose a reason for hiding this comment

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

LGTM

@ChuanqiXu9 ChuanqiXu9 merged commit b0f10a1 into llvm:main May 29, 2024
5 of 7 checks passed
vg0204 pushed a commit to vg0204/llvm-project that referenced this pull request May 29, 2024
…ble external variables (llvm#93530)

Close llvm#93497

The root cause of the problem is, we mark the variable from other
modules as constnant in LLVM incorrectly. This patch fixes this problem
by not emitting the defintition for non-const available external
variables. Since the non const available externally variable is not
helpful to the optimization.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:codegen IR generation bugs: mangling, exceptions, etc. clang:modules C++20 modules and Clang Header Modules clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[C++20] [Modules] Defaulted copy constructor behaves differently, sometimes, when struct is in module
3 participants