This is an archive of the discontinued LLVM Phabricator instance.

[Clang][AIX] Add .ref in frontend for AIX XCOFF to support `-bcdtors:csect` linker option
Needs ReviewPublic

Authored by tingwang on May 6 2022, 7:39 AM.

Details

Reviewers
nemanjai
shchenz
hubert.reinterpretcast
cebowleratibm
jsji
Group Reviewers
Restricted Project
Summary

This is the frontend part of .ref enablement. It works with D122198, and implements below items:

(1) variable to init/term functions (required to add functions to _cdtors array if the variable is included in linker output)
(2) dtor function to term function (required to correctly handle atexit/unatexit registration/unregistration)

Diff Detail

Event Timeline

tingwang created this revision.May 6 2022, 7:39 AM
Herald added a project: Restricted Project. · View Herald TranscriptMay 6 2022, 7:39 AM
tingwang requested review of this revision.May 6 2022, 7:39 AM
tingwang updated this revision to Diff 428268.May 9 2022, 7:49 PM

Update the three test cases introduced in this patch to use opaque-pointer
clang/test/CodeGen/PowerPC/aix-init-ref-null.cpp
clang/test/CodeGen/PowerPC/aix-ref-static-var.cpp
clang/test/CodeGen/PowerPC/aix-ref-tls_init.cpp

Gentle ping.

Thanks for doing this. I am not familiar with the frontend, so I may be wrong/stupid in the follow comments : )
Hope other experts like @hubert.reinterpretcast can give more meaningful comments.

I tested on AIX, seems for static variable static int x = foo(); in global scope, even compile with -bcdtors:csect, the init function also will not be eliminated. Could you please give an example to show why we need the new associated metadata for this case? Thanks.

And we may need to update the docs for associated metadata too in docs/LangRef.rst

clang/lib/CodeGen/CGDeclCXX.cpp
543

Should we use addVarTermAssoc?

870

FFDtorTermAssoc should store the mapping between dtor and term functions? So why we need to update this container when we generate wrapper function for init function? I think in the init function there should only be ctors related functions?

And why we don't need to update for VarsWithInitTerm, in that container there should be some static variables reply on the wrapper init function?

clang/lib/CodeGen/CodeGenModule.cpp
5088

Why do we need to add mapping between a variable and its address? We already map the global and its init function in above EmitCXXGlobalVarDeclInitFunc?

clang/lib/CodeGen/CodeGenModule.h
488

Is there any reason why we need vector here instead of map? Can you give an example that shows one global variable will be connected with more than one init functions?

jsji resigned from this revision.Jun 2 2022, 7:51 AM
tingwang added a comment.EditedJun 5 2022, 8:21 PM

Thanks for doing this. I am not familiar with the frontend, so I may be wrong/stupid in the follow comments : )
Hope other experts like @hubert.reinterpretcast can give more meaningful comments.

I tested on AIX, seems for static variable static int x = foo(); in global scope, even compile with -bcdtors:csect, the init function also will not be eliminated. Could you please give an example to show why we need the new associated metadata for this case? Thanks.

Here is one example to show:

TEST_FOLDER=/tmp/test
mkdir -p $TEST_FOLDER
cd $TEST_FOLDER
cat > libbar.cc <<EOF
int globalVar = 2;
extern "C" int puts(const char *);
int foo() {
  puts("foo");
  globalVar = 42;
  return 30;
}
static int x = foo();
EOF

cat > libbaz.cc <<EOF
extern "C" int puts(const char *);
template <typename = void>
struct A {
  ~A() { puts("struct A ~A() 2"); }
  static A instance;
};

template <typename T> A<T> A<T>::instance;
void *zap() { return &A<>::instance; }

EOF

cat > uselib.cc <<EOF
#include <dlfcn.h>

int main(void) {
    void *handle = dlopen("./libbaz.so", RTLD_NOW | RTLD_LOCAL);
    dlclose(handle);
}
EOF
  • g++
XLC=g++
$XLC -fno-exceptions -c libbar.cc
rm -f libbar.a
ar qs libbar.a libbar.o
ranlib libbar.a
$XLC -fno-exceptions -c libbaz.cc
$XLC -shared -o libbaz.so libbaz.o libbar.a
$XLC -fno-exceptions -ldl uselib.cc -o uselib -ldl
./uselib 
foo
struct A ~A() 2
  • XLC 16.1.0
XLC=/gsa/rtpgsa/projects/x/xlcmpbld/run/vacpp/16.1.0/aix/daily/191109/bin/xlclang++
$XLC -g -qnoeh -qfuncsect -c libbar.cc
rm -f libbar.a
ar qs libbar.a libbar.o
ranlib libbar.a
$XLC -g -qnoeh -qfuncsect -c libbaz.cc
$XLC -g -qtwolink -G -o libbaz.so libbaz.o libbar.a
$XLC -g -qnoeh uselib.cc -o uselib
./uselib 
foo
struct A ~A() 2
  • clang++ baseline
XLC=/home/tingwa/repo/llvm-project-base/dev/build/bin/clang++
$XLC -g -fignore-exceptions -ffunction-sections -c libbar.cc
rm -f libbar.a
ar qs libbar.a libbar.o
ranlib libbar.a
$XLC -g -fignore-exceptions -ffunction-sections -c libbaz.cc
$XLC -g -bcdtors:csect -shared -Wl,-G -o libbaz.so libbaz.o libbar.a
$XLC -g -fignore-exceptions uselib.cc -o uselib
./uselib
struct A ~A() 2
  • clang++ .ref
