Skip to content
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

[C++20] [Modules] [Itanium ABI] Generate the vtable in the module unit of dynamic classes #75912

Merged
merged 8 commits into from
Jun 17, 2024

Conversation

ChuanqiXu9
Copy link
Member

Close #70585 and reflect itanium-cxx-abi/cxx-abi#170.

The significant change of the patch is: for dynamic classes attached to module units, we generate the vtable to the attached module units directly and the key functions for such classes is meaningless.

@ChuanqiXu9 ChuanqiXu9 self-assigned this Dec 19, 2023
@ChuanqiXu9 ChuanqiXu9 added the clang:modules C++20 modules and Clang Header Modules label Dec 19, 2023
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:codegen labels Dec 19, 2023
@ChuanqiXu9 ChuanqiXu9 added ABI Application Binary Interface and removed clang Clang issues not falling into any other category clang:codegen labels Dec 19, 2023
@llvmbot
Copy link
Collaborator

llvmbot commented Dec 19, 2023

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

@llvm/pr-subscribers-clang-modules

Author: Chuanqi Xu (ChuanqiXu9)

Changes

Close #70585 and reflect itanium-cxx-abi/cxx-abi#170.

The significant change of the patch is: for dynamic classes attached to module units, we generate the vtable to the attached module units directly and the key functions for such classes is meaningless.


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

5 Files Affected:

  • (modified) clang/lib/CodeGen/CGVTables.cpp (+20-7)
  • (modified) clang/lib/CodeGen/ItaniumCXXABI.cpp (+6)
  • (modified) clang/lib/CodeGen/ModuleBuilder.cpp (+1-1)
  • (modified) clang/test/CodeGenCXX/modules-vtable.cppm (+17-10)
  • (added) clang/test/CodeGenCXX/pr70585.cppm (+50)
diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp
index 27a2cab4f75319..06b02df93b26d8 100644
--- a/clang/lib/CodeGen/CGVTables.cpp
+++ b/clang/lib/CodeGen/CGVTables.cpp
@@ -1045,6 +1045,15 @@ llvm::GlobalVariable::LinkageTypes
 CodeGenModule::getVTableLinkage(const CXXRecordDecl *RD) {
   if (!RD->isExternallyVisible())
     return llvm::GlobalVariable::InternalLinkage;
+  
+  // Previously we'll decide the linkage of the vtable by the linkage
+  // of the key function. But within modules, the concept of key functions
+  // becomes meaningless. So the linkage of the vtable should always be
+  // external if the class is externally visible.
+  //
+  // TODO: How about the case of AppleKext, DLLExportAttr and DLLImportAttr.
+  if (Module *M = RD->getOwningModule(); M && M->isNamedModule())
+    return llvm::GlobalVariable::ExternalLinkage;
 
   // We're at the end of the translation unit, so the current key
   // function is fully correct.
@@ -1180,6 +1189,16 @@ bool CodeGenVTables::isVTableExternal(const CXXRecordDecl *RD) {
       TSK == TSK_ExplicitInstantiationDefinition)
     return false;
 
+  // Itanium C++ ABI [5.2.3]:
+  // Virtual tables for dynamic classes are emitted as follows:
+  // 
+  // - If the class is templated, the tables are emitted in every object that references any of them.
+  // - Otherwise, if the class is attached to a module, the tables are uniquely emitted in the object for the module unit in which it is defined.
+  // - Otherwise, if the class has a key function (see below), the tables are emitted in the object for the translation unit containing the definition of the key function. This is unique if the key function is not inline.
+  // - Otherwise, the tables are emitted in every object that references any of them.
+  if (Module *M = RD->getOwningModule(); M && M->isNamedModule())
+    return M != CGM.getContext().getCurrentNamedModule();
+
   // Otherwise, if the class doesn't have a key function (possibly
   // anymore), the vtable must be defined here.
   const CXXMethodDecl *keyFunction = CGM.getContext().getCurrentKeyFunction(RD);
@@ -1189,13 +1208,7 @@ bool CodeGenVTables::isVTableExternal(const CXXRecordDecl *RD) {
   const FunctionDecl *Def;
   // Otherwise, if we don't have a definition of the key function, the
   // vtable must be defined somewhere else.
-  if (!keyFunction->hasBody(Def))
-    return true;
-
-  assert(Def && "The body of the key function is not assigned to Def?");
-  // If the non-inline key function comes from another module unit, the vtable
-  // must be defined there.
-  return Def->isInAnotherModuleUnit() && !Def->isInlineSpecified();
+  return !keyFunction->hasBody(Def);
 }
 
 /// Given that we're currently at the end of the translation unit, and
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index d173806ec8ce53..7e2c1191071c84 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -1801,6 +1801,12 @@ void ItaniumCXXABI::emitVTableDefinitions(CodeGenVTables &CGVT,
   if (VTable->hasInitializer())
     return;
 
+  // If the class are attached to a C++ named module other than the one
+  // we're currently compiling, the vtable should be defined there.
+  if (Module *M = RD->getOwningModule(); M && M->isNamedModule() &&
+      M != CGM.getContext().getCurrentNamedModule())
+    return;
+
   ItaniumVTableContext &VTContext = CGM.getItaniumVTableContext();
   const VTableLayout &VTLayout = VTContext.getVTableLayout(RD);
   llvm::GlobalVariable::LinkageTypes Linkage = CGM.getVTableLinkage(RD);
diff --git a/clang/lib/CodeGen/ModuleBuilder.cpp b/clang/lib/CodeGen/ModuleBuilder.cpp
index 3594f4c66e6774..76175a157ba7bb 100644
--- a/clang/lib/CodeGen/ModuleBuilder.cpp
+++ b/clang/lib/CodeGen/ModuleBuilder.cpp
@@ -317,7 +317,7 @@ namespace {
     void HandleVTable(CXXRecordDecl *RD) override {
       if (Diags.hasErrorOccurred())
         return;
-
+      
       Builder->EmitVTable(RD);
     }
   };
diff --git a/clang/test/CodeGenCXX/modules-vtable.cppm b/clang/test/CodeGenCXX/modules-vtable.cppm
index fb179b1de4880b..abbbc403ace243 100644
--- a/clang/test/CodeGenCXX/modules-vtable.cppm
+++ b/clang/test/CodeGenCXX/modules-vtable.cppm
@@ -24,6 +24,8 @@
 // RUN:     %t/M-A.cppm -o %t/M-A.pcm
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -fmodule-file=M:A=%t/M-A.pcm \
 // RUN:     %t/M-B.cppm  -emit-llvm -o - | FileCheck %t/M-B.cppm
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
+// RUN:     %t/M-A.pcm  -emit-llvm -o - | FileCheck %t/M-A.cppm
 
 //--- Mod.cppm
 export module Mod;
@@ -41,9 +43,10 @@ Base::~Base() {}
 // CHECK: @_ZTSW3Mod4Base = constant
 // CHECK: @_ZTIW3Mod4Base = constant
 
-// CHECK-INLINE: @_ZTVW3Mod4Base = linkonce_odr {{.*}}unnamed_addr constant
-// CHECK-INLINE: @_ZTSW3Mod4Base = linkonce_odr {{.*}}constant
-// CHECK-INLINE: @_ZTIW3Mod4Base = linkonce_odr {{.*}}constant
+// With the new Itanium C++ ABI, the linkage of vtables in modules don't need to be linkonce ODR.
+// CHECK-INLINE: @_ZTVW3Mod4Base = {{.*}}unnamed_addr constant
+// CHECK-INLINE: @_ZTSW3Mod4Base = {{.*}}constant
+// CHECK-INLINE: @_ZTIW3Mod4Base = {{.*}}constant
 
 module :private;
 int private_use() {
@@ -60,11 +63,11 @@ int use() {
 
 // CHECK-NOT: @_ZTSW3Mod4Base = constant
 // CHECK-NOT: @_ZTIW3Mod4Base = constant
-// CHECK: @_ZTVW3Mod4Base = external unnamed_addr
+// CHECK: @_ZTVW3Mod4Base = external
 
-// CHECK-INLINE: @_ZTVW3Mod4Base = linkonce_odr {{.*}}unnamed_addr constant
-// CHECK-INLINE: @_ZTSW3Mod4Base = linkonce_odr {{.*}}constant
-// CHECK-INLINE: @_ZTIW3Mod4Base = linkonce_odr {{.*}}constant
+// CHECK-INLINE-NOT: @_ZTSW3Mod4Base = constant
+// CHECK-INLINE-NOT: @_ZTIW3Mod4Base = constant
+// CHECK-INLINE: @_ZTVW3Mod4Base = external
 
 // Check the case that the declaration of the key function comes from another
 // module unit but the definition of the key function comes from the current
@@ -82,6 +85,10 @@ int a_use() {
     return 43;
 }
 
+// CHECK: @_ZTVW1M1C = unnamed_addr constant
+// CHECK: @_ZTSW1M1C = constant
+// CHECK: @_ZTIW1M1C = constant
+
 //--- M-B.cppm
 export module M:B;
 import :A;
@@ -93,6 +100,6 @@ int b_use() {
     return 43;
 }
 
-// CHECK: @_ZTVW1M1C = unnamed_addr constant
-// CHECK: @_ZTSW1M1C = constant
-// CHECK: @_ZTIW1M1C = constant
+// CHECK: @_ZTVW1M1C = external
+// CHECK-NOT: @_ZTSW1M1C = constant
+// CHECK-NOT: @_ZTIW1M1C = constant
diff --git a/clang/test/CodeGenCXX/pr70585.cppm b/clang/test/CodeGenCXX/pr70585.cppm
new file mode 100644
index 00000000000000..5c8245c0efbf80
--- /dev/null
+++ b/clang/test/CodeGenCXX/pr70585.cppm
@@ -0,0 +1,50 @@
+// REQUIRES: !system-windows
+
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: cd %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/layer1.cppm -triple %itanium_abi_triple \
+// RUN:     -emit-module-interface -o %t/foo-layer1.pcm
+// RUN: %clang_cc1 -std=c++20 %t/layer2.cppm -triple %itanium_abi_triple  \
+// RUN:     -emit-module-interface -fmodule-file=foo:layer1=%t/foo-layer1.pcm \
+// RUN:     -o %t/foo-layer2.pcm
+// RUN: %clang_cc1 -std=c++20 %t/foo-layer1.pcm -S -emit-llvm -o - | FileCheck %t/layer1.cppm
+// RUN: %clang_cc1 -std=c++20 %t/foo-layer2.pcm -S -emit-llvm -o - \
+// RUN:     -fmodule-file=foo:layer1=%t/foo-layer1.pcm | FileCheck %t/layer2.cppm
+
+//--- layer1.cppm
+export module foo:layer1;
+struct Fruit {
+    virtual ~Fruit() = default;
+    virtual void eval() = 0;
+};
+struct Banana : public Fruit {
+    Banana() {}
+    void eval() override;
+};
+
+// CHECK-DAG: @_ZTVW3foo6Banana = unnamed_addr constant
+// CHECK-DAG: @_ZTSW3foo6Banana = constant
+// CHECK-DAG: @_ZTIW3foo6Banana = constant
+//
+// CHECK-DAG: @_ZTVW3foo5Fruit = unnamed_addr constant
+// CHECK-DAG: @_ZTSW3foo5Fruit = constant
+// CHECK-DAG: @_ZTIW3foo5Fruit = constant
+
+//--- layer2.cppm
+export module foo:layer2;
+import :layer1;
+export void layer2_fun() {
+    Banana *b = new Banana();
+    b->eval();
+}
+void Banana::eval() {
+}
+
+// CHECK-NOT: @_ZTVW3foo6Banana
+// CHECK-NOT: @_ZTSW3foo6Banana
+// CHECK-NOT: @_ZTIW3foo6Banana
+// CHECK-NOT: @_ZTVW3foo5Fruit
+// CHECK-NOT: @_ZTSW3foo5Fruit
+// CHECK-NOT: @_ZTIW3foo5Fruit

Comment on lines 18 to 28
struct Fruit {
virtual ~Fruit() = default;
virtual void eval() = 0;
};
struct Banana : public Fruit {
Banana() {}
void eval() override;
};

Copy link
Collaborator

Choose a reason for hiding this comment

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

What different cases do these two classes test? Would one class be sufficient?

Copy link
Member Author

Choose a reason for hiding this comment

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

This comes from the original reproducer while the base class is a pure virtual class that can't be instantiated. While it may be sufficient to reduce them to a single class, I feel slightly better to keep the form since it is not complex.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Not complex, but does obscure the intent of the test somewhat - keeping things as reduced as possible makes the test easier to understand - having superfluous details makes it harder to read/understand the purpose of the test.

Copy link
Member Author

Choose a reason for hiding this comment

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

Great suggestion. I caught another bug after I removed Banana: previously we would only generate vtable if it is used. And my draft to fix that is ChuanqiXu9@f6a2284. It touches Sema and Serializer. So I feel it is a little bit complex even if it is small. Personally, I prefer to separate that into 2 patches later instead of combining everything in the current single patch. How do you feel about the direction? @dwblaikie @rjmccall

Copy link
Member Author

Choose a reason for hiding this comment

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

I've just tried to update it here. If you feel it is too sparse, I can try to make it into 2 patches.

Comment on lines 27 to 32
// CHECK-DAG: @_ZTVW3foo6Banana = unnamed_addr constant
// CHECK-DAG: @_ZTSW3foo6Banana = constant
// CHECK-DAG: @_ZTIW3foo6Banana = constant
//
// CHECK-DAG: @_ZTVW3foo5Fruit = unnamed_addr constant
// CHECK-DAG: @_ZTSW3foo5Fruit = constant
// CHECK-DAG: @_ZTIW3foo5Fruit = constant

Copy link
Collaborator

Choose a reason for hiding this comment

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

Should the linkage and be checked here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, the linkage is already tested. Currently we're checking the linkage is external (default linkage). For example, if the linkage is linkonce_odr, the output will be something like: // CHECK-DAG: @_ZTSW3foo5Fruit = linkonce_odr constant

Comment on lines 38 to 48
export void layer2_fun() {
Banana *b = new Banana();
b->eval();
}
void Banana::eval() {
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

What do these two uses check (I guess one invokes the ctor to check if the vtable is emitted wherever the ctor is, and the other defines a virtual member, to check if the key function triggers vtable emission?)? Perhaps they could be simpler and/or more explicit (maybe comments to document the cases they're trying to test) - but probably one case is sufficient? (key function, I guess)

Copy link
Member Author

Choose a reason for hiding this comment

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

We're testing:
(1) The use of virtual functions won't produce the vtable.
(2) The definition of key functions won't produce the vtable.

I'll try to add a comment here.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

Comment on lines 45 to 47
// CHECK-NOT: @_ZTVW3foo6Banana
// CHECK-NOT: @_ZTSW3foo6Banana
// CHECK-NOT: @_ZTIW3foo6Banana
// CHECK-NOT: @_ZTVW3foo5Fruit
// CHECK-NOT: @_ZTSW3foo5Fruit
// CHECK-NOT: @_ZTIW3foo5Fruit
Copy link
Collaborator

Choose a reason for hiding this comment

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

Perhaps this could be generalized to // CHECK-NOT: @_Z{{.*(Banana|Fruit)}} or something like that? (even simpler if only one type is used here)

Copy link
Member Author

Choose a reason for hiding this comment

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

I prefer the current pattern so that other developers can understand what we're testing by llvm-cxxfilt.

@ChuanqiXu9
Copy link
Member Author

@dwblaikie @rjmccall ping~

// becomes meaningless. So the linkage of the vtable should always be
// external if the class is externally visible.
//
// TODO: How about the case of AppleKext, DLLExportAttr and DLLImportAttr.
Copy link
Contributor

Choose a reason for hiding this comment

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

Comments should be timeless: they exist to explain the current code, not the patch you're making. So don't say stuff like "previously". You can just "V-tables for non-template classes with an owning module are always uniquely emitted in that module."

It's correct for this to apply even for the Apple kext, dllexport, and dllimport cases. But I think you do actually need to handle the template case here; we can't emit v-tables for template instantiations with strong linkage.

I think this function could probably be significantly simplified to eliminate a lot of the redundancy; it's essentially:

  • Use internal linkage if the class isn't externally visible.
  • Decide whether the v-table has vague linkage.
  • If not, it gets either external or available-externally linkage based on a few different criteria.
  • If so, there's a bunch of platform/ABI-specific decisions that affect the exact LLVM linkage we pick.

Copy link
Member Author

Choose a reason for hiding this comment

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

I've updated the comments. And I'd like to leave the simplification to later patches.

clang/lib/CodeGen/ItaniumCXXABI.cpp Outdated Show resolved Hide resolved
@llvmbot llvmbot added the clang:frontend Language frontend issues, e.g. anything involving "Sema" label Jan 25, 2024
Copy link

github-actions bot commented Jan 25, 2024

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

@ChuanqiXu9
Copy link
Member Author

@rjmccall @dwblaikie

@ChuanqiXu9
Copy link
Member Author

@rjmccall @dwblaikie ping

// CHECK-INLINE: @_ZTVW3Mod4Base = linkonce_odr {{.*}}unnamed_addr constant
// CHECK-INLINE: @_ZTSW3Mod4Base = linkonce_odr {{.*}}constant
// CHECK-INLINE: @_ZTIW3Mod4Base = linkonce_odr {{.*}}constant
// With the new Itanium C++ ABI, the linkage of vtables in modules don't need to be linkonce ODR.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would they benefit from still being linkonce? What if the vtable is never used? Or do we leave that up to --gc-sections linker behavior? (I'm not clear on when we use linkage to let things be dropped, and when we use the linker function-sections/data-sections/gc-sections behavior to drop unused stuff)

Copy link
Member Author

Choose a reason for hiding this comment

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

If it is linkonce, as long as I understand correctly, if the vtable is not used in the current TU, the middle end is free to remove the unused linkonce entity. But this is not wanted. Since all the things in the module purview is potentially used. Do I misunderstand anything?

Copy link
Collaborator

Choose a reason for hiding this comment

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

right, sorry, was misreading the linkages - I guess weak might be applicable?

“weak” linkage has the same merging semantics as linkonce linkage, except that unreferenced globals with weak linkage may not be discarded.
But maybe just normal external linkage is the right thing & we let the linker GC things if the user asks it to. Fair enough.

Copy link
Member Author

Choose a reason for hiding this comment

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

While I agree weak may work here, I feel it is odd. I feel the default strong linkage seems better when it is applicable. What's the advance to use weak linkage here?

// CHECK-INLINE: @_ZTVW3Mod4Base = linkonce_odr {{.*}}unnamed_addr constant
// CHECK-INLINE: @_ZTSW3Mod4Base = linkonce_odr {{.*}}constant
// CHECK-INLINE: @_ZTIW3Mod4Base = linkonce_odr {{.*}}constant
// CHECK-INLINE-NOT: @_ZTSW3Mod4Base = constant
Copy link
Collaborator

Choose a reason for hiding this comment

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

NOT checks should be pretty broad - for instance these NOT checks would've passed with the old code too (since it was linkonce_odr, not constant) - perhaps these should just be non-NOT checks, affirmatively checking for the linkage of these globals (which I assume they are emitted, as declarations - if they aren't emitted at all, then NOT: @foo might be enough, without the = constant?)

Copy link
Member Author

Choose a reason for hiding this comment

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

They should be emitted in other TU and not emitted at all in the current TU. So I updated the test to // CHECK-INLINE-NOT: @_ZTSW3Mod4Base as suggested.

Copy link
Contributor

@iains iains left a comment

Choose a reason for hiding this comment

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

I am going to defer to @rjmccall and @dwblaikie on this one (the comments so far seem reasonable to me).

@ChuanqiXu9 ChuanqiXu9 merged commit 15bb026 into llvm:main Jun 17, 2024
4 of 7 checks passed
searlmc1 pushed a commit to ROCm/llvm-project that referenced this pull request Jun 17, 2024
xfails:
* clang/test/CodeGenCXX/modules-vtable.cppm
* clang/test/CodeGenCXX/pr70585.cppm

cause:
llvm#75912
[C++20] [Modules] [Itanium ABI] Generate the vtable in
the module unit of dynamic classes

Change-Id: I7c502ffc1fe6bc7f98f3a60bc6c7bfe0af52b497
ChuanqiXu9 added a commit that referenced this pull request Jul 2, 2024
named modules

Close #97313

In the previous patch (#75912),
I made an oversight that I ignored the templates in named module when
calculating the linkage for the vtables. In this patch, I tried to
correct the behavior by merging the logics to calculate the linkage with
key functions with named modules.
kirillpyasecky pushed a commit to kirillpyasecky/llvm-project that referenced this pull request Jul 3, 2024
named modules

Close llvm#97313

In the previous patch (llvm#75912),
I made an oversight that I ignored the templates in named module when
calculating the linkage for the vtables. In this patch, I tried to
correct the behavior by merging the logics to calculate the linkage with
key functions with named modules.
kbluck pushed a commit to kbluck/llvm-project that referenced this pull request Jul 6, 2024
named modules

Close llvm#97313

In the previous patch (llvm#75912),
I made an oversight that I ignored the templates in named module when
calculating the linkage for the vtables. In this patch, I tried to
correct the behavior by merging the logics to calculate the linkage with
key functions with named modules.
@mizvekov
Copy link
Contributor

mizvekov commented Jul 9, 2024

FYI, the commit 99873b3, which lists this PR as a motivator, causes breakage building a project, which I am still looking for a reduced test case.

The commit is not NFC despite what is says, just by cursory inspection of the change.

Please avoid adding NFC tag to such commits in the future and create a pull request.

@ChuanqiXu9
Copy link
Member Author

FYI, the commit 99873b3, which lists this PR as a motivator, causes breakage building a project, which I am still looking for a reduced test case.

The commit is not NFC despite what is says, just by cursory inspection of the change.

Please avoid adding NFC tag to such commits in the future and create a pull request.

Oh, sorry, I took another look at the commit and it looks the change makes it not a NFC change is this line: 99873b3#diff-6fe53759e8d797c328c73ada5f3324c6248a8634ef36131c7eb2b9d89192bb64R6514

This shouldn't be in that commit but in this commit. It is not intentional. I guess we can't observe that if we put that in this PR too. And that change looks not bad. So maybe it makes something already bad to show up.

@ChuanqiXu9
Copy link
Member Author

Given this patch has another unexpected behavior in windows (#97447), I'd like to revert this temporarily to wait for the response from Windows devs and your reproducer @mizvekov

ChuanqiXu9 added a commit that referenced this pull request Jul 10, 2024
…dule unit of dynamic classes (#75912)"

This reverts commit 18f3bcb, 15bb026 and
99873b3.

See the post commit message in
#75912 to see the reasons.
ChuanqiXu9 added a commit that referenced this pull request Jul 10, 2024
Due to #75912 is reverted and
#70585 is reopened. It looks
riskful to fix the issue correctly before 19. So we provide a workaround
here to help people in this trouble as much as possible.
@mizvekov
Copy link
Contributor

The reproducer turned out to be pretty simple:

export module a;
module :private;
static void f() {}
void g() {
  f();
}

Compiles without that patch, otherwise produces:

error: no matching function for call to 'f'

Oh, sorry, I took another look at the commit and it looks the change makes it not a NFC change is this line: 99873b3#diff-6fe53759e8d797c328c73ada5f3324c6248a8634ef36131c7eb2b9d89192bb64R6514

This shouldn't be in that commit but in this commit. It is not intentional. I guess we can't observe that if we put that in this PR too. And that change looks not bad. So maybe it makes something already bad to show up.

Even without that change, the other changes are too complex, and there are other suspicious things you wouldn't expect in an NFC commit which doesn't call this out specifically, like the removal of that whole block with the FIXME included.

I think the appropriate tag for such commits would be NFCI, and should still require PR and review.

@ChuanqiXu9
Copy link
Member Author

The reproducer turned out to be pretty simple:

export module a;
module :private;
static void f() {}
void g() {
  f();
}

Compiles without that patch, otherwise produces:

error: no matching function for call to 'f'

Oh, sorry, I took another look at the commit and it looks the change makes it not a NFC change is this line: 99873b3#diff-6fe53759e8d797c328c73ada5f3324c6248a8634ef36131c7eb2b9d89192bb64R6514
This shouldn't be in that commit but in this commit. It is not intentional. I guess we can't observe that if we put that in this PR too. And that change looks not bad. So maybe it makes something already bad to show up.

Even without that change, the other changes are too complex, and there are other suspicious things you wouldn't expect in an NFC commit which doesn't call this out specifically, like the removal of that whole block with the FIXME included.

I think the appropriate tag for such commits would be NFCI, and should still require PR and review.

Thanks for the reproducer and the information about NFCI. It looks like I took an oversight in the refactoring of isInAnotherModuleUnit that I missed the case about private module fragment.

@dwblaikie
Copy link
Collaborator

I think the appropriate tag for such commits would be NFCI, and should still require PR and review.

Clarifying a couple of things here

  1. I tend to agree the patch might've been a bit subtle and maybe split up into smaller, independent parts (usual rules: be an isolated change. Independent changes should be submitted as separate patches as this makes reviewing easier
  2. I really don't think anyone should read into a distinction between NFC and NFCI, I don't think it's helpful to try to create a distinction here - NFC is already a statement of intent, as much as any patch description is always a statement of intent. We all make mistakes, but I don't think adding "intended" to the acronym is really helpful
  3. Non-NFC changes don't necessarily require PR/review - LLVM allows for, especially code owners, to commit without precommit review if the change is sufficiently obvious. So some simple cleanups, clear bug fixes, direction already has buy-in, etc, are often committed without precommit review.

But, yeah, bit more caution in the future, and probably smaller/more isolated patches would be good in any case.

@mizvekov
Copy link
Contributor

Clarifying a couple of things here

I think we need the distinction between simple NFC, for trivial, non-controversial and non-surprising changes like some renames, small whitespace / formatting change, documentation changes, and complex NFC, which requires some thinking and has more risk of not actually being NFC.

So in practical terms, I propose a complex NFC change should only be excused of changes to functional testing, nothing else.

Also, I think we presently have sufficient review bandwidth in clang proper, even including the area of modules, so that any non-trivial change can be reviewed, and I also propose that we abstain from trying to directly commit those.

@dwblaikie
Copy link
Collaborator

Also, I think we presently have sufficient review bandwidth in clang proper, even including the area of modules, so that any non-trivial change can be reviewed, and I also propose that we abstain from trying to directly commit those.

That's a pretty substantial policy change to propose, and this probably isn't the place to propose/discuss it. If that's your intent, probably best to take that up on discord.

NFC, to me, has always been basically "this has no observable change/can't be tested" - it's not a bar on whether something should be reviewed. (lots of NFC patches get sent for review, but not all of them to be sure - and equally not all non-NFC patches get sent for review)

(FWIW, check some of the recent modules changes @ChuanqiXu9 has been working on to see that reviewer bandwidth here is pretty thin (& my experience in LLVM in general, including clang, is that reviewer bandwidth is pretty thin - though it is something we should address & I do think it might be time to change LLVM's post-commit review policy, but I think it'll be a substantial amount of work))

aaryanshukla pushed a commit to aaryanshukla/llvm-project that referenced this pull request Jul 14, 2024
…dule unit of dynamic classes (llvm#75912)"

This reverts commit 18f3bcb, 15bb026 and
99873b3.

See the post commit message in
llvm#75912 to see the reasons.
aaryanshukla pushed a commit to aaryanshukla/llvm-project that referenced this pull request Jul 14, 2024
Due to llvm#75912 is reverted and
llvm#70585 is reopened. It looks
riskful to fix the issue correctly before 19. So we provide a workaround
here to help people in this trouble as much as possible.
@mizvekov
Copy link
Contributor

That's a pretty substantial policy change to propose, and this probably isn't the place to propose/discuss it. If that's your intent, probably best to take that up on discord.

I am not proposing a policy change. I believe the current policy is aimed at giving an escape hatch for projects which there is basically one or two active developers. I am pointing out that I believe we don't need for that escape hatch to apply to any parts of clang currently.

(FWIW, check some of the recent modules changes @ChuanqiXu9 has been working on to see that reviewer bandwidth here is pretty thin (& my experience in LLVM in general, including clang, is that reviewer bandwidth is pretty thin - though it is something we should address & I do think it might be time to change LLVM's post-commit review policy, but I think it'll be a substantial amount of work))

If you feel bandwidth for modules is pretty thin, I put myself available as a reviewer, so feel free to ping me.
I may not have a lot of time available for fully reviewing big patches, but I can certainly help with the smaller patches such as this one.

@ChuanqiXu9
Copy link
Member Author

(FWIW, check some of the recent modules changes @ChuanqiXu9 has been working on to see that reviewer bandwidth here is pretty thin (& my experience in LLVM in general, including clang, is that reviewer bandwidth is pretty thin - though it is something we should address & I do think it might be time to change LLVM's post-commit review policy, but I think it'll be a substantial amount of work))

If you feel bandwidth for modules is pretty thin, I put myself available as a reviewer, so feel free to ping me. I may not have a lot of time available for fully reviewing big patches, but I can certainly help with the smaller patches such as this one.

Thanks. I'll try to add you for future reviewings.

That's a pretty substantial policy change to propose, and this probably isn't the place to propose/discuss it. If that's your intent, probably best to take that up on discord.

I am not proposing a policy change. I believe the current policy is aimed at giving an escape hatch for projects which there is basically one or two active developers. I am pointing out that I believe we don't need for that escape hatch to apply to any parts of clang currently.

To be honest, this is not what I feel at least for coroutines and modules for a long time. I had multiple experience that some patches doesn't get reviewed for months. I still believe it will work better if I can push some simple patches directly.

@dwblaikie
Copy link
Collaborator

That's a pretty substantial policy change to propose, and this probably isn't the place to propose/discuss it. If that's your intent, probably best to take that up on discord.

I am not proposing a policy change. I believe the current policy is aimed at giving an escape hatch for projects which there is basically one or two active developers. I am pointing out that I believe we don't need for that escape hatch to apply to any parts of clang currently.

With a fair bit of experience on this project and the norms - I can say pretty confidently that was/is not the intent of the current policy. Especially for code owners, there's an expectation they probably know better than anyone else & can make summary judgments on direction in many cases. Every now and then they'll ask for second opinions on uncertain directions, but can probably pretty well determine what's acceptable for most work without a second opinion.

(honestly, moving to all changes being reviewed might be good for the project - though I think we'd need to work more on ownership and reviewer availability/authority/process, but it's not where we are today in either case (policy or socially))

The dev policy says "Smaller patches (or patches where the developer owns the component) that meet likely-community-consensus requirements (as apply to all patch approvals) can be committed prior to an explicit review. " (in this case @ChuanqiXu9 is the owner of clang's modules support)

(FWIW, check some of the recent modules changes @ChuanqiXu9 has been working on to see that reviewer bandwidth here is pretty thin (& my experience in LLVM in general, including clang, is that reviewer bandwidth is pretty thin - though it is something we should address & I do think it might be time to change LLVM's post-commit review policy, but I think it'll be a substantial amount of work))

If you feel bandwidth for modules is pretty thin, I put myself available as a reviewer, so feel free to ping me. I may not have a lot of time available for fully reviewing big patches, but I can certainly help with the smaller patches such as this one.

While I appreciate the offer and would encourage you to review these changes - I'll point out that one of the reasons these reviews haven't been forthcoming is that the feature area is fairly complicated and nuanced.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ABI Application Binary Interface clang:codegen clang:frontend Language frontend issues, e.g. anything involving "Sema" 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] Clang didn't generate the virtual table to the unit containing the key function
7 participants