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
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5341,6 +5341,18 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
!IsDefinitionAvailableExternally &&
D->needsDestruction(getContext()) == QualType::DK_cxx_destructor;

// It is helpless to emit the definition for an available_externally variable
// which can't be marked as const.
// We don't need to check if it needs global ctor or dtor. See the above
// comment for ideas.
if (IsDefinitionAvailableExternally &&
(!D->hasConstantInitialization() ||
// TODO: Update this when we have interface to check constexpr
// destructor.
D->needsDestruction(getContext()) ||
!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.

return;

const VarDecl *InitDecl;
const Expr *InitExpr = D->getAnyInitializer(InitDecl);

Expand Down
8 changes: 4 additions & 4 deletions clang/test/CodeGenCXX/partitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ export int use() {
}

// FIXME: The definition of the variables shouldn't be exported too.
// CHECK: @_ZW3mod1a = available_externally global
// CHECK: @_ZW3mod1b = available_externally global
// CHECK: @_ZW3mod1a = external global
// CHECK: @_ZW3mod1b = external global
// CHECK: declare{{.*}} i32 @_ZW3mod3foov
// CHECK: declare{{.*}} i32 @_ZW3mod3barv

// CHECK-OPT: @_ZW3mod1a = available_externally global
// CHECK-OPT: @_ZW3mod1b = available_externally global
// CHECK-OPT: @_ZW3mod1a = external global
// CHECK-OPT: @_ZW3mod1b = external global
// CHECK-OPT: declare{{.*}} i32 @_ZW3mod3foov
// CHECK-OPT: declare{{.*}} i32 @_ZW3mod3barv
106 changes: 106 additions & 0 deletions clang/test/Modules/pr93497.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// 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 - | opt -S --passes=simplifycfg | 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;

export struct NonConstexprDtor {
constexpr NonConstexprDtor(int raw) : raw(raw) {}
~NonConstexprDtor();

int raw;
};

export const NonConstexprDtor NonConstexprDtorValue = {1};

//--- 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() {
auto Ptr = ConstantPtr;
if (Ptr->value)
return consumeC(*Ptr);
return needed();
}

int use4() {
auto Ref = ConstantRef;
if (Ref.value)
return consumeC(Ref);
return needed();
}

int use5() {
NonConstexprDtor V = NonConstexprDtorValue;
if (V.raw)
return consume(V.raw);
return needed();
}

// CHECK: @_ZNW3mod5Thing3OneE = external
// CHECK: @_ZW3mod13ConstantValue ={{.*}}available_externally{{.*}} constant
// CHECK: @_ZW3mod11ConstantPtr = external
// CHECK: @_ZW3mod16NonConstantValue = external
// CHECK: @_ZW3mod21NonConstexprDtorValue = external

// 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(

// Check that the use of NonConstexprDtorValue won't get optimized incorrectly.
// CHECK-LABEL: @_Z4use5v(
// CHECK: @needed(
Loading