XLC=/home/tingwa/repo/llvm-project-12514-BE/dev/build/bin/clang++
$XLC -g -fignore-exceptions -ffunction-sections -c libbar.cc
rm -f libbar.a
ar qs libbar.a libbar.o
ranlib libbar.a
$XLC -g -fignore-exceptions -ffunction-sections -c libbaz.cc
$XLC -g -bcdtors:csect -shared -Wl,-G -o libbaz.so libbaz.o libbar.a
$XLC -g -fignore-exceptions uselib.cc -o uselib
./uselib 
foo
struct A ~A() 2

As shown in this example: without .ref association, clang++ baseline case will be wrong, since globalVar is updated in the init function.

tingwang marked an inline comment as done.Jun 8 2022, 4:41 AM
tingwang added inline comments.
clang/lib/CodeGen/CGDeclCXX.cpp
870

FFDtorTermAssoc should store the mapping between dtor and term functions? So why we need to update this container when we generate wrapper function for init function? I think in the init function there should only be ctors related functions?

Thank you for pointing out! This is redundant.

And why we don't need to update for VarsWithInitTerm, in that container there should be some static variables reply on the wrapper init function?

VarsWithInitTerm keeps track of mapping between variables in clang (Decl*) and the corresponding data structure in llvm (Constant *). To me it's stable, and not like functions which could be wrapped in new functions.

clang/lib/CodeGen/CodeGenModule.cpp
5088

It seems to me that clang most of the time operates on Decl*. However to generate metadata, we refer to llvm::Constant*. I did not find how to get llvm::Constant* from Decl* in clang, so I'm tracking that information. I will check again to see if there is any official way to do that but I'm not aware of.

clang/lib/CodeGen/CodeGenModule.h
488

One variable can have two functions associated: one init and one term, thus used vector for VFInitTermAssoc. Also it is better to use variable as key for the benefit of the inner for loop inside AddMeta.

Dtor-to-Term (FFDtorTermAssoc) could use map, however it shares similar update logic as VFInitTermAssoc (for example the code snippet AddMeta in CodeGenModule::genAssocMeta() is used on both data structure), so I prefer to use vector for both of them.

tingwang updated this revision to Diff 435107.Jun 8 2022, 4:59 AM

Update according to comments:
(1) Update docs
(2) Use addVarTermAssoc
(3) Remove redundant call to updateAssociatedFunc(FFDtorTermAssoc...
(4) Remove unnecessary llvm::array_pod_sort on FFDtorTermAssoc

Gentle ping.

Verified the patch works with latest code base, all tests green.

Sorry, I am really not familiar with these FE stuffs. Need approval from other reviewers who know more about this part.

clang/lib/CodeGen/CodeGenModule.cpp
558

Seems this dos not follow other functions call's style. Can we call a function like EmitAssociatedMetadata() here and do the clean up (cleanupAssoc() may not be needed) in the EmitAssociatedMetadata()?

clang/test/CodeGen/PowerPC/aix-init-ref-null.cpp
2

is -debug-info-kind=limited or -O3 necessary in this test? Same as other new added cases.

clang/test/CodeGen/PowerPC/aix-ref-tls_init.cpp
10 ↗(On Diff #435107)

Not sure if this is right or not. XLC on AIX seems refer to __tls_get_addr instead of __tls_init...

tingwang added inline comments.Jul 5 2022, 6:12 PM
clang/lib/CodeGen/CodeGenModule.cpp
558

Thanks! I will update the code.

clang/test/CodeGen/PowerPC/aix-init-ref-null.cpp
2

Oh, "-debug-info-kind=limited" is not required. I will remove those. The "-O3" flag is required to show that associated metadata can be nullptr. Without "-O3", normal llvm.global_ctors will be generated.

clang/test/CodeGen/PowerPC/aix-ref-tls_init.cpp
10 ↗(On Diff #435107)

I saw some case that AIX generated tls related association, and I was postulating that the association should be linked to _tls_init. I will revisit my case and update with more info or correct the association later.

tingwang updated this revision to Diff 442419.Jul 5 2022, 6:17 PM

Update according to comments:
(1) Merged cleanupAssoc() into genAssocMeta(), and renamed genAssocMeta() to EmitAssociatedMetadata().
(2) Removed "-debug-info-kind=limited" from all test cases.

tingwang updated this revision to Diff 442773.Jul 6 2022, 9:01 PM

Drop TLS related .ref for now

tingwang updated this revision to Diff 443201.Jul 8 2022, 4:17 AM

Add guards against TLS variables.

Currently this patch does not work due to limit set on associated metadata operand count (forced to be single operand).

commit 87f2e9448e82bbed4ac59bb61bea03256aa5f4de
Author: Matt Arsenault <Matthew.Arsenault@amd.com>
Date:   Mon Jan 9 12:17:38 2023 -0500

    Verifier: Add checks for associated metadata
    
    Also add missing assembler test for the valid cases.

I'm working on a patch to relieve this limit for TT.isOSAIX(), and will add that to the stack later.

tingwang updated this revision to Diff 504031.Mar 9 2023, 11:05 PM

Rebase and update patch
(1) Update verifier check on associated metadata to allow multiple operands for AIX.
(2) Update test cases to use opaque pointer.

tingwang updated this revision to Diff 505720.Mar 16 2023, 1:19 AM

As verifier change and baseline test cases have been moved into https://reviews.llvm.org/D145767, update this patch.