[Mesa-dev] [PATCH 1/2] gallivm: Allow drivers and state trackers to initialize gallivm LLVM targets
Tom Stellard
tom at stellard.net
Fri Sep 25 06:55:50 PDT 2015
On Thu, Sep 24, 2015 at 10:48:32PM +0100, Emil Velikov wrote:
> Hi Tom,
>
> On 24 September 2015 at 17:31, Tom Stellard <thomas.stellard at amd.com> wrote:
> > Drivers and state trackers that use LLVM for generating code, must
> > register the targets they use with LLVM's global TargetRegistry.
> > The TargetRegistry is not thread-safe, so all targets must be added
> > to the registry before it can be queried for target information.
> >
> > When drivers and state trackers initialize their own targets, they need
> > a way to force gallivm to initialize its targets at the same time.
> > Otherwise, there can be a race condition in some multi-threaded
> > applications (e.g. glx-multihreaded-shader-compile in piglit),
> > when one thread creates a context for a driver that uses LLVM (e.g.
> > radeonsi) and another thread creates a gallivm context (glxContextCreate
> > does this).
> >
> > The race happens when the driver thread initializes its LLVM targets and
> > then starts using the registry before the gallivm thread has a chance to
> > register its targets.
> >
> > This patch allows users to force gallivm to register its targets by
> > adding two new functions: gallivm_llvm_init_begin() which locks a mutex
> > guarding the TargetRegistry and initializes the gallivm targets, and
> > gallivm_llvm_init_end() which unlocks the mutex.
> >
> > Drivers and state trackers should use this sequence to correctly
> > initialize the LLVM targets they use:
> >
> > gallivm_init_llvm_begin();
> > LLVMInitializeFooTarget();
> > gallivm_init_llvm_end();
> >
> > CC: "10.6 11.0" <mesa-stable at lists.freedesktop.org>
> > ---
> > src/gallium/auxiliary/gallivm/lp_bld_misc.cpp | 68 ++++++++++++++++++++++++---
> > src/gallium/auxiliary/gallivm/lp_bld_misc.h | 5 ++
> > 2 files changed, 66 insertions(+), 7 deletions(-)
> >
> > diff --git a/src/gallium/auxiliary/gallivm/lp_bld_misc.cpp b/src/gallium/auxiliary/gallivm/lp_bld_misc.cpp
> > index 5e25819..8bf833e 100644
> > --- a/src/gallium/auxiliary/gallivm/lp_bld_misc.cpp
> > +++ b/src/gallium/auxiliary/gallivm/lp_bld_misc.cpp
> > @@ -81,6 +81,7 @@
> > # pragma pop_macro("DEBUG")
> > #endif
> >
> > +#include "os/os_thread.h"
> > #include "pipe/p_config.h"
> > #include "util/u_debug.h"
> > #include "util/u_cpu_detect.h"
> > @@ -101,6 +102,64 @@ public:
> >
> > static LLVMEnsureMultithreaded lLVMEnsureMultithreaded;
> >
> > +struct LLVMRegistryLock {
> > +private:
> > + pipe_mutex mutex;
> > +
> > +public:
> > + LLVMRegistryLock()
> > + {
> > + pipe_mutex_init(mutex);
> > + }
> > +
> > + void lock()
> > + {
> > + pipe_mutex_lock(mutex);
> > + }
> > +
> > + void unlock()
> > + {
> > + pipe_mutex_unlock(mutex);
> > + }
> > +};
> > +
> > +static LLVMRegistryLock gallivm_llvm_registry_lock;
> > +
> > +}
> > +
> > +/**
> > + * The llvm target registry is not thread-safe, so drivers and state-trackers
> > + * that want to initialize targets should use the gallivm_init_llvm_begin() and
> > + * gallivm_init_llvm_end() functions to safely initialize targets. For example:
> > + *
> > + * gallivm_init_llvm_begin();
> > + * LLVMInitializeFooTarget();
> > + * gallivm_init_llvm_end();
> > + *
> > + * LLVM targets should be initialized before the driver tries to access the
> > + * registry.
> > + */
> > +extern "C" void
> > +gallivm_init_llvm_begin(void)
> > +{
> > + static int init = 0;
> > + gallivm_llvm_registry_lock.lock();
> > + if (!init) {
> > + // If we have a native target, initialize it to ensure it is linked in and
> > + // usable by the JIT.
> > + llvm::InitializeNativeTarget();
> > +
> > + llvm::InitializeNativeTargetAsmPrinter();
> > +
> > + llvm::InitializeNativeTargetDisassembler();
> > + init = 1;
> > + }
> > +}
> > +
> > +extern "C" void
> > +gallivm_init_llvm_end(void)
> > +{
> > + gallivm_llvm_registry_lock.unlock();
> > }
> >
> > extern "C" void
> > @@ -115,13 +174,8 @@ lp_set_target_options(void)
> > llvm::DisablePrettyStackTrace = true;
> > #endif
> >
> > - // If we have a native target, initialize it to ensure it is linked in and
> > - // usable by the JIT.
> > - llvm::InitializeNativeTarget();
> > -
> > - llvm::InitializeNativeTargetAsmPrinter();
> > -
> > - llvm::InitializeNativeTargetDisassembler();
> > + gallivm_init_llvm_begin();
> > + gallivm_init_llvm_end();
> > }
> >
> What is the advantage of these wrappers instead of the (more obvious imho)
>
> pipe_mutex_init(mutex); // wrap in call_once() ?
I think this is a C11/C++11 feature. I forget whether or not these are
allowed.
> pipe_mutex_lock(mutex);
>
> llvm::InitializeNativeTarget...
>
> pipe_mutex_unlock(mutex);
>
> By adding these you effectively have extra calls to
> llvm::InitializeNativeTarget* for radeon. The second patch does not
> mention anything on the topic, is that intentional ?
>
It is intentional to have radeon call llvm::InitializeNativeTarget*.
radeon needs to preemptively initialize gallivm's targets to avoid the
race condition.
-Tom
> Cheers,
> Emil
> _______________________________________________
> mesa-dev mailing list
> mesa-dev at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/mesa-dev
More information about the mesa-dev
mailing list