[cfe-dev] Implementing OpenMP function variants
Doerfert, Johannes via cfe-dev
cfe-dev at lists.llvm.org
Wed Dec 18 08:16:21 PST 2019
On 12/18, John McCall wrote:
> On 13 Dec 2019, at 10:08, Finkel, Hal J. wrote:
> > Richard, John, et al.,
> >
> > Let me top-post here quickly to add that this question comes directly
> > from a disagreement about the application of Clang's design principle of
> > keeping the AST faithful to the source code (and I suspect that such a
> > question may be of wider interest to the community).
> >
> > Johannes has summarized the OpenMP language semantics below (thanks,
> > Johannes!).
> >
> > The relevant review thread is here: https://reviews.llvm.org/D71241
> >
> > Alexey's position is that, because the source code contains what appears
> > to be a call to the base() function, the AST should always reflect that
> > fact by having getCallee() return a reference to base(), and we should
> > lower the call to the selected variant using logic in CodeGen. Some
> > other member function should be used by AST-level tools to retrieve the
> > actually-called variant. This has the benefit that the primary AST
> > representation is independent of the compilation target and other
> > relevant OpenMP context.
> >
> > My position is that, like other cases where we perform overload
> > resolution and specializaton selection (including host/device overloads
> > in CUDA), we should resolve the variant selected in Sema, and
> > getCallee() should return a reference to the function that will actually
> > be called (even if that function has a different name from the name used
> > syntactically to form the call expression). This will ensure that
> > static-analysis tools see the correct call-site <-> callee relationship.
> > We should, I think, also keep a reference to the original OpenMP base
> > function in the AST, but some other member function should be used to
> > retrieve it.
> >
> > We say that we keep Clang's AST faithful to the source code, but how to
> > best apply that philosophy in this case is now under debate.
>
> Is it always immediately decidable when parsing a reference to a function
> which variant should be used, or is it sometimes dynamic or at least
> delayed?
For now, which means OpenMP 5.0 and TR8, it is conceptually immediately
decidable. It only depends on compilation parameters, e.g., the target
triple, and the lexical context.
> How is this expected to interact with C++ overloading? Can you
> independently declare variants of each overload?
While the standard is not specific on this, I don't see why not.
I tried to summarize what the standard says yesterday [0]:
OpenMP basically says, if you have a call to a (base)function* that has
variants with contexts that match at the call site, call the variant
with the highest score. The variants are specified by a variant-func-id,
which is a base language identifier or C++ template-id. For C++, the
variant declaration is identified by *performing the base language
lookup rules on the variant-func-id with arguments that correspond to
the base function argument types*.
* However you figured out that the base function is the one called in
the first place.
[0] https://reviews.llvm.org/D71241#1788003
> How does this interact with nested scopes? If all variants are unacceptable
> for the use context, is it like the declaration just doesn’t exist, and so
> lookup continues to outer scopes? Or is this impossible because there
> always has to be a non-variant declaration in the current scope?
The latter. As mentioned above, you first find the "normal" call target
and then apply the variant logic from there.
> My immediate intuition is that, assuming the semantics are always static and
> that there’s always a non-variant function, this should be handled as a sort
> of second level of overload resolution. The variant declarations should be
> considered more-or-less independent functions; they are not redeclarations
> of the original. 5.1-type variants should be hidden from lookup, so that
> only the original function is found. When we resolve a use of a declaration
> with variants we then pick the appropriate variant and treat the declaration
> as if it was using that originally. The fact that we resolved it via a
> variant should be recorded in the FoundDecl, which would now have an
> additional possible state: we could have looked through a using declaration,
> and we could have resolved a variant. This shouldn’t be a problem for
> FoundDecl.
I think your assumptions are met.
Is there a good reason to make 5.1-type variants different from
multi-versions (as we have them)? They do not depend on the lexical call
context but only on the compilation parameters.
> >
> > Thanks again,
> >
> > Hal
> >
> > On 12/12/19 11:28 PM, Doerfert, Johannes wrote:
> > > Background:
> > >
> > > The prototype for OpenMPs 5.1 feature `begin/end declare variant`
> > > caused
> > > a discussion that seems to be stuck. I'll briefly outline the
> > > semantics
> > > of the OpenMP 5.0 `declare variant` and the `begin/end declare
> > > variant`
> > > here before I present the two competing designs on a very high
> > > level. If
> > > there is a need for more information, a summary of what was said, or
> > > anything else, please feel free to ask for it.
> > >
> > > ---
> > >
> > > OpenMP 5.0 `declare variant` allows you to define a variant of a base
> > > function that is called instead of the base function if the context
> > > specified with the variant matches at a call site.
> > >
> > > As an example, the call to base below is replaced by a call to
> > > special
> > > if the code is compiled with a compiler that identifies as "llvm".
> > >
> > > ```
> > > void special();
> > >
> > > #pragma omp declare variant(special)
> > > match(implementation={vendor(llvm)})
> > > void base();
> > >
> > > void test() {
> > > base();
> > > }
> > > ```
> > >
> > > Context selectors for the match clause can be specific to the target,
> > > the implementation, or the context in which the call is executed,
> > > e.g.,
> > > syntactic inside a #omp parallel.
> > >
> > > ---
> > >
> > > OpenMP 5.1 `begin/end declare variant` is an extension of the 5.0
> > > version that (1) makes the user specific mangling obsolete, and (2)
> > > guards the code in between the begin/end if the context selector does
> > > not match at compile time. The use case can be simplified to the code
> > > below in which we want the `sin` call to resolve to the NVIDIA
> > > specific
> > > version if we are compiling for nvptx and to the one in math.h
> > > otherwise. Note that the variant in the begin/end range has the same
> > > name as the base it overloads.
> > >
> > > ```
> > > #include "math.h"
> > >
> > > #pragma omp begin declare variant match(device={arch(nvptx)})
> > > double sin(double s) { return __nv_sin(s); }
> > > #pragma omp end declare variant
> > >
> > > double calc(double s) {
> > > return sin(sin(sin(s)));
> > > }
> > > ```
> > >
> > > ---
> > >
> > > Now that we are all OpenMP experts, we need to decide how/where to
> > > implement this. Oversimplified, these are the options being
> > > discussed:
> > >
> > > Proposal 1, CodeGen:
> > > Introduce aliases that redirect from one function to the other or
> > > replace the callee in the CodeGen.
> > >
> > > Proposal 2), SemaOverload:
> > > Use the multi-version overloading for the 5.1 feature and
> > > introduce
> > > another level of overload resolution for the 5.0 feature.
> > >
> > > ---
> > >
> > > Thanks in advance for your time and input,
> > > Johannes
> >
> > --
> > Hal Finkel
> > Lead, Compiler Technology and Programming Languages
> > Leadership Computing Facility
> > Argonne National Laboratory
--
Johannes Doerfert
Researcher
Argonne National Laboratory
Lemont, IL 60439, USA
jdoerfert at anl.gov
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 228 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20191218/5fce5c7a/attachment.sig>
More information about the cfe-dev
mailing list