========
Multilib
========
Introduction
============
This document describes how multilib is implemented in Clang.
What is multilib and why might you care?
If you're :doc:`cross compiling<CrossCompilation>` then you can't use native
system headers and libraries. To address this, you can use a combination of
``--sysroot``, ``-isystem`` and ``-L`` options to point Clang at suitable
directories for your target.
However, when there are many possible directories to choose from, it's not
necessarily obvious which one to pick.
Multilib allows a toolchain designer to imbue the toolchain with the ability to
pick a suitable directory automatically, based on the options the user provides
to Clang. For example, if the user specifies
``--target=arm-none-eabi -mcpu=cortex-m4`` the toolchain can choose a directory
containing headers and libraries suitable for Armv7E-M, because it knows that's
a suitable architecture for Arm Cortex-M4.
Multilib can also choose between libraries for the same architecture based on
other options. For example if the user specifies ``-fno-exceptions`` then a
toolchain could select libraries built without exception support, thereby
reducing the size of the resulting binary.
Design
======
Clang supports GCC's ``-print-multi-lib`` and ``-print-multi-directory``
options. These are described in
`GCC Developer Options <https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gcc/Developer-Options.html>`_.
There are two ways to configure multilib in Clang: hard-coded or via a
configuration file.
Hard-coded Multilib
===================
The available libraries can be hard-coded in Clang. Typically this is done
using the ``MultilibBuilder`` interface in
``clang/include/clang/Driver/MultilibBuilder.h``.
There are many examples of this in ``lib/Driver/ToolChains/Gnu.cpp``.
The remainder of this document will not focus on this type of multilib.
EXPERIMENTAL Multilib via configuration file
============================================
Some Clang toolchains support loading multilib configuration from a
``multilib.yaml`` configuration file.
A ``multilib.yaml`` configuration file specifies which multilib variants are
available, their relative location, what compilation options were used to build
them, and the criteria by which they are selected.
Multilib processing
===================
Clang goes through the following steps to use multilib from a configuration
file:
#. Normalize command line options. Clang can accept the same
information via different options - for example,
``--target=arm-none-eabi -march=armv7-m`` and
``--target=armv7m-none-eabi`` are equivalent.
Clang normalizes the command line before passing them to the multilib system.
To see what flags are emitted for a given set of command line options, use
the ``-print-multi-flags-experimental`` command line option
along with the rest of the options you want to use.
#. Load ``multilib.yaml`` from sysroot.
#. Generate additional flags. ``multilib.yaml`` contains a ``Mappings`` section,
which specifies how to generate additional flags based on the flags derived
from command line options. Flags are matched using regular expressions.
These regular expressions shall use the POSIX extended regular expression
syntax.
#. Match flags against multilib variants. If the generated flags are a superset
of the flags specified for a multilib variant then the variant is considered
a match.
If more than one variant matches then a toolchain may opt to either use only
the *last* matching multilib variant, or may use all matching variants,
thereby :ref:`layering<multilib-layering>` them.
#. Generate ``-isystem`` and ``-L`` options. Iterate in reverse order over
the matching multilib variants, and generate ``-isystem`` and ``-L``
options based on the multilib variant's directory.
.. _multilib-layering:
Multilib layering
=================
When Clang selects multilib variants, it may find that more than one variant
matches.
It is up to the ToolChain subclass to decide what to do in this case.
There are two options permitted:
#. Use only the *last* matching multilib variant. This option exists primarily
for compatibility with the previous multilib design.
#. Use all matching variants, thereby layering them.
This decision is hard-coded per ToolChain subclass. The latter option is
preferred for ToolChain subclasses without backwards compatibility
requirements.
If the latter option is chosen then ``-isystem`` and ``-L`` options will be
generated for each matching multilib variant, in reverse order.
This means that the compiler or linker will find files in the last matching
multilib variant that has the given file.
This behaviour permits multilib variants with only a partial set of files.
This means a toolchain can be distributed with one base multilib variant
containing all system headers and includes, and more specialised multilib
variants containing only files that are different to those in the base variant.
For example, a multilib variant could be compiled with ``-fno-exceptions``.
This option doesn't affect the content of header files, nor does it affect the
C libraries. Therefore if multilib layering is supported by the ToolChain
subclass and a suitable base multilib variant is present then the
``-fno-exceptions`` multilib variant need only contain C++ libraries.
It is the responsibility of layered multilib authors to ensure that headers and
libraries in each layer are complete enough to mask any incompatibilities.
Stability
=========
Multilib via configuration file shall be considered an experimental feature
until LLVM 18, at which point ``-print-multi-flags-experimental``
should be renamed to ``-print-multi-flags``.
A toolchain can opt in to using this feature by including a ``multilib.yaml``
file in its distribution, once support for it is added in relevant ToolChain
subclasses.
Once stability is reached, flags emitted by ``-print-multi-flags``
should not be removed or changed, although new flags may be added.
Restrictions
============
Despite the name, multilib is used to locate both ``include`` and ``lib``
directories. Therefore it is important that consistent options are passed to
the Clang driver when both compiling and linking. Otherwise inconsistent
``include`` and ``lib`` directories may be used, and the results will be
undefined.
EXPERIMENTAL multilib.yaml
==========================
The below example serves as a small of a possible multilib, and documents
the available options.
For a more comprehensive example see
``clang/test/Driver/baremetal-multilib.yaml`` in the ``llvm-project`` sources.
.. code-block:: yaml
# multilib.yaml
# This format is experimental and is likely to change!
# Syntax is YAML 1.2
# This required field defines the version of the multilib.yaml format.
# Clang will emit an error if this number is greater than its current multilib
# version or if its major version differs, but will accept lesser minor
# versions.
MultilibVersion: 1.0
# The rest of this file is in two parts:
# 1. A list of multilib variants.
# 2. A list of regular expressions that may match flags generated from
# command line options, and further flags that shall be added if the
# regular expression matches.
# It is acceptable for the file to contain properties not documented here,
# and these will be ignored by Clang.
# List of multilib variants. Required.
# The ordering of items in the variants list is important if more than one
# variant can match the same set of flags. See the docs on multilib layering
# for more info.
Variants:
# Example of a multilib variant targeting Arm v6-M.
# Dir is the relative location of the directory containing the headers
# and/or libraries.
# Exactly how Dir is used is left up to the ToolChain subclass to define, but
# typically it will be joined to the sysroot.
- Dir: thumb/v6-m
# List of one or more normalized command line options, as generated by Clang
# from the command line options or from Mappings below.
# Here, if the flags are a superset of {target=thumbv6m-unknown-none-eabi}
# then this multilib variant will be considered a match.
Flags: [--target=thumbv6m-unknown-none-eabi]
# Similarly, a multilib variant targeting Arm v7-M with an FPU (floating
# point unit).
- Dir: thumb/v7-m
# Here, the flags generated by Clang must be a superset of
# {--target=thumbv7m-none-eabi, -mfpu=fpv4-sp-d16} for this multilib variant
# to be a match.
Flags: [--target=thumbv7m-none-eabi, -mfpu=fpv4-sp-d16]
# If there is no multilib available for a particular set of flags, and the
# other multilibs are not adequate fallbacks, then you can define a variant
# record with a FatalError key in place of the Dir key.
- FatalError: this multilib collection has no hard-float ABI support
Flags: [--target=thumbv7m-none-eabi, -mfloat-abi=hard]
# The second section of the file is a list of regular expressions that are
# used to map from flags generated from command line options to custom flags.
# This is optional.
# Each regular expression must match a whole flag string.
# Flags in the "Flags" list will be added if any flag generated from command
# line options matches the regular expression.
Mappings:
# Set a "--target=thumbv7m-none-eabi" flag if the regular expression matches
# any of the flags generated from the command line options.
# Match is a POSIX extended regular expression string.
- Match: --target=thumbv([7-9]|[1-9][0-9]+).*
# Flags is a list of one or more strings.
Flags: [--target=thumbv7m-none-eabi]
Design principles
=================
Stable interface
----------------
``multilib.yaml`` and ``-print-multi-flags-experimental`` are new
interfaces to Clang. In order for them to be usable over time and across LLVM
versions their interfaces should be stable.
The new multilib system will be considered experimental in LLVM 17, but in
LLVM 18 it will be stable. In particular this is important to which multilib
selection flags Clang generates from command line options. Once a flag is
generated by a released version of Clang it may be used in ``multilib.yaml``
files that exist independently of the LLVM release cycle, and therefore
ceasing to generate the flag would be a breaking change and should be
avoided.
However, an exception is the normalization of ``-march``.
``-march`` for Arm architectures contains a list of enabled and disabled
extensions and this list is likely to grow. Therefore ``-march`` flags are
unstable.
Incomplete interface
--------------------
The new multilib system does multilib selection based on only a limited set of
command line options, and limits which flags can be used for multilib
selection. This is in order to avoid committing to too large an interface.
Later LLVM versions can add support for multilib selection from more command
line options as needed.
Extensible
----------
It is likely that the configuration format will need to evolve in future to
adapt to new requirements.
Using a format like YAML that supports key-value pairs helps here as it's
trivial to add new keys alongside existing ones.
Backwards compatibility
-----------------------
New versions of Clang should be able to use configuration written for earlier
Clang versions.
To avoid behaving in a way that may be subtly incorrect, Clang should be able
to detect if the configuration is too new and emit an error.
Forwards compatibility
----------------------
As an author of a multilib configuration, it should be possible to design the
configuration in such a way that it is likely to work well with future Clang
versions. For example, if a future version of Clang is likely to add support
for newer versions of an architecture and the architecture is known to be
designed for backwards compatibility then it should be possible to express
compatibility for such architecture versions in the multilib configuration.
Not GNU spec files
------------------
The GNU spec files standard is large and complex and there's little desire to
import that complexity to LLVM. It's also heavily oriented towards processing
command line argument strings which is hard to do correctly, hence the large
amount of logic dedicated to that task in the Clang driver. While compatibility
with GNU would bring benefits, the cost in this case is deemed too high.
Avoid re-inventing feature detection in the configuration
---------------------------------------------------------
A large amount of logic in the Clang driver is dedicated to inferring which
architectural features are available based on the given command line options.
It is neither desirable nor practical to repeat such logic in each multilib
configuration. Instead the configuration should be able to benefit from the
heavy lifting Clang already does to detect features.
Low maintenance
---------------
Multilib is a relatively small feature in the scheme of things so supporting it
should accordingly take little time. Where possible this should be achieved by
implementing it in terms of existing features in the LLVM codebase.
Minimal additional API surface
------------------------------
The greater the API surface, the greater the difficulty of keeping it stable.
Where possible the additional API surface should be kept small by defining it
in relation to existing APIs. An example of this is keeping a simple
relationship between flag names and command line options where possible.
Since the command line options are part of a stable API they are unlikely
to change, and therefore the flag names get the same stability.
Low compile-time overhead
-------------------------
If the process of selecting multilib directories must be done on every
invocation of the Clang driver then it must have a negligible impact on
overall compile time.