Target Code Generation
Attention
This page is for internal and development use only.
The general process to the target code generation is as follows:
- The code's AST is processed with
RecursiveASTVisitor
s (clang/tools/sotoc/Visitors.{h,cpp}
) - The found target code is recorded in
TargetCodeFragment
(clang/tools/sotoc/TargetCode.{h,pp}
andclang/tools/sotoc/TargetCodeFragment.{h,cpp}
) - The fragments are then 'reconstituted' into valid target code.
This transforms C code like
1 2 3 4 5 6 7 8 9 10 |
|
1 2 3 4 5 6 7 8 9 10 |
|
The target code is thereby transformed into a function with the mapped variables as arguments.
Extracting Target Code from the AST
The Clang AST of a source file can be viewed using the following command:
clang -Xclang -ast-dump -fsyntax-only -fopenmp code.c
Info
Without the -fopenmp
flag Clang ignores #pragma omp
.
Target regions are separate statements in the AST and functions and variables declared with #pragma omp declare target
get a special attribute added to their AST node.
The FindTargetCodeVisitor
in Visitors.{h,cpp}
is used for that.
Warning
This does not apply the type declarations and there is no guaranty that all functions used in the target region are declared in #pragma omp declare target
.
To solve the issue we search all target regions, declared functions and variables to additional types, functions, and variables.
This is done by the DiscoverTypesInDeclVisitor
and DiscoverFunctionsInDeclVisitor
.
As types can be derived from other types (e.g., struct
s or typedef
-chains) and
functions can depend on other functions and additional types,
those dependencies have to be resolved using the DeclResolver
(in clang/tools/sotoc/DeclResolver.{h,cpp}
).
Analysing and Recording the Target Code
Before target code can be generated, additional information about variable mappings have to be collected.
-
Private variables are not mapped. For those we have to create a declaration in the target region using the
FindPrivateVariablesVisitor
. -
First-private variables (which is the default) are passed by value into the target region.
-
Variables mapped using
from
ortofrom
, are passed by pointer. Forncc
to process them, we bring the variable into scope by copying them into a local variable and afterwards back. -
Arrays are mapped as pointer. If only an array slice (e.g.,
map(to:array[10:])
) is mapped, we have to detect the bounds with theFindArraySectionVisitor
and move the returned pointer (array[10]
) in the target region. Multi-dimensional arrays have to be cast, so they can be accessed using thearray[x]
notation.
To recognize the variable form we use the TargetRegionVariable
class in clang/tools/sotoc/TargetRegionVariable.{h,cpp}
.
In addition, we have to rewrite the OpenMP pragma directive and add the appropriate clauses (OmpPragma.{h,cpp}
).
For example if a #pragma omp target parallel
is encountered, an additional parallel
region has to be added.
Some clauses are not supported by the reduced #pragma
. Those are filtered out or replaced.
Parameters to clauses are also transferred as function arguments of the target function.
Code Generation
An Object of the TargetCode
class, serves as a collection of TargetCodeFragment
s and generates the code.
When adding code fragments, TargetCode
sorts them according to their position in the original source file.
We try to generate as little code as possible ourselves and let Clangs PrettyPrinter
do most of the work, like functions and global variables.
Bug
Clangs PrettyPrinter
is designed for compiler diagnostics and can not do e.g., anonymous struct
s and enum
s.
Note
We extended the PrettyPrinter
to handle Decl
s.
We still have to generate functions for regions with mapped variables and clause parameters as arguments and local declarations to bring variables into scope as mentioned above.
Bug
Mappings with missing elements in the middle are currently not completely mapped by libomptarget/the OpenMP runtime.