"weak" attribute semantics on const variables

Hi,

with a variable declared (in C) as follows
const char x[] __attribute__ ((weak)) = "X";
clang/llvm will use initializer value for optimizations.

gcc does not.

In clang variables with weak attribute gets “weak” linkage, unless they are const then they get “weak_odr” linkage. (CodeGenModule::getLLVMLinkageForDeclarator)

And “weak” linkage is “interposable” while “weak_odr” isn’t. (GlobalValue::isInterposableLinkage). Allowing optimizations to use the value of the initializer in the “weak_odr” case.

Is this expected/correct behavior?
Is there some specification describing this behavior?

Unfortunately the “weak” attribute isn’t documented in AttrDocs.td. And gcc’s documentation doesn’t help much, but does use the wording “overriding symbol” which definitely alludes to interposition being allowed.

The closest thing to documentation in clang I can find is this comment in the testcase:
https://github.com/llvm/llvm-project/blob/main/clang/test/CodeGen/global-init.c#L15

// Since this is marked const, it should get weak_odr linkage, since all
// definitions have to be the same.
// CHECK: @d = weak_odr constant i32 0
const int d __attribute__((weak))= 0;

I can easily accept that in case of multiple weak definitions of weak symbol all must have same value (odr).
However my intuition (which may be based on misunderstanding) is that a non-weak (strong) definition of that same symbol does not have to have the same value even if it is const. I would like to believe that the point of having weak symbols is that they provide a sort of default which can be overridden by a different value during linking (and that is how it works for non-const variables (and functions)).

Oh btw, the LangRef says

weak linkage has the same merging semantics as linkonce linkage, except that unreferenced globals with weak linkage may not be discarded. This is used for globals that are declared “weak” in C source code.

Which leaves out the part about it not being used it is a const variable.

The reward for reading this far is an example showing the behavior in compiler explorer: Compiler Explorer

I spoke with Anders about this at the devmtg. I don’t have any recollection of the original patch - it looks like it was back in the days of setting up the MC layer and cleaning up a bunch of linkage issues. It looks like we reject this construct entirely in C++ mode, and I don’t see a reason to preserve this behavior in C mode. I support dropping the "odr’ness’ of the generated global variable.

-Chris

For reference, the patch referred to is weak globals that are const should get weak_odr linkage. · llvm/llvm-project@f49573d · GitHub

We do support this construct in C++ though:
extern const char x[] __attribute__ ((weak)) = "X";
(const makes it internal linkage in C++ adding extern overrides that)

So I guess I’ll have make sure it still ends up as weak_odr in that case for C++. Or should the fact that it is declared extern there also imply that it can be interposed?

Yeah, I think it makes sense for that to be weak_odr in c++ mode. ‘extern’ with an initializer is crazy. :slight_smile:

1 Like

Switching from weak_odr to weak for both C and C++ makes sense to me. It should match the GCC semantics.