Hi,

I've been looking into existing function multiversioning implementations (while
working to add support for fmv in GCC on aarch64).  It seems there are various
inconsistencies among current implementations, and it's unclear to me which (if
any) of these differences could be problematic.  There's a list of observations
of current behaviour at the end of the email.

I've also seen the GCC documentation for the ifunc attribute [1].  This states
that "the indirect function needs to be defined in the same translation unit as
the resolver function".  This is not how function multiversioning is currently
implemented.  Instead, the resolver functions are added to the translation
units of every caller.  What is the reason for the restriction specified in the
documentation?  Does this mean that current function multiversioning
implementations are subtly broken when used across different translation units?

On a related point - how important is the mangling of the symbols involved (for
the function implementations, the resolver, and the symbol that will be
resolved at load time)?  We're not consistent about how we mangle these on any
target - even the explicit mangling requirements in the aarch64 Beta
specification [2] apply only to the function implementations, and make no
mention of how the other symbols should be mangled.

It seems to me that things would have been much simpler if the resolver were
always in the same translation unit as the implementations, and the symbol that
is resolved at run time used the original function symbol name.  This would
allow the mangling of the function versions and the resolver function to be a
hidden implementation detail, with no need to specify it as part of an explicit
or implicit ABI.  It would add an explicit restriction that all function
implementations must be in the same translation unit, but it seems that might
be required for absolute correctness anyway.

So, how much of this stuff matters?  Is some of this stuff actually broken?
Will we have horrible compatibility issues if we try to fix some of it?

Thanks,
Andrew

(cc'd to Daniel Kiss and Pavel Iliin, who have been working on the aarch64
function multiversioning specification and LLVM implementation respectively.)

---
Current behaviour:


For all combinations of the following, I checked to see whether the compiler
would emit a resolver function:
a) GCC / Clang
b) aarch64 / rs6000 / i386
c) target_clones attribute / target (or target_version) attribute
d) function is fully implemented / function is just declared
e) function is referenced / function is unreferenced

Function multiversioning is not supported for rs6000 (power) in Clang.  For
aarch64, I used my WIP implementation in GCC.

If the function was referenced, then the compiler always emitted a resolver.
However, when the function is declared but not implemented, then GCC for rs6000
would emit a resolver that always used the default version.

If the function was not referenced, then the compiler usually did not emit a
resolver.  The exceptions to this were both in GCC, with resolvers being
emitted both for rs6000 when using an implemented function with target_clones,
and for aarch64 (with my WIP patch) when using an implementations with
target_version.


There are also many discrepancies in the mangling used in the various symbol
names:

For the symbol that will be resolved at load time, Clang always uses the
original name and append ".ifunc".  GCC leaves the symbol name unchanged when
using target_clones, but adds a second layer of C++ mangling when using target
attribute versions.

For the resolver function, the symbol name is always generated by appending
".resolver" to the original mangled function name.

For the individual versions, the following changes are made:
- Clang for i386 numbers the versions when using target_clones (by appending
  ".0", ".1", etc.).  GCC used to share this behaviour, but the numbering was
removed for GCC 12 (commit bfc9250e).
- GCC for i386, when using target_clones, will substitute any non-alphanumeric
  character with "_" when formign the suffix.  E.g, an "sse4.2" version of foo
has a symbol name of "_Z3foov.sse4.2" using target attributes, but
"_Z3foov.sse4_2" using target_clones.
- GCC for rs6000 has no target-defined mangling for versions using the "target"
  attribute, so the compiler output will fail to assemble due to duplicate
symbol names.  However, there seem to be no errors or warnings to detect this
at an earlier stage.

(Incidentally, is there any specific documentation for rs6000 function
multiversioning? I was surprised to discover during testing that the resolver
was ignoring some of my function clones, until I found the list of supported
multiversioning targets in rs6000_clone_map.)

---

[1] 
https://gcc.gnu.org/onlinedocs/gcc-13.2.0/gcc/Common-Function-Attributes.html#index-ifunc-function-attribute

[2] 
https://github.com/ARM-software/acle/blob/main/main/acle.md#function-multi-versioning

Reply via email to